2.1.0 (#241)
• removes the <esc> keybinding for popups which was causing folks to lose their changes • deprecates the backup register. • updates go-gitlab to latest in order to get "drafts" functionality • fixes issues with labels not deleting correctly • creates a new data() function to get data from the plugin directly, see :h gitlab.nvim.data • fixes issues with line values not being computed directly, blocking jumps to/from discussion tree This is a #MINOR release.
This commit is contained in:
committed by
GitHub
parent
12c4acb297
commit
36f512cd6d
@@ -115,7 +115,6 @@ require("gitlab").setup({
|
|||||||
},
|
},
|
||||||
help = "g?", -- Opens a help popup for local keymaps when a relevant view is focused (popup, discussion panel, etc)
|
help = "g?", -- Opens a help popup for local keymaps when a relevant view is focused (popup, discussion panel, etc)
|
||||||
popup = { -- The popup for comment creation, editing, and replying
|
popup = { -- The popup for comment creation, editing, and replying
|
||||||
exit = "<Esc>",
|
|
||||||
perform_action = "<leader>s", -- Once in normal mode, does action (like saving comment or editing description, etc)
|
perform_action = "<leader>s", -- Once in normal mode, does action (like saving comment or editing description, etc)
|
||||||
perform_linewise_action = "<leader>l", -- Once in normal mode, does the linewise action (see logs for this job, etc)
|
perform_linewise_action = "<leader>l", -- Once in normal mode, does the linewise action (see logs for this job, etc)
|
||||||
width = "40%",
|
width = "40%",
|
||||||
@@ -128,7 +127,6 @@ require("gitlab").setup({
|
|||||||
pipeline = nil,
|
pipeline = nil,
|
||||||
reply = nil,
|
reply = nil,
|
||||||
squash_message = nil,
|
squash_message = nil,
|
||||||
backup_register = nil,
|
|
||||||
},
|
},
|
||||||
discussion_tree = { -- The discussion tree that holds all comments
|
discussion_tree = { -- The discussion tree that holds all comments
|
||||||
auto_open = true, -- Automatically open when the reviewer is opened
|
auto_open = true, -- Automatically open when the reviewer is opened
|
||||||
|
|||||||
@@ -86,10 +86,6 @@ func GetCurrentBranchNameFromNativeGitCmd() (res string, e error) {
|
|||||||
|
|
||||||
branchName := strings.TrimSpace(string(output))
|
branchName := strings.TrimSpace(string(output))
|
||||||
|
|
||||||
if branchName == "main" || branchName == "master" {
|
|
||||||
return "", fmt.Errorf("Cannot run on %s branch", branchName)
|
|
||||||
}
|
|
||||||
|
|
||||||
return branchName, nil
|
return branchName, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ func (a *api) updateLabels(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var labels = gitlab.Labels(labelUpdateRequest.Labels)
|
var labels = gitlab.LabelOptions(labelUpdateRequest.Labels)
|
||||||
mr, res, err := a.client.UpdateMergeRequest(a.projectInfo.ProjectId, a.projectInfo.MergeId, &gitlab.UpdateMergeRequestOptions{
|
mr, res, err := a.client.UpdateMergeRequest(a.projectInfo.ProjectId, a.projectInfo.MergeId, &gitlab.UpdateMergeRequestOptions{
|
||||||
Labels: &labels,
|
Labels: &labels,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -12,12 +12,17 @@ import (
|
|||||||
|
|
||||||
type RetriggerPipelineResponse struct {
|
type RetriggerPipelineResponse struct {
|
||||||
SuccessResponse
|
SuccessResponse
|
||||||
Pipeline *gitlab.Pipeline
|
LatestPipeline *gitlab.Pipeline `json:"latest_pipeline"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type GetJobsResponse struct {
|
type PipelineWithJobs struct {
|
||||||
|
Jobs []*gitlab.Job `json:"jobs"`
|
||||||
|
LatestPipeline *gitlab.Pipeline `json:"latest_pipeline"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetPipelineAndJobsResponse struct {
|
||||||
SuccessResponse
|
SuccessResponse
|
||||||
Jobs []*gitlab.Job
|
Pipeline PipelineWithJobs `json:"latest_pipeline"`
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -27,7 +32,7 @@ about a given job in a pipeline, see the jobHandler function
|
|||||||
func (a *api) pipelineHandler(w http.ResponseWriter, r *http.Request) {
|
func (a *api) pipelineHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
switch r.Method {
|
switch r.Method {
|
||||||
case http.MethodGet:
|
case http.MethodGet:
|
||||||
a.GetJobs(w, r)
|
a.GetPipelineAndJobs(w, r)
|
||||||
case http.MethodPost:
|
case http.MethodPost:
|
||||||
a.RetriggerPipeline(w, r)
|
a.RetriggerPipeline(w, r)
|
||||||
default:
|
default:
|
||||||
@@ -37,18 +42,29 @@ func (a *api) pipelineHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *api) GetJobs(w http.ResponseWriter, r *http.Request) {
|
func (a *api) GetPipelineAndJobs(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
|
||||||
id := strings.TrimPrefix(r.URL.Path, "/pipeline/")
|
pipeline, res, err := a.client.GetLatestPipeline(a.projectInfo.ProjectId, &gitlab.GetLatestPipelineOptions{
|
||||||
idInt, err := strconv.Atoi(id)
|
Ref: &a.gitInfo.BranchName,
|
||||||
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
handleError(w, err, "Could not convert pipeline ID to integer", http.StatusBadRequest)
|
handleError(w, err, fmt.Sprintf("Gitlab failed to get latest pipeline for %s branch", a.gitInfo.BranchName), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
jobs, res, err := a.client.ListPipelineJobs(a.projectInfo.ProjectId, idInt, &gitlab.ListJobsOptions{})
|
if res.StatusCode >= 300 {
|
||||||
|
handleError(w, GenericError{endpoint: "/pipeline"}, fmt.Sprintf("Could not get latest pipeline for %s branch", a.gitInfo.BranchName), res.StatusCode)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if pipeline == nil {
|
||||||
|
handleError(w, GenericError{endpoint: "/pipeline"}, fmt.Sprintf("No pipeline found for %s branch", a.gitInfo.BranchName), res.StatusCode)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
jobs, res, err := a.client.ListPipelineJobs(a.projectInfo.ProjectId, pipeline.ID, &gitlab.ListJobsOptions{})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
handleError(w, err, "Could not get pipeline jobs", http.StatusInternalServerError)
|
handleError(w, err, "Could not get pipeline jobs", http.StatusInternalServerError)
|
||||||
@@ -61,12 +77,15 @@ func (a *api) GetJobs(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
response := GetJobsResponse{
|
response := GetPipelineAndJobsResponse{
|
||||||
SuccessResponse: SuccessResponse{
|
SuccessResponse: SuccessResponse{
|
||||||
Status: http.StatusOK,
|
Status: http.StatusOK,
|
||||||
Message: "Pipeline jobs retrieved",
|
Message: "Pipeline retrieved",
|
||||||
|
},
|
||||||
|
Pipeline: PipelineWithJobs{
|
||||||
|
LatestPipeline: pipeline,
|
||||||
|
Jobs: jobs,
|
||||||
},
|
},
|
||||||
Jobs: jobs,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err = json.NewEncoder(w).Encode(response)
|
err = json.NewEncoder(w).Encode(response)
|
||||||
@@ -78,7 +97,7 @@ func (a *api) GetJobs(w http.ResponseWriter, r *http.Request) {
|
|||||||
func (a *api) RetriggerPipeline(w http.ResponseWriter, r *http.Request) {
|
func (a *api) RetriggerPipeline(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
|
||||||
id := strings.TrimPrefix(r.URL.Path, "/pipeline/")
|
id := strings.TrimPrefix(r.URL.Path, "/pipeline/trigger/")
|
||||||
|
|
||||||
idInt, err := strconv.Atoi(id)
|
idInt, err := strconv.Atoi(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -104,7 +123,7 @@ func (a *api) RetriggerPipeline(w http.ResponseWriter, r *http.Request) {
|
|||||||
Message: "Pipeline retriggered",
|
Message: "Pipeline retriggered",
|
||||||
Status: http.StatusOK,
|
Status: http.StatusOK,
|
||||||
},
|
},
|
||||||
Pipeline: pipeline,
|
LatestPipeline: pipeline,
|
||||||
}
|
}
|
||||||
|
|
||||||
err = json.NewEncoder(w).Encode(response)
|
err = json.NewEncoder(w).Encode(response)
|
||||||
|
|||||||
@@ -32,54 +32,79 @@ func retryPipelineBuildNon200(pid interface{}, pipeline int, options ...gitlab.R
|
|||||||
return nil, makeResponse(http.StatusSeeOther), nil
|
return nil, makeResponse(http.StatusSeeOther), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getLatestPipeline200(pid interface{}, opts *gitlab.GetLatestPipelineOptions, options ...gitlab.RequestOptionFunc) (*gitlab.Pipeline, *gitlab.Response, error) {
|
||||||
|
return &gitlab.Pipeline{ID: 1}, makeResponse(http.StatusOK), nil
|
||||||
|
}
|
||||||
|
|
||||||
func TestPipelineHandler(t *testing.T) {
|
func TestPipelineHandler(t *testing.T) {
|
||||||
t.Run("Gets all pipeline jobs", func(t *testing.T) {
|
t.Run("Gets all pipeline jobs", func(t *testing.T) {
|
||||||
request := makeRequest(t, http.MethodGet, "/pipeline/1", nil)
|
request := makeRequest(t, http.MethodGet, "/pipeline", nil)
|
||||||
server, _ := createRouterAndApi(fakeClient{listPipelineJobs: listPipelineJobs})
|
server, _ := createRouterAndApi(fakeClient{
|
||||||
data := serveRequest(t, server, request, GetJobsResponse{})
|
listPipelineJobs: listPipelineJobs,
|
||||||
assert(t, data.SuccessResponse.Message, "Pipeline jobs retrieved")
|
getLatestPipeline: getLatestPipeline200,
|
||||||
|
})
|
||||||
|
data := serveRequest(t, server, request, GetPipelineAndJobsResponse{})
|
||||||
|
assert(t, data.SuccessResponse.Message, "Pipeline retrieved")
|
||||||
assert(t, data.SuccessResponse.Status, http.StatusOK)
|
assert(t, data.SuccessResponse.Status, http.StatusOK)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Disallows non-GET, non-POST methods", func(t *testing.T) {
|
t.Run("Disallows non-GET, non-POST methods", func(t *testing.T) {
|
||||||
request := makeRequest(t, http.MethodPatch, "/pipeline/1", nil)
|
request := makeRequest(t, http.MethodPatch, "/pipeline", nil)
|
||||||
server, _ := createRouterAndApi(fakeClient{listPipelineJobs: listPipelineJobs})
|
server, _ := createRouterAndApi(fakeClient{
|
||||||
|
listPipelineJobs: listPipelineJobs,
|
||||||
|
getLatestPipeline: getLatestPipeline200,
|
||||||
|
})
|
||||||
data := serveRequest(t, server, request, ErrorResponse{})
|
data := serveRequest(t, server, request, ErrorResponse{})
|
||||||
checkBadMethod(t, *data, http.MethodGet, http.MethodPost)
|
checkBadMethod(t, *data, http.MethodGet, http.MethodPost)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Handles errors from Gitlab client", func(t *testing.T) {
|
t.Run("Handles errors from Gitlab client", func(t *testing.T) {
|
||||||
request := makeRequest(t, http.MethodGet, "/pipeline/1", nil)
|
request := makeRequest(t, http.MethodGet, "/pipeline", nil)
|
||||||
server, _ := createRouterAndApi(fakeClient{listPipelineJobs: listPipelineJobsErr})
|
server, _ := createRouterAndApi(fakeClient{
|
||||||
|
listPipelineJobs: listPipelineJobsErr,
|
||||||
|
getLatestPipeline: getLatestPipeline200,
|
||||||
|
})
|
||||||
data := serveRequest(t, server, request, ErrorResponse{})
|
data := serveRequest(t, server, request, ErrorResponse{})
|
||||||
checkErrorFromGitlab(t, *data, "Could not get pipeline jobs")
|
checkErrorFromGitlab(t, *data, "Could not get pipeline jobs")
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Handles non-200s from Gitlab client", func(t *testing.T) {
|
t.Run("Handles non-200s from Gitlab client", func(t *testing.T) {
|
||||||
request := makeRequest(t, http.MethodGet, "/pipeline/1", nil)
|
request := makeRequest(t, http.MethodGet, "/pipeline", nil)
|
||||||
server, _ := createRouterAndApi(fakeClient{listPipelineJobs: listPipelineJobsNon200})
|
server, _ := createRouterAndApi(fakeClient{
|
||||||
|
listPipelineJobs: listPipelineJobsNon200,
|
||||||
|
getLatestPipeline: getLatestPipeline200,
|
||||||
|
})
|
||||||
data := serveRequest(t, server, request, ErrorResponse{})
|
data := serveRequest(t, server, request, ErrorResponse{})
|
||||||
checkNon200(t, *data, "Could not get pipeline jobs", "/pipeline")
|
checkNon200(t, *data, "Could not get pipeline jobs", "/pipeline")
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Retriggers pipeline", func(t *testing.T) {
|
|
||||||
request := makeRequest(t, http.MethodPost, "/pipeline/1", nil)
|
|
||||||
server, _ := createRouterAndApi(fakeClient{retryPipelineBuild: retryPipelineBuild})
|
|
||||||
data := serveRequest(t, server, request, GetJobsResponse{})
|
|
||||||
assert(t, data.SuccessResponse.Message, "Pipeline retriggered")
|
|
||||||
assert(t, data.SuccessResponse.Status, http.StatusOK)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("Handles errors from Gitlab client", func(t *testing.T) {
|
t.Run("Handles errors from Gitlab client", func(t *testing.T) {
|
||||||
request := makeRequest(t, http.MethodPost, "/pipeline/1", nil)
|
request := makeRequest(t, http.MethodPost, "/pipeline/trigger/1", nil)
|
||||||
server, _ := createRouterAndApi(fakeClient{retryPipelineBuild: retryPipelineBuildErr})
|
server, _ := createRouterAndApi(fakeClient{
|
||||||
|
retryPipelineBuild: retryPipelineBuildErr,
|
||||||
|
getLatestPipeline: getLatestPipeline200,
|
||||||
|
})
|
||||||
data := serveRequest(t, server, request, ErrorResponse{})
|
data := serveRequest(t, server, request, ErrorResponse{})
|
||||||
checkErrorFromGitlab(t, *data, "Could not retrigger pipeline")
|
checkErrorFromGitlab(t, *data, "Could not retrigger pipeline")
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("Retriggers pipeline", func(t *testing.T) {
|
||||||
|
request := makeRequest(t, http.MethodPost, "/pipeline/trigger/1", nil)
|
||||||
|
server, _ := createRouterAndApi(fakeClient{
|
||||||
|
retryPipelineBuild: retryPipelineBuild,
|
||||||
|
getLatestPipeline: getLatestPipeline200,
|
||||||
|
})
|
||||||
|
data := serveRequest(t, server, request, GetPipelineAndJobsResponse{})
|
||||||
|
assert(t, data.SuccessResponse.Message, "Pipeline retriggered")
|
||||||
|
assert(t, data.SuccessResponse.Status, http.StatusOK)
|
||||||
|
})
|
||||||
|
|
||||||
t.Run("Handles non-200s from Gitlab client on retrigger", func(t *testing.T) {
|
t.Run("Handles non-200s from Gitlab client on retrigger", func(t *testing.T) {
|
||||||
request := makeRequest(t, http.MethodPost, "/pipeline/1", nil)
|
request := makeRequest(t, http.MethodPost, "/pipeline/trigger/1", nil)
|
||||||
server, _ := createRouterAndApi(fakeClient{retryPipelineBuild: retryPipelineBuildNon200})
|
server, _ := createRouterAndApi(fakeClient{
|
||||||
|
retryPipelineBuild: retryPipelineBuildNon200,
|
||||||
|
getLatestPipeline: getLatestPipeline200,
|
||||||
|
})
|
||||||
data := serveRequest(t, server, request, ErrorResponse{})
|
data := serveRequest(t, server, request, ErrorResponse{})
|
||||||
checkNon200(t, *data, "Could not retrigger pipeline", "/pipeline")
|
checkNon200(t, *data, "Could not retrigger pipeline", "/pipeline")
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -132,11 +132,12 @@ func createRouterAndApi(client ClientInterface, optFuncs ...optFunc) (*http.Serv
|
|||||||
m.HandleFunc("/mr/revoke", a.withMr(a.revokeHandler))
|
m.HandleFunc("/mr/revoke", a.withMr(a.revokeHandler))
|
||||||
m.HandleFunc("/mr/awardable/note/", a.withMr(a.emojiNoteHandler))
|
m.HandleFunc("/mr/awardable/note/", a.withMr(a.emojiNoteHandler))
|
||||||
|
|
||||||
|
m.HandleFunc("/pipeline", a.pipelineHandler)
|
||||||
|
m.HandleFunc("/pipeline/trigger/", a.pipelineHandler)
|
||||||
m.HandleFunc("/users/me", a.meHandler)
|
m.HandleFunc("/users/me", a.meHandler)
|
||||||
m.HandleFunc("/attachment", a.attachmentHandler)
|
m.HandleFunc("/attachment", a.attachmentHandler)
|
||||||
m.HandleFunc("/create_mr", a.createMr)
|
m.HandleFunc("/create_mr", a.createMr)
|
||||||
m.HandleFunc("/job", a.jobHandler)
|
m.HandleFunc("/job", a.jobHandler)
|
||||||
m.HandleFunc("/pipeline/", a.pipelineHandler)
|
|
||||||
m.HandleFunc("/project/members", a.projectMembersHandler)
|
m.HandleFunc("/project/members", a.projectMembersHandler)
|
||||||
m.HandleFunc("/shutdown", a.shutdownHandler)
|
m.HandleFunc("/shutdown", a.shutdownHandler)
|
||||||
|
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ type fakeClient struct {
|
|||||||
listAllProjectMembers func(pid interface{}, opt *gitlab.ListProjectMembersOptions, options ...gitlab.RequestOptionFunc) ([]*gitlab.ProjectMember, *gitlab.Response, error)
|
listAllProjectMembers func(pid interface{}, opt *gitlab.ListProjectMembersOptions, options ...gitlab.RequestOptionFunc) ([]*gitlab.ProjectMember, *gitlab.Response, error)
|
||||||
retryPipelineBuild func(pid interface{}, pipeline int, options ...gitlab.RequestOptionFunc) (*gitlab.Pipeline, *gitlab.Response, error)
|
retryPipelineBuild func(pid interface{}, pipeline int, options ...gitlab.RequestOptionFunc) (*gitlab.Pipeline, *gitlab.Response, error)
|
||||||
listPipelineJobs func(pid interface{}, pipelineID int, opts *gitlab.ListJobsOptions, options ...gitlab.RequestOptionFunc) ([]*gitlab.Job, *gitlab.Response, error)
|
listPipelineJobs func(pid interface{}, pipelineID int, opts *gitlab.ListJobsOptions, options ...gitlab.RequestOptionFunc) ([]*gitlab.Job, *gitlab.Response, error)
|
||||||
|
getLatestPipeline func(pid interface{}, opt *gitlab.GetLatestPipelineOptions, options ...gitlab.RequestOptionFunc) (*gitlab.Pipeline, *gitlab.Response, error)
|
||||||
getTraceFile func(pid interface{}, jobID int, options ...gitlab.RequestOptionFunc) (*bytes.Reader, *gitlab.Response, error)
|
getTraceFile func(pid interface{}, jobID int, options ...gitlab.RequestOptionFunc) (*bytes.Reader, *gitlab.Response, error)
|
||||||
listLabels func(pid interface{}, opt *gitlab.ListLabelsOptions, options ...gitlab.RequestOptionFunc) ([]*gitlab.Label, *gitlab.Response, error)
|
listLabels func(pid interface{}, opt *gitlab.ListLabelsOptions, options ...gitlab.RequestOptionFunc) ([]*gitlab.Label, *gitlab.Response, error)
|
||||||
listMergeRequestAwardEmojiOnNote func(pid interface{}, mergeRequestIID, noteID int, opt *gitlab.ListAwardEmojiOptions, options ...gitlab.RequestOptionFunc) ([]*gitlab.AwardEmoji, *gitlab.Response, error)
|
listMergeRequestAwardEmojiOnNote func(pid interface{}, mergeRequestIID, noteID int, opt *gitlab.ListAwardEmojiOptions, options ...gitlab.RequestOptionFunc) ([]*gitlab.AwardEmoji, *gitlab.Response, error)
|
||||||
@@ -120,6 +121,10 @@ func (f fakeClient) ListPipelineJobs(pid interface{}, pipelineID int, opts *gitl
|
|||||||
return f.listPipelineJobs(pid, pipelineID, opts, options...)
|
return f.listPipelineJobs(pid, pipelineID, opts, options...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f fakeClient) GetLatestPipeline(pid interface{}, opts *gitlab.GetLatestPipelineOptions, options ...gitlab.RequestOptionFunc) (*gitlab.Pipeline, *gitlab.Response, error) {
|
||||||
|
return f.getLatestPipeline(pid, opts, options...)
|
||||||
|
}
|
||||||
|
|
||||||
func (f fakeClient) GetTraceFile(pid interface{}, jobID int, options ...gitlab.RequestOptionFunc) (*bytes.Reader, *gitlab.Response, error) {
|
func (f fakeClient) GetTraceFile(pid interface{}, jobID int, options ...gitlab.RequestOptionFunc) (*bytes.Reader, *gitlab.Response, error) {
|
||||||
return f.getTraceFile(pid, jobID, options...)
|
return f.getTraceFile(pid, jobID, options...)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ type ClientInterface interface {
|
|||||||
ListAllProjectMembers(pid interface{}, opt *gitlab.ListProjectMembersOptions, options ...gitlab.RequestOptionFunc) ([]*gitlab.ProjectMember, *gitlab.Response, error)
|
ListAllProjectMembers(pid interface{}, opt *gitlab.ListProjectMembersOptions, options ...gitlab.RequestOptionFunc) ([]*gitlab.ProjectMember, *gitlab.Response, error)
|
||||||
RetryPipelineBuild(pid interface{}, pipeline int, options ...gitlab.RequestOptionFunc) (*gitlab.Pipeline, *gitlab.Response, error)
|
RetryPipelineBuild(pid interface{}, pipeline int, options ...gitlab.RequestOptionFunc) (*gitlab.Pipeline, *gitlab.Response, error)
|
||||||
ListPipelineJobs(pid interface{}, pipelineID int, opts *gitlab.ListJobsOptions, options ...gitlab.RequestOptionFunc) ([]*gitlab.Job, *gitlab.Response, error)
|
ListPipelineJobs(pid interface{}, pipelineID int, opts *gitlab.ListJobsOptions, options ...gitlab.RequestOptionFunc) ([]*gitlab.Job, *gitlab.Response, error)
|
||||||
|
GetLatestPipeline(pid interface{}, opt *gitlab.GetLatestPipelineOptions, options ...gitlab.RequestOptionFunc) (*gitlab.Pipeline, *gitlab.Response, error)
|
||||||
GetTraceFile(pid interface{}, jobID int, options ...gitlab.RequestOptionFunc) (*bytes.Reader, *gitlab.Response, error)
|
GetTraceFile(pid interface{}, jobID int, options ...gitlab.RequestOptionFunc) (*bytes.Reader, *gitlab.Response, error)
|
||||||
ListLabels(pid interface{}, opt *gitlab.ListLabelsOptions, options ...gitlab.RequestOptionFunc) ([]*gitlab.Label, *gitlab.Response, error)
|
ListLabels(pid interface{}, opt *gitlab.ListLabelsOptions, options ...gitlab.RequestOptionFunc) ([]*gitlab.Label, *gitlab.Response, error)
|
||||||
ListMergeRequestAwardEmojiOnNote(pid interface{}, mergeRequestIID int, noteID int, opt *gitlab.ListAwardEmojiOptions, options ...gitlab.RequestOptionFunc) ([]*gitlab.AwardEmoji, *gitlab.Response, error)
|
ListMergeRequestAwardEmojiOnNote(pid interface{}, mergeRequestIID int, noteID int, opt *gitlab.ListAwardEmojiOptions, options ...gitlab.RequestOptionFunc) ([]*gitlab.AwardEmoji, *gitlab.Response, error)
|
||||||
|
|||||||
@@ -146,7 +146,6 @@ you call this function with no values the defaults will be used:
|
|||||||
},
|
},
|
||||||
help = "g?", -- Opens a help popup for local keymaps when a relevant view is focused (popup, discussion panel, etc)
|
help = "g?", -- Opens a help popup for local keymaps when a relevant view is focused (popup, discussion panel, etc)
|
||||||
popup = { -- The popup for comment creation, editing, and replying
|
popup = { -- The popup for comment creation, editing, and replying
|
||||||
exit = "<Esc>",
|
|
||||||
perform_action = "<leader>s", -- Once in normal mode, does action (like saving comment or editing description, etc)
|
perform_action = "<leader>s", -- Once in normal mode, does action (like saving comment or editing description, etc)
|
||||||
perform_linewise_action = "<leader>l", -- Once in normal mode, does the linewise action (see logs for this job, etc)
|
perform_linewise_action = "<leader>l", -- Once in normal mode, does the linewise action (see logs for this job, etc)
|
||||||
width = "40%",
|
width = "40%",
|
||||||
@@ -159,7 +158,6 @@ you call this function with no values the defaults will be used:
|
|||||||
pipeline = nil,
|
pipeline = nil,
|
||||||
reply = nil,
|
reply = nil,
|
||||||
squash_message = nil,
|
squash_message = nil,
|
||||||
backup_register = nil,
|
|
||||||
},
|
},
|
||||||
discussion_tree = { -- The discussion tree that holds all comments
|
discussion_tree = { -- The discussion tree that holds all comments
|
||||||
auto_open = true, -- Automatically open when the reviewer is opened
|
auto_open = true, -- Automatically open when the reviewer is opened
|
||||||
@@ -305,22 +303,6 @@ code block with prefilled code from the visual selection.
|
|||||||
Just like the summary, all the different kinds of comments are saved via the
|
Just like the summary, all the different kinds of comments are saved via the
|
||||||
`settings.popup.perform_action` keybinding.
|
`settings.popup.perform_action` keybinding.
|
||||||
|
|
||||||
BACKUP REGISTER *gitlab.nvim.backup-register*
|
|
||||||
|
|
||||||
Sometimes, the action triggered by `settings.popup.perform_action` can fail.
|
|
||||||
To prevent losing your carefully crafted note/comment/suggestion you can set
|
|
||||||
`settings.popup.backup_register` to a writable register (see |registers|) to
|
|
||||||
which the contents of the popup window will be saved just before the action is
|
|
||||||
performed. A practical setting is `settings.popup.backup_register = "+"` which
|
|
||||||
saves to the system clipboard (see |quoteplus|). This lets you easily apply
|
|
||||||
the action on Gitlab in a browser, if it keeps failing in `gitlab.nvim`.
|
|
||||||
|
|
||||||
If you experience such problems, please first read the
|
|
||||||
|gitlab.nvim.troubleshooting| section. If it does not help, see if there are
|
|
||||||
any relevant known <https://github.com/harrisoncramer/gitlab.nvim/issues>. If
|
|
||||||
there are none, please open one and provide any error messages that
|
|
||||||
`gitlab.nvim` may be showing.
|
|
||||||
|
|
||||||
DISCUSSIONS AND NOTES *gitlab.nvim.discussions-and-notes*
|
DISCUSSIONS AND NOTES *gitlab.nvim.discussions-and-notes*
|
||||||
|
|
||||||
Gitlab groups threads of comments together into "discussions."
|
Gitlab groups threads of comments together into "discussions."
|
||||||
@@ -751,4 +733,38 @@ Merges the merge request into the target branch
|
|||||||
>lua
|
>lua
|
||||||
require("gitlab").merge()
|
require("gitlab").merge()
|
||||||
|
|
||||||
|
gitlab.data({ opts }, cb) *gitlab.nvim.data*
|
||||||
|
|
||||||
|
The data function can be used to integrate `gitlab.nvim` with other plugins and tooling, by fetching
|
||||||
|
raw data about the current MR, including the summary information (title, description, etc);
|
||||||
|
reviewers, assignees, pipeline status.
|
||||||
|
>lua
|
||||||
|
require("gitlab").data({
|
||||||
|
{ type = "info", refresh = false },
|
||||||
|
{ type = "user", refresh = false } }, function (data)
|
||||||
|
vim.print("The info data is: ", data.info)
|
||||||
|
vim.print("The user data is: ", data.user)
|
||||||
|
end)
|
||||||
|
|
||||||
|
If the resources have not yet been fetched from Gitlab, this function will
|
||||||
|
perform API calls for them. Once the data has been fetched, the callback will
|
||||||
|
execute and passed the data as an argument.
|
||||||
|
|
||||||
|
Parameters: ~
|
||||||
|
• {resources} (table) A list of resource blocks to fetch.
|
||||||
|
• {resource} (table) A resource to fetch, such as job information, etc.
|
||||||
|
• {resource.type}: (string) The type of resource, either: "user"
|
||||||
|
"labels", "project_members", "pipeline," or "revisions"." The types are:
|
||||||
|
• {user}: Information about the currently authenticated user
|
||||||
|
• {labels}: The labels available in the current project
|
||||||
|
• {project_members}: The list of current project members
|
||||||
|
• {revisions}: Revision information about the MR
|
||||||
|
• {pipeline}: Information about the current branch's pipeline. Returns
|
||||||
|
and object with `latest_pipeline` and `jobs` as fields.
|
||||||
|
• {resource.refresh}: (bool) Whether to re-fetch the data from Gitlab
|
||||||
|
or use the cached data locally, if available.
|
||||||
|
• {cb} (function) The callback function that runs after all of the
|
||||||
|
resources have been fetched. Will be passed a table with the data,
|
||||||
|
with each resource as a key-value pair, with the key being it's type.
|
||||||
|
|
||||||
vim:tw=78:ts=8:noet:ft=help:norl:
|
vim:tw=78:ts=8:noet:ft=help:norl:
|
||||||
|
|||||||
6
go.mod
6
go.mod
@@ -2,13 +2,15 @@ module gitlab.com/harrisoncramer/gitlab.nvim
|
|||||||
|
|
||||||
go 1.19
|
go 1.19
|
||||||
|
|
||||||
require github.com/xanzy/go-gitlab v0.93.2
|
require (
|
||||||
|
github.com/hashicorp/go-retryablehttp v0.7.2
|
||||||
|
github.com/xanzy/go-gitlab v0.102.0
|
||||||
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/golang/protobuf v1.5.3 // indirect
|
github.com/golang/protobuf v1.5.3 // indirect
|
||||||
github.com/google/go-querystring v1.1.0 // indirect
|
github.com/google/go-querystring v1.1.0 // indirect
|
||||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||||
github.com/hashicorp/go-retryablehttp v0.7.2 // indirect
|
|
||||||
golang.org/x/net v0.8.0 // indirect
|
golang.org/x/net v0.8.0 // indirect
|
||||||
golang.org/x/oauth2 v0.6.0 // indirect
|
golang.org/x/oauth2 v0.6.0 // indirect
|
||||||
golang.org/x/time v0.3.0 // indirect
|
golang.org/x/time v0.3.0 // indirect
|
||||||
|
|||||||
6
go.sum
6
go.sum
@@ -19,10 +19,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
|
|||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
||||||
github.com/xanzy/go-gitlab v0.83.0 h1:37p0MpTPNbsTMKX/JnmJtY8Ch1sFiJzVF342+RvZEGw=
|
github.com/xanzy/go-gitlab v0.102.0 h1:ExHuJ1OTQ2yt25zBMMj0G96ChBirGYv8U7HyUiYkZ+4=
|
||||||
github.com/xanzy/go-gitlab v0.83.0/go.mod h1:5ryv+MnpZStBH8I/77HuQBsMbBGANtVpLWC15qOjWAw=
|
github.com/xanzy/go-gitlab v0.102.0/go.mod h1:ETg8tcj4OhrB84UEgeE8dSuV/0h4BBL1uOV/qK0vlyI=
|
||||||
github.com/xanzy/go-gitlab v0.93.2 h1:kNNf3BYNYn/Zkig0B89fma12l36VLcYSGu7OnaRlRDg=
|
|
||||||
github.com/xanzy/go-gitlab v0.93.2/go.mod h1:5ryv+MnpZStBH8I/77HuQBsMbBGANtVpLWC15qOjWAw=
|
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||||
golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
|
golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
|
||||||
|
|||||||
@@ -196,10 +196,6 @@ M.add_title = function(mr)
|
|||||||
mr.title = value
|
mr.title = value
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
input:map("n", "<Esc>", function()
|
|
||||||
input:unmount()
|
|
||||||
end, { noremap = true })
|
|
||||||
|
|
||||||
input:mount()
|
input:mount()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
45
lua/gitlab/actions/data.lua
Normal file
45
lua/gitlab/actions/data.lua
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
local u = require("gitlab.utils")
|
||||||
|
local async = require("gitlab.async")
|
||||||
|
local state = require("gitlab.state")
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
local user = state.dependencies.user
|
||||||
|
local info = state.dependencies.info
|
||||||
|
local labels = state.dependencies.labels
|
||||||
|
local project_members = state.dependencies.project_members
|
||||||
|
local revisions = state.dependencies.revisions
|
||||||
|
local latest_pipeline = state.dependencies.latest_pipeline
|
||||||
|
|
||||||
|
M.data = function(resources, cb)
|
||||||
|
if type(resources) ~= "table" or type(cb) ~= "function" then
|
||||||
|
u.notify("The data function must be passed a resources table and a callback function", vim.log.levels.ERROR)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local all_resources = {
|
||||||
|
info = info,
|
||||||
|
user = user,
|
||||||
|
labels = labels,
|
||||||
|
project_members = project_members,
|
||||||
|
revisions = revisions,
|
||||||
|
pipeline = latest_pipeline,
|
||||||
|
}
|
||||||
|
|
||||||
|
local api_calls = {}
|
||||||
|
for _, resource in ipairs(resources) do
|
||||||
|
local api_call = all_resources[resource.type]
|
||||||
|
table.insert(api_calls, u.merge(api_call, { refresh = resource.refresh }))
|
||||||
|
end
|
||||||
|
|
||||||
|
-- TODO: Build an async "parallel" that fetches the resources
|
||||||
|
-- in parallel where possible to speed up this API
|
||||||
|
return async.sequence(api_calls, function()
|
||||||
|
local data = {}
|
||||||
|
for k, v in pairs(all_resources) do
|
||||||
|
data[k] = state[v.state]
|
||||||
|
end
|
||||||
|
cb(data)
|
||||||
|
end)()
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
||||||
@@ -398,10 +398,6 @@ local function get_new_line(node)
|
|||||||
return node.new_line
|
return node.new_line
|
||||||
end
|
end
|
||||||
|
|
||||||
if range.start.new_line ~= nil then
|
|
||||||
return range.start.new_line
|
|
||||||
end
|
|
||||||
|
|
||||||
local _, start_new_line = common.parse_line_code(range.start.line_code)
|
local _, start_new_line = common.parse_line_code(range.start.line_code)
|
||||||
return start_new_line
|
return start_new_line
|
||||||
end
|
end
|
||||||
@@ -417,10 +413,6 @@ local function get_old_line(node)
|
|||||||
return node.old_line
|
return node.old_line
|
||||||
end
|
end
|
||||||
|
|
||||||
if range.start.old_line ~= nil then
|
|
||||||
return range.start.old_line
|
|
||||||
end
|
|
||||||
|
|
||||||
local start_old_line, _ = common.parse_line_code(range.start.line_code)
|
local start_old_line, _ = common.parse_line_code(range.start.line_code)
|
||||||
return start_old_line
|
return start_old_line
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -15,18 +15,11 @@ M.delete_label = function()
|
|||||||
end
|
end
|
||||||
|
|
||||||
local refresh_label_state = function(labels)
|
local refresh_label_state = function(labels)
|
||||||
state.INFO.labels = List.new(labels):reduce(function(agg, label)
|
state.INFO.labels = labels
|
||||||
return agg .. "," .. label
|
|
||||||
end, "")
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local get_current_labels = function()
|
local get_current_labels = function()
|
||||||
local label_string = state.INFO.labels
|
return state.INFO.labels
|
||||||
local current_labels = {}
|
|
||||||
for value in label_string:gmatch("[^,]+") do
|
|
||||||
table.insert(current_labels, value)
|
|
||||||
end
|
|
||||||
return current_labels
|
|
||||||
end
|
end
|
||||||
|
|
||||||
local get_all_labels = function()
|
local get_all_labels = function()
|
||||||
@@ -45,16 +38,11 @@ M.add_popup = function(type)
|
|||||||
if not choice then
|
if not choice then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local label_string = state.INFO.labels
|
table.insert(current_labels, choice)
|
||||||
local new_labels = {}
|
local body = { labels = current_labels }
|
||||||
for value in label_string:gmatch("[^,]+") do
|
|
||||||
table.insert(new_labels, value)
|
|
||||||
end
|
|
||||||
|
|
||||||
table.insert(new_labels, choice)
|
|
||||||
local body = { labels = new_labels }
|
|
||||||
job.run_job("/mr/" .. type, "PUT", body, function(data)
|
job.run_job("/mr/" .. type, "PUT", body, function(data)
|
||||||
u.notify(data.message, vim.log.levels.INFO)
|
u.notify(data.message, vim.log.levels.INFO)
|
||||||
|
|
||||||
refresh_label_state(data.labels)
|
refresh_label_state(data.labels)
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|||||||
@@ -7,12 +7,12 @@ local job = require("gitlab.job")
|
|||||||
local u = require("gitlab.utils")
|
local u = require("gitlab.utils")
|
||||||
local M = {
|
local M = {
|
||||||
pipeline_jobs = nil,
|
pipeline_jobs = nil,
|
||||||
|
latest_pipeline = nil,
|
||||||
pipeline_popup = nil,
|
pipeline_popup = nil,
|
||||||
}
|
}
|
||||||
|
|
||||||
local function get_pipeline()
|
local function get_latest_pipeline()
|
||||||
local pipeline = state.INFO.head_pipeline or state.INFO.pipeline
|
local pipeline = state.PIPELINE and state.PIPELINE.latest_pipeline
|
||||||
|
|
||||||
if type(pipeline) ~= "table" or (type(pipeline) == "table" and u.table_size(pipeline) == 0) then
|
if type(pipeline) ~= "table" or (type(pipeline) == "table" and u.table_size(pipeline) == 0) then
|
||||||
u.notify("Pipeline not found", vim.log.levels.WARN)
|
u.notify("Pipeline not found", vim.log.levels.WARN)
|
||||||
return
|
return
|
||||||
@@ -20,97 +20,93 @@ local function get_pipeline()
|
|||||||
return pipeline
|
return pipeline
|
||||||
end
|
end
|
||||||
|
|
||||||
M.get_pipeline_status = function()
|
local function get_pipeline_jobs()
|
||||||
local pipeline = get_pipeline()
|
M.latest_pipeline = get_latest_pipeline()
|
||||||
if pipeline == nil then
|
if not M.latest_pipeline then
|
||||||
return nil
|
return
|
||||||
end
|
end
|
||||||
return string.format("%s (%s)", state.settings.pipeline[pipeline.status], pipeline.status)
|
return u.reverse(type(state.PIPELINE.jobs) == "table" and state.PIPELINE.jobs or {})
|
||||||
end
|
end
|
||||||
|
|
||||||
-- The function will render the Pipeline state in a popup
|
-- The function will render the Pipeline state in a popup
|
||||||
M.open = function()
|
M.open = function()
|
||||||
local pipeline = get_pipeline()
|
M.pipeline_jobs = get_pipeline_jobs()
|
||||||
if not pipeline then
|
M.latest_pipeline = get_latest_pipeline()
|
||||||
|
if M.latest_pipeline == nil then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
job.run_job("/pipeline/" .. pipeline.id, "GET", nil, function(data)
|
local width = string.len(M.latest_pipeline.web_url) + 10
|
||||||
local pipeline_jobs = u.reverse(type(data.Jobs) == "table" and data.Jobs or {})
|
local height = 6 + #M.pipeline_jobs + 3
|
||||||
M.pipeline_jobs = pipeline_jobs
|
|
||||||
|
|
||||||
local width = string.len(pipeline.web_url) + 10
|
local pipeline_popup =
|
||||||
local height = 6 + #pipeline_jobs + 3
|
Popup(u.create_popup_state("Loading Pipeline...", state.settings.popup.pipeline, width, height, 60))
|
||||||
|
M.pipeline_popup = pipeline_popup
|
||||||
|
pipeline_popup:mount()
|
||||||
|
|
||||||
local pipeline_popup =
|
local bufnr = vim.api.nvim_get_current_buf()
|
||||||
Popup(u.create_popup_state("Loading Pipeline...", state.settings.popup.pipeline, width, height, 60))
|
vim.opt_local.wrap = false
|
||||||
M.pipeline_popup = pipeline_popup
|
|
||||||
pipeline_popup:mount()
|
|
||||||
|
|
||||||
local bufnr = vim.api.nvim_get_current_buf()
|
local lines = {}
|
||||||
vim.opt_local.wrap = false
|
|
||||||
|
|
||||||
local lines = {}
|
u.switch_can_edit_buf(bufnr, true)
|
||||||
|
table.insert(lines, "Status: " .. M.get_pipeline_status(false))
|
||||||
|
table.insert(lines, "")
|
||||||
|
table.insert(lines, string.format("Last Run: %s", u.time_since(M.latest_pipeline.created_at)))
|
||||||
|
table.insert(lines, string.format("Url: %s", M.latest_pipeline.web_url))
|
||||||
|
table.insert(lines, string.format("Triggered By: %s", M.latest_pipeline.source))
|
||||||
|
|
||||||
u.switch_can_edit_buf(bufnr, true)
|
table.insert(lines, "")
|
||||||
table.insert(lines, "Status: " .. M.get_pipeline_status())
|
table.insert(lines, "Jobs:")
|
||||||
table.insert(lines, "")
|
|
||||||
table.insert(lines, string.format("Last Run: %s", u.time_since(pipeline.created_at)))
|
|
||||||
table.insert(lines, string.format("Url: %s", pipeline.web_url))
|
|
||||||
table.insert(lines, string.format("Triggered By: %s", pipeline.source))
|
|
||||||
|
|
||||||
table.insert(lines, "")
|
local longest_title = u.get_longest_string(u.map(M.pipeline_jobs, function(v)
|
||||||
table.insert(lines, "Jobs:")
|
return v.name
|
||||||
|
end))
|
||||||
|
|
||||||
local longest_title = u.get_longest_string(u.map(pipeline_jobs, function(v)
|
local function row_offset(name)
|
||||||
return v.name
|
local offset = longest_title - string.len(name)
|
||||||
end))
|
local res = string.rep(" ", offset + 5)
|
||||||
|
return res
|
||||||
|
end
|
||||||
|
|
||||||
local function row_offset(name)
|
for _, pipeline_job in ipairs(M.pipeline_jobs) do
|
||||||
local offset = longest_title - string.len(name)
|
local offset = row_offset(pipeline_job.name)
|
||||||
local res = string.rep(" ", offset + 5)
|
local row = string.format(
|
||||||
return res
|
"%s%s %s (%s)",
|
||||||
|
pipeline_job.name,
|
||||||
|
offset,
|
||||||
|
state.settings.pipeline[pipeline_job.status] or "*",
|
||||||
|
pipeline_job.status or ""
|
||||||
|
)
|
||||||
|
|
||||||
|
table.insert(lines, row)
|
||||||
|
end
|
||||||
|
|
||||||
|
vim.schedule(function()
|
||||||
|
vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, lines)
|
||||||
|
M.color_status(M.latest_pipeline.status, bufnr, lines[1], 1)
|
||||||
|
|
||||||
|
for i, pipeline_job in ipairs(M.pipeline_jobs) do
|
||||||
|
M.color_status(pipeline_job.status, bufnr, lines[7 + i], 7 + i)
|
||||||
end
|
end
|
||||||
|
|
||||||
for _, pipeline_job in ipairs(pipeline_jobs) do
|
pipeline_popup.border:set_text("top", "Pipeline Status", "center")
|
||||||
local offset = row_offset(pipeline_job.name)
|
state.set_popup_keymaps(pipeline_popup, M.retrigger, M.see_logs)
|
||||||
local row = string.format(
|
u.switch_can_edit_buf(bufnr, false)
|
||||||
"%s%s %s (%s)",
|
|
||||||
pipeline_job.name,
|
|
||||||
offset,
|
|
||||||
state.settings.pipeline[pipeline_job.status] or "*",
|
|
||||||
pipeline_job.status or ""
|
|
||||||
)
|
|
||||||
|
|
||||||
table.insert(lines, row)
|
|
||||||
end
|
|
||||||
|
|
||||||
vim.schedule(function()
|
|
||||||
vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, lines)
|
|
||||||
M.color_status(pipeline.status, bufnr, lines[1], 1)
|
|
||||||
|
|
||||||
for i, pipeline_job in ipairs(pipeline_jobs) do
|
|
||||||
M.color_status(pipeline_job.status, bufnr, lines[7 + i], 7 + i)
|
|
||||||
end
|
|
||||||
|
|
||||||
pipeline_popup.border:set_text("top", "Pipeline Status", "center")
|
|
||||||
state.set_popup_keymaps(pipeline_popup, M.retrigger, M.see_logs)
|
|
||||||
u.switch_can_edit_buf(bufnr, false)
|
|
||||||
end)
|
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
M.retrigger = function()
|
M.retrigger = function()
|
||||||
local pipeline = get_pipeline()
|
M.latest_pipeline = get_latest_pipeline()
|
||||||
if not pipeline then
|
if not M.latest_pipeline then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
if pipeline.status ~= "failed" then
|
if M.latest_pipeline.status ~= "failed" then
|
||||||
u.notify("Pipeline is not in a failed state!", vim.log.levels.WARN)
|
u.notify("Pipeline is not in a failed state!", vim.log.levels.WARN)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
job.run_job("/pipeline/" .. pipeline.id, "POST", nil, function()
|
job.run_job("/pipeline/" .. M.latest_pipeline.id, "POST", nil, function()
|
||||||
u.notify("Pipeline re-triggered!", vim.log.levels.INFO)
|
u.notify("Pipeline re-triggered!", vim.log.levels.INFO)
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
@@ -173,6 +169,42 @@ M.see_logs = function()
|
|||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---Returns the user-defined symbol representing the status
|
||||||
|
---of the current pipeline. Takes an optional argument to
|
||||||
|
---colorize the pipeline icon.
|
||||||
|
---@param wrap_with_color boolean
|
||||||
|
---@return string
|
||||||
|
M.get_pipeline_icon = function(wrap_with_color)
|
||||||
|
M.latest_pipeline = get_latest_pipeline()
|
||||||
|
if not M.latest_pipeline then
|
||||||
|
return ""
|
||||||
|
end
|
||||||
|
local symbol = state.settings.pipeline[M.latest_pipeline.status]
|
||||||
|
if not wrap_with_color then
|
||||||
|
return symbol
|
||||||
|
end
|
||||||
|
if M.latest_pipeline.status == "failed" then
|
||||||
|
return "%#DiagnosticError#" .. symbol
|
||||||
|
end
|
||||||
|
if M.latest_pipeline.status == "success" then
|
||||||
|
return "%#DiagnosticOk#" .. symbol
|
||||||
|
end
|
||||||
|
return "%#DiagnosticWarn#" .. symbol
|
||||||
|
end
|
||||||
|
|
||||||
|
---Returns the status of the latest pipeline and the symbol
|
||||||
|
--representing the status of the current pipeline. Takes an optional argument to
|
||||||
|
---colorize the pipeline icon.
|
||||||
|
---@param wrap_with_color boolean
|
||||||
|
---@return string
|
||||||
|
M.get_pipeline_status = function(wrap_with_color)
|
||||||
|
M.latest_pipeline = get_latest_pipeline()
|
||||||
|
if not M.latest_pipeline then
|
||||||
|
return ""
|
||||||
|
end
|
||||||
|
return string.format("%s (%s)", M.get_pipeline_icon(wrap_with_color), M.latest_pipeline.status)
|
||||||
|
end
|
||||||
|
|
||||||
M.color_status = function(status, bufnr, status_line, linnr)
|
M.color_status = function(status, bufnr, status_line, linnr)
|
||||||
local ns_id = vim.api.nvim_create_namespace("GitlabNamespace")
|
local ns_id = vim.api.nvim_create_namespace("GitlabNamespace")
|
||||||
vim.cmd(string.format("highlight default StatusHighlight guifg=%s", state.settings.pipeline[status]))
|
vim.cmd(string.format("highlight default StatusHighlight guifg=%s", state.settings.pipeline[status]))
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ local u = require("gitlab.utils")
|
|||||||
local List = require("gitlab.utils.list")
|
local List = require("gitlab.utils.list")
|
||||||
local state = require("gitlab.state")
|
local state = require("gitlab.state")
|
||||||
local miscellaneous = require("gitlab.actions.miscellaneous")
|
local miscellaneous = require("gitlab.actions.miscellaneous")
|
||||||
local pipeline = require("gitlab.actions.pipeline")
|
|
||||||
|
|
||||||
local M = {
|
local M = {
|
||||||
layout_visible = false,
|
layout_visible = false,
|
||||||
@@ -134,12 +133,16 @@ M.build_info_lines = function()
|
|||||||
assignees = { title = "Assignees", content = u.make_readable_list(info.assignees, "name") },
|
assignees = { title = "Assignees", content = u.make_readable_list(info.assignees, "name") },
|
||||||
reviewers = { title = "Reviewers", content = u.make_readable_list(info.reviewers, "name") },
|
reviewers = { title = "Reviewers", content = u.make_readable_list(info.reviewers, "name") },
|
||||||
branch = { title = "Branch", content = info.source_branch },
|
branch = { title = "Branch", content = info.source_branch },
|
||||||
labels = { title = "Labels", content = u.make_comma_separated_readable(info.labels) },
|
labels = { title = "Labels", content = table.concat(info.labels, ", ") },
|
||||||
target_branch = { title = "Target Branch", content = state.INFO.target_branch },
|
target_branch = { title = "Target Branch", content = state.INFO.target_branch },
|
||||||
pipeline = {
|
pipeline = {
|
||||||
title = "Pipeline Status",
|
title = "Pipeline Status",
|
||||||
content = function()
|
content = function()
|
||||||
return pipeline.get_pipeline_status()
|
local pipeline = state.INFO.pipeline
|
||||||
|
if type(pipeline) ~= "table" or (type(pipeline) == "table" and u.table_size(pipeline) == 0) then
|
||||||
|
return ""
|
||||||
|
end
|
||||||
|
return pipeline.status
|
||||||
end,
|
end,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ local reviewer = require("gitlab.reviewer")
|
|||||||
local discussions = require("gitlab.actions.discussions")
|
local discussions = require("gitlab.actions.discussions")
|
||||||
local merge = require("gitlab.actions.merge")
|
local merge = require("gitlab.actions.merge")
|
||||||
local summary = require("gitlab.actions.summary")
|
local summary = require("gitlab.actions.summary")
|
||||||
|
local data = require("gitlab.actions.data")
|
||||||
local assignees_and_reviewers = require("gitlab.actions.assignees_and_reviewers")
|
local assignees_and_reviewers = require("gitlab.actions.assignees_and_reviewers")
|
||||||
local comment = require("gitlab.actions.comment")
|
local comment = require("gitlab.actions.comment")
|
||||||
local pipeline = require("gitlab.actions.pipeline")
|
local pipeline = require("gitlab.actions.pipeline")
|
||||||
@@ -19,6 +20,7 @@ local user = state.dependencies.user
|
|||||||
local info = state.dependencies.info
|
local info = state.dependencies.info
|
||||||
local labels_dep = state.dependencies.labels
|
local labels_dep = state.dependencies.labels
|
||||||
local project_members = state.dependencies.project_members
|
local project_members = state.dependencies.project_members
|
||||||
|
local latest_pipeline = state.dependencies.latest_pipeline
|
||||||
local revisions = state.dependencies.revisions
|
local revisions = state.dependencies.revisions
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -34,7 +36,10 @@ return {
|
|||||||
emoji.init() -- Read in emojis for lookup purposes
|
emoji.init() -- Read in emojis for lookup purposes
|
||||||
end,
|
end,
|
||||||
-- Global Actions 🌎
|
-- Global Actions 🌎
|
||||||
summary = async.sequence({ u.merge(info, { refresh = true }), labels_dep }, summary.summary),
|
summary = async.sequence({
|
||||||
|
u.merge(info, { refresh = true }),
|
||||||
|
labels_dep,
|
||||||
|
}, summary.summary),
|
||||||
approve = async.sequence({ info }, approvals.approve),
|
approve = async.sequence({ info }, approvals.approve),
|
||||||
revoke = async.sequence({ info }, approvals.revoke),
|
revoke = async.sequence({ info }, approvals.revoke),
|
||||||
add_reviewer = async.sequence({ info, project_members }, assignees_and_reviewers.add_reviewer),
|
add_reviewer = async.sequence({ info, project_members }, assignees_and_reviewers.add_reviewer),
|
||||||
@@ -55,7 +60,7 @@ return {
|
|||||||
close_review = function()
|
close_review = function()
|
||||||
reviewer.close()
|
reviewer.close()
|
||||||
end,
|
end,
|
||||||
pipeline = async.sequence({ info }, pipeline.open),
|
pipeline = async.sequence({ latest_pipeline }, pipeline.open),
|
||||||
merge = async.sequence({ u.merge(info, { refresh = true }) }, merge.merge),
|
merge = async.sequence({ u.merge(info, { refresh = true }) }, merge.merge),
|
||||||
-- Discussion Tree Actions 🌴
|
-- Discussion Tree Actions 🌴
|
||||||
toggle_discussions = async.sequence({ info, user }, discussions.toggle),
|
toggle_discussions = async.sequence({ info, user }, discussions.toggle),
|
||||||
@@ -65,6 +70,7 @@ return {
|
|||||||
reply = async.sequence({ info }, discussions.reply),
|
reply = async.sequence({ info }, discussions.reply),
|
||||||
-- Other functions 🤷
|
-- Other functions 🤷
|
||||||
state = state,
|
state = state,
|
||||||
|
data = data.data,
|
||||||
print_settings = state.print_settings,
|
print_settings = state.print_settings,
|
||||||
open_in_browser = async.sequence({ info }, function()
|
open_in_browser = async.sequence({ info }, function()
|
||||||
if state.INFO.web_url == nil then
|
if state.INFO.web_url == nil then
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ M.settings = {
|
|||||||
attachment_dir = "",
|
attachment_dir = "",
|
||||||
help = "g?",
|
help = "g?",
|
||||||
popup = {
|
popup = {
|
||||||
exit = "<Esc>",
|
|
||||||
perform_action = "<leader>s",
|
perform_action = "<leader>s",
|
||||||
perform_linewise_action = "<leader>l",
|
perform_linewise_action = "<leader>l",
|
||||||
width = "40%",
|
width = "40%",
|
||||||
@@ -40,7 +39,6 @@ M.settings = {
|
|||||||
help = nil,
|
help = nil,
|
||||||
pipeline = nil,
|
pipeline = nil,
|
||||||
squash_message = nil,
|
squash_message = nil,
|
||||||
backup_register = nil,
|
|
||||||
},
|
},
|
||||||
discussion_tree = {
|
discussion_tree = {
|
||||||
auto_open = true,
|
auto_open = true,
|
||||||
@@ -270,10 +268,6 @@ M.set_popup_keymaps = function(popup, action, linewise_action, opts)
|
|||||||
if opts == nil then
|
if opts == nil then
|
||||||
opts = {}
|
opts = {}
|
||||||
end
|
end
|
||||||
vim.keymap.set("n", M.settings.popup.exit, function()
|
|
||||||
exit(popup, opts)
|
|
||||||
end, { buffer = popup.bufnr, desc = "Exit popup" })
|
|
||||||
|
|
||||||
if action ~= "Help" then -- Don't show help on the help popup
|
if action ~= "Help" then -- Don't show help on the help popup
|
||||||
vim.keymap.set("n", M.settings.help, function()
|
vim.keymap.set("n", M.settings.help, function()
|
||||||
local help = require("gitlab.actions.help")
|
local help = require("gitlab.actions.help")
|
||||||
@@ -283,9 +277,6 @@ M.set_popup_keymaps = function(popup, action, linewise_action, opts)
|
|||||||
if action ~= nil then
|
if action ~= nil then
|
||||||
vim.keymap.set("n", M.settings.popup.perform_action, function()
|
vim.keymap.set("n", M.settings.popup.perform_action, function()
|
||||||
local text = u.get_buffer_text(popup.bufnr)
|
local text = u.get_buffer_text(popup.bufnr)
|
||||||
if M.settings.popup.backup_register ~= nil then
|
|
||||||
vim.cmd("0,$yank " .. M.settings.popup.backup_register)
|
|
||||||
end
|
|
||||||
if opts.action_before_close then
|
if opts.action_before_close then
|
||||||
action(text, popup.bufnr)
|
action(text, popup.bufnr)
|
||||||
exit(popup, opts)
|
exit(popup, opts)
|
||||||
@@ -304,6 +295,13 @@ M.set_popup_keymaps = function(popup, action, linewise_action, opts)
|
|||||||
linewise_action(text)
|
linewise_action(text)
|
||||||
end, { buffer = popup.bufnr, desc = "Perform linewise action" })
|
end, { buffer = popup.bufnr, desc = "Perform linewise action" })
|
||||||
end
|
end
|
||||||
|
|
||||||
|
vim.api.nvim_create_autocmd("BufUnload", {
|
||||||
|
buffer = popup.bufnr,
|
||||||
|
callback = function()
|
||||||
|
exit(popup, opts)
|
||||||
|
end,
|
||||||
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Dependencies
|
-- Dependencies
|
||||||
@@ -314,6 +312,7 @@ end
|
|||||||
M.dependencies = {
|
M.dependencies = {
|
||||||
user = { endpoint = "/users/me", key = "user", state = "USER", refresh = false },
|
user = { endpoint = "/users/me", key = "user", state = "USER", refresh = false },
|
||||||
info = { endpoint = "/mr/info", key = "info", state = "INFO", refresh = false },
|
info = { endpoint = "/mr/info", key = "info", state = "INFO", refresh = false },
|
||||||
|
latest_pipeline = { endpoint = "/pipeline", key = "latest_pipeline", state = "PIPELINE", refresh = true },
|
||||||
labels = { endpoint = "/mr/label", key = "labels", state = "LABELS", refresh = false },
|
labels = { endpoint = "/mr/label", key = "labels", state = "LABELS", refresh = false },
|
||||||
revisions = { endpoint = "/mr/revisions", key = "Revisions", state = "MR_REVISIONS", refresh = false },
|
revisions = { endpoint = "/mr/revisions", key = "Revisions", state = "MR_REVISIONS", refresh = false },
|
||||||
project_members = {
|
project_members = {
|
||||||
|
|||||||
Reference in New Issue
Block a user