Skip to content

Commit

Permalink
Internal cli code (#2503)
Browse files Browse the repository at this point in the history
  • Loading branch information
sverdlov93 committed Mar 28, 2024
1 parent 26ead66 commit 4a0a9a4
Show file tree
Hide file tree
Showing 7 changed files with 173 additions and 26 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/analysis.yml
Expand Up @@ -48,7 +48,7 @@ jobs:
go-version: 1.20.x
cache: false
- name: Static Code Analysis
uses: golangci/golangci-lint-action@v3
uses: golangci/golangci-lint-action@v4
with:
args: |
--timeout 5m --out-${NO_FUTURE}format colored-line-number --enable errcheck,gosimple,govet,ineffassign,staticcheck,typecheck,unused,gocritic,asasalint,asciicheck,errchkjson,exportloopref,forcetypeassert,makezero,nilerr,unparam,unconvert,wastedassign,usestdlibvars
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/nugetTests.yml
Expand Up @@ -28,7 +28,7 @@ jobs:
go-version: 1.20.x
cache: false
- name: Install NuGet
uses: nuget/setup-nuget@v1
uses: nuget/setup-nuget@v2
with:
nuget-version: 6.x
- name: Install dotnet
Expand Down
7 changes: 7 additions & 0 deletions docs/general/ai/help.go
@@ -0,0 +1,7 @@
package ai

var Usage = []string{"how"}

func GetDescription() string {
return "Ask questions about JFrog CLI commands and their usage."
}
129 changes: 129 additions & 0 deletions general/ai/cli.go
@@ -0,0 +1,129 @@
package ai

import (
"bytes"
"encoding/json"
"errors"
"fmt"
"github.com/jfrog/jfrog-cli-core/v2/utils/coreutils"
"github.com/jfrog/jfrog-cli-core/v2/utils/ioutils"
"github.com/jfrog/jfrog-cli/utils/cliutils"
"github.com/jfrog/jfrog-client-go/http/httpclient"
"github.com/jfrog/jfrog-client-go/utils/errorutils"
"github.com/jfrog/jfrog-client-go/utils/log"
"github.com/urfave/cli"
"io"
"net/http"
"strings"
)

type ApiCommand string

const (
cliAiApiPath = "https://cli-ai.jfrog.info/"
questionApi ApiCommand = "ask"
feedbackApi ApiCommand = "feedback"
)

type questionBody struct {
Question string `json:"question"`
}

type feedbackBody struct {
questionBody
LlmAnswer string `json:"llm_answer"`
IsAccurate bool `json:"is_accurate"`
ExpectedAnswer string `json:"expected_answer"`
}

func HowCmd(c *cli.Context) error {
if show, err := cliutils.ShowCmdHelpIfNeeded(c, c.Args()); show || err != nil {
return err
}
if c.NArg() < 1 {
return cliutils.WrongNumberOfArgumentsHandler(c)
}

args := cliutils.ExtractCommand(c)
question := questionBody{Question: fmt.Sprintf("How %s", strings.Join(args, " "))}
llmAnswer, err := askQuestion(question)
if err != nil {
return err
}
log.Output("AI generated JFrog CLI command:")
err = coreutils.PrintTable("", "", coreutils.PrintTitle(llmAnswer), false)
if err != nil {
return err
}

feedback := feedbackBody{questionBody: question, LlmAnswer: llmAnswer}
feedback.getUserFeedback()
if err = sendFeedback(feedback); err != nil {
return err
}
log.Output("Thank you for your feedback!")
return nil
}

func (fb *feedbackBody) getUserFeedback() {
fb.IsAccurate = coreutils.AskYesNo(coreutils.PrintLink("Is the provided command accurate?"), true)
if !fb.IsAccurate {
ioutils.ScanFromConsole("Please provide the exact command you expected (Example: 'jf rt u ...')", &fb.ExpectedAnswer, "")
}
}

func askQuestion(question questionBody) (response string, err error) {
return sendRequestToCliAiServer(question, questionApi)
}

func sendFeedback(feedback feedbackBody) (err error) {
_, err = sendRequestToCliAiServer(feedback, feedbackApi)
return
}

func sendRequestToCliAiServer(content interface{}, apiCommand ApiCommand) (response string, err error) {
contentBytes, err := json.Marshal(content)
if errorutils.CheckError(err) != nil {
return
}
client, err := httpclient.ClientBuilder().Build()
if errorutils.CheckError(err) != nil {
return
}
req, err := http.NewRequest(http.MethodPost, cliAiApiPath+string(apiCommand), bytes.NewBuffer(contentBytes))
if errorutils.CheckError(err) != nil {
return
}
req.Header.Set("Content-Type", "application/json")
log.Debug(fmt.Sprintf("Sending HTTP %s request to: %s", req.Method, req.URL))
resp, err := client.GetClient().Do(req)
if errorutils.CheckError(err) != nil {
return
}
if resp == nil {
err = errorutils.CheckErrorf("received empty response from server")
return
}
if err = errorutils.CheckResponseStatus(resp, http.StatusOK); err != nil {
if resp.StatusCode == http.StatusInternalServerError {
err = errorutils.CheckErrorf("AI model Endpoint is not available.\n" + err.Error())
} else if resp.StatusCode == http.StatusNotFound {
err = errorutils.CheckErrorf("CLI-AI app server is no available. Note that the this command is supported while inside JFrog's internal network only.\n" + err.Error())
}
return
}
if apiCommand == questionApi {
defer func() {
if resp.Body != nil {
err = errors.Join(err, errorutils.CheckError(resp.Body.Close()))
}
}()
var body []byte
body, err = io.ReadAll(resp.Body)
if errorutils.CheckError(err) != nil {
return
}
response = string(body)
}
return
}
20 changes: 10 additions & 10 deletions go.mod
Expand Up @@ -8,17 +8,17 @@ require (
github.com/go-git/go-git/v5 v5.11.0
github.com/gocarina/gocsv v0.0.0-20231116093920-b87c2d0e983a
github.com/jfrog/archiver/v3 v3.6.0
github.com/jfrog/build-info-go v1.9.24
github.com/jfrog/build-info-go v1.9.25
github.com/jfrog/gofrog v1.6.3
github.com/jfrog/jfrog-cli-core/v2 v2.49.0
github.com/jfrog/jfrog-cli-security v1.0.4
github.com/jfrog/jfrog-client-go v1.38.0
github.com/jfrog/jfrog-cli-core/v2 v2.50.0
github.com/jfrog/jfrog-cli-security v1.0.5
github.com/jfrog/jfrog-client-go v1.39.0
github.com/jszwec/csvutil v1.10.0
github.com/stretchr/testify v1.9.0
github.com/testcontainers/testcontainers-go v0.23.0
github.com/urfave/cli v1.22.14
github.com/xeipuuv/gojsonschema v1.2.0
golang.org/x/exp v0.0.0-20240318143956-a85f2c67cd81
golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8
golang.org/x/term v0.18.0
gopkg.in/yaml.v2 v2.4.0
)
Expand Down Expand Up @@ -51,7 +51,7 @@ require (
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/felixge/httpsnoop v1.0.3 // indirect
github.com/forPelevin/gomoji v1.1.8 // indirect
github.com/forPelevin/gomoji v1.2.0 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-git/go-billy/v5 v5.5.0 // indirect
Expand All @@ -66,7 +66,7 @@ require (
github.com/gookit/color v1.5.4 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/jedib0t/go-pretty/v6 v6.5.5 // indirect
github.com/jedib0t/go-pretty/v6 v6.5.6 // indirect
github.com/jfrog/jfrog-apps-config v1.0.1 // indirect
github.com/kevinburke/ssh_config v1.2.0 // indirect
github.com/klauspost/compress v1.17.4 // indirect
Expand Down Expand Up @@ -137,11 +137,11 @@ require (
gopkg.in/yaml.v3 v3.0.1 // indirect
)

replace github.com/jfrog/jfrog-cli-core/v2 => github.com/jfrog/jfrog-cli-core/v2 v2.31.1-0.20240327081155-4a0e1442bf74
// replace github.com/jfrog/jfrog-cli-core/v2 => github.com/jfrog/jfrog-cli-core/v2 v2.31.1-0.20240327155856-8054e0dc39f5

replace github.com/jfrog/jfrog-client-go => github.com/jfrog/jfrog-client-go v1.28.1-0.20240320102352-af2f392bb490
// replace github.com/jfrog/jfrog-client-go => github.com/jfrog/jfrog-client-go v1.28.1-0.20240327154209-77a304635e42

replace github.com/jfrog/jfrog-cli-security => github.com/jfrog/jfrog-cli-security v1.0.5-0.20240321101458-c86ea9b28dfd
// replace github.com/jfrog/jfrog-cli-security => github.com/jfrog/jfrog-cli-security v1.0.5-0.20240321101458-c86ea9b28dfd

// replace github.com/jfrog/build-info-go => github.com/jfrog/build-info-go dev

Expand Down
28 changes: 14 additions & 14 deletions go.sum
Expand Up @@ -80,8 +80,8 @@ github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk=
github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/forPelevin/gomoji v1.1.8 h1:JElzDdt0TyiUlecy6PfITDL6eGvIaxqYH1V52zrd0qQ=
github.com/forPelevin/gomoji v1.1.8/go.mod h1:8+Z3KNGkdslmeGZBC3tCrwMrcPy5GRzAD+gL9NAwMXg=
github.com/forPelevin/gomoji v1.2.0 h1:9k4WVSSkE1ARO/BWywxgEUBvR/jMnao6EZzrql5nxJ8=
github.com/forPelevin/gomoji v1.2.0/go.mod h1:8+Z3KNGkdslmeGZBC3tCrwMrcPy5GRzAD+gL9NAwMXg=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
Expand Down Expand Up @@ -125,22 +125,22 @@ github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jedib0t/go-pretty/v6 v6.5.5 h1:PpIU8lOjxvVYGGKule0QxxJfNysUSbC9lggQU2cpZJc=
github.com/jedib0t/go-pretty/v6 v6.5.5/go.mod h1:5LQIxa52oJ/DlDSLv0HEkWOFMDGoWkJb9ss5KqPpJBg=
github.com/jedib0t/go-pretty/v6 v6.5.6 h1:nKXVLqPfAwY7sWcYXdNZZZ2fjqDpAtj9UeWupgfUxSg=
github.com/jedib0t/go-pretty/v6 v6.5.6/go.mod h1:5LQIxa52oJ/DlDSLv0HEkWOFMDGoWkJb9ss5KqPpJBg=
github.com/jfrog/archiver/v3 v3.6.0 h1:OVZ50vudkIQmKMgA8mmFF9S0gA47lcag22N13iV3F1w=
github.com/jfrog/archiver/v3 v3.6.0/go.mod h1:fCAof46C3rAXgZurS8kNRNdSVMKBbZs+bNNhPYxLldI=
github.com/jfrog/build-info-go v1.9.24 h1:MjT+4bYecbNQ+dbLczg0lkE5DoLAhdyrF0cRXtnEJqI=
github.com/jfrog/build-info-go v1.9.24/go.mod h1:CaCKqcg3V2W9/ZysE4ZvXZMgsvunclhjrTTQQGp3CzM=
github.com/jfrog/build-info-go v1.9.25 h1:IkjydGQA/HjOWjRaoKq1hOEgCCyBEJwQgXJSo4WVBSA=
github.com/jfrog/build-info-go v1.9.25/go.mod h1:doFB4bFDVHeGulD6GF9LzsrRaIOrSoklV9DgIAEqHgc=
github.com/jfrog/gofrog v1.6.3 h1:F7He0+75HcgCe6SGTSHLFCBDxiE2Ja0tekvvcktW6wc=
github.com/jfrog/gofrog v1.6.3/go.mod h1:SZ1EPJUruxrVGndOzHd+LTiwWYKMlHqhKD+eu+v5Hqg=
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-core/v2 v2.31.1-0.20240327081155-4a0e1442bf74 h1:8hyoBxxXywmAzLXGMmmjFxXmp//KMBQgDITbqAN16LE=
github.com/jfrog/jfrog-cli-core/v2 v2.31.1-0.20240327081155-4a0e1442bf74/go.mod h1:XZP7fmNBBoieQTUE2p2mvA8h/CFO5z4PE7KW1s2cdNk=
github.com/jfrog/jfrog-cli-security v1.0.5-0.20240321101458-c86ea9b28dfd h1:yr3nfponKdXZ2hSBE9CT9EaVCUUpHDJQC5hPgsLqQSg=
github.com/jfrog/jfrog-cli-security v1.0.5-0.20240321101458-c86ea9b28dfd/go.mod h1:P3r0ebMCrBiLBXdwq2j4eVqySEWd4NzDleUkvP22V5I=
github.com/jfrog/jfrog-client-go v1.28.1-0.20240320102352-af2f392bb490 h1:oGgwRJatirSNZyqO3e4FtHCe5W30VNgULCW/GYhHdao=
github.com/jfrog/jfrog-client-go v1.28.1-0.20240320102352-af2f392bb490/go.mod h1:8z6in1qalzL1DqchUCrDKVgz2gKoPRhJpzm2Ww+VWYI=
github.com/jfrog/jfrog-cli-core/v2 v2.50.0 h1:QmjSIktMKAbNH7OGY+eVZKx9husqgMANSI5kB8MlvlA=
github.com/jfrog/jfrog-cli-core/v2 v2.50.0/go.mod h1:95AsjwlMLNWU0v71/3dS715e1RAQfvPO47RRHz2xKh8=
github.com/jfrog/jfrog-cli-security v1.0.5 h1:tBJWiSTiBYpFB5LlHTUeBZ1wYdnrsG0ILNcHGKCiPgc=
github.com/jfrog/jfrog-cli-security v1.0.5/go.mod h1:ipG+b3qQqF0M8SFKkZ7DlMUsgMpdL+iIHc40j9JEs3E=
github.com/jfrog/jfrog-client-go v1.39.0 h1:GZ1qbpUDzYz8ZEycYicDkbVMN2H0VSCuz8mUNTyf7tc=
github.com/jfrog/jfrog-client-go v1.39.0/go.mod h1:tUyEmxznphh0nwAGo6xz9Sps7RRW/TBMxIJZteo+j2k=
github.com/jszwec/csvutil v1.10.0 h1:upMDUxhQKqZ5ZDCs/wy+8Kib8rZR8I8lOR34yJkdqhI=
github.com/jszwec/csvutil v1.10.0/go.mod h1:/E4ONrmGkwmWsk9ae9jpXnv9QT8pLHEPcCirMFhxG9I=
github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
Expand Down Expand Up @@ -315,8 +315,8 @@ golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2Uz
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
golang.org/x/exp v0.0.0-20240318143956-a85f2c67cd81 h1:6R2FC06FonbXQ8pK11/PDFY6N6LWlf9KlzibaCapmqc=
golang.org/x/exp v0.0.0-20240318143956-a85f2c67cd81/go.mod h1:CQ1k9gNrJ50XIzaKCRR2hssIjF07kZFEiieALBM/ARQ=
golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 h1:aAcj0Da7eBAtrTp03QXWvm88pSyOt+UgdZw2BFZ+lEw=
golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8/go.mod h1:CQ1k9gNrJ50XIzaKCRR2hssIjF07kZFEiieALBM/ARQ=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
Expand Down
11 changes: 11 additions & 0 deletions main.go
Expand Up @@ -2,6 +2,7 @@ package main

import (
"fmt"
"github.com/jfrog/jfrog-cli/general/ai"
"os"
"runtime"
"sort"
Expand All @@ -21,6 +22,7 @@ import (
"github.com/jfrog/jfrog-cli/config"
"github.com/jfrog/jfrog-cli/distribution"
"github.com/jfrog/jfrog-cli/docs/common"
aiDocs "github.com/jfrog/jfrog-cli/docs/general/ai"
"github.com/jfrog/jfrog-cli/docs/general/cisetup"
loginDocs "github.com/jfrog/jfrog-cli/docs/general/login"
tokenDocs "github.com/jfrog/jfrog-cli/docs/general/token"
Expand Down Expand Up @@ -280,6 +282,15 @@ func getCommands() ([]cli.Command, error) {
Category: otherCategory,
Action: login.LoginCmd,
},
{
Hidden: true,
Name: "how",
Usage: aiDocs.GetDescription(),
HelpName: corecommon.CreateUsage("how", aiDocs.GetDescription(), aiDocs.Usage),
BashComplete: corecommon.CreateBashCompletionFunc(),
Category: otherCategory,
Action: ai.HowCmd,
},
{
Name: "access-token-create",
Aliases: []string{"atc"},
Expand Down

0 comments on commit 4a0a9a4

Please sign in to comment.