Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions buildtools/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -516,6 +516,20 @@ func MvnCmd(c *cli.Context) (err error) {
return err
}

// FlexPack bypasses all config file requirements
if os.Getenv("JFROG_RUN_NATIVE") == "true" {
log.Debug("Routing to Maven native implementation")
// Extract build configuration for FlexPack
args := cliutils.ExtractCommand(c)
filteredMavenArgs, buildConfiguration, err := build.ExtractBuildDetailsFromArgs(args)
if err != nil {
return err
}
// Create Maven command with empty config for FlexPack
mvnCmd := mvn.NewMvnCommand().SetConfigPath("").SetGoals(filteredMavenArgs).SetConfiguration(buildConfiguration)
return commands.Exec(mvnCmd)
}

configFilePath, err := getProjectConfigPathOrThrow(project.Maven, "mvn", "mvn-config")
if err != nil {
return err
Expand Down
5 changes: 2 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ go 1.24.6
replace (
// Should not be updated to 0.2.6 due to a bug (https://github.com/jfrog/jfrog-cli-core/pull/372)
github.com/c-bata/go-prompt => github.com/c-bata/go-prompt v0.2.5
github.com/jfrog/jfrog-cli-artifactory => github.com/jfrog/jfrog-cli-artifactory v0.7.3-0.20250930060931-d9c7efa90fbb

// Should not be updated to 0.2.0-beta.2 due to a bug (https://github.com/jfrog/jfrog-cli-core/pull/372)
github.com/pkg/term => github.com/pkg/term v1.1.0
Expand All @@ -17,9 +16,9 @@ require (
github.com/docker/docker v27.5.1+incompatible
github.com/gocarina/gocsv v0.0.0-20240520201108-78e41c74b4b1
github.com/jfrog/archiver/v3 v3.6.1
github.com/jfrog/build-info-go v1.11.0
github.com/jfrog/build-info-go v1.12.0
github.com/jfrog/gofrog v1.7.6
github.com/jfrog/jfrog-cli-artifactory v0.7.3-0.20250930060931-d9c7efa90fbb
github.com/jfrog/jfrog-cli-artifactory v0.7.3-0.20251006064115-2929fd81e48d
github.com/jfrog/jfrog-cli-core/v2 v2.60.1-0.20250929083739-7ec32085edd8
github.com/jfrog/jfrog-cli-platform-services v1.10.0
github.com/jfrog/jfrog-cli-security v1.21.8
Expand Down
8 changes: 4 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -351,8 +351,8 @@ github.com/jellydator/ttlcache/v3 v3.3.0 h1:BdoC9cE81qXfrxeb9eoJi9dWrdhSuwXMAnHT
github.com/jellydator/ttlcache/v3 v3.3.0/go.mod h1:bj2/e0l4jRnQdrnSTaGTsh4GSXvMjQcy41i7th0GVGw=
github.com/jfrog/archiver/v3 v3.6.1 h1:LOxnkw9pOn45DzCbZNFV6K0+6dCsQ0L8mR3ZcujO5eI=
github.com/jfrog/archiver/v3 v3.6.1/go.mod h1:VgR+3WZS4N+i9FaDwLZbq+jeU4B4zctXL+gL4EMzfLw=
github.com/jfrog/build-info-go v1.11.0 h1:qEONCgaHKlW3e2y0zIwTZVbgS/ERZrPlBWEbOYJbaSU=
github.com/jfrog/build-info-go v1.11.0/go.mod h1:szdz9+WzB7+7PGnILLUgyY+OF5qD5geBT7UGNIxibyw=
github.com/jfrog/build-info-go v1.12.0 h1:/abBQdIxrkYjOwO79sIL0p+XPnMCCtKhiWToHKXXqHg=
github.com/jfrog/build-info-go v1.12.0/go.mod h1:szdz9+WzB7+7PGnILLUgyY+OF5qD5geBT7UGNIxibyw=
github.com/jfrog/froggit-go v1.20.3 h1:U3HHT0+AEHUVSSyQBbagQR4fLRqGqzSptPujDZuuDTk=
github.com/jfrog/froggit-go v1.20.3/go.mod h1:obSG1SlsWjktkuqmKtpq7MNTTL63e0ot+ucTnlOMV88=
github.com/jfrog/go-mockhttp v0.3.1 h1:/wac8v4GMZx62viZmv4wazB5GNKs+GxawuS1u3maJH8=
Expand All @@ -361,8 +361,8 @@ github.com/jfrog/gofrog v1.7.6 h1:QmfAiRzVyaI7JYGsB7cxfAJePAZTzFz0gRWZSE27c6s=
github.com/jfrog/gofrog v1.7.6/go.mod h1:ntr1txqNOZtHplmaNd7rS4f8jpA5Apx8em70oYEe7+4=
github.com/jfrog/jfrog-apps-config v1.0.1 h1:mtv6k7g8A8BVhlHGlSveapqf4mJfonwvXYLipdsOFMY=
github.com/jfrog/jfrog-apps-config v1.0.1/go.mod h1:8AIIr1oY9JuH5dylz2S6f8Ym2MaadPLR6noCBO4C22w=
github.com/jfrog/jfrog-cli-artifactory v0.7.3-0.20250930060931-d9c7efa90fbb h1:wrl9zkkzfc2lFgEY+LhPbLK5quRX6/N8utq1WVdiM7I=
github.com/jfrog/jfrog-cli-artifactory v0.7.3-0.20250930060931-d9c7efa90fbb/go.mod h1:N1nn6tNbyAjtdhLp89tO1CstT+iWcQSzAJGvAml7Deg=
github.com/jfrog/jfrog-cli-artifactory v0.7.3-0.20251006064115-2929fd81e48d h1:eYFjCAxRhVMx6NrfViFdxtomQn872pbgQnIklAVD47Y=
github.com/jfrog/jfrog-cli-artifactory v0.7.3-0.20251006064115-2929fd81e48d/go.mod h1:Yzb9bN7rouvTDiN0GYP/WI1z6h9V0eLsk2r2wqt8yoM=
github.com/jfrog/jfrog-cli-core/v2 v2.60.1-0.20250929083739-7ec32085edd8 h1:75pslZpPWy6w5mM/mpEssbnKLyivUr7YXkGoUaQY4Oc=
github.com/jfrog/jfrog-cli-core/v2 v2.60.1-0.20250929083739-7ec32085edd8/go.mod h1:Z1nBKbz0ZOZrOIS9maGp5LZpHaxuufZAABVF+DAz9wo=
github.com/jfrog/jfrog-cli-platform-services v1.10.0 h1:O+N/VAF+QjFvq9xkHpmzKLcdl9aJu3IP204Su0L14rw=
Expand Down
209 changes: 0 additions & 209 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@ import (
"strings"

"github.com/agnivade/levenshtein"
gofrogcmd "github.com/jfrog/gofrog/io"
artifactoryCLI "github.com/jfrog/jfrog-cli-artifactory/cli"
"github.com/jfrog/jfrog-cli-core/v2/common/build"
corecommon "github.com/jfrog/jfrog-cli-core/v2/docs/common"
"github.com/jfrog/jfrog-cli-core/v2/plugins/components"
coreconfig "github.com/jfrog/jfrog-cli-core/v2/utils/config"
Expand All @@ -38,7 +36,6 @@ import (
"github.com/jfrog/jfrog-cli/pipelines"
"github.com/jfrog/jfrog-cli/plugins"
"github.com/jfrog/jfrog-cli/plugins/utils"
"github.com/jfrog/jfrog-cli/utils/buildinfo"
"github.com/jfrog/jfrog-cli/utils/cliutils"
"github.com/jfrog/jfrog-client-go/http/httpclient"
clientutils "github.com/jfrog/jfrog-client-go/utils"
Expand Down Expand Up @@ -132,66 +129,15 @@ func execMain() error {
if err = setUberTraceIdToken(); err != nil {
clientlog.Warn("failed generating a trace ID token:", err.Error())
}
if os.Getenv("JFROG_RUN_NATIVE") == "true" {
// If the JFROG_RUN_NATIVE environment variable is set to true, we run the new implementation
// but only for package manager commands, not for JFrog CLI commands
args := ctx.Args()
if args.Present() && len(args) > 0 {
firstArg := args.Get(0)
if isPackageManagerCommand(firstArg) {
if err = runNativeImplementation(ctx); err != nil {
clientlog.Error("Failed to run native implementation:", err)
os.Exit(1)
}
os.Exit(0)
}
}
// For non-package-manager commands, continue with normal CLI processing
}
return nil
}

app.CommandNotFound = func(c *cli.Context, command string) {
// Try to handle as native package manager command only when JFROG_RUN_NATIVE is true
if os.Getenv("JFROG_RUN_NATIVE") == "true" && isPackageManagerCommand(command) {
clientlog.Debug("Attempting to handle as native package manager command:", command)
err := runNativeImplementation(c)
if err != nil {
clientlog.Error("Failed to run native implementation:", err)
os.Exit(1)
}
os.Exit(0)
}

// Original behavior for unknown commands
_, err = fmt.Fprintf(c.App.Writer, "'%s %s' is not a jf command. See --help\n", c.App.Name, command)
if err != nil {
clientlog.Debug(err)
os.Exit(1)
}
if bestSimilarity := searchSimilarCmds(c.App.Commands, command); len(bestSimilarity) > 0 {
text := "The most similar "
if len(bestSimilarity) == 1 {
text += "command is:\n\tjf " + bestSimilarity[0]
} else {
sort.Strings(bestSimilarity)
text += "commands are:\n\tjf " + strings.Join(bestSimilarity, "\n\tjf ")
}
_, err = fmt.Fprintln(c.App.Writer, text)
if err != nil {
clientlog.Debug(err)
}
}
os.Exit(1)
}

err = app.Run(args)
logTraceIdOnFailure(err)

if err == nil {
displaySurveyLinkIfNeeded()
}

return err
}

Expand All @@ -203,161 +149,6 @@ func displaySurveyLinkIfNeeded() {
fmt.Fprintln(os.Stderr, "\n💬 Help us improve JFrog CLI! \033]8;;https://www.surveymonkey.com/r/JFCLICLI\033\\https://www.surveymonkey.com/r/JFCLICLI\033]8;;\033\\")
}

func runNativeImplementation(ctx *cli.Context) error {
clientlog.Debug("Starting native implementation...")

// Extract the build name and number from the command arguments
args, buildArgs, err := build.ExtractBuildDetailsFromArgs(ctx.Args())
if err != nil {
clientlog.Error("Failed to extract build details from args: ", err)
return fmt.Errorf("ExtractBuildDetailsFromArgs failed: %w", err)
}

if len(args) < 2 {
return fmt.Errorf("insufficient arguments: expected at least package-manager and command, got %v", args)
}

packageManager := args[0]
command := args[1]
clientlog.Debug("Executing native command: " + packageManager + " " + command)

buildName, err := buildArgs.GetBuildName()
if err != nil {
clientlog.Error("Failed to get build name: ", err)
return fmt.Errorf("GetBuildName failed: %w", err)
}

buildNumber, err := buildArgs.GetBuildNumber()
if err != nil {
clientlog.Error("Failed to get build number: ", err)
return fmt.Errorf("GetBuildNumber failed: %w", err)
}

// Execute the native command
err = RunActions(args)
if err != nil {
clientlog.Error("Failed to run actions: ", err)
return fmt.Errorf("RunActions failed: %w", err)
}

// Collect build info if build name and number are provided
if buildName != "" && buildNumber != "" {
clientlog.Info("Collecting build info for executed command...")
workingDir := ctx.GlobalString("working-dir")
if workingDir == "" {
workingDir = "."
}

// Use the enhanced build info collection that supports Poetry
err = buildinfo.GetBuildInfoForPackageManager(packageManager, workingDir, buildArgs)
if err != nil {
clientlog.Error("Failed to collect build info: ", err)
return fmt.Errorf("GetBuildInfoForPackageManager failed: %w", err)
}
}

clientlog.Info("Native implementation completed successfully.")
return nil
}

// isPackageManagerCommand checks if the command is a supported package manager
func isPackageManagerCommand(command string) bool {
supportedPackageManagers := []string{"poetry", "pip", "pipenv", "gem", "bundle", "npm", "yarn", "gradle", "mvn", "maven", "nuget", "go"}
for _, pm := range supportedPackageManagers {
if command == pm {
return true
}
}
return false
}

// cleanProgressOutput handles carriage returns and progress updates properly
func cleanProgressOutput(output string) string {
if output == "" {
return ""
}

// First, split by both \n and \r to handle all line breaks
lines := strings.FieldsFunc(output, func(c rune) bool {
return c == '\n' || c == '\r'
})

var cleanedLines []string
var progressLines = make(map[string]string) // Track progress lines by filename

for _, line := range lines {
line = strings.TrimSpace(line)
if line == "" {
continue
}

// Check if this is a progress line (contains % and "Uploading")
if strings.Contains(line, "Uploading") && strings.Contains(line, "%") {
// Extract filename for tracking progress
if strings.Contains(line, " - Uploading ") {
parts := strings.Split(line, " - Uploading ")
if len(parts) == 2 {
filename := strings.Split(parts[1], " ")[0]
progressLines[filename] = line
continue
}
}
}

// Add non-progress lines immediately
cleanedLines = append(cleanedLines, line)
}

// Add final progress states
for _, progressLine := range progressLines {
cleanedLines = append(cleanedLines, progressLine)
}

if len(cleanedLines) > 0 {
return strings.Join(cleanedLines, "\n") + "\n"
}
return ""
}

func RunActions(args []string) error {
if len(args) < 2 {
return fmt.Errorf("insufficient arguments for RunActions: expected at least 2, got %d", len(args))
}

packageManager := args[0]
subCommand := args[1]
executableCommand := append([]string{}, args[2:]...)

clientlog.Debug("Executing command: " + packageManager + " " + subCommand)
command := gofrogcmd.NewCommand(packageManager, subCommand, executableCommand)

// Use RunCmdWithOutputParser but handle the output better
stdout, stderr, exitOk, err := gofrogcmd.RunCmdWithOutputParser(command, false)
if err != nil {
clientlog.Error("Command execution failed: ", err)
if stderr != "" {
clientlog.Error("Command stderr: ", stderr)
}
return fmt.Errorf("command '%s %s' failed (exitOk=%t): %w", packageManager, subCommand, exitOk, err)
}

// Print stdout directly without parsing to preserve Poetry's output format
if stdout != "" {
fmt.Print(stdout)
}

// Also print stderr, but clean it up for progress information
if stderr != "" {
cleanStderr := cleanProgressOutput(stderr)
if cleanStderr != "" {
fmt.Print(cleanStderr)
}
}

clientlog.Debug("Command executed successfully")
return nil
}

// This command generates and sets an Uber Trace ID token which will be attached as a header to every request.
// This allows users to easily identify which logs on the server side are related to the command executed by the CLI.
func setUberTraceIdToken() error {
Expand Down
Loading
Loading