From 08e55765ea4e97638bbd5138b745dea3c03040d0 Mon Sep 17 00:00:00 2001 From: James Baker Date: Wed, 2 Oct 2024 20:29:00 +1000 Subject: [PATCH] feat: added '-u` to update PR from base branch (#458) * Feat: added '-u` to update PR from base branch * feat: added mergeStateStatus pill * Update ui/components/prsidebar/prsidebar.go Change naming Co-authored-by: Dolev Hadar <6196971+dlvhdr@users.noreply.github.com> * Update ui/components/pr/pr.go Change naming Co-authored-by: Dolev Hadar <6196971+dlvhdr@users.noreply.github.com> * fix: github added / and changed icon for up-to date --------- Co-authored-by: Dolev Hadar <6196971+dlvhdr@users.noreply.github.com> --- README.md | 2 +- config/parser.go | 1 + data/prapi.go | 19 ++++++++++-------- ui/components/pr/pr.go | 13 ++++++++++++ ui/components/prsidebar/prsidebar.go | 25 +++++++++++++++++++++--- ui/components/prssection/prssection.go | 2 ++ ui/components/reposection/reposection.go | 2 ++ ui/components/section/section.go | 3 +++ ui/components/tasks/pr.go | 23 ++++++++++++++++++++++ ui/constants/constants.go | 10 ++++++---- ui/keys/branchKeys.go | 8 ++++++++ ui/keys/prKeys.go | 8 ++++++++ ui/theme/theme.go | 5 ++++- ui/ui.go | 7 +++++++ 14 files changed, 111 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index cd104a4b..2df367d2 100644 --- a/README.md +++ b/README.md @@ -183,7 +183,7 @@ keybindings: The list of available builtin commands are: 1. `universal`: up, down, firstLine, lastLine, togglePreview, openGithub, refresh, refreshAll, pageDown, pageUp, nextSection, prevSection, search, copyurl, copyNumber, help, quit -2. `prs`: approve, assign, unassign, comment, diff, checkout, close, ready, reopen, merge, watchChecks, viewIssues +2. `prs`: approve, assign, unassign, comment, diff, checkout, close, ready, reopen, merge, update, watchChecks, viewIssues 3. `Issues`: assign, unassign, comment, close, reopen, viewPrs #### Defining custom keybindings diff --git a/config/parser.go b/config/parser.go index 590916de..34c67fd5 100644 --- a/config/parser.go +++ b/config/parser.go @@ -148,6 +148,7 @@ type ColorThemeText struct { Faint HexColor `yaml:"faint" validate:"hexcolor"` Warning HexColor `yaml:"warning" validate:"hexcolor"` Success HexColor `yaml:"success" validate:"hexcolor"` + Error HexColor `yaml:"error" validate:"hexcolor"` } type ColorThemeBorder struct { diff --git a/data/prapi.go b/data/prapi.go index 27dbf066..5f595498 100644 --- a/data/prapi.go +++ b/data/prapi.go @@ -33,14 +33,15 @@ type PullRequestData struct { HeadRef struct { Name string } - Repository Repository - Assignees Assignees `graphql:"assignees(first: 3)"` - Comments Comments `graphql:"comments(last: 5, orderBy: { field: UPDATED_AT, direction: DESC })"` - LatestReviews Reviews `graphql:"latestReviews(last: 3)"` - ReviewThreads ReviewThreads `graphql:"reviewThreads(last: 20)"` - IsDraft bool - Commits Commits `graphql:"commits(last: 1)"` - Labels PRLabels `graphql:"labels(first: 3)"` + Repository Repository + Assignees Assignees `graphql:"assignees(first: 3)"` + Comments Comments `graphql:"comments(last: 5, orderBy: { field: UPDATED_AT, direction: DESC })"` + LatestReviews Reviews `graphql:"latestReviews(last: 3)"` + ReviewThreads ReviewThreads `graphql:"reviewThreads(last: 20)"` + IsDraft bool + Commits Commits `graphql:"commits(last: 1)"` + Labels PRLabels `graphql:"labels(first: 3)"` + MergeStateStatus MergeStateStatus `graphql:"mergeStateStatus"` } type CheckRun struct { @@ -152,6 +153,8 @@ type PRLabels struct { Nodes []Label } +type MergeStateStatus string + type PageInfo struct { HasNextPage bool StartCursor string diff --git a/ui/components/pr/pr.go b/ui/components/pr/pr.go index 82b05395..2e1cbd8a 100644 --- a/ui/components/pr/pr.go +++ b/ui/components/pr/pr.go @@ -283,6 +283,19 @@ func (pr *PullRequest) RenderState() string { } } +func (pr *PullRequest) RenderMergeStateStatus() string { + switch pr.Data.MergeStateStatus { + case "CLEAN": + return constants.SuccessIcon + " Up-to-date" + case "BLOCKED": + return constants.BlockedIcon + " Blocked" + case "BEHIND": + return constants.BehindIcon + " Behind" + default: + return "" + } +} + func (pr *PullRequest) ToTableRow(isSelected bool) table.Row { if !pr.Ctx.Config.Theme.Ui.Table.Compact { return table.Row{ diff --git a/ui/components/prsidebar/prsidebar.go b/ui/components/prsidebar/prsidebar.go index 715f0e83..e6e7418d 100644 --- a/ui/components/prsidebar/prsidebar.go +++ b/ui/components/prsidebar/prsidebar.go @@ -223,12 +223,30 @@ func (m *Model) renderMergeablePill() string { } else if status == "MERGEABLE" { return m.ctx.Styles.PrSidebar.PillStyle. Background(m.ctx.Theme.SuccessText). - Render("󰃸 Mergeable") + Render("󰃸 No Merge Conflicts") } return "" } +func (m *Model) renderMergeStateStatusPill() string { + status := m.pr.Data.MergeStateStatus + if status == "CLEAN" { + return m.ctx.Styles.PrSidebar.PillStyle. + Background(m.ctx.Theme.SuccessText). + Render("󰄬 Branch Up-To-Date") + } else if status == "BLOCKED" { + return m.ctx.Styles.PrSidebar.PillStyle. + Background(m.ctx.Theme.ErrorText). + Render("󰅖 Branch Blocked") + } else if status == "BEHIND" { + return m.ctx.Styles.PrSidebar.PillStyle. + Background(m.ctx.Theme.WarningText). + Render(" Branch Behind") + } + return "" +} + func (m *Model) renderChecksPill() string { s := m.ctx.Styles.PrSidebar.PillStyle t := m.ctx.Theme @@ -236,7 +254,7 @@ func (m *Model) renderChecksPill() string { status := m.pr.GetStatusChecksRollup() if status == "FAILURE" { return s. - Background(t.WarningText). + Background(t.ErrorText). Render("󰅖 Checks") } else if status == "PENDING" { return s. @@ -256,7 +274,8 @@ func (m *Model) renderPills() string { statusPill := m.renderStatusPill() mergeablePill := m.renderMergeablePill() checksPill := m.renderChecksPill() - return lipgloss.JoinHorizontal(lipgloss.Top, statusPill, " ", mergeablePill, " ", checksPill) + uptoDatePill := m.renderMergeStateStatusPill() + return lipgloss.JoinHorizontal(lipgloss.Top, statusPill, " ", mergeablePill, " ", checksPill, " ", uptoDatePill) } func (m *Model) renderLabels() string { diff --git a/ui/components/prssection/prssection.go b/ui/components/prssection/prssection.go index 1a82ce25..6357a17a 100644 --- a/ui/components/prssection/prssection.go +++ b/ui/components/prssection/prssection.go @@ -99,6 +99,8 @@ func (m Model) Update(msg tea.Msg) (section.Section, tea.Cmd) { cmd = tasks.PRReady(m.Ctx, sid, pr) case "merge": cmd = tasks.MergePR(m.Ctx, sid, pr) + case "update": + cmd = tasks.UpdatePR(m.Ctx, sid, pr) } } diff --git a/ui/components/reposection/reposection.go b/ui/components/reposection/reposection.go index 9a83a97e..49a09571 100644 --- a/ui/components/reposection/reposection.go +++ b/ui/components/reposection/reposection.go @@ -124,6 +124,8 @@ func (m *Model) Update(msg tea.Msg) (section.Section, tea.Cmd) { cmd = tasks.PRReady(m.Ctx, sid, pr) case "merge": cmd = tasks.MergePR(m.Ctx, sid, pr) + case "update": + cmd = tasks.UpdatePR(m.Ctx, sid, pr) } } } diff --git a/ui/components/section/section.go b/ui/components/section/section.go index c601fd67..74502c18 100644 --- a/ui/components/section/section.go +++ b/ui/components/section/section.go @@ -346,6 +346,9 @@ func (m *BaseModel) GetPromptConfirmation() string { case m.PromptConfirmationAction == "merge" && m.Ctx.View == config.PRsView: prompt = "Are you sure you want to merge this PR? (Y/n) " + case m.PromptConfirmationAction == "update" && m.Ctx.View == config.PRsView: + prompt = "Are you sure you want to update this PR? (Y/n) " + case m.PromptConfirmationAction == "close" && m.Ctx.View == config.IssuesView: prompt = "Are you sure you want to close this issue? (Y/n) " diff --git a/ui/components/tasks/pr.go b/ui/components/tasks/pr.go index cff8f477..235db4fe 100644 --- a/ui/components/tasks/pr.go +++ b/ui/components/tasks/pr.go @@ -238,3 +238,26 @@ func CreatePR(ctx *context.ProgramContext, section SectionIdentifer, branchName } })) } + +func UpdatePR(ctx *context.ProgramContext, section SectionIdentifer, pr data.RowData) tea.Cmd { + prNumber := pr.GetNumber() + return fireTask(ctx, GitHubTask{ + Id: buildTaskId("pr_update", prNumber), + Args: []string{ + "pr", + "update-branch", + fmt.Sprint(prNumber), + "-R", + pr.GetRepoNameWithOwner(), + }, + Section: section, + StartText: fmt.Sprintf("Updating PR #%d", prNumber), + FinishedText: fmt.Sprintf("PR #%d has been updated", prNumber), + Msg: func(c *exec.Cmd, err error) tea.Msg { + return UpdatePRMsg{ + PrNumber: prNumber, + IsClosed: utils.BoolPtr(true), + } + }, + }) +} diff --git a/ui/constants/constants.go b/ui/constants/constants.go index addc0a4f..5de457e4 100644 --- a/ui/constants/constants.go +++ b/ui/constants/constants.go @@ -30,8 +30,10 @@ const ( FailureIcon = "󰅙" SuccessIcon = "" - DraftIcon = "" - MergedIcon = "" - OpenIcon = "" - ClosedIcon = "" + DraftIcon = "" + BehindIcon = "󰇮" + BlockedIcon = "" + MergedIcon = "" + OpenIcon = "" + ClosedIcon = "" ) diff --git a/ui/keys/branchKeys.go b/ui/keys/branchKeys.go index 4d313bd0..26dbb1c8 100644 --- a/ui/keys/branchKeys.go +++ b/ui/keys/branchKeys.go @@ -17,6 +17,7 @@ type BranchKeyMap struct { Push key.Binding ForcePush key.Binding Delete key.Binding + UpdatePr key.Binding ViewPRs key.Binding } @@ -49,6 +50,10 @@ var BranchKeys = BranchKeyMap{ key.WithKeys("d", "backspace"), key.WithHelp("d/backspace", "delete"), ), + UpdatePr: key.NewBinding( + key.WithKeys("u"), + key.WithHelp("u", "update PR"), + ), ViewPRs: key.NewBinding( key.WithKeys("s"), key.WithHelp("s", "Switch to PRs"), @@ -64,6 +69,7 @@ func BranchFullHelp() []key.Binding { BranchKeys.New, BranchKeys.CreatePr, BranchKeys.Delete, + BranchKeys.UpdatePr, BranchKeys.ViewPRs, } } @@ -95,6 +101,8 @@ func rebindBranchKeys(keys []config.Keybinding) error { key = &BranchKeys.Checkout case "viewPRs": key = &BranchKeys.ViewPRs + case "updatePr": + key = &BranchKeys.UpdatePr default: return fmt.Errorf("unknown built-in branch key: '%s'", branchKey.Builtin) } diff --git a/ui/keys/prKeys.go b/ui/keys/prKeys.go index 55f1fc5d..8f25132b 100644 --- a/ui/keys/prKeys.go +++ b/ui/keys/prKeys.go @@ -20,6 +20,7 @@ type PRKeyMap struct { Ready key.Binding Reopen key.Binding Merge key.Binding + Update key.Binding WatchChecks key.Binding ViewIssues key.Binding } @@ -65,6 +66,10 @@ var PRKeys = PRKeyMap{ key.WithKeys("m"), key.WithHelp("m", "merge"), ), + Update: key.NewBinding( + key.WithKeys("u"), + key.WithHelp("u", "update pr from base branch"), + ), WatchChecks: key.NewBinding( key.WithKeys("w"), key.WithHelp("w", "Watch checks"), @@ -87,6 +92,7 @@ func PRFullHelp() []key.Binding { PRKeys.Ready, PRKeys.Reopen, PRKeys.Merge, + PRKeys.Update, PRKeys.WatchChecks, PRKeys.ViewIssues, } @@ -123,6 +129,8 @@ func rebindPRKeys(keys []config.Keybinding) error { key = &PRKeys.Reopen case "merge": key = &PRKeys.Merge + case "update": + key = &PRKeys.Update case "watchChecks": key = &PRKeys.WatchChecks case "viewIssues": diff --git a/ui/theme/theme.go b/ui/theme/theme.go index 730717b4..0c9078e6 100644 --- a/ui/theme/theme.go +++ b/ui/theme/theme.go @@ -17,6 +17,7 @@ type Theme struct { InvertedText lipgloss.AdaptiveColor // config.Theme.Colors.Text.Inverted SuccessText lipgloss.AdaptiveColor // config.Theme.Colors.Text.Success WarningText lipgloss.AdaptiveColor // config.Theme.Colors.Text.Warning + ErrorText lipgloss.AdaptiveColor // config.Theme.Colors.Text.Error } var DefaultTheme = &Theme{ @@ -29,7 +30,8 @@ var DefaultTheme = &Theme{ FaintText: lipgloss.AdaptiveColor{Light: "007", Dark: "245"}, InvertedText: lipgloss.AdaptiveColor{Light: "015", Dark: "236"}, SuccessText: lipgloss.AdaptiveColor{Light: "002", Dark: "002"}, - WarningText: lipgloss.AdaptiveColor{Light: "001", Dark: "001"}, + WarningText: lipgloss.AdaptiveColor{Light: "003", Dark: "003"}, + ErrorText: lipgloss.AdaptiveColor{Light: "001", Dark: "001"}, } func ParseTheme(cfg *config.Config) Theme { @@ -57,6 +59,7 @@ func ParseTheme(cfg *config.Config) Theme { InvertedText: _shimHex(cfg.Theme.Colors.Inline.Text.Inverted), SuccessText: _shimHex(cfg.Theme.Colors.Inline.Text.Success), WarningText: _shimHex(cfg.Theme.Colors.Inline.Text.Warning), + ErrorText: _shimHex(cfg.Theme.Colors.Inline.Text.Error), } } diff --git a/ui/ui.go b/ui/ui.go index e8170e05..dfe4dc93 100644 --- a/ui/ui.go +++ b/ui/ui.go @@ -396,6 +396,13 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } return m, cmd + case key.Matches(msg, keys.PRKeys.Update): + if currSection != nil { + currSection.SetPromptConfirmationAction("update") + cmd = currSection.SetIsPromptConfirmationShown(true) + } + return m, cmd + case key.Matches(msg, keys.PRKeys.ViewIssues): m.ctx.View = m.switchSelectedView() m.syncMainContentWidth()