From b75c5ac6090c71e4d259698852e907886233149d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 27 Apr 2026 10:04:22 +0200 Subject: [PATCH 1/2] JGC-478 - Add --format flag support to rt commands --- artifactory/cli.go | 238 +++++++++++++-- artifactory/cli_test.go | 509 ++++++++++++++++++++++++++++++++ go.mod | 2 + go.sum | 6 +- utils/cliutils/commandsflags.go | 50 +++- 5 files changed, 779 insertions(+), 26 deletions(-) diff --git a/artifactory/cli.go b/artifactory/cli.go index 75076fab6..a52fbd8d4 100644 --- a/artifactory/cli.go +++ b/artifactory/cli.go @@ -1,10 +1,13 @@ package artifactory import ( + "encoding/json" "errors" "fmt" + "io" "os" "strings" + "text/tabwriter" "github.com/jfrog/jfrog-cli-artifactory/artifactory/commands/python" "github.com/jfrog/jfrog-cli/docs/artifactory/cocoapodsconfig" @@ -27,6 +30,7 @@ import ( "github.com/jfrog/jfrog-cli-core/v2/artifactory/utils" commonCliUtils "github.com/jfrog/jfrog-cli-core/v2/common/cliutils" "github.com/jfrog/jfrog-cli-core/v2/common/commands" + coreformat "github.com/jfrog/jfrog-cli-core/v2/common/format" "github.com/jfrog/jfrog-cli-core/v2/common/project" "github.com/jfrog/jfrog-cli-core/v2/common/spec" corecommon "github.com/jfrog/jfrog-cli-core/v2/docs/common" @@ -397,7 +401,7 @@ func GetCommands() []cli.Command { { Name: "permission-target-update", Aliases: []string{"ptu"}, - Flags: cliutils.GetCommandFlags(cliutils.TemplateConsumer), + Flags: cliutils.GetCommandFlags(cliutils.PermissionTargetUpdate), Usage: permissiontargetupdate.GetDescription(), HelpName: corecommon.CreateUsage("rt ptu", permissiontargetupdate.GetDescription(), permissiontargetupdate.Usage), UsageText: permissiontargetupdate.GetArguments(), @@ -813,15 +817,28 @@ func permissionTargetUpdateCmd(c *cli.Context) error { return cliutils.WrongNumberOfArgumentsHandler(c) } + if c.IsSet(cliutils.Format) { + if _, fmtErr := coreformat.ParseOutputFormat(c.String(cliutils.Format), []coreformat.OutputFormat{coreformat.Json}); fmtErr != nil { + return fmtErr + } + } + rtDetails, err := cliutils.CreateArtifactoryDetailsByFlags(c) if err != nil { return err } // Run command. - permissionTargetUpdateCmd := permissiontarget.NewPermissionTargetUpdateCommand() - permissionTargetUpdateCmd.SetTemplatePath(c.Args().Get(0)).SetServerDetails(rtDetails).SetVars(c.String("vars")) - return commands.Exec(permissionTargetUpdateCmd) + permissionTargetUpdateCommand := permissiontarget.NewPermissionTargetUpdateCommand() + permissionTargetUpdateCommand.SetTemplatePath(c.Args().Get(0)).SetServerDetails(rtDetails).SetVars(c.String("vars")) + if err = commands.Exec(permissionTargetUpdateCommand); err != nil { + return err + } + if c.IsSet(cliutils.Format) { + // Client layer discards body; nil + 200 produces {"status_code":200,"message":"OK"} + cliutils.FormatHTTPResponseJSON(nil, 200) + } + return nil } func permissionTargetDeleteCmd(c *cli.Context) error { @@ -871,12 +888,18 @@ func usersCreateCmd(c *cli.Context) error { return cliutils.WrongNumberOfArgumentsHandler(c) } + if c.IsSet(cliutils.Format) { + if _, fmtErr := coreformat.ParseOutputFormat(c.String(cliutils.Format), []coreformat.OutputFormat{coreformat.Json}); fmtErr != nil { + return fmtErr + } + } + rtDetails, err := cliutils.CreateArtifactoryDetailsByFlags(c) if err != nil { return err } - usersCreateCmd := usersmanagement.NewUsersCreateCommand() + usersCreateCommand := usersmanagement.NewUsersCreateCommand() csvFilePath := c.String("csv") if csvFilePath == "" { return cliutils.PrintHelpAndReturnError("missing --csv ", c) @@ -890,8 +913,15 @@ func usersCreateCmd(c *cli.Context) error { } usersGroups := parseUsersGroupsFlag(c) // Run command. - usersCreateCmd.SetServerDetails(rtDetails).SetUsers(usersList).SetUsersGroups(usersGroups).SetReplaceIfExists(c.Bool(cliutils.Replace)) - return commands.Exec(usersCreateCmd) + usersCreateCommand.SetServerDetails(rtDetails).SetUsers(usersList).SetUsersGroups(usersGroups).SetReplaceIfExists(c.Bool(cliutils.Replace)) + if err = commands.Exec(usersCreateCommand); err != nil { + return err + } + if c.IsSet(cliutils.Format) { + // Client layer discards body; nil + 200 produces {"status_code":200,"message":"OK"} + cliutils.FormatHTTPResponseJSON(nil, 200) + } + return nil } func parseUsersGroupsFlag(c *cli.Context) *[]string { @@ -1053,6 +1083,12 @@ func transferConfigCmd(c *cli.Context) error { return cliutils.WrongNumberOfArgumentsHandler(c) } + if c.IsSet(cliutils.Format) { + if _, fmtErr := coreformat.ParseOutputFormat(c.String(cliutils.Format), []coreformat.OutputFormat{coreformat.Json}); fmtErr != nil { + return fmtErr + } + } + // Get source Artifactory server sourceServerDetails, err := coreConfig.GetSpecificConfig(c.Args()[0], false, true) if err != nil { @@ -1066,15 +1102,22 @@ func transferConfigCmd(c *cli.Context) error { } // Run transfer config command - transferConfigCmd := transferconfigcore.NewTransferConfigCommand(sourceServerDetails, targetServerDetails). + transferConfigCommand := transferconfigcore.NewTransferConfigCommand(sourceServerDetails, targetServerDetails). SetForce(c.Bool(cliutils.Force)).SetVerbose(c.Bool(cliutils.Verbose)).SetPreChecks(c.Bool(cliutils.PreChecks)). SetSourceWorkingDir(c.String(cliutils.SourceWorkingDir)). SetTargetWorkingDir(c.String(cliutils.TargetWorkingDir)) includeReposPatterns, excludeReposPatterns := getTransferIncludeExcludeRepos(c) - transferConfigCmd.SetIncludeReposPatterns(includeReposPatterns) - transferConfigCmd.SetExcludeReposPatterns(excludeReposPatterns) + transferConfigCommand.SetIncludeReposPatterns(includeReposPatterns) + transferConfigCommand.SetExcludeReposPatterns(excludeReposPatterns) - return transferConfigCmd.Run() + if err = transferConfigCommand.Run(); err != nil { + return err + } + if c.IsSet(cliutils.Format) { + // Client layer discards body; nil + 200 produces {"status_code":200,"message":"OK"} + cliutils.FormatHTTPResponseJSON(nil, 200) + } + return nil } func transferConfigMergeCmd(c *cli.Context) error { @@ -1094,14 +1137,99 @@ func transferConfigMergeCmd(c *cli.Context) error { return err } - // Run transfer config command + // Run transfer config merge command includeReposPatterns, excludeReposPatterns := getTransferIncludeExcludeRepos(c) includeProjectsPatterns, excludeProjectsPatterns := getTransferIncludeExcludeProjects(c) - transferConfigMergeCmd := transferconfigmergecore.NewTransferConfigMergeCommand(sourceServerDetails, targetServerDetails). + cmd := transferconfigmergecore.NewTransferConfigMergeCommand(sourceServerDetails, targetServerDetails). SetIncludeProjectsPatterns(includeProjectsPatterns).SetExcludeProjectsPatterns(excludeProjectsPatterns) - transferConfigMergeCmd.SetIncludeReposPatterns(includeReposPatterns).SetExcludeReposPatterns(excludeReposPatterns) - _, err = transferConfigMergeCmd.Run() - return err + cmd.SetIncludeReposPatterns(includeReposPatterns).SetExcludeReposPatterns(excludeReposPatterns) + csvPath, runErr := cmd.Run() + + outputFormat, fmtErr := getTransferConfigMergeOutputFormat(c) + if fmtErr != nil { + return fmtErr + } + if outputFormat == coreformat.None { + return runErr + } + return printTransferConfigMergeResponse(csvPath, outputFormat, os.Stdout, runErr) +} + +// transferConfigMergeResult is the structured result of a transfer-config-merge operation. +type transferConfigMergeResult struct { + Status string `json:"status"` + ConflictsReportPath string `json:"conflicts_report_path,omitempty"` + Message string `json:"message"` +} + +// getTransferConfigMergeOutputFormat reads the --format flag and returns the resolved output format. +// When the flag is not set the function returns coreformat.None, preserving the +// previous behaviour (log output only). +func getTransferConfigMergeOutputFormat(c *cli.Context) (coreformat.OutputFormat, error) { + if !c.IsSet(cliutils.Format) { + return coreformat.None, nil + } + return coreformat.ParseOutputFormat(c.String(cliutils.Format), []coreformat.OutputFormat{coreformat.Json, coreformat.Table}) +} + +// printTransferConfigMergeResponse renders the transfer-config-merge result in the requested output format. +func printTransferConfigMergeResponse(csvPath string, outputFormat coreformat.OutputFormat, w io.Writer, originalErr error) error { + switch outputFormat { + case coreformat.Json: + return printTransferConfigMergeJSON(csvPath, originalErr) + case coreformat.Table: + return printTransferConfigMergeTable(csvPath, w, originalErr) + default: + return errorutils.CheckErrorf("unsupported format '%s' for rt transfer-config-merge. Acceptable values are: json, table", outputFormat) + } +} + +// printTransferConfigMergeJSON emits a JSON summary of the transfer-config-merge operation to stdout via log.Output. +func printTransferConfigMergeJSON(csvPath string, originalErr error) error { + result := buildTransferConfigMergeResult(csvPath, originalErr) + data, err := json.Marshal(result) + if err != nil { + return err + } + log.Output(clientutils.IndentJson(data)) + return originalErr +} + +// printTransferConfigMergeTable renders a summary table for the transfer-config-merge operation. +func printTransferConfigMergeTable(csvPath string, w io.Writer, originalErr error) error { + result := buildTransferConfigMergeResult(csvPath, originalErr) + tw := tabwriter.NewWriter(w, 0, 0, 2, ' ', 0) + _, _ = fmt.Fprintln(tw, "FIELD\tVALUE") + _, _ = fmt.Fprintf(tw, "status\t%s\n", result.Status) + _, _ = fmt.Fprintf(tw, "message\t%s\n", result.Message) + if result.ConflictsReportPath != "" { + _, _ = fmt.Fprintf(tw, "conflicts_report_path\t%s\n", result.ConflictsReportPath) + } + if err := tw.Flush(); err != nil { + return err + } + return originalErr +} + +// buildTransferConfigMergeResult builds a transferConfigMergeResult from the command output. +func buildTransferConfigMergeResult(csvPath string, originalErr error) transferConfigMergeResult { + if originalErr != nil { + return transferConfigMergeResult{ + Status: "failure", + Message: originalErr.Error(), + } + } + if csvPath != "" { + return transferConfigMergeResult{ + Status: "conflicts_found", + ConflictsReportPath: csvPath, + Message: "Conflicts were found. Review the report at: " + csvPath, + } + } + return transferConfigMergeResult{ + Status: "success", + Message: "Config transfer merge completed successfully!", + } } func dataTransferPluginInstallCmd(c *cli.Context) error { @@ -1164,6 +1292,11 @@ func transferFilesCmd(c *cli.Context) error { return err } + outputFormat, err := commonCliUtils.GetOutputFormat(c, coreformat.None) + if err != nil { + return err + } + // Run transfer data command newTransferFilesCmd, err := transferfilescore.NewTransferFilesCommand(sourceServerDetails, targetServerDetails) if err != nil { @@ -1177,7 +1310,78 @@ func transferFilesCmd(c *cli.Context) error { newTransferFilesCmd.SetIncludeFilesPatterns(getIncludeFilesPatterns(c)) newTransferFilesCmd.SetIgnoreState(c.Bool(cliutils.IgnoreState)) newTransferFilesCmd.SetProxyKey(c.String(cliutils.ProxyKey)) - return newTransferFilesCmd.Run() + runErr := newTransferFilesCmd.Run() + if outputFormat == coreformat.None { + return runErr + } + return printTransferFilesResponse(newTransferFilesCmd.Result(), outputFormat, os.Stdout, runErr) +} + +// transferFilesResult is the structured result of a transfer-files operation. +type transferFilesResult struct { + Status string `json:"status"` + TotalRepositories int64 `json:"total_repositories"` + RepositoriesTransferred int64 `json:"repositories_transferred"` + TotalFiles int64 `json:"total_files"` + FilesTransferred int64 `json:"files_transferred"` + TotalSizeBytes int64 `json:"total_size_bytes"` + TransferredSizeBytes int64 `json:"transferred_size_bytes"` + FilesFailed uint64 `json:"files_failed"` +} + +func buildTransferFilesResult(result transferfilescore.TransferFilesResult, originalErr error) transferFilesResult { + status := "success" + if originalErr != nil { + status = "failure" + } + return transferFilesResult{ + Status: status, + TotalRepositories: result.TotalRepositories, + RepositoriesTransferred: result.TransferredRepositories, + TotalFiles: result.TotalFiles, + FilesTransferred: result.TransferredFiles, + TotalSizeBytes: result.TotalSizeBytes, + TransferredSizeBytes: result.TransferredSizeBytes, + FilesFailed: result.TransferFailures, + } +} + +// printTransferFilesResponse renders the transfer-files result in the requested output format. +func printTransferFilesResponse(result transferfilescore.TransferFilesResult, outputFormat coreformat.OutputFormat, w io.Writer, originalErr error) error { + switch outputFormat { + case coreformat.Json: + return printTransferFilesJSON(result, originalErr) + case coreformat.Table: + return printTransferFilesTable(result, w, originalErr) + default: + return errorutils.CheckErrorf("unsupported format '%s' for rt transfer-files. Acceptable values are: json, table", outputFormat) + } +} + +// printTransferFilesJSON emits a JSON summary of the transfer-files operation to stdout via log.Output. +func printTransferFilesJSON(result transferfilescore.TransferFilesResult, originalErr error) error { + out := buildTransferFilesResult(result, originalErr) + data, err := json.Marshal(out) + if err != nil { + return err + } + log.Output(clientutils.IndentJson(data)) + return originalErr +} + +// printTransferFilesTable renders a summary table for the transfer-files operation. +func printTransferFilesTable(result transferfilescore.TransferFilesResult, w io.Writer, originalErr error) error { + out := buildTransferFilesResult(result, originalErr) + tw := tabwriter.NewWriter(w, 0, 0, 2, ' ', 0) + _, _ = fmt.Fprintln(tw, "FIELD\tVALUE") + _, _ = fmt.Fprintf(tw, "status\t%s\n", out.Status) + _, _ = fmt.Fprintf(tw, "repositories_transferred\t%d/%d\n", out.RepositoriesTransferred, out.TotalRepositories) + _, _ = fmt.Fprintf(tw, "files_transferred\t%d/%d\n", out.FilesTransferred, out.TotalFiles) + _, _ = fmt.Fprintf(tw, "files_failed\t%d\n", out.FilesFailed) + if err := tw.Flush(); err != nil { + return err + } + return originalErr } func getTransferIncludeExcludeRepos(c *cli.Context) (includeReposPatterns, excludeReposPatterns []string) { diff --git a/artifactory/cli_test.go b/artifactory/cli_test.go index af6caae8b..065682811 100644 --- a/artifactory/cli_test.go +++ b/artifactory/cli_test.go @@ -2,16 +2,22 @@ package artifactory import ( "bytes" + "flag" + "fmt" "path/filepath" + "strings" "testing" "github.com/jfrog/jfrog-cli-artifactory/cliutils/flagkit" + transferfilescore "github.com/jfrog/jfrog-cli-core/v2/artifactory/commands/transferfiles" commonCliUtils "github.com/jfrog/jfrog-cli-core/v2/common/cliutils" + coreformat "github.com/jfrog/jfrog-cli-core/v2/common/format" "github.com/jfrog/jfrog-cli-core/v2/common/spec" "github.com/jfrog/jfrog-cli/utils/cliutils" "github.com/jfrog/jfrog-cli/utils/tests" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/urfave/cli" ) @@ -198,3 +204,506 @@ func TestGetIncludeFilesPatterns(t *testing.T) { }) } } + +// --------------------------------------------------------------------------- +// helpers for transfer-files format tests +// --------------------------------------------------------------------------- + +// newTransferFilesFormatContext creates a *cli.Context with --format set to formatVal. +// If formatVal is empty, the flag is registered but left unset. +func newTransferFilesFormatContext(formatVal string) *cli.Context { + app := cli.NewApp() + app.Flags = []cli.Flag{cli.StringFlag{Name: "format"}} + fs := flag.NewFlagSet("test", flag.ContinueOnError) + fs.String("format", "", "") + if formatVal != "" { + _ = fs.Set("format", formatVal) + } + return cli.NewContext(app, fs, nil) +} + +// --------------------------------------------------------------------------- +// GetOutputFormat (transfer-files) tests +// --------------------------------------------------------------------------- + +func TestGetTransferFilesOutputFormat_Default(t *testing.T) { + // No --format flag set → backward-compatible, returns None. + c := newTransferFilesFormatContext("") + f, err := commonCliUtils.GetOutputFormat(c, coreformat.None) + require.NoError(t, err) + assert.Equal(t, coreformat.None, f) +} + +func TestGetTransferFilesOutputFormat_ExplicitJSON(t *testing.T) { + c := newTransferFilesFormatContext("json") + f, err := commonCliUtils.GetOutputFormat(c, coreformat.None) + require.NoError(t, err) + assert.Equal(t, coreformat.Json, f) +} + +func TestGetTransferFilesOutputFormat_ExplicitTable(t *testing.T) { + c := newTransferFilesFormatContext("table") + f, err := commonCliUtils.GetOutputFormat(c, coreformat.None) + require.NoError(t, err) + assert.Equal(t, coreformat.Table, f) +} + +func TestGetTransferFilesOutputFormat_Invalid(t *testing.T) { + c := newTransferFilesFormatContext("xml") + _, err := commonCliUtils.GetOutputFormat(c, coreformat.None) + require.Error(t, err) + assert.True(t, strings.Contains(err.Error(), "only the following output formats are supported")) +} + +// --------------------------------------------------------------------------- +// printTransferFilesResponse tests +// --------------------------------------------------------------------------- + +func TestPrintTransferFilesResponse_JSON(t *testing.T) { + result := transferfilescore.TransferFilesResult{ + TotalRepositories: 5, + TransferredRepositories: 5, + TotalFiles: 1000, + TransferredFiles: 990, + TotalSizeBytes: 1048576, + TransferredSizeBytes: 1038336, + TransferFailures: 10, + } + var buf bytes.Buffer + err := printTransferFilesResponse(result, coreformat.Json, &buf, nil) + require.NoError(t, err) + // The JSON is printed via log.Output (not to buf), so we verify no error. +} + +func TestPrintTransferFilesResponse_JSON_WithError(t *testing.T) { + result := transferfilescore.TransferFilesResult{ + TotalRepositories: 3, + TransferredRepositories: 2, + TotalFiles: 500, + TransferredFiles: 400, + TransferFailures: 100, + } + var buf bytes.Buffer + originalErr := assert.AnError + // JSON path propagates the original error. + err := printTransferFilesResponse(result, coreformat.Json, &buf, originalErr) + assert.Equal(t, originalErr, err) +} + +func TestPrintTransferFilesResponse_Table(t *testing.T) { + result := transferfilescore.TransferFilesResult{ + TotalRepositories: 4, + TransferredRepositories: 4, + TotalFiles: 200, + TransferredFiles: 198, + TotalSizeBytes: 2097152, + TransferredSizeBytes: 2076672, + TransferFailures: 2, + } + var buf bytes.Buffer + err := printTransferFilesResponse(result, coreformat.Table, &buf, nil) + require.NoError(t, err) + + output := buf.String() + // Header + assert.True(t, strings.Contains(output, "FIELD"), "output should contain FIELD header") + assert.True(t, strings.Contains(output, "VALUE"), "output should contain VALUE header") + // Key rows + assert.True(t, strings.Contains(output, "status"), "output should contain status row") + assert.True(t, strings.Contains(output, "success"), "output should contain success status") + assert.True(t, strings.Contains(output, "repositories_transferred"), "output should contain repositories_transferred row") + assert.True(t, strings.Contains(output, "files_transferred"), "output should contain files_transferred row") + assert.True(t, strings.Contains(output, "files_failed"), "output should contain files_failed row") +} + +func TestPrintTransferFilesResponse_Table_FailureStatus(t *testing.T) { + result := transferfilescore.TransferFilesResult{ + TotalRepositories: 2, + TotalFiles: 100, + TransferredFiles: 50, + TransferFailures: 50, + } + var buf bytes.Buffer + originalErr := assert.AnError + err := printTransferFilesResponse(result, coreformat.Table, &buf, originalErr) + // Table path propagates the original error. + assert.Equal(t, originalErr, err) + + output := buf.String() + assert.True(t, strings.Contains(output, "failure"), "output should show failure status when error occurred") +} + +func TestPrintTransferFilesResponse_UnsupportedFormat(t *testing.T) { + result := transferfilescore.TransferFilesResult{} + var buf bytes.Buffer + err := printTransferFilesResponse(result, coreformat.SimpleJson, &buf, nil) + require.Error(t, err) + assert.True(t, strings.Contains(err.Error(), "unsupported format")) +} + +// --- permission-target-create --format tests --- + +// newPermissionTargetCreateFormatApp returns a minimal *cli.App with a --format flag for +// testing the format-guard logic in permissionTargetCreateCmd without a live server. +func newPermissionTargetCreateFormatApp(action cli.ActionFunc) *cli.App { + app := cli.NewApp() + app.Flags = []cli.Flag{cli.StringFlag{Name: cliutils.Format}} + app.Action = action + return app +} + +// permissionTargetCreateFormatGuard mirrors the format-check block in permissionTargetCreateCmd +// so we can test it independently of the HTTP call. +func permissionTargetCreateFormatGuard(c *cli.Context) error { + if !c.IsSet(cliutils.Format) { + return nil + } + _, err := coreformat.ParseOutputFormat(c.String(cliutils.Format), []coreformat.OutputFormat{coreformat.Json}) + return err +} + +func TestPermissionTargetCreate_FormatNotSet_NoError(t *testing.T) { + app := newPermissionTargetCreateFormatApp(func(c *cli.Context) error { + return permissionTargetCreateFormatGuard(c) + }) + require.NoError(t, app.Run([]string{"app"})) +} + +func TestPermissionTargetCreate_FormatJSON_NoError(t *testing.T) { + app := newPermissionTargetCreateFormatApp(func(c *cli.Context) error { + return permissionTargetCreateFormatGuard(c) + }) + require.NoError(t, app.Run([]string{"app", "--format", "json"})) +} + +func TestPermissionTargetCreate_FormatTable_ReturnsError(t *testing.T) { + var gotErr error + app := newPermissionTargetCreateFormatApp(func(c *cli.Context) error { + gotErr = permissionTargetCreateFormatGuard(c) + return nil + }) + require.NoError(t, app.Run([]string{"app", "--format", "table"})) + require.Error(t, gotErr) + assert.Contains(t, gotErr.Error(), "only the following output formats are supported") +} + +func TestPermissionTargetCreate_InvalidFormat_ReturnsError(t *testing.T) { + var gotErr error + app := newPermissionTargetCreateFormatApp(func(c *cli.Context) error { + gotErr = permissionTargetCreateFormatGuard(c) + return nil + }) + require.NoError(t, app.Run([]string{"app", "--format", "xml"})) + require.Error(t, gotErr) + assert.Contains(t, gotErr.Error(), "only the following output formats are supported") +} + +// --- transfer-config --format tests --- + +// newTransferConfigFormatApp returns a minimal *cli.App with a --format flag for +// testing the format-guard logic in transferConfigCmd without a live server. +func newTransferConfigFormatApp(action cli.ActionFunc) *cli.App { + app := cli.NewApp() + app.Flags = []cli.Flag{cli.StringFlag{Name: cliutils.Format}} + app.Action = action + return app +} + +// transferConfigFormatGuard mirrors the format-check block in transferConfigCmd +// so we can test it independently of the HTTP call. +func transferConfigFormatGuard(c *cli.Context) error { + if !c.IsSet(cliutils.Format) { + return nil + } + _, err := coreformat.ParseOutputFormat(c.String(cliutils.Format), []coreformat.OutputFormat{coreformat.Json}) + return err +} + +func TestTransferConfig_FormatNotSet_NoError(t *testing.T) { + app := newTransferConfigFormatApp(func(c *cli.Context) error { + return transferConfigFormatGuard(c) + }) + require.NoError(t, app.Run([]string{"app"})) +} + +func TestTransferConfig_FormatJSON_NoError(t *testing.T) { + app := newTransferConfigFormatApp(func(c *cli.Context) error { + return transferConfigFormatGuard(c) + }) + require.NoError(t, app.Run([]string{"app", "--format", "json"})) +} + +func TestTransferConfig_FormatTable_ReturnsError(t *testing.T) { + var gotErr error + app := newTransferConfigFormatApp(func(c *cli.Context) error { + gotErr = transferConfigFormatGuard(c) + return nil + }) + require.NoError(t, app.Run([]string{"app", "--format", "table"})) + require.Error(t, gotErr) + assert.Contains(t, gotErr.Error(), "only the following output formats are supported") +} + +func TestTransferConfig_InvalidFormat_ReturnsError(t *testing.T) { + var gotErr error + app := newTransferConfigFormatApp(func(c *cli.Context) error { + gotErr = transferConfigFormatGuard(c) + return nil + }) + require.NoError(t, app.Run([]string{"app", "--format", "xml"})) + require.Error(t, gotErr) + assert.Contains(t, gotErr.Error(), "only the following output formats are supported") +} + +// --- permission-target-update --format tests --- + +// newPermissionTargetUpdateFormatApp returns a minimal *cli.App with a --format flag for +// testing the format-guard logic in permissionTargetUpdateCmd without a live server. +func newPermissionTargetUpdateFormatApp(action cli.ActionFunc) *cli.App { + app := cli.NewApp() + app.Flags = []cli.Flag{cli.StringFlag{Name: cliutils.Format}} + app.Action = action + return app +} + +// permissionTargetUpdateFormatGuard mirrors the format-check block in permissionTargetUpdateCmd +// so we can test it independently of the HTTP call. +func permissionTargetUpdateFormatGuard(c *cli.Context) error { + if !c.IsSet(cliutils.Format) { + return nil + } + _, err := coreformat.ParseOutputFormat(c.String(cliutils.Format), []coreformat.OutputFormat{coreformat.Json}) + return err +} + +func TestPermissionTargetUpdate_FormatNotSet_NoError(t *testing.T) { + app := newPermissionTargetUpdateFormatApp(func(c *cli.Context) error { + return permissionTargetUpdateFormatGuard(c) + }) + require.NoError(t, app.Run([]string{"app"})) +} + +func TestPermissionTargetUpdate_FormatJSON_NoError(t *testing.T) { + app := newPermissionTargetUpdateFormatApp(func(c *cli.Context) error { + return permissionTargetUpdateFormatGuard(c) + }) + require.NoError(t, app.Run([]string{"app", "--format", "json"})) +} + +func TestPermissionTargetUpdate_FormatTable_ReturnsError(t *testing.T) { + var gotErr error + app := newPermissionTargetUpdateFormatApp(func(c *cli.Context) error { + gotErr = permissionTargetUpdateFormatGuard(c) + return nil + }) + require.NoError(t, app.Run([]string{"app", "--format", "table"})) + require.Error(t, gotErr) + assert.Contains(t, gotErr.Error(), "only the following output formats are supported") +} + +func TestPermissionTargetUpdate_InvalidFormat_ReturnsError(t *testing.T) { + var gotErr error + app := newPermissionTargetUpdateFormatApp(func(c *cli.Context) error { + gotErr = permissionTargetUpdateFormatGuard(c) + return nil + }) + require.NoError(t, app.Run([]string{"app", "--format", "xml"})) + require.Error(t, gotErr) + assert.Contains(t, gotErr.Error(), "only the following output formats are supported") +} + +// --- transfer-config-merge --format tests --- + +func newTransferConfigMergeFormatApp(action cli.ActionFunc) *cli.App { + app := cli.NewApp() + app.Flags = []cli.Flag{cli.StringFlag{Name: cliutils.Format}} + app.Action = action + return app +} + +// TestGetTransferConfigMergeOutputFormat_Default verifies that when --format is not set, +// the function returns coreformat.None (backward-compatible: log output only). +func TestGetTransferConfigMergeOutputFormat_Default(t *testing.T) { + var got coreformat.OutputFormat + app := newTransferConfigMergeFormatApp(func(c *cli.Context) error { + var err error + got, err = getTransferConfigMergeOutputFormat(c) + return err + }) + require.NoError(t, app.Run([]string{"app"})) + assert.Equal(t, coreformat.None, got) +} + +// TestGetTransferConfigMergeOutputFormat_ExplicitJSON verifies --format json is accepted. +func TestGetTransferConfigMergeOutputFormat_ExplicitJSON(t *testing.T) { + var got coreformat.OutputFormat + app := newTransferConfigMergeFormatApp(func(c *cli.Context) error { + var err error + got, err = getTransferConfigMergeOutputFormat(c) + return err + }) + require.NoError(t, app.Run([]string{"app", "--format", "json"})) + assert.Equal(t, coreformat.Json, got) +} + +// TestGetTransferConfigMergeOutputFormat_ExplicitTable verifies --format table is accepted. +func TestGetTransferConfigMergeOutputFormat_ExplicitTable(t *testing.T) { + var got coreformat.OutputFormat + app := newTransferConfigMergeFormatApp(func(c *cli.Context) error { + var err error + got, err = getTransferConfigMergeOutputFormat(c) + return err + }) + require.NoError(t, app.Run([]string{"app", "--format", "table"})) + assert.Equal(t, coreformat.Table, got) +} + +// TestGetTransferConfigMergeOutputFormat_Invalid verifies that an unsupported format returns an error. +func TestGetTransferConfigMergeOutputFormat_Invalid(t *testing.T) { + var gotErr error + app := newTransferConfigMergeFormatApp(func(c *cli.Context) error { + _, gotErr = getTransferConfigMergeOutputFormat(c) + return nil + }) + require.NoError(t, app.Run([]string{"app", "--format", "xml"})) + require.Error(t, gotErr) + assert.Contains(t, gotErr.Error(), "only the following output formats are supported") +} + +// TestPrintTransferConfigMergeResponse_JSON_Success verifies JSON output when there are no conflicts. +func TestPrintTransferConfigMergeResponse_JSON_Success(t *testing.T) { + var buf bytes.Buffer + err := printTransferConfigMergeResponse("", coreformat.Json, &buf, nil) + require.NoError(t, err) + // JSON is emitted via log.Output (not to buf), so we just verify no error. +} + +// TestPrintTransferConfigMergeResponse_JSON_ConflictsFound verifies JSON output when conflicts are found. +func TestPrintTransferConfigMergeResponse_JSON_ConflictsFound(t *testing.T) { + var buf bytes.Buffer + err := printTransferConfigMergeResponse("/tmp/conflicts.csv", coreformat.Json, &buf, nil) + require.NoError(t, err) +} + +// TestPrintTransferConfigMergeResponse_Table_Success verifies table output with no conflicts. +func TestPrintTransferConfigMergeResponse_Table_Success(t *testing.T) { + var buf bytes.Buffer + err := printTransferConfigMergeResponse("", coreformat.Table, &buf, nil) + require.NoError(t, err) + out := buf.String() + assert.Contains(t, out, "FIELD") + assert.Contains(t, out, "VALUE") + assert.Contains(t, out, "status") + assert.Contains(t, out, "success") + assert.Contains(t, out, "message") +} + +// TestPrintTransferConfigMergeResponse_Table_ConflictsFound verifies table output when conflicts are found. +func TestPrintTransferConfigMergeResponse_Table_ConflictsFound(t *testing.T) { + var buf bytes.Buffer + err := printTransferConfigMergeResponse("/tmp/conflicts.csv", coreformat.Table, &buf, nil) + require.NoError(t, err) + out := buf.String() + assert.Contains(t, out, "status") + assert.Contains(t, out, "conflicts_found") + assert.Contains(t, out, "conflicts_report_path") + assert.Contains(t, out, "/tmp/conflicts.csv") +} + +// TestPrintTransferConfigMergeResponse_Table_NoConflictsReportPath verifies that conflicts_report_path is +// absent from table output when there are no conflicts. +func TestPrintTransferConfigMergeResponse_Table_NoConflictsReportPath(t *testing.T) { + var buf bytes.Buffer + err := printTransferConfigMergeResponse("", coreformat.Table, &buf, nil) + require.NoError(t, err) + out := buf.String() + assert.NotContains(t, out, "conflicts_report_path") +} + +// TestPrintTransferConfigMergeResponse_UnsupportedFormat verifies that an unsupported format returns an error. +func TestPrintTransferConfigMergeResponse_UnsupportedFormat(t *testing.T) { + var buf bytes.Buffer + err := printTransferConfigMergeResponse("", coreformat.SimpleJson, &buf, nil) + require.Error(t, err) + assert.Contains(t, err.Error(), "unsupported format") +} + +// TestBuildTransferConfigMergeResult_Success verifies the result struct for a successful merge. +func TestBuildTransferConfigMergeResult_Success(t *testing.T) { + result := buildTransferConfigMergeResult("", nil) + assert.Equal(t, "success", result.Status) + assert.Empty(t, result.ConflictsReportPath) + assert.NotEmpty(t, result.Message) +} + +// TestBuildTransferConfigMergeResult_ConflictsFound verifies the result struct when conflicts are found. +func TestBuildTransferConfigMergeResult_ConflictsFound(t *testing.T) { + result := buildTransferConfigMergeResult("/tmp/conflicts.csv", nil) + assert.Equal(t, "conflicts_found", result.Status) + assert.Equal(t, "/tmp/conflicts.csv", result.ConflictsReportPath) + assert.Contains(t, result.Message, "/tmp/conflicts.csv") +} + +// TestBuildTransferConfigMergeResult_Failure verifies the result struct when the command failed. +func TestBuildTransferConfigMergeResult_Failure(t *testing.T) { + result := buildTransferConfigMergeResult("", fmt.Errorf("something went wrong")) + assert.Equal(t, "failure", result.Status) + assert.Contains(t, result.Message, "something went wrong") +} + +// --- users-create --format tests --- + +// newUsersCreateFormatApp returns a minimal *cli.App with a --format flag for +// testing the format-guard logic in usersCreateCmd without a live server. +func newUsersCreateFormatApp(action cli.ActionFunc) *cli.App { + app := cli.NewApp() + app.Flags = []cli.Flag{cli.StringFlag{Name: cliutils.Format}} + app.Action = action + return app +} + +// usersCreateFormatGuard mirrors the format-check block in usersCreateCmd so we +// can test it independently of the HTTP call. +func usersCreateFormatGuard(c *cli.Context) error { + if !c.IsSet(cliutils.Format) { + return nil + } + _, err := coreformat.ParseOutputFormat(c.String(cliutils.Format), []coreformat.OutputFormat{coreformat.Json}) + return err +} + +func TestUsersCreate_FormatNotSet_NoError(t *testing.T) { + app := newUsersCreateFormatApp(func(c *cli.Context) error { + return usersCreateFormatGuard(c) + }) + require.NoError(t, app.Run([]string{"app"})) +} + +func TestUsersCreate_FormatJSON_NoError(t *testing.T) { + app := newUsersCreateFormatApp(func(c *cli.Context) error { + return usersCreateFormatGuard(c) + }) + require.NoError(t, app.Run([]string{"app", "--format", "json"})) +} + +func TestUsersCreate_FormatTable_ReturnsError(t *testing.T) { + var gotErr error + app := newUsersCreateFormatApp(func(c *cli.Context) error { + gotErr = usersCreateFormatGuard(c) + return nil + }) + require.NoError(t, app.Run([]string{"app", "--format", "table"})) + require.Error(t, gotErr) + assert.Contains(t, gotErr.Error(), "only the following output formats are supported") +} + +func TestUsersCreate_InvalidFormat_ReturnsError(t *testing.T) { + var gotErr error + app := newUsersCreateFormatApp(func(c *cli.Context) error { + gotErr = usersCreateFormatGuard(c) + return nil + }) + require.NoError(t, app.Run([]string{"app", "--format", "xml"})) + require.Error(t, gotErr) + assert.Contains(t, gotErr.Error(), "only the following output formats are supported") +} diff --git a/go.mod b/go.mod index 78e4089af..9cc235972 100644 --- a/go.mod +++ b/go.mod @@ -259,3 +259,5 @@ require ( // replace github.com/jfrog/jfrog-cli-security => github.com/jfrog/jfrog-cli-security dev // replace github.com/jfrog/build-info-go => github.com/reshmifrog/build-info-go v1.8.9-0.20260106113011-c7f131cea484 + +replace github.com/jfrog/jfrog-cli-artifactory => github.com/ehl-jf/jfrog-cli-artifactory v0.0.0-20260504070720-ecafa4282384 diff --git a/go.sum b/go.sum index a227a9f55..ca5eb21eb 100644 --- a/go.sum +++ b/go.sum @@ -200,6 +200,8 @@ github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707/go.mod h1:qssHWj6 github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY= github.com/ebitengine/purego v0.8.4 h1:CF7LEKg5FFOsASUj0+QwaXf8Ht6TlFxg09+S9wz0omw= github.com/ebitengine/purego v0.8.4/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= +github.com/ehl-jf/jfrog-cli-artifactory v0.0.0-20260504070720-ecafa4282384 h1:Lsnmq5ef/pumkHh7qYCDmt2iVE1Sr1exB0doGnnPWZg= +github.com/ehl-jf/jfrog-cli-artifactory v0.0.0-20260504070720-ecafa4282384/go.mod h1:A3sgUG+62BBtF7CLSbjwEfCJLC5ATL/rF8AxVHsuJDQ= github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o= github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= @@ -418,10 +420,6 @@ github.com/jfrog/jfrog-apps-config v1.0.1 h1:mtv6k7g8A8BVhlHGlSveapqf4mJfonwvXYL github.com/jfrog/jfrog-apps-config v1.0.1/go.mod h1:8AIIr1oY9JuH5dylz2S6f8Ym2MaadPLR6noCBO4C22w= github.com/jfrog/jfrog-cli-application v1.0.2-0.20260405065840-c930d515ef34 h1:qD53oDmaw7+5HjaU7FupqbB55saabNzMoMtu3kJfmg4= github.com/jfrog/jfrog-cli-application v1.0.2-0.20260405065840-c930d515ef34/go.mod h1:xum2HquWO5uExa/A7MQs3TgJJVEeoqTR+6Z4mfBr1Xw= -github.com/jfrog/jfrog-cli-artifactory v0.8.1-0.20260429074430-a5871f2898b5 h1:+52DDmdSZFP1dxgeu0pkB1sQuoHa0PWbW7HVdFOqK3A= -github.com/jfrog/jfrog-cli-artifactory v0.8.1-0.20260429074430-a5871f2898b5/go.mod h1:BV+aCTQsaZeFec2WjgmQjqlxecju4CkkM9NqfiFyjo0= -github.com/jfrog/jfrog-cli-core/v2 v2.60.1-0.20260430091103-6242ecf15d29 h1:J5+08rOpv/avgt53jNFZ+j5gU8mllcj7Dcfja5Ewodw= -github.com/jfrog/jfrog-cli-core/v2 v2.60.1-0.20260430091103-6242ecf15d29/go.mod h1:bjAkVD8c2W+jg4whqy10bSXDC/c+Se8/ll/GPp5F/+0= github.com/jfrog/jfrog-cli-core/v2 v2.60.1-0.20260430125911-ad12ac6f1316 h1:xAl5D+SjLeRH1gCsSHFPpXJeQQBv2HDGqDTDkFOKJ2s= github.com/jfrog/jfrog-cli-core/v2 v2.60.1-0.20260430125911-ad12ac6f1316/go.mod h1:bjAkVD8c2W+jg4whqy10bSXDC/c+Se8/ll/GPp5F/+0= github.com/jfrog/jfrog-cli-evidence v0.9.2 h1:huiBzQSI9z3OF3l2RphthdXl1aH9zBsvAt+zLsApORI= diff --git a/utils/cliutils/commandsflags.go b/utils/cliutils/commandsflags.go index 60e5c9a8c..04a2e955a 100644 --- a/utils/cliutils/commandsflags.go +++ b/utils/cliutils/commandsflags.go @@ -94,6 +94,8 @@ const ( RepoDelete = "repo-delete" ReplicationDelete = "replication-delete" PermissionTargetDelete = "permission-target-delete" + PermissionTargetCreate = "permission-target-create" + PermissionTargetUpdate = "permission-target-update" // #nosec G101 -- False positive - no hardcoded credentials. ArtifactoryAccessTokenCreate = "artifactory-access-token-create" UserCreate = "user-create" @@ -517,7 +519,13 @@ const ( plStatusFormat = "pl-status-format" plTriggerFormat = "pl-trigger-format" plSyncFormat = "pl-sync-format" - plSyncStatusFormat = "pl-sync-status-format" + plSyncStatusFormat = "pl-sync-status-format" + permissionTargetCreateFormat = "permission-target-create-format" + permissionTargetUpdateFormat = "permission-target-update-format" + transferConfigFormat = "transfer-config-format" + transferConfigMergeFormat = "transfer-config-merge-format" + transferFilesFormat = "transfer-files-format" + usersCreateFormat = "users-create-format" // Unique scan flags scanPrefix = "scan-" @@ -690,6 +698,30 @@ var flagsMap = map[string]cli.Flag{ Name: Format, Usage: "[Optional] " + components.GetFormatFlagDescription([]format.OutputFormat{format.Table, format.Json}) + "` `", }, + permissionTargetCreateFormat: cli.StringFlag{ + Name: Format, + Usage: "[Optional] " + components.GetFormatFlagDescription([]format.OutputFormat{format.Json}) + "` `", + }, + permissionTargetUpdateFormat: cli.StringFlag{ + Name: Format, + Usage: "[Optional] " + components.GetFormatFlagDescription([]format.OutputFormat{format.Json}) + "` `", + }, + transferConfigFormat: cli.StringFlag{ + Name: Format, + Usage: "[Optional] " + components.GetFormatFlagDescription([]format.OutputFormat{format.Json}) + "` `", + }, + transferConfigMergeFormat: cli.StringFlag{ + Name: Format, + Usage: "[Optional] " + components.GetFormatFlagDescription([]format.OutputFormat{format.Json, format.Table}) + "` `", + }, + transferFilesFormat: cli.StringFlag{ + Name: Format, + Usage: "[Optional] " + components.GetFormatFlagDescription([]format.OutputFormat{format.Json, format.Table}) + "` `", + }, + usersCreateFormat: cli.StringFlag{ + Name: Format, + Usage: "[Optional] " + components.GetFormatFlagDescription([]format.OutputFormat{format.Json}) + "` `", + }, // Common commands flags platformUrl: cli.StringFlag{ Name: url, @@ -2093,10 +2125,10 @@ var commandFlags = map[string][]string{ BuildName, BuildNumber, module, Project, }, TransferConfig: { - Force, Verbose, IncludeRepos, ExcludeRepos, SourceWorkingDir, TargetWorkingDir, PreChecks, + Force, Verbose, IncludeRepos, ExcludeRepos, SourceWorkingDir, TargetWorkingDir, PreChecks, transferConfigFormat, }, TransferConfigMerge: { - IncludeRepos, ExcludeRepos, IncludeProjects, ExcludeProjects, + IncludeRepos, ExcludeRepos, IncludeProjects, ExcludeProjects, transferConfigMergeFormat, }, Ping: { url, user, password, accessToken, sshPassphrase, sshKeyPath, serverId, ClientCertPath, @@ -2171,6 +2203,14 @@ var commandFlags = map[string][]string{ url, user, password, accessToken, sshPassphrase, sshKeyPath, serverId, ClientCertPath, ClientCertKeyPath, deleteQuiet, }, + PermissionTargetCreate: { + url, user, password, accessToken, sshPassphrase, sshKeyPath, serverId, ClientCertPath, + ClientCertKeyPath, vars, permissionTargetCreateFormat, + }, + PermissionTargetUpdate: { + url, user, password, accessToken, sshPassphrase, sshKeyPath, serverId, ClientCertPath, + ClientCertKeyPath, vars, permissionTargetUpdateFormat, + }, ArtifactoryAccessTokenCreate: { url, user, password, accessToken, sshPassphrase, sshKeyPath, serverId, ClientCertPath, ClientCertKeyPath, rtAtcGroups, rtAtcGrantAdmin, rtAtcExpiry, rtAtcRefreshable, rtAtcAudience, @@ -2189,7 +2229,7 @@ var commandFlags = map[string][]string{ }, UsersCreate: { url, user, password, accessToken, sshPassphrase, sshKeyPath, serverId, - usersCreateCsv, UsersGroups, Replace, + usersCreateCsv, UsersGroups, Replace, usersCreateFormat, }, UsersDelete: { url, user, password, accessToken, sshPassphrase, sshKeyPath, serverId, @@ -2206,7 +2246,7 @@ var commandFlags = map[string][]string{ url, user, password, accessToken, sshPassphrase, sshKeyPath, serverId, deleteQuiet, }, TransferFiles: { - Filestore, IncludeRepos, ExcludeRepos, IncludeFiles, IgnoreState, ProxyKey, transferFilesStatus, Stop, PreChecks, + Filestore, IncludeRepos, ExcludeRepos, IncludeFiles, IgnoreState, ProxyKey, transferFilesStatus, Stop, PreChecks, transferFilesFormat, }, TransferInstall: { installPluginVersion, InstallPluginSrcDir, InstallPluginHomeDir, From 6d3934654091ea62ba318bb25011312ce317200c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 4 May 2026 13:43:03 +0200 Subject: [PATCH 2/2] JGC-478 - Fix --format flag wiring for rt permission-target-create --- artifactory/cli.go | 21 +++++++++++++++++---- go.mod | 2 +- go.sum | 4 ++-- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/artifactory/cli.go b/artifactory/cli.go index a52fbd8d4..287206636 100644 --- a/artifactory/cli.go +++ b/artifactory/cli.go @@ -389,7 +389,7 @@ func GetCommands() []cli.Command { { Name: "permission-target-create", Aliases: []string{"ptc"}, - Flags: cliutils.GetCommandFlags(cliutils.TemplateConsumer), + Flags: cliutils.GetCommandFlags(cliutils.PermissionTargetCreate), Usage: permissiontargetcreate.GetDescription(), HelpName: corecommon.CreateUsage("rt ptc", permissiontargetcreate.GetDescription(), permissiontargetcreate.Usage), UsageText: permissiontargetcreate.GetArguments(), @@ -801,15 +801,28 @@ func permissionTargetCreateCmd(c *cli.Context) error { return cliutils.WrongNumberOfArgumentsHandler(c) } + if c.IsSet(cliutils.Format) { + if _, fmtErr := coreformat.ParseOutputFormat(c.String(cliutils.Format), []coreformat.OutputFormat{coreformat.Json}); fmtErr != nil { + return fmtErr + } + } + rtDetails, err := cliutils.CreateArtifactoryDetailsByFlags(c) if err != nil { return err } // Run command. - permissionTargetCreateCmd := permissiontarget.NewPermissionTargetCreateCommand() - permissionTargetCreateCmd.SetTemplatePath(c.Args().Get(0)).SetServerDetails(rtDetails).SetVars(c.String("vars")) - return commands.Exec(permissionTargetCreateCmd) + permissionTargetCreateCommand := permissiontarget.NewPermissionTargetCreateCommand() + permissionTargetCreateCommand.SetTemplatePath(c.Args().Get(0)).SetServerDetails(rtDetails).SetVars(c.String("vars")) + if err = commands.Exec(permissionTargetCreateCommand); err != nil { + return err + } + if c.IsSet(cliutils.Format) { + // Client layer discards body; nil + 200 produces {"status_code":200,"message":"OK"} + cliutils.FormatHTTPResponseJSON(nil, 200) + } + return nil } func permissionTargetUpdateCmd(c *cli.Context) error { diff --git a/go.mod b/go.mod index 9cc235972..246fdac21 100644 --- a/go.mod +++ b/go.mod @@ -260,4 +260,4 @@ require ( // replace github.com/jfrog/build-info-go => github.com/reshmifrog/build-info-go v1.8.9-0.20260106113011-c7f131cea484 -replace github.com/jfrog/jfrog-cli-artifactory => github.com/ehl-jf/jfrog-cli-artifactory v0.0.0-20260504070720-ecafa4282384 +replace github.com/jfrog/jfrog-cli-artifactory => github.com/ehl-jf/jfrog-cli-artifactory v0.0.0-20260504114357-eced4616bc48 diff --git a/go.sum b/go.sum index ca5eb21eb..655c27776 100644 --- a/go.sum +++ b/go.sum @@ -200,8 +200,8 @@ github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707/go.mod h1:qssHWj6 github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY= github.com/ebitengine/purego v0.8.4 h1:CF7LEKg5FFOsASUj0+QwaXf8Ht6TlFxg09+S9wz0omw= github.com/ebitengine/purego v0.8.4/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= -github.com/ehl-jf/jfrog-cli-artifactory v0.0.0-20260504070720-ecafa4282384 h1:Lsnmq5ef/pumkHh7qYCDmt2iVE1Sr1exB0doGnnPWZg= -github.com/ehl-jf/jfrog-cli-artifactory v0.0.0-20260504070720-ecafa4282384/go.mod h1:A3sgUG+62BBtF7CLSbjwEfCJLC5ATL/rF8AxVHsuJDQ= +github.com/ehl-jf/jfrog-cli-artifactory v0.0.0-20260504114357-eced4616bc48 h1:NrhkA4lRmatkmsdlweEgW300kAyvzUEW6LcRcobrAU4= +github.com/ehl-jf/jfrog-cli-artifactory v0.0.0-20260504114357-eced4616bc48/go.mod h1:A3sgUG+62BBtF7CLSbjwEfCJLC5ATL/rF8AxVHsuJDQ= github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o= github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=