Skip to content

Commit 96fa7ce

Browse files
authored
Setup command - added support for custom configuration and pnpm (#1326)
1 parent 506d2fe commit 96fa7ce

File tree

13 files changed

+450
-220
lines changed

13 files changed

+450
-220
lines changed

.github/workflows/test.yml

+5
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@ jobs:
3838
with:
3939
dotnet-version: '6.x'
4040

41+
- name: Install pnpm
42+
uses: pnpm/action-setup@v4
43+
with:
44+
version: 9
45+
4146
# Install Mono on Ubuntu to run nuget.exe (due to this issue on Ubuntu 24 that hasn't been fixed yet - https://github.com/NuGet/setup-nuget/issues/168)
4247
- name: Install Mono on Ubuntu
4348
if: matrix.os == 'ubuntu'

artifactory/commands/dotnet/dotnetcommand.go

+53-7
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"net/url"
1616
"os"
1717
"path"
18+
"path/filepath"
1819
"strings"
1920
)
2021

@@ -168,7 +169,7 @@ func changeWorkingDir(newWorkingDir string) (string, error) {
168169
}
169170

170171
// Runs nuget/dotnet source add command
171-
func AddSourceToNugetConfig(cmdType dotnet.ToolchainType, sourceUrl, user, password string) error {
172+
func AddSourceToNugetConfig(cmdType dotnet.ToolchainType, sourceUrl, user, password, customConfigPath string) error {
172173
cmd, err := dotnet.CreateDotnetAddSourceCmd(cmdType, sourceUrl)
173174
if err != nil {
174175
return err
@@ -178,15 +179,21 @@ func AddSourceToNugetConfig(cmdType dotnet.ToolchainType, sourceUrl, user, passw
178179
cmd.CommandFlags = append(cmd.CommandFlags, flagPrefix+"name", SourceName)
179180
cmd.CommandFlags = append(cmd.CommandFlags, flagPrefix+"username", user)
180181
cmd.CommandFlags = append(cmd.CommandFlags, flagPrefix+"password", password)
181-
stdOut, errorOut, _, err := frogio.RunCmdWithOutputParser(cmd, false)
182+
183+
if customConfigPath != "" {
184+
addConfigFileFlag(cmd, customConfigPath)
185+
}
186+
187+
_, _, _, err = frogio.RunCmdWithOutputParser(cmd, false)
182188
if err != nil {
183-
return fmt.Errorf("failed to add source: %w\n%s", err, strings.TrimSpace(stdOut+errorOut))
189+
return fmt.Errorf("failed to add source: %w", err)
184190
}
185191
return nil
186192
}
187193

188-
// Runs nuget/dotnet source remove command
189-
func RemoveSourceFromNugetConfigIfExists(cmdType dotnet.ToolchainType) error {
194+
// RemoveSourceFromNugetConfigIfExists runs the nuget/dotnet source remove command.
195+
// Removes the source if it exists in the configuration.
196+
func RemoveSourceFromNugetConfigIfExists(cmdType dotnet.ToolchainType, customConfigPath string) error {
190197
cmd, err := dotnet.NewToolchainCmd(cmdType)
191198
if err != nil {
192199
return err
@@ -197,16 +204,55 @@ func RemoveSourceFromNugetConfigIfExists(cmdType dotnet.ToolchainType) error {
197204
cmd.Command = append(cmd.Command, "sources", "remove")
198205
cmd.CommandFlags = append(cmd.CommandFlags, "-name", SourceName)
199206
}
207+
208+
if customConfigPath != "" {
209+
addConfigFileFlag(cmd, customConfigPath)
210+
}
211+
200212
stdOut, stdErr, _, err := frogio.RunCmdWithOutputParser(cmd, false)
201213
if err != nil {
202-
if strings.Contains(stdOut+stdErr, "Unable to find") {
214+
if strings.Contains(stdOut+stdErr, "Unable to find") || strings.Contains(stdOut+stdErr, "does not exist") {
203215
return nil
204216
}
205-
return fmt.Errorf("failed to remove source: %w\n%s", err, strings.TrimSpace(stdOut+stdErr))
217+
return errorutils.CheckErrorf("failed to remove source: %s", err.Error())
206218
}
207219
return nil
208220
}
209221

222+
// GetConfigPathFromEnvIfProvided returns the path to the custom NuGet.Config file if it was provided by the user.
223+
func GetConfigPathFromEnvIfProvided(cmdType dotnet.ToolchainType) string {
224+
if cmdType == dotnet.DotnetCore {
225+
if customDotnetDir := os.Getenv("DOTNET_CLI_HOME"); customDotnetDir != "" {
226+
return filepath.Join(customDotnetDir, "NuGet.Config")
227+
}
228+
}
229+
return os.Getenv("NUGET_CONFIG_FILE")
230+
}
231+
232+
// CreateConfigFileIfNeeded creates a new config file if it does not exist.
233+
func CreateConfigFileIfNeeded(customConfigPath string) error {
234+
// Ensure the file exists
235+
exists, err := fileutils.IsFileExists(customConfigPath, false)
236+
if err != nil || exists {
237+
return err
238+
}
239+
// If the file does not exist, create it
240+
if err = os.MkdirAll(filepath.Dir(customConfigPath), 0755); err != nil {
241+
return err
242+
}
243+
// Write the default config content to the file
244+
return os.WriteFile(customConfigPath, []byte("<configuration></configuration>"), 0644)
245+
}
246+
247+
func addConfigFileFlag(cmd *dotnet.Cmd, configFilePath string) {
248+
// Add the config file flag if needed.
249+
if cmd.GetToolchain() == dotnet.DotnetCore {
250+
cmd.CommandFlags = append(cmd.CommandFlags, "--configfile", configFilePath)
251+
} else {
252+
cmd.CommandFlags = append(cmd.CommandFlags, "-ConfigFile", configFilePath)
253+
}
254+
}
255+
210256
// Checks if the user provided input such as -configfile flag or -Source flag.
211257
// If those flags were provided, NuGet will use the provided configs (default config file or the one with -configfile)
212258
// If neither provided, we are initializing our own config.

artifactory/commands/dotnet/dotnetcommand_test.go

+122
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package dotnet
22

33
import (
4+
"github.com/jfrog/jfrog-cli-core/v2/utils/ioutils"
45
"os"
56
"path/filepath"
67
"reflect"
@@ -223,3 +224,124 @@ func createNewDotnetModule(t *testing.T, tmpDir string) *build.DotnetModule {
223224
assert.NoError(t, err)
224225
return module
225226
}
227+
228+
func TestGetConfigPathFromEnvIfProvided(t *testing.T) {
229+
testCases := []struct {
230+
name string
231+
mockEnv map[string]string
232+
cmdType dotnet.ToolchainType
233+
expectedPath string
234+
}{
235+
{
236+
name: "DotnetCore with DOTNET_CLI_HOME",
237+
mockEnv: map[string]string{
238+
"DOTNET_CLI_HOME": "/custom/dotnet",
239+
},
240+
cmdType: dotnet.DotnetCore,
241+
expectedPath: "/custom/dotnet/NuGet.Config",
242+
},
243+
{
244+
name: "NuGet with NUGET_CONFIG_FILE",
245+
mockEnv: map[string]string{
246+
"NUGET_CONFIG_FILE": "/custom/nuget.config",
247+
},
248+
cmdType: dotnet.Nuget,
249+
expectedPath: "/custom/nuget.config",
250+
},
251+
{
252+
name: "No env variable",
253+
mockEnv: map[string]string{},
254+
cmdType: dotnet.Nuget,
255+
expectedPath: "",
256+
},
257+
}
258+
259+
// Test the function with different environment variable settings
260+
for _, testCase := range testCases {
261+
t.Run(testCase.name, func(t *testing.T) {
262+
t.Setenv("DOTNET_CLI_HOME", testCase.mockEnv["DOTNET_CLI_HOME"])
263+
264+
// Set other environment variables if needed
265+
if testCase.mockEnv["NUGET_CONFIG_FILE"] != "" {
266+
t.Setenv("NUGET_CONFIG_FILE", testCase.mockEnv["NUGET_CONFIG_FILE"])
267+
}
268+
result := GetConfigPathFromEnvIfProvided(testCase.cmdType)
269+
assert.Equal(t, testCase.expectedPath, ioutils.WinToUnixPathSeparator(result))
270+
})
271+
}
272+
}
273+
274+
func TestCreateConfigFileIfNeeded(t *testing.T) {
275+
testCases := []struct {
276+
name string
277+
configPath string
278+
fileExists bool
279+
expectedError error
280+
}{
281+
{
282+
name: "File does not exist, create file with default content",
283+
configPath: "/custom/path/NuGet.Config",
284+
fileExists: false,
285+
},
286+
{
287+
name: "File exists, no changes",
288+
configPath: "/custom/path/NuGet.Config",
289+
fileExists: true,
290+
},
291+
}
292+
293+
// Setup for testing file existence and creation
294+
for _, testCase := range testCases {
295+
t.Run(testCase.name, func(t *testing.T) {
296+
configPath := filepath.Join(t.TempDir(), testCase.configPath)
297+
if testCase.fileExists {
298+
assert.NoError(t, os.MkdirAll(filepath.Dir(configPath), 0777))
299+
assert.NoError(t, os.WriteFile(configPath, []byte{}, 0644))
300+
}
301+
err := CreateConfigFileIfNeeded(configPath)
302+
assert.NoError(t, err)
303+
304+
if !testCase.fileExists {
305+
// Read the content of the file
306+
content, err := os.ReadFile(configPath)
307+
assert.NoError(t, err)
308+
309+
// Assert the content is the default config content
310+
assert.Equal(t, "<configuration></configuration>", string(content))
311+
}
312+
})
313+
}
314+
}
315+
316+
func TestAddConfigFileFlag(t *testing.T) {
317+
testCases := []struct {
318+
name string
319+
toolchainType dotnet.ToolchainType
320+
expectedFlags []string
321+
}{
322+
{
323+
name: "DotnetCore toolchain",
324+
toolchainType: dotnet.DotnetCore,
325+
expectedFlags: []string{"--configfile", "/path/to/NuGet.Config"},
326+
},
327+
{
328+
name: "NuGet toolchain",
329+
toolchainType: dotnet.Nuget,
330+
expectedFlags: []string{"-ConfigFile", "/path/to/NuGet.Config"},
331+
},
332+
}
333+
334+
for _, testCase := range testCases {
335+
t.Run(testCase.name, func(t *testing.T) {
336+
// Create a mock command object
337+
cmd, err := dotnet.NewToolchainCmd(testCase.toolchainType)
338+
assert.NoError(t, err)
339+
340+
// Call the function
341+
addConfigFileFlag(cmd, "/path/to/NuGet.Config")
342+
343+
// Assert that the flags are as expected
344+
assert.Equal(t, testCase.expectedFlags, cmd.CommandFlags)
345+
})
346+
}
347+
}

artifactory/commands/golang/go_test.go

+6-6
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ func TestGetArtifactoryApiUrl(t *testing.T) {
128128
}
129129

130130
func TestGoProxyUrlParams_BuildUrl(t *testing.T) {
131-
tests := []struct {
131+
testCases := []struct {
132132
name string
133133
RepoName string
134134
Direct bool
@@ -153,15 +153,15 @@ func TestGoProxyUrlParams_BuildUrl(t *testing.T) {
153153
ExpectedUrl: "https://test/prefix/api/go/go",
154154
},
155155
}
156-
for _, tt := range tests {
157-
t.Run(tt.name, func(t *testing.T) {
156+
for _, testCase := range testCases {
157+
t.Run(testCase.name, func(t *testing.T) {
158158
remoteUrl, err := url.Parse("https://test")
159159
require.NoError(t, err)
160160
gdu := &GoProxyUrlParams{
161-
Direct: tt.Direct,
162-
EndpointPrefix: tt.EndpointPrefix,
161+
Direct: testCase.Direct,
162+
EndpointPrefix: testCase.EndpointPrefix,
163163
}
164-
assert.Equalf(t, tt.ExpectedUrl, gdu.BuildUrl(remoteUrl, tt.RepoName), "BuildUrl(%v, %v)", remoteUrl, tt.RepoName)
164+
assert.Equalf(t, testCase.ExpectedUrl, gdu.BuildUrl(remoteUrl, testCase.RepoName), "BuildUrl(%v, %v)", remoteUrl, testCase.RepoName)
165165
})
166166
}
167167
}

artifactory/commands/python/pip.go

+12
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
package python
22

33
import (
4+
"fmt"
45
"io"
6+
"os"
57
"os/exec"
8+
"path/filepath"
69

710
"github.com/jfrog/build-info-go/entities"
811
"github.com/jfrog/build-info-go/utils/pythonutils"
@@ -46,6 +49,15 @@ func (pc *PipCommand) SetCommandName(commandName string) *PipCommand {
4649
return pc
4750
}
4851

52+
func CreatePipConfigManually(customPipConfigPath, repoWithCredsUrl string) error {
53+
if err := os.MkdirAll(filepath.Dir(customPipConfigPath), os.ModePerm); err != nil {
54+
return err
55+
}
56+
// Write the configuration to pip.conf.
57+
configContent := fmt.Sprintf("[global]\nindex-url = %s\n", repoWithCredsUrl)
58+
return os.WriteFile(customPipConfigPath, []byte(configContent), 0644)
59+
}
60+
4961
func (pc *PipCommand) CommandName() string {
5062
return "rt_python_pip"
5163
}
+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package python
2+
3+
import (
4+
"github.com/stretchr/testify/assert"
5+
"os"
6+
"path/filepath"
7+
"testing"
8+
)
9+
10+
func TestCreatePipConfigManually(t *testing.T) {
11+
// Define the test parameters
12+
customConfigPath := filepath.Join(t.TempDir(), "/tmp/test/pip.conf")
13+
// #nosec G101 -- False positive - no hardcoded credentials.
14+
repoWithCredsUrl := "https://example.com/simple/"
15+
expectedContent := "[global]\nindex-url = https://example.com/simple/\n"
16+
17+
// Call the function under test
18+
err := CreatePipConfigManually(customConfigPath, repoWithCredsUrl)
19+
20+
// Assert no error occurred
21+
assert.NoError(t, err)
22+
23+
// Verify the file exists and has the correct content
24+
fileContent, err := os.ReadFile(customConfigPath)
25+
assert.NoError(t, err)
26+
assert.Equal(t, expectedContent, string(fileContent))
27+
}

artifactory/commands/python/python_test.go

+5-5
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import (
1010
)
1111

1212
func TestGetPypiRepoUrlWithCredentials(t *testing.T) {
13-
tests := []struct {
13+
testCases := []struct {
1414
name string
1515
curationCmd bool
1616
}{
@@ -24,11 +24,11 @@ func TestGetPypiRepoUrlWithCredentials(t *testing.T) {
2424
},
2525
}
2626

27-
for _, tt := range tests {
28-
t.Run(tt.name, func(t *testing.T) {
29-
url, _, _, err := GetPypiRepoUrlWithCredentials(&config.ServerDetails{}, "test", tt.curationCmd)
27+
for _, testCase := range testCases {
28+
t.Run(testCase.name, func(t *testing.T) {
29+
url, _, _, err := GetPypiRepoUrlWithCredentials(&config.ServerDetails{}, "test", testCase.curationCmd)
3030
require.NoError(t, err)
31-
assert.Equal(t, tt.curationCmd, strings.Contains(url.Path, coreutils.CurationPassThroughApi))
31+
assert.Equal(t, testCase.curationCmd, strings.Contains(url.Path, coreutils.CurationPassThroughApi))
3232
})
3333
}
3434
}

0 commit comments

Comments
 (0)