diff --git a/actions.go b/actions.go index 7f028f5..a70279d 100644 --- a/actions.go +++ b/actions.go @@ -1,8 +1,9 @@ package main import ( + "context" "fmt" - "github.com/andygrunwald/go-jira" + jira "github.com/andygrunwald/go-jira/v2/cloud" "github.com/charmbracelet/huh" "github.com/fatih/color" "github.com/spf13/viper" @@ -38,7 +39,7 @@ func ActionLogout(_ []string) { } func ActionListProjects(_ []string) { - projects, _, err := jiraClient.Project.GetList() + projects, err := GetAllProjects() if err != nil { printError("Failed to list projects", err) return @@ -52,7 +53,7 @@ func ActionUseProject(args []string) { var projectId string if len(args) == 0 { println("Fetching projects...") - projects, _, err := jiraClient.Project.GetList() + projects, err := GetAllProjects() if err != nil { printError("Failed to list projects", err) return @@ -79,7 +80,7 @@ func ActionUseProject(args []string) { printError("Need a project ID", nil) return } - project, _, err := jiraClient.Project.Get(projectId) + project, _, err := jiraClient.Project.Get(context.Background(), projectId) if err != nil { printError("Failed to get project", err) return @@ -91,12 +92,11 @@ func ActionUseProject(args []string) { } func ActionListBoards(_ []string) { - project := GetProjectId() - if project == "" { + if !HasProject() { printError("Please select a project first", nil) return } - list, _, err := jiraClient.Board.GetAllBoards(&jira.BoardListOptions{ProjectKeyOrID: project}) + list, err := GetAllBoardsForProject(GetProjectId()) if err != nil { printError("Failed to list boards", err) return @@ -107,11 +107,15 @@ func ActionListBoards(_ []string) { } func ActionUseBoard(args []string) { + if !HasIssue() { + printError("Please select a project first", nil) + return + } var boardId int64 var err error if len(args) == 0 { println("Fetching boards...") - boards, _, err := jiraClient.Board.GetAllBoards(&jira.BoardListOptions{ProjectKeyOrID: GetProjectId()}) + boards, err := GetAllBoardsForProject(GetProjectId()) if err != nil { printError("Failed to list boards", err) return @@ -141,7 +145,7 @@ func ActionUseBoard(args []string) { } else { printError("Invalid number of arguments", nil) } - board, _, err := jiraClient.Board.GetBoardConfiguration(int(boardId)) + board, err := GetBoardConfiguration(int(boardId)) if err != nil { printError("Failed to get project", err) return @@ -153,19 +157,12 @@ func ActionUseBoard(args []string) { } func ActionUseCurrentSprint(_ []string) { - boardId := viper.GetInt("board.id") - list, _, err := jiraClient.Board.GetAllSprintsWithOptions(boardId, &jira.GetAllSprintsOptions{ - State: "active", - }) + boardId := GetBoardId() + sprint, err := GetCurrentSprint(boardId) if err != nil { - printError("Failed to get current board", err) + printError("Failed to get current sprint", err) return } - if len(list.Values) != 1 { - printError("There should be only one sprint at a time", nil) - return - } - sprint := list.Values[0] fmt.Printf("Using sprint %s (%d)\n", sprint.Name, sprint.ID) SetSprint(sprint) SaveConfig() @@ -198,8 +195,7 @@ func actionUseIssueWithFilter(args []string, allowIssue func(issue *jira.Issue) var issueKey string var err error if len(args) == 0 { - println("Fetching issues...") - issues, _, err := jiraClient.Sprint.GetIssuesForSprint(sprintId) + issues, err := GetIssuesForSprint(sprintId) if err != nil { printError("Failed to fetch issues", err) return @@ -238,7 +234,7 @@ func actionUseIssueWithFilter(args []string, allowIssue func(issue *jira.Issue) } else { printError("Invalid number of arguments", nil) } - issue, _, err := jiraClient.Issue.Get(issueKey, nil) + issue, err := GetIssue(issueKey) if err != nil { printError("Failed to get issue", err) return @@ -255,7 +251,7 @@ func ActionListIssues(_ []string) { printError("No sprint selected", nil) return } - issues, _, err := jiraClient.Sprint.GetIssuesForSprint(sprintId) + issues, err := GetIssuesForSprint(sprintId) if err != nil { printError("Failed to list issues", err) } @@ -284,11 +280,12 @@ func ActionViewCard(_ []string) { printError("No issue selected", nil) return } - issue, _, err := jiraClient.Issue.Get(GetIssueKey(), nil) + issue, err := GetIssue(GetIssueKey()) if err != nil { printError("Failed to get issue", err) return } + fmt.Printf("Key: %s\n", issue.Key) fmt.Printf("Summary: %s\n", issue.Fields.Summary) fmt.Printf("Type: %s\n", issue.Fields.Type.Name) fmt.Printf("Status: %s\n", issue.Fields.Status.Name) @@ -304,7 +301,7 @@ func ActionViewDescription(_ []string) { printError("No issue selected", nil) return } - issue, _, err := jiraClient.Issue.Get(GetIssueKey(), nil) + issue, err := GetIssue(GetIssueKey()) if err != nil { printError("Failed to get issue", err) return @@ -317,11 +314,19 @@ func ActionSetStatus(_ []string) { printError("No issue selected", nil) return } - //board, _, err := jiraClient.Boardd.GetBoardConfiguration(GetBoardId()) - //if err != nil { - // printError("Failed to get board configuration", err) - // return - //} - //board.ColumnConfig.Columns[0] - //huh.NewSelect(). + + issueTypes, err := GetAllStatuses(GetProjectId()) + if err != nil { + printError("Failed to get sprint statuses", err) + return + } + options := []huh.Option[string]{} + for _, status := range (*issueTypes)[0].Statuses { + name := fmt.Sprintf("%s (%s)", status.Name, status.ID) + options = append(options, huh.Option[string]{Key: name, Value: status.ID}) + } + huh.NewSelect[string](). + Title("Select Option"). + Options(options...). + Run() } diff --git a/config.go b/config.go index f48c2d4..c9b37d5 100644 --- a/config.go +++ b/config.go @@ -1,7 +1,7 @@ package main import ( - "github.com/andygrunwald/go-jira" + jira "github.com/andygrunwald/go-jira/v2/cloud" "github.com/spf13/viper" ) @@ -45,7 +45,7 @@ func HasBoard() bool { return viper.GetBool("board.valid") } -func SetSprint(sprint jira.Sprint) { +func SetSprint(sprint *jira.Sprint) { viper.Set("sprint.valid", true) viper.Set("sprint.id", sprint.ID) viper.Set("sprint.name", sprint.Name) diff --git a/go.mod b/go.mod index 7b8bb4f..df06436 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,9 @@ go 1.23.2 require ( github.com/abiosoft/ishell/v2 v2.0.2 github.com/adrg/xdg v0.5.0 - github.com/andygrunwald/go-jira v1.16.0 + github.com/andygrunwald/go-jira/v2 v2.0.0-20241001064241-17678c7ba6fe + github.com/charmbracelet/huh v0.6.0 + github.com/charmbracelet/huh/spinner v0.0.0-20241011224433-983a50776b31 github.com/elk-language/go-prompt v1.1.5 github.com/fatih/color v1.14.1 github.com/pkg/errors v0.9.1 @@ -20,8 +22,7 @@ require ( github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/catppuccin/go v0.2.0 // indirect github.com/charmbracelet/bubbles v0.20.0 // indirect - github.com/charmbracelet/bubbletea v1.1.0 // indirect - github.com/charmbracelet/huh v0.6.0 // indirect + github.com/charmbracelet/bubbletea v1.1.1 // indirect github.com/charmbracelet/lipgloss v0.13.0 // indirect github.com/charmbracelet/x/ansi v0.2.3 // indirect github.com/charmbracelet/x/exp/strings v0.0.0-20240722160745-212f7b056ed0 // indirect @@ -32,7 +33,7 @@ require ( github.com/fatih/structs v1.1.0 // indirect github.com/flynn-archive/go-shlex v0.0.0-20150515145356-3f9db97f8568 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect - github.com/golang-jwt/jwt/v4 v4.4.2 // indirect + github.com/golang-jwt/jwt/v4 v4.5.0 // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect @@ -68,3 +69,7 @@ require ( gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) + +replace github.com/andygrunwald/go-jira/v2 => ../go-jira + +replace github.com/andygrunwald/go-jira/v2/cloud => ../go-jira/cloud diff --git a/go.sum b/go.sum index 5ca3c13..7cedb86 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= +github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= github.com/abiosoft/ishell v2.0.0+incompatible h1:zpwIuEHc37EzrsIYah3cpevrIc8Oma7oZPxr03tlmmw= github.com/abiosoft/ishell v2.0.0+incompatible/go.mod h1:HQR9AqF2R3P4XXpMpI0NAzgHf/aS6+zVXRj14cVk9qg= github.com/abiosoft/ishell/v2 v2.0.2 h1:5qVfGiQISaYM8TkbBl7RFO6MddABoXpATrsFbVI+SNo= @@ -6,8 +8,6 @@ github.com/abiosoft/readline v0.0.0-20180607040430-155bce2042db h1:CjPUSXOiYptLb github.com/abiosoft/readline v0.0.0-20180607040430-155bce2042db/go.mod h1:rB3B4rKii8V21ydCbIzH5hZiCQE7f5E9SzUb/ZZx530= github.com/adrg/xdg v0.5.0 h1:dDaZvhMXatArP1NPHhnfaQUqWBLBsmx1h1HXQdMoFCY= github.com/adrg/xdg v0.5.0/go.mod h1:dDdY4M4DF9Rjy4kHPeNL+ilVF+p2lK8IdM9/rTSGcI4= -github.com/andygrunwald/go-jira v1.16.0 h1:PU7C7Fkk5L96JvPc6vDVIrd99vdPnYudHu4ju2c2ikQ= -github.com/andygrunwald/go-jira v1.16.0/go.mod h1:UQH4IBVxIYWbgagc0LF/k9FRs9xjIiQ8hIcC6HfLwFU= github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= @@ -16,10 +16,12 @@ github.com/catppuccin/go v0.2.0 h1:ktBeIrIP42b/8FGiScP9sgrWOss3lw0Z5SktRoithGA= github.com/catppuccin/go v0.2.0/go.mod h1:8IHJuMGaUUjQM82qBrGNBv7LFq6JI3NnQCF6MOlZjpc= github.com/charmbracelet/bubbles v0.20.0 h1:jSZu6qD8cRQ6k9OMfR1WlM+ruM8fkPWkHvQWD9LIutE= github.com/charmbracelet/bubbles v0.20.0/go.mod h1:39slydyswPy+uVOHZ5x/GjwVAFkCsV8IIVy+4MhzwwU= -github.com/charmbracelet/bubbletea v1.1.0 h1:FjAl9eAL3HBCHenhz/ZPjkKdScmaS5SK69JAK2YJK9c= -github.com/charmbracelet/bubbletea v1.1.0/go.mod h1:9Ogk0HrdbHolIKHdjfFpyXJmiCzGwy+FesYkZr7hYU4= +github.com/charmbracelet/bubbletea v1.1.1 h1:KJ2/DnmpfqFtDNVTvYZ6zpPFL9iRCRr0qqKOCvppbPY= +github.com/charmbracelet/bubbletea v1.1.1/go.mod h1:9Ogk0HrdbHolIKHdjfFpyXJmiCzGwy+FesYkZr7hYU4= github.com/charmbracelet/huh v0.6.0 h1:mZM8VvZGuE0hoDXq6XLxRtgfWyTI3b2jZNKh0xWmax8= github.com/charmbracelet/huh v0.6.0/go.mod h1:GGNKeWCeNzKpEOh/OJD8WBwTQjV3prFAtQPpLv+AVwU= +github.com/charmbracelet/huh/spinner v0.0.0-20241011224433-983a50776b31 h1:HqaYBKXy1eQBnN9tCLJJHaQ+3btqonOVh25LZ/Xaxps= +github.com/charmbracelet/huh/spinner v0.0.0-20241011224433-983a50776b31/go.mod h1:Cxhgl8N0sX9A+EQxedzzGZAalaF8fUVL+JP/pSOW8cI= github.com/charmbracelet/lipgloss v0.13.0 h1:4X3PPeoWEDCMvzDvGmTajSyYPcZM4+y8sCA/SsA3cjw= github.com/charmbracelet/lipgloss v0.13.0/go.mod h1:nw4zy0SBX/F/eAO1cWdcvy6qnkDUxr8Lw7dvFrAIbbY= github.com/charmbracelet/x/ansi v0.2.3 h1:VfFN0NUpcjBRd4DnKfRaIRo53KRgey/nhOoEqosGDEY= @@ -53,12 +55,11 @@ github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHk github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= -github.com/golang-jwt/jwt/v4 v4.4.2 h1:rcc4lwaZgFMCZ5jxF9ABolDcIHdBytAFgqFPbSJQAYs= -github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= +github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= @@ -79,15 +80,11 @@ github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= -github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4= github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88= github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= -github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-tty v0.0.3 h1:5OfyWorkyO7xP52Mq7tB36ajHDG5OHrmBGIS/DtakQI= @@ -112,8 +109,6 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= -github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= @@ -162,18 +157,11 @@ golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220330033206-e17cdc41300f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= -golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/install.sh b/install.sh new file mode 100755 index 0000000..e2bb135 --- /dev/null +++ b/install.sh @@ -0,0 +1,2 @@ +go build . +cp -v jirashell ~/.local/bin/jirashell diff --git a/jira.go b/jira.go new file mode 100644 index 0000000..5b7504f --- /dev/null +++ b/jira.go @@ -0,0 +1,99 @@ +package main + +import ( + "context" + "errors" + "fmt" + jira "github.com/andygrunwald/go-jira/v2/cloud" + "github.com/charmbracelet/huh/spinner" +) + +var errorAborted = errors.New("aborted by user") + +func CreateSpinner(title string) *spinner.Spinner { + return spinner.New(). + Title(" " + title). + Type(spinner.Globe) +} + +func RunSpinner[T any](title string, callback func() (*T, any, error)) (*T, error) { + var result *T + var err error + err2 := CreateSpinner(title). + Action(func() { + result, _, err = callback() + }).Run() + if err2 != nil { + return nil, err2 + } + if result == nil { + return nil, errorAborted + } + return result, err +} + +func GetAllStatuses(projectId string) (*jira.IssueTypesWithStatus, error) { + return RunSpinner("Fetching statuses...", func() (*jira.IssueTypesWithStatus, any, error) { + return jiraClient.Project.GetAllStatuses(context.Background(), projectId) + }) +} + +func GetUserData() (*jira.User, error) { + return RunSpinner("Fetching user info...", func() (*jira.User, any, error) { + return jiraClient.User.GetCurrentUser(context.Background()) + }) +} + +func GetAllProjects() (*jira.ProjectList, error) { + return RunSpinner("Fetching all projects...", func() (*jira.ProjectList, any, error) { + return jiraClient.Project.GetAll(context.Background(), nil) + }) +} + +func GetAllBoardsForProject(projectIdOrKey string) (*jira.BoardsList, error) { + return RunSpinner(fmt.Sprintf("Fetching boards for project '%s'...", projectIdOrKey), func() (*jira.BoardsList, any, error) { + return jiraClient.Board.GetAllBoards(context.Background(), &jira.BoardListOptions{ProjectKeyOrID: projectIdOrKey}) + }) +} + +func GetBoardConfiguration(boardId int) (*jira.BoardConfiguration, error) { + return RunSpinner("Fetching board...", func() (*jira.BoardConfiguration, any, error) { + return jiraClient.Board.GetBoardConfiguration(context.Background(), boardId) + }) +} + +func GetCurrentSprint(boardId int) (*jira.Sprint, error) { + list, err := RunSpinner("Fetching sprint...", func() (*jira.SprintsList, any, error) { + return jiraClient.Board.GetAllSprints(context.Background(), int64(boardId), &jira.GetAllSprintsOptions{ + State: "active", + }) + }) + if err != nil { + return nil, err + } + if len(list.Values) == 0 { + return nil, errors.New("No active sprint found") + } + if len(list.Values) > 1 { + return nil, errors.New("Multiple active sprints found") + } + return &list.Values[0], nil +} + +func GetIssuesForSprint(sprintId int) ([]jira.Issue, error) { + issues, err := RunSpinner("Fetching issues...", func() (*[]jira.Issue, any, error) { + list, t, err := jiraClient.Sprint.GetIssuesForSprint(context.Background(), sprintId) + return &list, t, err + }) + if issues == nil { + return nil, err + } else { + return *issues, err + } +} + +func GetIssue(issueKey string) (*jira.Issue, error) { + return RunSpinner("Fetching issue...", func() (*jira.Issue, any, error) { + return jiraClient.Issue.Get(context.Background(), issueKey, nil) + }) +} diff --git a/main.go b/main.go index 974eaf7..01c2244 100644 --- a/main.go +++ b/main.go @@ -3,13 +3,14 @@ package main import ( "github.com/abiosoft/ishell/v2" "github.com/adrg/xdg" - "github.com/andygrunwald/go-jira" + jira "github.com/andygrunwald/go-jira/v2/cloud" prompt "github.com/elk-language/go-prompt" pstrings "github.com/elk-language/go-prompt/strings" "github.com/pkg/errors" "github.com/spf13/viper" "log" "os" + "strings" ) var configPath string @@ -80,33 +81,37 @@ func main() { loadConfig() } - ensureLoggedIn() - UpdatePrompt() + if !ensureLoggedIn() { + os.Exit(0) + } + if len(os.Args) > 1 { + executor(strings.Join(os.Args[1:], " ")) + } else { + UpdatePrompt() - promptRunner = prompt.New( - executor, - prompt.WithCompleter(completer), - prompt.WithTitle("jirashell"), - prompt.WithPrefixCallback(func() string { return prefix }), - ) - promptRunner.Run() + promptRunner = prompt.New( + executor, + prompt.WithCompleter(completer), + prompt.WithTitle("jirashell"), + prompt.WithPrefixCallback(func() string { return prefix }), + ) + promptRunner.Run() + } } -func ensureLoggedIn() { +func ensureLoggedIn() bool { for { username := viper.GetString("username") host := viper.GetString("host") token := viper.GetString("token") if host != "" && token != "" && username != "" { - println("Logging in...") err := login(host, username, token) if err != nil { println("Could not log in:", err) } else { - println("Logged in!") SaveConfig() - return + return true } } @@ -114,7 +119,7 @@ func ensureLoggedIn() { println("A host name is required.") host = prompt.Input(prompt.WithPrefix("Host: "), prompt.WithInitialText(host)) if host == "" { - return + return false } viper.Set("host", host) @@ -122,7 +127,7 @@ func ensureLoggedIn() { println("A username is required.") username = prompt.Input(prompt.WithPrefix("Username: "), prompt.WithInitialText(username)) if username == "" { - return + return false } viper.Set("username", username) @@ -130,7 +135,7 @@ func ensureLoggedIn() { println("A token is required.") token = prompt.Input(prompt.WithPrefix("Token: "), prompt.WithInitialText(token)) if token == "" { - return + return false } viper.Set("token", token) } @@ -140,16 +145,13 @@ func login(host, username, token string) error { var err error tp := jira.BasicAuthTransport{ Username: username, - Password: token, + APIToken: token, } - jiraClient, err = jira.NewClient(tp.Client(), host) + jiraClient, err = jira.NewClient(host, tp.Client()) if err != nil { return err } - jiraUser, _, err = jiraClient.User.GetSelf() - if err != nil { - return err - } - return nil + jiraUser, err = GetUserData() + return err }