mirror of
https://github.com/jesseduffield/lazygit.git
synced 2026-08-24 02:24:25 -05:00
Show a Github PR's combined checks state in branches list and main view
In the branches list we show the checks icon (✓, ✗ etc) instead of the gihub icon for branches that are open and have a state. It is a little confusing, because the ✓ in front of the name means something very different than the ✓ after it, but the checks status is just too useful to see in the list. In the main view we show it as a compact status before the PR title, with a hyperlink that takes you directly to the checks tab in Github.
This commit is contained in:
@@ -211,7 +211,7 @@ func (self *BranchesController) GetOnRenderToMain() func() {
|
||||
|
||||
pr, ok := self.c.Model().PullRequestsMap[branch.Name]
|
||||
if ok && presentation.ShouldShowPrForBranch(pr, branch.Name, self.c.UserConfig()) {
|
||||
ptyTask.Prefix = presentation.FormatPullRequestHeader(pr)
|
||||
ptyTask.Prefix = presentation.FormatPullRequestHeader(pr, self.c.Tr)
|
||||
ptyTask.Prefix += strings.Repeat("─", self.c.Contexts().Normal.GetView().InnerWidth()) + "\n"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,6 +150,12 @@ func getBranchDisplayStrings(
|
||||
prIcon = "●"
|
||||
}
|
||||
coloredPrIcon = WithPrColor(pr.State, prIcon, false)
|
||||
if pr.State == "OPEN" {
|
||||
icon, _, textStyle := checksStatePresentation(pr.ChecksState, tr)
|
||||
if icon != "" {
|
||||
coloredPrIcon = textStyle.Sprint(icon)
|
||||
}
|
||||
}
|
||||
}
|
||||
res = append(res, coloredPrIcon)
|
||||
|
||||
@@ -287,14 +293,21 @@ func WithPrColor(state string, text string, isBg bool) string {
|
||||
}
|
||||
}
|
||||
|
||||
func FormatPullRequestHeader(pr *models.GithubPullRequest) string {
|
||||
func FormatPullRequestHeader(pr *models.GithubPullRequest, tr *i18n.TranslationSet) string {
|
||||
icon := lo.Ternary(icons.IsIconEnabled(), icons.IconForRemoteUrl(pr.Url)+" ", "")
|
||||
return style.PrintHyperlink(fmt.Sprintf("%s%s %s %s\n",
|
||||
icon,
|
||||
coloredPullRequestStateText(pr.State),
|
||||
pr.Title,
|
||||
style.FgCyan.Sprintf("#%d", pr.Number)),
|
||||
pr.Url)
|
||||
stateText := coloredPullRequestStateText(pr.State)
|
||||
checksStateText := coloredChecksStateText(pr.ChecksState, tr)
|
||||
numberText := style.FgCyan.Sprintf("#%d", pr.Number)
|
||||
|
||||
// The checks status links to the checks tab, so it needs to be its own
|
||||
// hyperlink separate from the rest of the header.
|
||||
parts := []string{style.PrintHyperlink(icon+stateText, pr.Url)}
|
||||
if checksStateText != "" {
|
||||
parts = append(parts, style.PrintHyperlink(checksStateText, strings.TrimSuffix(pr.Url, "/")+"/checks"))
|
||||
}
|
||||
parts = append(parts, style.PrintHyperlink(fmt.Sprintf("%s %s\n", pr.Title, numberText), pr.Url))
|
||||
|
||||
return strings.Join(parts, " ")
|
||||
}
|
||||
|
||||
func pullRequestStateText(state string) string {
|
||||
@@ -328,6 +341,31 @@ func coloredPullRequestStateText(state string) string {
|
||||
return WithPrColor(state, pullRequestStateText(state), false)
|
||||
}
|
||||
|
||||
func checksStatePresentation(state string, tr *i18n.TranslationSet) (string, string, style.TextStyle) {
|
||||
switch state {
|
||||
case "SUCCESS":
|
||||
return "✓", tr.PullRequestChecksPassing, style.FgGreen
|
||||
case "PENDING":
|
||||
return "●", tr.PullRequestChecksPending, style.FgYellow
|
||||
case "FAILURE":
|
||||
return "✗", tr.PullRequestChecksFailing, style.FgRed
|
||||
case "ERROR":
|
||||
return "!", tr.PullRequestChecksError, style.FgRed
|
||||
case "EXPECTED":
|
||||
return "○", tr.PullRequestChecksExpected, style.FgDefault
|
||||
default:
|
||||
return "", "", style.Nothing
|
||||
}
|
||||
}
|
||||
|
||||
func coloredChecksStateText(state string, tr *i18n.TranslationSet) string {
|
||||
icon, text, textStyle := checksStatePresentation(state, tr)
|
||||
if text != "" {
|
||||
return textStyle.Sprintf("%s %s", icon, text)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func ShouldShowPrForBranch(pr *models.GithubPullRequest, branchName string, userConfig *config.UserConfig) bool {
|
||||
if !lo.Contains(userConfig.Git.MainBranches, branchName) {
|
||||
return true
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/presentation/icons"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/style"
|
||||
"github.com/jesseduffield/lazygit/pkg/gui/types"
|
||||
"github.com/jesseduffield/lazygit/pkg/i18n"
|
||||
"github.com/samber/lo"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/xo/terminfo"
|
||||
@@ -29,17 +30,76 @@ func TestFormatPullRequestHeader(t *testing.T) {
|
||||
icons.SetNerdFontsVersion("")
|
||||
|
||||
pr := &models.GithubPullRequest{
|
||||
Title: "Improve checks",
|
||||
Number: 5871,
|
||||
State: "OPEN",
|
||||
Url: "https://github.com/jesseduffield/lazygit/pull/5871",
|
||||
Title: "Improve checks",
|
||||
Number: 5871,
|
||||
State: "OPEN",
|
||||
ChecksState: "SUCCESS",
|
||||
Url: "https://github.com/jesseduffield/lazygit/pull/5871",
|
||||
}
|
||||
numberText := style.FgCyan.Sprint("#5871")
|
||||
tr := i18n.EnglishTranslationSet()
|
||||
|
||||
actual := FormatPullRequestHeader(pr)
|
||||
t.Run("links checks separately from the rest of the header", func(t *testing.T) {
|
||||
actual := FormatPullRequestHeader(pr, tr)
|
||||
|
||||
expected := style.PrintHyperlink("Open Improve checks "+numberText+"\n", pr.Url)
|
||||
assert.Equal(t, expected, actual)
|
||||
expected := style.PrintHyperlink("Open", pr.Url) +
|
||||
" " +
|
||||
style.PrintHyperlink("✓ Passing", pr.Url+"/checks") +
|
||||
" " +
|
||||
style.PrintHyperlink("Improve checks "+numberText+"\n", pr.Url)
|
||||
assert.Equal(t, expected, actual)
|
||||
})
|
||||
|
||||
t.Run("leaves the separator unlinked when checks are unavailable", func(t *testing.T) {
|
||||
prWithoutChecks := *pr
|
||||
prWithoutChecks.ChecksState = ""
|
||||
|
||||
actual := FormatPullRequestHeader(&prWithoutChecks, tr)
|
||||
|
||||
expected := style.PrintHyperlink("Open", pr.Url) +
|
||||
" " +
|
||||
style.PrintHyperlink("Improve checks "+numberText+"\n", pr.Url)
|
||||
assert.Equal(t, expected, actual)
|
||||
})
|
||||
|
||||
t.Run("avoids a double slash in the checks URL", func(t *testing.T) {
|
||||
prWithTrailingSlash := *pr
|
||||
prWithTrailingSlash.Url += "/"
|
||||
|
||||
actual := FormatPullRequestHeader(&prWithTrailingSlash, tr)
|
||||
|
||||
assert.Contains(t, actual, "https://github.com/jesseduffield/lazygit/pull/5871/checks")
|
||||
assert.NotContains(t, actual, "pull/5871//checks")
|
||||
})
|
||||
}
|
||||
|
||||
func TestChecksStatePresentation(t *testing.T) {
|
||||
tr := i18n.EnglishTranslationSet()
|
||||
testCases := []struct {
|
||||
name string
|
||||
state string
|
||||
expectedIcon string
|
||||
expectedText string
|
||||
expectedStyle style.TextStyle
|
||||
}{
|
||||
{name: "success", state: "SUCCESS", expectedIcon: "✓", expectedText: "Passing", expectedStyle: style.FgGreen},
|
||||
{name: "pending", state: "PENDING", expectedIcon: "●", expectedText: "Pending", expectedStyle: style.FgYellow},
|
||||
{name: "failure", state: "FAILURE", expectedIcon: "✗", expectedText: "Failing", expectedStyle: style.FgRed},
|
||||
{name: "error", state: "ERROR", expectedIcon: "!", expectedText: "Error", expectedStyle: style.FgRed},
|
||||
{name: "expected", state: "EXPECTED", expectedIcon: "○", expectedText: "Expected", expectedStyle: style.FgDefault},
|
||||
{name: "empty", state: "", expectedIcon: "", expectedText: "", expectedStyle: style.Nothing},
|
||||
{name: "unknown", state: "FUTURE_STATE", expectedIcon: "", expectedText: "", expectedStyle: style.Nothing},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
icon, text, textStyle := checksStatePresentation(testCase.state, tr)
|
||||
|
||||
assert.Equal(t, testCase.expectedIcon, icon)
|
||||
assert.Equal(t, testCase.expectedText, text)
|
||||
assert.Equal(t, testCase.expectedStyle, textStyle)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_getBranchDisplayStrings(t *testing.T) {
|
||||
|
||||
@@ -367,6 +367,11 @@ type TranslationSet struct {
|
||||
FwdNoLocalUpstream string
|
||||
FwdCommitsToPush string
|
||||
PullRequestNoUpstream string
|
||||
PullRequestChecksPassing string
|
||||
PullRequestChecksPending string
|
||||
PullRequestChecksFailing string
|
||||
PullRequestChecksError string
|
||||
PullRequestChecksExpected string
|
||||
ErrorOccurred string
|
||||
ConflictLabel string
|
||||
PendingRebaseTodosSectionHeader string
|
||||
@@ -1519,6 +1524,11 @@ func EnglishTranslationSet() *TranslationSet {
|
||||
FwdNoLocalUpstream: "Cannot fast-forward a branch whose remote is not registered locally",
|
||||
FwdCommitsToPush: "Cannot fast-forward a branch with commits to push",
|
||||
PullRequestNoUpstream: "Cannot open a pull request for a branch with no upstream",
|
||||
PullRequestChecksPassing: "Passing",
|
||||
PullRequestChecksPending: "Pending",
|
||||
PullRequestChecksFailing: "Failing",
|
||||
PullRequestChecksError: "Error",
|
||||
PullRequestChecksExpected: "Expected",
|
||||
ErrorOccurred: "An error occurred! Please create an issue at",
|
||||
ConflictLabel: "CONFLICT",
|
||||
PendingRebaseTodosSectionHeader: "Pending rebase todos",
|
||||
|
||||
Reference in New Issue
Block a user