From 3f3859e63750e6e97b192c400fb6b884ff33c28c Mon Sep 17 00:00:00 2001 From: Devashish Taneja Date: Sun, 9 Jul 2023 18:17:33 +0530 Subject: [PATCH] Add support for interactive search --- go.mod | 15 +- go.sum | 53 ++- jira_client.go | 209 ++++++++++++ main.go | 114 ++----- .../savioxavier/termlink/.gitignore | 25 -- .../savioxavier/termlink/CODE_OF_CONDUCT.md | 128 -------- .../github.com/savioxavier/termlink/LICENSE | 21 -- .../github.com/savioxavier/termlink/README.md | 175 ---------- .../savioxavier/termlink/termlink.go | 309 ------------------ vendor/modules.txt | 3 - 10 files changed, 299 insertions(+), 753 deletions(-) create mode 100644 jira_client.go delete mode 100644 vendor/github.com/savioxavier/termlink/.gitignore delete mode 100644 vendor/github.com/savioxavier/termlink/CODE_OF_CONDUCT.md delete mode 100644 vendor/github.com/savioxavier/termlink/LICENSE delete mode 100644 vendor/github.com/savioxavier/termlink/README.md delete mode 100644 vendor/github.com/savioxavier/termlink/termlink.go delete mode 100644 vendor/modules.txt diff --git a/go.mod b/go.mod index 4aeace1..24c70e1 100644 --- a/go.mod +++ b/go.mod @@ -2,4 +2,17 @@ module github.com/devashishTaneja/jira-cli go 1.20 -require github.com/savioxavier/termlink v1.3.0 +require ( + github.com/AlecAivazis/survey/v2 v2.3.7 + github.com/savioxavier/termlink v1.3.0 +) + +require ( + github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect + github.com/mattn/go-colorable v0.1.2 // indirect + github.com/mattn/go-isatty v0.0.8 // indirect + github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect + golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect + golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect + golang.org/x/text v0.4.0 // indirect +) diff --git a/go.sum b/go.sum index 4344c50..695f0ed 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,57 @@ -github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkkhIiSjQ= +github.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo= +github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s= +github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w= +github.com/creack/pty v1.1.17 h1:QeVUsEDNrLBW4tMgZHvxy18sKtr6VI492kBhUfhDJNI= +github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog= +github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= +github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU= +github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4= +github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/savioxavier/termlink v1.3.0 h1:3Gl4FzQjUyiHzmoEDfmWEhgIwDiJY4poOQHP+k8ReA4= github.com/savioxavier/termlink v1.3.0/go.mod h1:5T5ePUlWbxCHIwyF8/Ez1qufOoGM89RCg9NvG+3G3gc= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/jira_client.go b/jira_client.go new file mode 100644 index 0000000..0908cfa --- /dev/null +++ b/jira_client.go @@ -0,0 +1,209 @@ +package main + +import ( + "encoding/json" + "fmt" + "github.com/AlecAivazis/survey/v2" + "log" + "math" + "net/http" + "net/url" + "os/exec" + "runtime" +) + +type Credential struct { + ApiUser string + ApiKey string +} + +type JiraClient struct { + Domain string + Credential Credential +} + +// Issue By Issue Picker - struct +type Issue struct { + SummaryText, Key string +} + +type Section struct { + Label, Sub, Id string + Issues []Issue +} + +type IssuePickerResp struct { + Sections []Section +} + +// Issue By JQL - struct + +type Fields struct { + Summary string +} + +type IssueJql struct { + Key string + Fields Fields +} + +type IssueJqlResp struct { + Issues []IssueJql +} + +func (j *JiraClient) call(req *http.Request) (*http.Response, error) { + client := http.Client{} + req.SetBasicAuth(j.Credential.ApiUser, j.Credential.ApiKey) + req.Header.Set("Accept", "application/json") + req.Header.Set("Content-Type", "application/json") + return client.Do(req) +} + +func (j *JiraClient) search() { + args := getUserInput() + issues := j.getIssuesUsingIssuePicker(args) + selectedIssueKey := loadIssueListPrompt(issues) + if len(selectedIssueKey) > 0 { + j.openIssueInBrowser(selectedIssueKey) + } +} + +func (j *JiraClient) currentUserSearch(jql string) { + issues := j.getIssuesUsingJql(jql) + selectedIssueKey := loadIssueListPrompt(issues[:]) + if len(selectedIssueKey) > 0 { + j.openIssueInBrowser(selectedIssueKey) + } +} + +func (j *JiraClient) advancedJqlSearch(jql string) { + if len(jql) == 0 { + jql = getUserInput() + } + issues := j.getIssuesUsingJql(jql) + selectedIssueKey := loadIssueListPrompt(issues[:]) + if len(selectedIssueKey) > 0 { + j.openIssueInBrowser(selectedIssueKey) + } +} + +func (j *JiraClient) openIssueInBrowser(issueKey string) { + jiraLink := fmt.Sprintf("%s/browse/%s", j.Domain, issueKey) + fmt.Printf("Opening %s in browser\n", issueKey) + openbrowser(jiraLink) +} + +// https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issue-search/#api-rest-api-3-issue-picker-get +func (j *JiraClient) getIssuesUsingIssuePicker(args string) []Issue { + // Call Rest API + requestUrl := fmt.Sprintf("%s/rest/api/3/issue/picker?query=%s", j.Domain, url.QueryEscape(args)) + req, err := http.NewRequest("GET", requestUrl, nil) + resp, err := j.call(req) + if err != nil || resp.StatusCode != 200 { + log.Fatal("Error Occurred: ", resp.StatusCode) + } + defer resp.Body.Close() + + var issuePickerResp IssuePickerResp + err = json.NewDecoder(resp.Body).Decode(&issuePickerResp) + if err != nil { + log.Fatal(err) + } + return issuePickerResp.Sections[0].Issues +} + +func (j *JiraClient) getIssuesUsingJql(jql string) []Issue { + // Call Rest API + requestUrl := fmt.Sprintf("%s/rest/api/3/search?jql=%s", j.Domain, url.QueryEscape(jql)) + req, err := http.NewRequest("GET", requestUrl, nil) + resp, err := j.call(req) + if err != nil || resp.StatusCode != 200 { + log.Println("Error Occurred: No Issues Found") + return []Issue{} + } + defer resp.Body.Close() + + var issueJqlResp IssueJqlResp + err = json.NewDecoder(resp.Body).Decode(&issueJqlResp) + if err != nil { + log.Fatal(err) + } + + issuesJql := issueJqlResp.Issues + issuesLen := math.Min(5, float64(len(issuesJql))) + issues := make([]Issue, int(issuesLen)) + for i, issueJql := range issuesJql { + if i == 5 { + break + } + issues[i] = Issue{ + SummaryText: issueJql.Fields.Summary, + Key: issueJql.Key, + } + } + + return issues[:] +} + +func loadIssueListPrompt(issues []Issue) string { + if len(issues) == 0 { + return "" + } + + // Create a prompt for JIRA Link + descriptions := make(map[string]string) + issuesLen := math.Min(5, float64(len(issues))) + options := make([]string, int(issuesLen)) + + for i, issue := range issues { + if i == 5 { + break + } + issueKey := issue.Key + options[i] = issueKey + descriptions[issueKey] = issue.SummaryText + } + + issueKey := "" + prompt := &survey.Select{ + Message: "Select Issue:", + Options: options[:], + Description: func(value string, index int) string { + return descriptions[value] + }, + } + err := survey.AskOne(prompt, &issueKey) + + if err != nil { + log.Fatal(err) + } + + return issueKey +} + +func openbrowser(url string) { + var err error + switch runtime.GOOS { + case "linux": + err = exec.Command("xdg-open", url).Start() + case "windows": + err = exec.Command("rundll32", "url.dll,FileProtocolHandler", url).Start() + case "darwin": + err = exec.Command("open", url).Start() + default: + err = fmt.Errorf("unsupported platform") + } + if err != nil { + log.Fatal(err) + } +} + +func getUserInput() string { + var userInput = "" + prompt := &survey.Input{ + Renderer: survey.Renderer{}, + Message: "Enter Query Here: ", + } + survey.AskOne(prompt, &userInput) + return userInput +} diff --git a/main.go b/main.go index d1794de..2be09dc 100644 --- a/main.go +++ b/main.go @@ -1,108 +1,42 @@ package main import ( - "encoding/base64" - "encoding/json" - "fmt" - "github.com/savioxavier/termlink" + "github.com/AlecAivazis/survey/v2" "log" - "net/http" "os" - "os/exec" - "runtime" - "strings" ) -type issue struct { - SummaryText, Key string -} - -type section struct { - Label, Sub, Id string - Issues []issue -} - -type issuePickerResp struct { - Sections []section -} - -func openbrowser(url string) { - var err error - switch runtime.GOOS { - case "linux": - err = exec.Command("xdg-open", url).Start() - case "windows": - err = exec.Command("rundll32", "url.dll,FileProtocolHandler", url).Start() - case "darwin": - err = exec.Command("open", url).Start() - default: - err = fmt.Errorf("unsupported platform") +func executePrompt() { + option := "" + prompt := &survey.Select{ + Message: "Select option:", + Options: []string{"My Issues", "Search Issue", "Advanced Search"}, } + err := survey.AskOne(prompt, &option) if err != nil { - log.Fatal(err) + return } -} - -func jiraSearch(args []string) { - jiraDomain := os.Getenv("JIRA_DOMAIN") - username := os.Getenv("JIRA_API_USER") - apikey := os.Getenv("JIRA_API_KEY") - base64Auth := "Basic " + base64.StdEncoding.EncodeToString([]byte(username + ":" + apikey)) - queryUrl := jiraDomain + "/rest/api/3/issue/picker?query=" + strings.Join(args, "%20") - - client := http.Client{} - req, err := http.NewRequest("GET", queryUrl, nil) - req.Header.Add("Authorization", base64Auth) - req.Header.Add("Accept", "application/json") - req.Header.Add("Content-Type", "application/json") - resp, err := client.Do(req) - if err != nil || resp.StatusCode != 200 { - fmt.Println("Error Occured: ", resp.StatusCode) - os.Exit(1) + jiraClient := JiraClient{ + Domain: os.Getenv("JIRA_DOMAIN"), + Credential: Credential{ + ApiUser: os.Getenv("JIRA_API_USER"), + ApiKey: os.Getenv("JIRA_API_KEY"), + }, } - defer resp.Body.Close() - - var issuePickerResp issuePickerResp - err = json.NewDecoder(resp.Body).Decode(&issuePickerResp) - - if err != nil { - log.Fatal(err) - } - - issues := issuePickerResp.Sections[0].Issues - issueLen := len(issues) - if issueLen > 5 { - issueLen = 5 - } - - if issueLen > 1 { - for i := 0; i < issueLen; i++ { - fmt.Println("______________________________________________") - issue := issues[i] - fmt.Println(issue.SummaryText) - jiraLink := getJiraLink(issue.Key) - fmt.Println(termlink.ColorLink(issue.Key, jiraLink, "red")) - } - } else if issueLen == 1 { - openbrowser(getJiraLink(issues[0].Key)) - } else { - fmt.Println("No related issues found") + switch option { + case "My Issues": + jiraClient.advancedJqlSearch("assignee in (currentUser()) ORDER BY created desc") + case "Search Issue": + jiraClient.search() + case "Advanced Search": + jiraClient.advancedJqlSearch("") + default: + log.Fatal("Invalid option") } } -func getJiraLink(issueKey string) string{ - jiraLink := os.Getenv("JIRA_DOMAIN") + "/browse/" + issueKey - return jiraLink -} - func main() { - args := os.Args - - if len(args) < 2 { - log.Fatal("Missing Arguments") - } - - jiraSearch(args[1:]) + executePrompt() } diff --git a/vendor/github.com/savioxavier/termlink/.gitignore b/vendor/github.com/savioxavier/termlink/.gitignore deleted file mode 100644 index 1bc1fa7..0000000 --- a/vendor/github.com/savioxavier/termlink/.gitignore +++ /dev/null @@ -1,25 +0,0 @@ -# Binaries for programs and plugins -*.exe -*.exe~ -*.dll -*.so -*.dylib - -# Test binary, built with `go test -c` -*.test - -# Output of the go coverage tool, specifically when used with LiteIDE -*.out - -# Temp directories -tmp/ - -# Dependency directories -vendor/ - -# Editor files -.idea/ -.vscode/ - -# Go workspace file -go.work \ No newline at end of file diff --git a/vendor/github.com/savioxavier/termlink/CODE_OF_CONDUCT.md b/vendor/github.com/savioxavier/termlink/CODE_OF_CONDUCT.md deleted file mode 100644 index 149adc5..0000000 --- a/vendor/github.com/savioxavier/termlink/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,128 +0,0 @@ -# Contributor Covenant Code of Conduct - -## Our Pledge - -We as members, contributors, and leaders pledge to make participation in our -community a harassment-free experience for everyone, regardless of age, body -size, visible or invisible disability, ethnicity, sex characteristics, gender -identity and expression, level of experience, education, socio-economic status, -nationality, personal appearance, race, religion, or sexual identity -and orientation. - -We pledge to act and interact in ways that contribute to an open, welcoming, -diverse, inclusive, and healthy community. - -## Our Standards - -Examples of behavior that contributes to a positive environment for our -community include: - -* Demonstrating empathy and kindness toward other people -* Being respectful of differing opinions, viewpoints, and experiences -* Giving and gracefully accepting constructive feedback -* Accepting responsibility and apologizing to those affected by our mistakes, - and learning from the experience -* Focusing on what is best not just for us as individuals, but for the - overall community - -Examples of unacceptable behavior include: - -* The use of sexualized language or imagery, and sexual attention or - advances of any kind -* Trolling, insulting or derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or email - address, without their explicit permission -* Other conduct which could reasonably be considered inappropriate in a - professional setting - -## Enforcement Responsibilities - -Community leaders are responsible for clarifying and enforcing our standards of -acceptable behavior and will take appropriate and fair corrective action in -response to any behavior that they deem inappropriate, threatening, offensive, -or harmful. - -Community leaders have the right and responsibility to remove, edit, or reject -comments, commits, code, wiki edits, issues, and other contributions that are -not aligned to this Code of Conduct, and will communicate reasons for moderation -decisions when appropriate. - -## Scope - -This Code of Conduct applies within all community spaces, and also applies when -an individual is officially representing the community in public spaces. -Examples of representing our community include using an official e-mail address, -posting via an official social media account, or acting as an appointed -representative at an online or offline event. - -## Enforcement - -Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported to the community leaders responsible for enforcement at -savioxavier112@gmail.com. -All complaints will be reviewed and investigated promptly and fairly. - -All community leaders are obligated to respect the privacy and security of the -reporter of any incident. - -## Enforcement Guidelines - -Community leaders will follow these Community Impact Guidelines in determining -the consequences for any action they deem in violation of this Code of Conduct: - -### 1. Correction - -**Community Impact**: Use of inappropriate language or other behavior deemed -unprofessional or unwelcome in the community. - -**Consequence**: A private, written warning from community leaders, providing -clarity around the nature of the violation and an explanation of why the -behavior was inappropriate. A public apology may be requested. - -### 2. Warning - -**Community Impact**: A violation through a single incident or series -of actions. - -**Consequence**: A warning with consequences for continued behavior. No -interaction with the people involved, including unsolicited interaction with -those enforcing the Code of Conduct, for a specified period of time. This -includes avoiding interactions in community spaces as well as external channels -like social media. Violating these terms may lead to a temporary or -permanent ban. - -### 3. Temporary Ban - -**Community Impact**: A serious violation of community standards, including -sustained inappropriate behavior. - -**Consequence**: A temporary ban from any sort of interaction or public -communication with the community for a specified period of time. No public or -private interaction with the people involved, including unsolicited interaction -with those enforcing the Code of Conduct, is allowed during this period. -Violating these terms may lead to a permanent ban. - -### 4. Permanent Ban - -**Community Impact**: Demonstrating a pattern of violation of community -standards, including sustained inappropriate behavior, harassment of an -individual, or aggression toward or disparagement of classes of individuals. - -**Consequence**: A permanent ban from any sort of public interaction within -the community. - -## Attribution - -This Code of Conduct is adapted from the [Contributor Covenant][homepage], -version 2.0, available at -https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. - -Community Impact Guidelines were inspired by [Mozilla's code of conduct -enforcement ladder](https://github.com/mozilla/diversity). - -[homepage]: https://www.contributor-covenant.org - -For answers to common questions about this code of conduct, see the FAQ at -https://www.contributor-covenant.org/faq. Translations are available at -https://www.contributor-covenant.org/translations. diff --git a/vendor/github.com/savioxavier/termlink/LICENSE b/vendor/github.com/savioxavier/termlink/LICENSE deleted file mode 100644 index 6b110b5..0000000 --- a/vendor/github.com/savioxavier/termlink/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2022 Skyascii - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/vendor/github.com/savioxavier/termlink/README.md b/vendor/github.com/savioxavier/termlink/README.md deleted file mode 100644 index 6f5f0a2..0000000 --- a/vendor/github.com/savioxavier/termlink/README.md +++ /dev/null @@ -1,175 +0,0 @@ - - -# termlink - -> Clickable links in the terminal for Go - -[![Release](https://img.shields.io/github/release/savioxavier/termlink.svg)](https://github.com/savioxavier/termlink/releases/latest) -[![Go Reference](https://pkg.go.dev/badge/github.com/savioxavier/termlink.svg)](https://pkg.go.dev/github.com/savioxavier/termlink) -[![License](https://img.shields.io/github/license/savioxavier/termlink)](/LICENSE) -[![Go Report Card](https://goreportcard.com/badge/github.com/savioxavier/termlink)](https://goreportcard.com/report/github.com/savioxavier/termlink) - -![termlink_demo_nyoom](https://user-images.githubusercontent.com/38729705/163217599-6fecf944-c10e-4546-9669-1c7d047da55e.gif) * - -**Termlink is a Go package that allows you to create fully customizable clickable links in the terminal. It is the Go version of Sindre Sorhus' popular [terminal-link](https://github.com/sindresorhus/terminal-link/) library.** - -**It includes multiple features including dynamic and fully customizable colored links in terminal, along with smart hyperlink support detection.** - -## ๐Ÿ› ๏ธ Install - -Using `go get`: - -```text -go get github.com/savioxavier/termlink -``` - ---- - -## ๐Ÿ”— Usage - -- Basic regular link: - -```go -import ( - "fmt" - - "github.com/savioxavier/termlink" -) - -func main() { - fmt.Println(termlink.Link("Example", "https://example.com")) -} -``` - -- Customizable colored link: - -```go -import ( - "fmt" - - "github.com/savioxavier/termlink" -) - -func main() { - fmt.Println(termlink.ColorLink("Example", "https://example.com", "italic green")) -} -``` - -- Check if your current terminal supports hyperlinks: - -```go -import ( - "fmt" - - "github.com/savioxavier/termlink" -) - -func main() { - fmt.Println(termlink.SupportsHyperlinks()) - // prints either true or false, depending on your terminal -} -``` - -- Since `termlink.Link` returns a string, you can use it with your favorite text color formatting libraries such as [fatih/color](https://github.com/fatih/color), [mgutz/ansi](https://github.com/mgutz/ansi), etc. Alternatively, you can use `termlink.ColorLink` as well. - -```go -import ( - "fmt" - - "github.com/fatih/color" - "github.com/savioxavier/termlink" -) - -func main() { - // With fatih/color package - color.Cyan(termlink.Link("Example link using the fatih/color package", "https://example.com")) -} -``` - -> **Note** -> **For unsupported terminals, the link will be printed in parentheses after the text (see below image)** -> -> ![image](https://user-images.githubusercontent.com/38729705/163216009-abb81d39-aff0-4fb5-8c5f-da36e241b395.png) - ---- - -## ๐Ÿต Examples - -More examples can be found in the [`examples/`](examples/) directory. - -For a quick demo, execute the following commands in your terminal: - -```bash -git clone https://github.com/savioxavier/termlink.git -cd termlink/ -go get github.com/savioxavier/termlink -go run examples/start.go -``` - ---- - -## ๐Ÿ”ฎ Features - -- **`termlink.Link(text, url, [shouldForce])`** - - - Creates a regular, clickable link in the terminal - - For unsupported terminals, the link will be printed in parentheses after the text: `Example Link (https://example.com)`. - - The `shouldForce` is an optional boolean parameter which allows you to force the above unsupported terminal hyperlinks format `text (url)` to be printed, even in supported terminals - -- **`termlink.ColorLink(text, url, color, [shouldForce])`** - - - Creates a clickable link in the terminal with custom color formatting - - Examples of color options include: - - Foreground only: `green`, `red`, `blue`, etc. - - Background only: `bgGreen`, `bgRed`, `bgBlue`, etc. - - Foreground and background: `green bgRed`, `bgBlue red`, etc. - - With formatting: `green bold`, `red bgGreen italic`, `italic blue bgGreen`, etc. - - The `shouldForce` is an optional boolean parameter which allows you to force the above unsupported terminal hyperlinks format `text (url)` to be printed, even in supported terminals - -> `shouldForce` can be used in the following manner: -> -> ```go -> termlink.Link(text, url, true) -> termlink.ColorLink(text, url, color, true) -> ``` -> -> You don't always need to specify this argument. By default, this parameter is `false` - -- **`termlink.SupportsHyperlinks()`**: - - - Returns `true` if the terminal supports hyperlinks, `false` otherwise. - ---- - -## ๐Ÿงช Tests - -You can run unit tests _locally_ by running the following command: - -```bash -go test -v -``` - -Tests can be found in [`termlink_test.go`](./termlink_test.go) - ---- - -## โค๏ธ Support - -You can support further development of this project by **giving it a ๐ŸŒŸ** and help me make even better stuff in the future by **buying me a โ˜•** - - - - - -
- -**Also, if you liked this repo, consider checking out my other projects, that would be real cool!** - ---- - -## ๐Ÿ’ซ Attributions and special thanks - -- [terminal-link](https://github.com/sindresorhus/terminal-link) - Sindre Sorhus' original package for providing inspiration for this package. -- [supports-hyperlinks](https://github.com/zkat/supports-hyperlinks) - Zkat's package for additional hyperlink handling support. - -* The paperclip icon shown in the demo at the top of this README isn't included when you create the link, it's purely for decorative purposes only. diff --git a/vendor/github.com/savioxavier/termlink/termlink.go b/vendor/github.com/savioxavier/termlink/termlink.go deleted file mode 100644 index 88833a8..0000000 --- a/vendor/github.com/savioxavier/termlink/termlink.go +++ /dev/null @@ -1,309 +0,0 @@ -// Package termlink implements a set of functions to create customizable, clickable hyperlinks in the terminal. -package termlink - -import ( - "fmt" - "os" - "strings" -) - -// EnvironmentVariables represent the set of standalone environment variables -// ie, those which do not require any special handling or version checking -var EnvironmentVariables = []string{ - "DOMTERM", - "WT_SESSION", - "KONSOLE_VERSION", -} - -// Version struct represents a semver version (usually, with some exceptions) -// with major, minor, and patch segments -type Version struct { - major int - minor int - patch int -} - -// parseVersion takes a string "version" number and returns -// a Version struct with the major, minor, and patch -// segments parsed from the string. -// If a version number is not provided -func parseVersion(version string) Version { - var major, minor, patch int - fmt.Sscanf(version, "%d.%d.%d", &major, &minor, &patch) - return Version{ - major: major, - minor: minor, - patch: patch, - } -} - -// hasEnvironmentVariables returns true if the environment variable "name" -// is present in the environment, false otherwise -func hasEnv(name string) bool { - _, envExists := os.LookupEnv(name) - - return envExists -} - -// checkAllEnvs returns true if any of the environment variables in the "vars" -// string slice are actually present in the environment, false otherwise -func checkAllEnvs(vars []string) bool { - for _, v := range vars { - if hasEnv(v) { - return true - } - } - - return false -} - -// getEnv returns the value of the environment variable, if it exists -func getEnv(name string) string { - envValue, _ := os.LookupEnv(name) - - return envValue -} - -// matchesEnv returns true if the environment variable "name" matches any -// of the given values in the "values" string slice, false otherwise -func matchesEnv(name string, values []string) bool { - if hasEnv(name) { - for _, value := range values { - if getEnv(name) == value { - return true - } - } - } - return false -} - -func supportsHyperlinks() bool { - // Allow hyperlinks to be forced, independent of any environment variables - // Instead of checking whether it is equal to anything other than "0", - // a set of allowed values are provided, as something like - // FORCE_HYPERLINK="do-not-enable-it" wouldn't make sense if it returned true - if matchesEnv("FORCE_HYPERLINK", []string{"1", "true", "always", "enabled"}) { - return true - } - - // VTE-based terminals (Gnome Terminal, Guake, ROXTerm, etc) - // VTE_VERSION is rendered as four-digit version string - // eg: 0.52.2 => 5202 - // parseVersion will parse it with a standalone major segment - // with minor and patch segments set to 0 - // 0.50.0 (parsed as 5000) was supposed to support hyperlinks, but throws a segfault - // so we check if the "major" version is greater than 5000 (5000 exclusive) - if hasEnv("VTE_VERSION") { - v := parseVersion(getEnv("VTE_VERSION")) - return v.major > 5000 - } - - // Terminals which have a TERM_PROGRAM variable set - // This is the most versatile environment variable as it also provides another - // variable called TERM_PROGRAM_VERSION, which helps us to determine - // the exact version of the program, and allow for stricter variable checks - if hasEnv("TERM_PROGRAM") { - v := parseVersion(getEnv("TERM_PROGRAM_VERSION")) - - switch term := getEnv("TERM_PROGRAM"); term { - case "iTerm.app": - if v.major == 3 { - return v.minor >= 1 - } - return v.major > 3 - case "WezTerm": - // Even though WezTerm's version is something like 20200620-160318-e00b076c - // parseVersion will still parse it with a standalone major segment (ie: 20200620) - // with minor and patch segments set to 0 - return v.major >= 20200620 - case "vscode": - return v.major > 1 || (v.major == 1 && v.minor >= 72) - - // Hyper Terminal used to be included in this list, and it even supports hyperlinks - // but the hyperlinks are pseudo-hyperlinks and are actually not clickable - } - } - - // Terminals which have a TERM variable set - if matchesEnv("TERM", []string{"xterm-kitty", "alacritty", "alacritty-direct"}) { - return true - } - - // Terminals which have a COLORTERM variable set - if matchesEnv("COLORTERM", []string{"xfce4-terminal"}) { - return true - } - - // Match standalone environment variables - // ie, those which do not require any special handling - // or version checking - if checkAllEnvs(EnvironmentVariables) { - return true - } - - return false -} - -var colorsList = map[string]int{ - "reset": 0, - "bold": 1, - "dim": 2, - "italic": 3, - "underline": 4, - "blink": 5, - "black": 30, - "red": 31, - "green": 32, - "yellow": 33, - "blue": 34, - "magenta": 35, - "cyan": 36, - "white": 37, - "bgBlack": 40, - "bgRed": 41, - "bgGreen": 42, - "bgYellow": 43, - "bgBlue": 44, - "bgMagenta": 45, - "bgCyan": 46, - "bgWhite": 47, -} - -// isInList returns true if a given value is present in a string slice, false otherwise -func isInList(list []string, value string) bool { - for _, v := range list { - if v == value { - return true - } - } - return false -} - -var colors []string - -// addColor adds a color to be later used while parsing the color string -func addColor(value string) []string { - colors = append(colors, fmt.Sprint(colorsList[value])) - - return colors -} - -// clearSlice clears a string slice by re-slicing it to have a length of 0 -func clearSlice(slice []string) []string { - slice = slice[:0] - return slice -} - -// isValidColor checks if a given color string is a valid key in a map of predefined colors -func isValidColor(color string) bool { - // Create a slice with keys of the colorsList map - keys := make([]string, len(colorsList)) - - i := 0 - for k := range colorsList { - keys[i] = k - i++ - } - - // Check if the color is in the keys slice - return isInList(keys, color) -} - -// parseColor yields a set of ANSI color codes, based on the "color" string. -// The color codes are parsed from the colorsList map -func parseColor(color string) string { - // Clear the colors slice first - colors = clearSlice(colors) - - // If nothing is provided, return empty string - if color == "" { - return "" - } - - colorNames := strings.Split(color, " ") - - for _, c := range colorNames { - // If the color doesn't exist, skip and go to the next word - if !isValidColor(c) { - continue - } - - // Add the color, if present in colorsList - addColor(c) - - } - - return "\u001b[" + strings.Join(colors, ";") + "m" -} - -// Link returns a clickable link, which can be used in the terminal -// -// The function takes two required parameters: text and url -// and one optional parameter: shouldForce -// -// The text parameter is the text to be displayed. -// The url parameter is the URL to be opened when the link is clicked. -// The shouldForce is an optional parameter indicates whether to force the non-hyperlink supported behavior (i.e., text (url)) -// -// The function returns the clickable link. -func Link(text string, url string, shouldForce ...bool) string { - - // Default shouldForce to false - shouldForceDefault := false - - if len(shouldForce) > 0 { - // If a value for shouldForce is provided, set it to that instead - // Since shouldForce is a slice, we only consider its first element - shouldForceDefault = shouldForce[0] - } - - if shouldForceDefault { - return text + " (" + url + ")" + "\u001b[0m" - } else { - if supportsHyperlinks() { - return "\x1b]8;;" + url + "\x07" + text + "\x1b]8;;\x07" + "\u001b[0m" - } - return text + " (" + url + ")" + "\u001b[0m" - } -} - -// ColorLink returns a colored clickable link, which can be used in the terminal -// -// The function takes three required parameters: text, url and color -// and one optional parameter: shouldForce -// -// The text parameter is the text to be displayed. -// The url parameter is the URL to be opened when the link is clicked. -// The color parameter is the color of the link. -// The shouldForce is an optional parameter indicates whether to force the non-hyperlink supported behavior (i.e., text (url)) -// -// The function returns the clickable link. -func ColorLink(text string, url string, color string, shouldForce ...bool) string { - textColor := parseColor(color) - - // Default shouldForce to false - shouldForceDefault := false - - if len(shouldForce) > 0 { - // If a value for shouldForce is provided, set it to that instead - // Since shouldForce is a slice, we only consider its first element - shouldForceDefault = shouldForce[0] - } - - if shouldForceDefault { - return textColor + text + " (" + url + ")" + "\u001b[0m" - } else { - if supportsHyperlinks() { - return "\x1b]8;;" + url + "\x07" + textColor + text + "\x1b]8;;\x07" + "\u001b[0m" - } - return textColor + text + " (" + url + ")" + "\u001b[0m" - } - -} - -// SupportsHyperlinks returns true if the terminal supports hyperlinks. -// -// The function returns true if the terminal supports hyperlinks, false otherwise. -func SupportsHyperlinks() bool { - return supportsHyperlinks() -} diff --git a/vendor/modules.txt b/vendor/modules.txt deleted file mode 100644 index 8ea0d50..0000000 --- a/vendor/modules.txt +++ /dev/null @@ -1,3 +0,0 @@ -# github.com/savioxavier/termlink v1.3.0 -## explicit; go 1.17 -github.com/savioxavier/termlink