Skip to content

Commit 4b8f4bd

Browse files
committed
Merge remote-tracking branch 'origin/dev'
2 parents d3e1554 + 893219d commit 4b8f4bd

File tree

12 files changed

+1730
-10
lines changed

12 files changed

+1730
-10
lines changed

general/token/cli.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ package token
33
import (
44
"errors"
55
"fmt"
6+
"os"
7+
"strconv"
8+
69
commonCliUtils "github.com/jfrog/jfrog-cli-core/v2/common/cliutils"
710
"github.com/jfrog/jfrog-cli-core/v2/common/commands"
811
generic "github.com/jfrog/jfrog-cli-core/v2/general/token"
@@ -14,8 +17,6 @@ import (
1417
"github.com/jfrog/jfrog-client-go/utils/errorutils"
1518
"github.com/jfrog/jfrog-client-go/utils/log"
1619
"github.com/urfave/cli"
17-
"os"
18-
"strconv"
1920
)
2021

2122
func AccessTokenCreateCmd(c *cli.Context) error {

go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -242,9 +242,9 @@ require (
242242
sigs.k8s.io/yaml v1.4.0 // indirect
243243
)
244244

245-
// replace github.com/jfrog/build-info-go => github.com/jfrog/build-info-go v1.10.17-0.20250829085619-7e78b7bc67f8
245+
replace github.com/jfrog/build-info-go => github.com/jfrog/build-info-go v1.8.9-0.20250907205057-2bfe50c014b7
246246

247-
// replace github.com/jfrog/jfrog-cli-artifactory => github.com/jfrog/jfrog-cli-artifactory v0.4.1-0.20250718083259-4a60768eb51b
247+
replace github.com/jfrog/jfrog-cli-artifactory => github.com/jfrog/jfrog-cli-artifactory v0.7.2-0.20250907212025-fb4679f280a0
248248

249249
// replace github.com/jfrog/jfrog-cli-core/v2 => github.com/jfrog/jfrog-cli-core/v2 v2.59.2-0.20250804083101-9cf424ecc926
250250

go.sum

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -349,8 +349,8 @@ github.com/jellydator/ttlcache/v3 v3.3.0 h1:BdoC9cE81qXfrxeb9eoJi9dWrdhSuwXMAnHT
349349
github.com/jellydator/ttlcache/v3 v3.3.0/go.mod h1:bj2/e0l4jRnQdrnSTaGTsh4GSXvMjQcy41i7th0GVGw=
350350
github.com/jfrog/archiver/v3 v3.6.1 h1:LOxnkw9pOn45DzCbZNFV6K0+6dCsQ0L8mR3ZcujO5eI=
351351
github.com/jfrog/archiver/v3 v3.6.1/go.mod h1:VgR+3WZS4N+i9FaDwLZbq+jeU4B4zctXL+gL4EMzfLw=
352-
github.com/jfrog/build-info-go v1.10.17 h1:wnVd9KkyFGQgNL+oU1wXyJB7/Ui9O/MqUnNKUMsyoRw=
353-
github.com/jfrog/build-info-go v1.10.17/go.mod h1:szdz9+WzB7+7PGnILLUgyY+OF5qD5geBT7UGNIxibyw=
352+
github.com/jfrog/build-info-go v1.8.9-0.20250907205057-2bfe50c014b7 h1:AH31gF//1Dv0ETrT2I4XLR4bCbG6anvw7tEEO8SkHzo=
353+
github.com/jfrog/build-info-go v1.8.9-0.20250907205057-2bfe50c014b7/go.mod h1:szdz9+WzB7+7PGnILLUgyY+OF5qD5geBT7UGNIxibyw=
354354
github.com/jfrog/froggit-go v1.20.3 h1:U3HHT0+AEHUVSSyQBbagQR4fLRqGqzSptPujDZuuDTk=
355355
github.com/jfrog/froggit-go v1.20.3/go.mod h1:obSG1SlsWjktkuqmKtpq7MNTTL63e0ot+ucTnlOMV88=
356356
github.com/jfrog/go-mockhttp v0.3.1 h1:/wac8v4GMZx62viZmv4wazB5GNKs+GxawuS1u3maJH8=
@@ -359,8 +359,8 @@ github.com/jfrog/gofrog v1.7.6 h1:QmfAiRzVyaI7JYGsB7cxfAJePAZTzFz0gRWZSE27c6s=
359359
github.com/jfrog/gofrog v1.7.6/go.mod h1:ntr1txqNOZtHplmaNd7rS4f8jpA5Apx8em70oYEe7+4=
360360
github.com/jfrog/jfrog-apps-config v1.0.1 h1:mtv6k7g8A8BVhlHGlSveapqf4mJfonwvXYLipdsOFMY=
361361
github.com/jfrog/jfrog-apps-config v1.0.1/go.mod h1:8AIIr1oY9JuH5dylz2S6f8Ym2MaadPLR6noCBO4C22w=
362-
github.com/jfrog/jfrog-cli-artifactory v0.7.1 h1:MNKrC21z/sfnIGZ9wopFEMuOHAgWPzSA1AXznOo5Zik=
363-
github.com/jfrog/jfrog-cli-artifactory v0.7.1/go.mod h1:tmhbLMaRjB6ENFjDTKwbqVqYAHBuZxe8vCu2iSTPGvE=
362+
github.com/jfrog/jfrog-cli-artifactory v0.7.2-0.20250907212025-fb4679f280a0 h1:Q549dc6kE6JBbZUVDMK6Nlr8N6MdGt8jxpR3KGF9LCg=
363+
github.com/jfrog/jfrog-cli-artifactory v0.7.2-0.20250907212025-fb4679f280a0/go.mod h1:8HIp5gFglg9wKaYHJS8jgawXv5uwoVZZUo6M8vDzLCQ=
364364
github.com/jfrog/jfrog-cli-core/v2 v2.59.7 h1:qn4HVqP5L9GQ5py0ZeJzXkc3UXq4cHT20AJnwcco7ys=
365365
github.com/jfrog/jfrog-cli-core/v2 v2.59.7/go.mod h1:W6o+7kLLy7GEWhCN9I2vzjGRGeeOZzsJ0uMaKCyr1CI=
366366
github.com/jfrog/jfrog-cli-platform-services v1.10.0 h1:O+N/VAF+QjFvq9xkHpmzKLcdl9aJu3IP204Su0L14rw=

main.go

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ import (
1010
"strings"
1111

1212
"github.com/agnivade/levenshtein"
13+
gofrogcmd "github.com/jfrog/gofrog/io"
1314
artifactoryCLI "github.com/jfrog/jfrog-cli-artifactory/cli"
15+
"github.com/jfrog/jfrog-cli-core/v2/common/build"
1416
corecommon "github.com/jfrog/jfrog-cli-core/v2/docs/common"
1517
"github.com/jfrog/jfrog-cli-core/v2/plugins/components"
1618
coreconfig "github.com/jfrog/jfrog-cli-core/v2/utils/config"
@@ -36,6 +38,7 @@ import (
3638
"github.com/jfrog/jfrog-cli/pipelines"
3739
"github.com/jfrog/jfrog-cli/plugins"
3840
"github.com/jfrog/jfrog-cli/plugins/utils"
41+
"github.com/jfrog/jfrog-cli/utils/buildinfo"
3942
"github.com/jfrog/jfrog-cli/utils/cliutils"
4043
"github.com/jfrog/jfrog-client-go/http/httpclient"
4144
clientutils "github.com/jfrog/jfrog-client-go/utils"
@@ -129,13 +132,219 @@ func execMain() error {
129132
if err = setUberTraceIdToken(); err != nil {
130133
clientlog.Warn("failed generating a trace ID token:", err.Error())
131134
}
135+
if os.Getenv("JFROG_RUN_NATIVE") == "true" {
136+
// If the JFROG_RUN_NATIVE environment variable is set to true, we run the new implementation
137+
// but only for package manager commands, not for JFrog CLI commands
138+
args := ctx.Args()
139+
if args.Present() && len(args) > 0 {
140+
firstArg := args.Get(0)
141+
if isPackageManagerCommand(firstArg) {
142+
if err = runNativeImplementation(ctx); err != nil {
143+
clientlog.Error("Failed to run native implementation:", err)
144+
os.Exit(1)
145+
}
146+
os.Exit(0)
147+
}
148+
}
149+
// For non-package-manager commands, continue with normal CLI processing
150+
}
132151
return nil
133152
}
153+
154+
app.CommandNotFound = func(c *cli.Context, command string) {
155+
// Try to handle as native package manager command only when JFROG_RUN_NATIVE is true
156+
if os.Getenv("JFROG_RUN_NATIVE") == "true" && isPackageManagerCommand(command) {
157+
clientlog.Debug("Attempting to handle as native package manager command:", command)
158+
err := runNativeImplementation(c)
159+
if err != nil {
160+
clientlog.Error("Failed to run native implementation:", err)
161+
os.Exit(1)
162+
}
163+
os.Exit(0)
164+
}
165+
166+
// Original behavior for unknown commands
167+
_, err = fmt.Fprintf(c.App.Writer, "'%s %s' is not a jf command. See --help\n", c.App.Name, command)
168+
if err != nil {
169+
clientlog.Debug(err)
170+
os.Exit(1)
171+
}
172+
if bestSimilarity := searchSimilarCmds(c.App.Commands, command); len(bestSimilarity) > 0 {
173+
text := "The most similar "
174+
if len(bestSimilarity) == 1 {
175+
text += "command is:\n\tjf " + bestSimilarity[0]
176+
} else {
177+
sort.Strings(bestSimilarity)
178+
text += "commands are:\n\tjf " + strings.Join(bestSimilarity, "\n\tjf ")
179+
}
180+
_, err = fmt.Fprintln(c.App.Writer, text)
181+
if err != nil {
182+
clientlog.Debug(err)
183+
}
184+
}
185+
os.Exit(1)
186+
}
187+
134188
err = app.Run(args)
135189
logTraceIdOnFailure(err)
136190
return err
137191
}
138192

193+
func runNativeImplementation(ctx *cli.Context) error {
194+
clientlog.Debug("Starting native implementation...")
195+
196+
// Extract the build name and number from the command arguments
197+
args, buildArgs, err := build.ExtractBuildDetailsFromArgs(ctx.Args())
198+
if err != nil {
199+
clientlog.Error("Failed to extract build details from args: ", err)
200+
return fmt.Errorf("ExtractBuildDetailsFromArgs failed: %w", err)
201+
}
202+
203+
if len(args) < 2 {
204+
return fmt.Errorf("insufficient arguments: expected at least package-manager and command, got %v", args)
205+
}
206+
207+
packageManager := args[0]
208+
command := args[1]
209+
clientlog.Debug("Executing native command: " + packageManager + " " + command)
210+
211+
buildName, err := buildArgs.GetBuildName()
212+
if err != nil {
213+
clientlog.Error("Failed to get build name: ", err)
214+
return fmt.Errorf("GetBuildName failed: %w", err)
215+
}
216+
217+
buildNumber, err := buildArgs.GetBuildNumber()
218+
if err != nil {
219+
clientlog.Error("Failed to get build number: ", err)
220+
return fmt.Errorf("GetBuildNumber failed: %w", err)
221+
}
222+
223+
// Execute the native command
224+
err = RunActions(args)
225+
if err != nil {
226+
clientlog.Error("Failed to run actions: ", err)
227+
return fmt.Errorf("RunActions failed: %w", err)
228+
}
229+
230+
// Collect build info if build name and number are provided
231+
if buildName != "" && buildNumber != "" {
232+
clientlog.Info("Collecting build info for executed command...")
233+
workingDir := ctx.GlobalString("working-dir")
234+
if workingDir == "" {
235+
workingDir = "."
236+
}
237+
238+
// Use the enhanced build info collection that supports Poetry
239+
err = buildinfo.GetBuildInfoForPackageManager(packageManager, workingDir, buildArgs)
240+
if err != nil {
241+
clientlog.Error("Failed to collect build info: ", err)
242+
return fmt.Errorf("GetBuildInfoForPackageManager failed: %w", err)
243+
}
244+
}
245+
246+
clientlog.Info("Native implementation completed successfully.")
247+
return nil
248+
}
249+
250+
// isPackageManagerCommand checks if the command is a supported package manager
251+
func isPackageManagerCommand(command string) bool {
252+
supportedPackageManagers := []string{"poetry", "pip", "pipenv", "gem", "bundle", "npm", "yarn", "gradle", "mvn", "maven", "nuget", "go"}
253+
for _, pm := range supportedPackageManagers {
254+
if command == pm {
255+
return true
256+
}
257+
}
258+
return false
259+
}
260+
261+
// cleanProgressOutput handles carriage returns and progress updates properly
262+
func cleanProgressOutput(output string) string {
263+
if output == "" {
264+
return ""
265+
}
266+
267+
// First, split by both \n and \r to handle all line breaks
268+
lines := strings.FieldsFunc(output, func(c rune) bool {
269+
return c == '\n' || c == '\r'
270+
})
271+
272+
var cleanedLines []string
273+
var progressLines = make(map[string]string) // Track progress lines by filename
274+
275+
for _, line := range lines {
276+
line = strings.TrimSpace(line)
277+
if line == "" {
278+
continue
279+
}
280+
281+
// Check if this is a progress line (contains % and "Uploading")
282+
if strings.Contains(line, "Uploading") && strings.Contains(line, "%") {
283+
// Extract filename for tracking progress
284+
if strings.Contains(line, " - Uploading ") {
285+
parts := strings.Split(line, " - Uploading ")
286+
if len(parts) == 2 {
287+
filename := strings.Split(parts[1], " ")[0]
288+
progressLines[filename] = line
289+
continue
290+
}
291+
}
292+
}
293+
294+
// Add non-progress lines immediately
295+
cleanedLines = append(cleanedLines, line)
296+
}
297+
298+
// Add final progress states
299+
for _, progressLine := range progressLines {
300+
cleanedLines = append(cleanedLines, progressLine)
301+
}
302+
303+
if len(cleanedLines) > 0 {
304+
return strings.Join(cleanedLines, "\n") + "\n"
305+
}
306+
return ""
307+
}
308+
309+
func RunActions(args []string) error {
310+
if len(args) < 2 {
311+
return fmt.Errorf("insufficient arguments for RunActions: expected at least 2, got %d", len(args))
312+
}
313+
314+
packageManager := args[0]
315+
subCommand := args[1]
316+
executableCommand := append([]string{}, args[2:]...)
317+
318+
clientlog.Debug("Executing command: " + packageManager + " " + subCommand)
319+
command := gofrogcmd.NewCommand(packageManager, subCommand, executableCommand)
320+
321+
// Use RunCmdWithOutputParser but handle the output better
322+
stdout, stderr, exitOk, err := gofrogcmd.RunCmdWithOutputParser(command, false)
323+
if err != nil {
324+
clientlog.Error("Command execution failed: ", err)
325+
if stderr != "" {
326+
clientlog.Error("Command stderr: ", stderr)
327+
}
328+
return fmt.Errorf("command '%s %s' failed (exitOk=%t): %w", packageManager, subCommand, exitOk, err)
329+
}
330+
331+
// Print stdout directly without parsing to preserve Poetry's output format
332+
if stdout != "" {
333+
fmt.Print(stdout)
334+
}
335+
336+
// Also print stderr, but clean it up for progress information
337+
if stderr != "" {
338+
cleanStderr := cleanProgressOutput(stderr)
339+
if cleanStderr != "" {
340+
fmt.Print(cleanStderr)
341+
}
342+
}
343+
344+
clientlog.Debug("Command executed successfully")
345+
return nil
346+
}
347+
139348
// This command generates and sets an Uber Trace ID token which will be attached as a header to every request.
140349
// This allows users to easily identify which logs on the server side are related to the command executed by the CLI.
141350
func setUberTraceIdToken() error {

0 commit comments

Comments
 (0)