Skip to content

Commit 5152f72

Browse files
Merge pull request #541 from harrisoncramer/develop
Bugfixes and Other Minor Improvements. This is a #MINOR release.
2 parents 19c6883 + 418c9c0 commit 5152f72

32 files changed

Lines changed: 874 additions & 153 deletions
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
name: Update gomod2nix
2+
3+
on:
4+
push:
5+
branches:
6+
- develop
7+
paths:
8+
- go.mod
9+
- go.sum
10+
11+
jobs:
12+
update:
13+
runs-on: ubuntu-latest
14+
permissions:
15+
contents: write
16+
steps:
17+
- uses: actions/checkout@v4
18+
19+
- uses: cachix/install-nix-action@v27
20+
with:
21+
nix_path: nixpkgs=channel:nixos-unstable
22+
23+
- name: Generate gomod2nix.toml
24+
run: nix run github:nix-community/gomod2nix -- generate
25+
26+
- name: Commit updated gomod2nix.toml
27+
uses: stefanzweifel/git-auto-commit-action@v5
28+
with:
29+
commit_message: "chore: update gomod2nix.toml"
30+
file_pattern: gomod2nix.toml

README.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ With <a href="https://github.com/folke/lazy.nvim">Lazy</a>:
4040
dependencies = {
4141
"MunifTanjim/nui.nvim",
4242
"nvim-lua/plenary.nvim",
43-
"sindrets/diffview.nvim",
43+
"dlyongemallo/diffview.nvim", -- Maintained fork of "sindrets/diffview.nvim".
4444
"stevearc/dressing.nvim", -- Recommended but not required. Better UI for pickers.
4545
"nvim-tree/nvim-web-devicons", -- Recommended but not required. Icons in discussion tree.
4646
},
@@ -59,7 +59,7 @@ And with <a href="https://github.com/lewis6991/pckr.nvim">pckr.nvim</a>:
5959
requires = {
6060
"MunifTanjim/nui.nvim",
6161
"nvim-lua/plenary.nvim",
62-
"sindrets/diffview.nvim",
62+
"dlyongemallo/diffview.nvim", -- Maintained fork of "sindrets/diffview.nvim".
6363
"stevearc/dressing.nvim", -- Recommended but not required. Better UI for pickers.
6464
"nvim-tree/nvim-web-devicons", -- Recommended but not required. Icons in discussion tree.
6565
},
@@ -73,6 +73,8 @@ And with <a href="https://github.com/lewis6991/pckr.nvim">pckr.nvim</a>:
7373

7474
Add `branch = "develop",` to your configuration if you want to use the (possibly unstable) development version of `gitlab.nvim`.
7575

76+
`gitlab.nvim` uses the `diffview.nvim` plugin for showing the diffs in a MR. We recommend using [dlyongemallo's](https://github.com/dlyongemallo/diffview.nvim) fork which is the de-facto maintained version of the plugin with many fixes and improvements (e.g., marking files as viewed).
77+
7678
## Contributing
7779

7880
Contributions to the plugin are welcome. Please read [.github/CONTRIBUTING.md](.github/CONTRIBUTING.md) before you start working on a pull request.
@@ -140,6 +142,7 @@ glrd Delete reviewer
140142
glA Approve MR
141143
glR Revoke MR approval
142144
glM Merge the feature branch to the target branch and close MR
145+
glm Set MR to merge automatically when the pipeline succeeds
143146
glC Create a new MR for currently checked-out feature branch
144147
glc Chose MR for review
145148
glS Start review for the currently checked-out branch

cmd/app/client.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ type Client struct {
3232
gitlab.UsersServiceInterface
3333
gitlab.DraftNotesServiceInterface
3434
gitlab.ProjectMarkdownUploadsServiceInterface
35+
gitlab.GraphQLInterface
3536
}
3637

3738
/* NewClient parses and validates the project settings and initializes the Gitlab client. */
@@ -100,6 +101,7 @@ func NewClient() (*Client, error) {
100101
client.Users,
101102
client.DraftNotes,
102103
client.ProjectMarkdownUploads,
104+
client.GraphQL,
103105
}, nil
104106
}
105107

cmd/app/list_discussions.go

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package app
22

33
import (
44
"net/http"
5+
"slices"
56
"sort"
67
"sync"
78
"time"
@@ -90,18 +91,16 @@ func (a discussionsListerService) ServeHTTP(w http.ResponseWriter, r *http.Reque
9091
},
9192
}
9293

93-
discussions, res, err := a.client.ListMergeRequestDiscussions(a.projectInfo.ProjectId, a.projectInfo.MergeId, &mergeRequestDiscussionOptions)
94+
it, hasErr := gitlab.Scan(func(p gitlab.PaginationOptionFunc) ([]*gitlab.Discussion, *gitlab.Response, error) {
95+
return a.client.ListMergeRequestDiscussions(a.projectInfo.ProjectId, a.projectInfo.MergeId, &mergeRequestDiscussionOptions, p)
96+
})
97+
discussions := slices.Collect(it)
9498

95-
if err != nil {
99+
if err := hasErr(); err != nil {
96100
handleError(w, err, "Could not list discussions", http.StatusInternalServerError)
97101
return
98102
}
99103

100-
if res.StatusCode >= 300 {
101-
handleError(w, GenericError{r.URL.Path}, "Could not list discussions", res.StatusCode)
102-
return
103-
}
104-
105104
/* Filter out any discussions started by a blacklisted user
106105
and system discussions, then return them sorted by created date */
107106
var unlinkedDiscussions []*gitlab.Discussion
@@ -124,7 +123,7 @@ func (a discussionsListerService) ServeHTTP(w http.ResponseWriter, r *http.Reque
124123

125124
/* Collect IDs in order to fetch emojis */
126125
var noteIds []int64
127-
for _, discussion := range discussions {
126+
for _, discussion := range slices.Concat(linkedDiscussions, unlinkedDiscussions) {
128127
for _, note := range discussion.Notes {
129128
noteIds = append(noteIds, note.ID)
130129
}

cmd/app/list_discussions_test.go

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -127,17 +127,6 @@ func TestListDiscussions(t *testing.T) {
127127
data, _ := getFailData(t, svc, request)
128128
checkErrorFromGitlab(t, data, "Could not list discussions")
129129
})
130-
t.Run("Handles non-200s from Gitlab client", func(t *testing.T) {
131-
request := makeRequest(t, http.MethodPost, "/mr/discussions/list", DiscussionsRequest{Blacklist: []string{}})
132-
svc := middleware(
133-
discussionsListerService{testProjectData, fakeDiscussionsLister{testBase: testBase{status: http.StatusSeeOther}}},
134-
withMr(testProjectData, fakeMergeRequestLister{}),
135-
withPayloadValidation(methodToPayload{http.MethodPost: newPayload[DiscussionsRequest]}),
136-
withMethodCheck(http.MethodPost),
137-
)
138-
data, _ := getFailData(t, svc, request)
139-
checkNon200(t, data, "Could not list discussions", "/mr/discussions/list")
140-
})
141130
t.Run("Handles error from emoji service", func(t *testing.T) {
142131
request := makeRequest(t, http.MethodPost, "/mr/discussions/list", DiscussionsRequest{Blacklist: []string{}})
143132
svc := middleware(

cmd/app/merge_mr.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
)
99

1010
type AcceptMergeRequestRequest struct {
11+
AutoMerge bool `json:"auto_merge"`
1112
DeleteBranch bool `json:"delete_branch"`
1213
SquashMessage string `json:"squash_message"`
1314
Squash bool `json:"squash"`
@@ -27,6 +28,7 @@ func (a mergeRequestAccepterService) ServeHTTP(w http.ResponseWriter, r *http.Re
2728
payload := r.Context().Value(payload("payload")).(*AcceptMergeRequestRequest)
2829

2930
opts := gitlab.AcceptMergeRequestOptions{
31+
AutoMerge: &payload.AutoMerge,
3032
Squash: &payload.Squash,
3133
ShouldRemoveSourceBranch: &payload.DeleteBranch,
3234
}
@@ -47,7 +49,13 @@ func (a mergeRequestAccepterService) ServeHTTP(w http.ResponseWriter, r *http.Re
4749
return
4850
}
4951

50-
response := SuccessResponse{Message: "MR merged successfully"}
52+
var message string
53+
if payload.AutoMerge {
54+
message = "MR set to be merged when all checks pass"
55+
} else {
56+
message = "MR merged successfully"
57+
}
58+
response := SuccessResponse{Message: message}
5159

5260
w.WriteHeader(http.StatusOK)
5361

cmd/app/mergeability_checks.go

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
package app
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"net/http"
7+
8+
gitlab "gitlab.com/gitlab-org/api/client-go"
9+
)
10+
11+
type MergeabilityCheck struct {
12+
Identifier string `json:"identifier"`
13+
Status string `json:"status"`
14+
}
15+
16+
type MergeabilityChecksResponse struct {
17+
SuccessResponse
18+
MergeabilityChecks []*MergeabilityCheck `json:"mergeability_checks"`
19+
}
20+
21+
type mergeabilityChecksGraphQLResponse struct {
22+
Data struct {
23+
Project struct {
24+
MergeRequest struct {
25+
MergeabilityChecks []*MergeabilityCheck `json:"mergeabilityChecks"`
26+
} `json:"mergeRequest"`
27+
} `json:"project"`
28+
} `json:"data"`
29+
}
30+
31+
const mergeabilityChecksQuery = `
32+
query GetMergeabilityChecks($projectPath: ID!, $iid: String!) {
33+
project(fullPath: $projectPath) {
34+
mergeRequest(iid: $iid) {
35+
mergeabilityChecks {
36+
identifier
37+
status
38+
}
39+
}
40+
}
41+
}
42+
`
43+
44+
type mergeabilityChecksService struct {
45+
data
46+
client gitlab.GraphQLInterface
47+
}
48+
49+
func (a mergeabilityChecksService) ServeHTTP(w http.ResponseWriter, r *http.Request) {
50+
checks, err := a.fetchMergeabilityChecks()
51+
if err != nil {
52+
handleError(w, err, "Could not get mergeability checks", http.StatusInternalServerError)
53+
return
54+
}
55+
56+
w.WriteHeader(http.StatusOK)
57+
response := MergeabilityChecksResponse{
58+
SuccessResponse: SuccessResponse{Message: "Mergeability checks retrieved"},
59+
MergeabilityChecks: checks,
60+
}
61+
62+
err = json.NewEncoder(w).Encode(response)
63+
if err != nil {
64+
handleError(w, err, "Could not encode response", http.StatusInternalServerError)
65+
}
66+
}
67+
68+
func (a mergeabilityChecksService) fetchMergeabilityChecks() ([]*MergeabilityCheck, error) {
69+
var response mergeabilityChecksGraphQLResponse
70+
71+
_, err := a.client.Do(gitlab.GraphQLQuery{
72+
Query: mergeabilityChecksQuery,
73+
Variables: map[string]any{
74+
"projectPath": a.gitInfo.ProjectPath(),
75+
"iid": fmt.Sprintf("%d", a.projectInfo.MergeId),
76+
},
77+
}, &response)
78+
if err != nil {
79+
return nil, fmt.Errorf("failed to fetch mergeability checks: %w", err)
80+
}
81+
82+
return response.Data.Project.MergeRequest.MergeabilityChecks, nil
83+
}
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
package app
2+
3+
import (
4+
"encoding/json"
5+
"net/http"
6+
"net/http/httptest"
7+
"testing"
8+
9+
"github.com/harrisoncramer/gitlab.nvim/cmd/app/git"
10+
gitlab "gitlab.com/gitlab-org/api/client-go"
11+
)
12+
13+
type fakeGraphQLClient struct {
14+
err error
15+
jsonData []byte
16+
}
17+
18+
func (f fakeGraphQLClient) Do(query gitlab.GraphQLQuery, response any, options ...gitlab.RequestOptionFunc) (*gitlab.Response, error) {
19+
if f.err != nil {
20+
return nil, f.err
21+
}
22+
23+
// Actually unmarshal JSON into the response struct
24+
if err := json.Unmarshal(f.jsonData, response); err != nil {
25+
return nil, err
26+
}
27+
28+
// if resp, ok := response.(mergeabilityChecksGraphQLResponse); ok {
29+
// resp.Data.Project.MergeRequest.MergeabilityChecks = f.checks
30+
// }
31+
32+
return makeResponse(http.StatusOK), nil
33+
}
34+
35+
var testMergeabilityData = data{
36+
projectInfo: &ProjectInfo{MergeId: 123},
37+
gitInfo: &git.GitData{
38+
BranchName: "feature-branch",
39+
Namespace: "test-namespace",
40+
ProjectName: "test-project",
41+
},
42+
}
43+
44+
func TestMergeabilityChecksHandler(t *testing.T) {
45+
t.Run("Returns mergeability checks", func(t *testing.T) {
46+
request := makeRequest(t, http.MethodGet, "/mr/mergeability_checks", nil)
47+
client := fakeGraphQLClient{
48+
jsonData: []byte(`{
49+
"data": {
50+
"project": {
51+
"mergeRequest": {
52+
"mergeabilityChecks": [
53+
{"identifier": "CI_MUST_PASS", "status": "SUCCESS"},
54+
{"identifier": "CONFLICT", "status": "FAILED"}
55+
]
56+
}
57+
}
58+
}
59+
}`),
60+
}
61+
svc := middleware(
62+
mergeabilityChecksService{testMergeabilityData, client},
63+
withMethodCheck(http.MethodGet),
64+
)
65+
66+
res := httptest.NewRecorder()
67+
svc.ServeHTTP(res, request)
68+
69+
var data MergeabilityChecksResponse
70+
err := json.Unmarshal(res.Body.Bytes(), &data)
71+
assert(t, err, nil)
72+
73+
assert(t, data.Message, "Mergeability checks retrieved")
74+
assert(t, len(data.MergeabilityChecks), 2)
75+
assert(t, data.MergeabilityChecks[0].Identifier, "CI_MUST_PASS")
76+
assert(t, data.MergeabilityChecks[0].Status, "SUCCESS")
77+
assert(t, data.MergeabilityChecks[1].Identifier, "CONFLICT")
78+
assert(t, data.MergeabilityChecks[1].Status, "FAILED")
79+
})
80+
81+
t.Run("Returns empty list when there are no checks", func(t *testing.T) {
82+
request := makeRequest(t, http.MethodGet, "/mr/mergeability_checks", nil)
83+
client := fakeGraphQLClient{
84+
jsonData: []byte(`{
85+
"data": {
86+
"project": {
87+
"mergeRequest": {
88+
"mergeabilityChecks": []
89+
}
90+
}
91+
}
92+
}`),
93+
}
94+
svc := middleware(
95+
mergeabilityChecksService{testMergeabilityData, client},
96+
withMethodCheck(http.MethodGet),
97+
)
98+
99+
res := httptest.NewRecorder()
100+
svc.ServeHTTP(res, request)
101+
102+
var data MergeabilityChecksResponse
103+
err := json.Unmarshal(res.Body.Bytes(), &data)
104+
assert(t, err, nil)
105+
106+
assert(t, data.Message, "Mergeability checks retrieved")
107+
assert(t, len(data.MergeabilityChecks), 0)
108+
})
109+
110+
t.Run("Handles errors from Gitlab client", func(t *testing.T) {
111+
request := makeRequest(t, http.MethodGet, "/mr/mergeability_checks", nil)
112+
client := fakeGraphQLClient{err: errorFromGitlab}
113+
svc := middleware(
114+
mergeabilityChecksService{testMergeabilityData, client},
115+
withMethodCheck(http.MethodGet),
116+
)
117+
data, _ := getFailData(t, svc, request)
118+
assert(t, data.Message, "Could not get mergeability checks")
119+
assert(t, data.Details, "failed to fetch mergeability checks: "+errorFromGitlab.Error())
120+
})
121+
}

cmd/app/server.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,11 @@ func CreateRouter(gitlabClient *Client, projectInfo *ProjectInfo, s *shutdownSer
134134
withMr(d, gitlabClient),
135135
withMethodCheck(http.MethodGet),
136136
))
137+
m.HandleFunc("/mr/info/mergeability", middleware(
138+
mergeabilityChecksService{d, gitlabClient},
139+
withMr(d, gitlabClient),
140+
withMethodCheck(http.MethodGet),
141+
))
137142
m.HandleFunc("/mr/assignee", middleware(
138143
assigneesService{d, gitlabClient},
139144
withMr(d, gitlabClient),

0 commit comments

Comments
 (0)