Comments on deleted lines
This commit is contained in:
160
cmd/comment.go
160
cmd/comment.go
@@ -1,14 +1,17 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
|
"mime/multipart"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -16,6 +19,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const mrVersionsUrl = "https://gitlab.com/api/v4/projects/%s/merge_requests/%d/versions"
|
const mrVersionsUrl = "https://gitlab.com/api/v4/projects/%s/merge_requests/%d/versions"
|
||||||
|
const commentUrl = "https://gitlab.com/api/v4/projects/%s/repository/commits/%s/comments"
|
||||||
|
|
||||||
type MRVersion struct {
|
type MRVersion struct {
|
||||||
ID int `json:"id"`
|
ID int `json:"id"`
|
||||||
@@ -29,11 +33,11 @@ type MRVersion struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) Comment() error {
|
func (c *Client) Comment() error {
|
||||||
if len(os.Args) < 6 {
|
if len(os.Args) < 7 {
|
||||||
c.Usage("comment")
|
c.Usage("comment")
|
||||||
}
|
}
|
||||||
|
|
||||||
lineNumber, fileName, comment := os.Args[3], os.Args[4], os.Args[5]
|
lineNumber, fileName, comment, sha := os.Args[3], os.Args[4], os.Args[5], os.Args[6]
|
||||||
if lineNumber == "" || fileName == "" || comment == "" {
|
if lineNumber == "" || fileName == "" || comment == "" {
|
||||||
c.Usage("comment")
|
c.Usage("comment")
|
||||||
}
|
}
|
||||||
@@ -53,67 +57,97 @@ func (c *Client) Comment() error {
|
|||||||
|
|
||||||
var diffVersionInfo []MRVersion
|
var diffVersionInfo []MRVersion
|
||||||
err = json.Unmarshal(body, &diffVersionInfo)
|
err = json.Unmarshal(body, &diffVersionInfo)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error unmarshalling version info JSON: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
time := time.Now()
|
time := time.Now()
|
||||||
|
|
||||||
/* This is necessary since we do not know whether the comment is on a line that
|
if sha == "" {
|
||||||
has been changed or not. Making all three of these API calls will let us leave
|
/* This is necessary since we do not know whether the comment is on a line that
|
||||||
the comment regardless. See the Gitlab documentation: https://docs.gitlab.com/ee/api/discussions.html#create-a-new-thread-in-the-merge-request-diff */
|
has been changed or not. Making all three of these API calls will let us leave
|
||||||
wg := sync.WaitGroup{}
|
the comment regardless. See the Gitlab documentation: https://docs.gitlab.com/ee/api/discussions.html#create-a-new-thread-in-the-merge-request-diff */
|
||||||
wg.Add(3)
|
wg := sync.WaitGroup{}
|
||||||
|
wg.Add(2)
|
||||||
|
|
||||||
resultChannel := make(chan *gitlab.Discussion, 3)
|
resultChannel := make(chan *gitlab.Discussion, 2)
|
||||||
|
|
||||||
for i := 0; i < 3; i++ {
|
for i := 0; i < 2; i++ {
|
||||||
ii := i
|
ii := i
|
||||||
go func() {
|
go func() {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
options := gitlab.CreateMergeRequestDiscussionOptions{
|
options := gitlab.CreateMergeRequestDiscussionOptions{
|
||||||
Body: &comment,
|
Body: &comment,
|
||||||
CreatedAt: &time,
|
CreatedAt: &time,
|
||||||
Position: &gitlab.NotePosition{
|
Position: &gitlab.NotePosition{
|
||||||
PositionType: "text",
|
PositionType: "text",
|
||||||
BaseSHA: diffVersionInfo[0].BaseCommitSHA,
|
BaseSHA: diffVersionInfo[0].BaseCommitSHA,
|
||||||
HeadSHA: diffVersionInfo[0].HeadCommitSHA,
|
HeadSHA: diffVersionInfo[0].HeadCommitSHA,
|
||||||
StartSHA: diffVersionInfo[0].StartCommitSHA,
|
StartSHA: diffVersionInfo[0].StartCommitSHA,
|
||||||
NewPath: fileName,
|
NewPath: fileName,
|
||||||
OldPath: fileName,
|
OldPath: fileName,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if ii == 0 {
|
if ii == 0 {
|
||||||
options.Position.NewLine = lineNumberInt
|
options.Position.NewLine = lineNumberInt
|
||||||
} else if ii == 1 {
|
} else {
|
||||||
options.Position.OldLine = lineNumberInt
|
options.Position.NewLine = lineNumberInt
|
||||||
} else {
|
options.Position.OldLine = lineNumberInt
|
||||||
options.Position.NewLine = lineNumberInt
|
}
|
||||||
options.Position.OldLine = lineNumberInt
|
|
||||||
}
|
|
||||||
|
|
||||||
discussion, _, _ := c.git.Discussions.CreateMergeRequestDiscussion(c.projectId, c.mergeId, &options)
|
discussion, _, _ := c.git.Discussions.CreateMergeRequestDiscussion(c.projectId, c.mergeId, &options)
|
||||||
resultChannel <- discussion
|
resultChannel <- discussion
|
||||||
|
|
||||||
}()
|
}()
|
||||||
}
|
|
||||||
|
|
||||||
go func() {
|
|
||||||
wg.Wait()
|
|
||||||
close(resultChannel)
|
|
||||||
}()
|
|
||||||
|
|
||||||
var createdDiscussion *gitlab.Discussion
|
|
||||||
for discussion := range resultChannel {
|
|
||||||
if discussion != nil {
|
|
||||||
createdDiscussion = discussion
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if createdDiscussion == nil {
|
go func() {
|
||||||
return fmt.Errorf("Could not leave comment")
|
wg.Wait()
|
||||||
|
close(resultChannel)
|
||||||
|
}()
|
||||||
|
|
||||||
|
var createdDiscussion *gitlab.Discussion
|
||||||
|
for discussion := range resultChannel {
|
||||||
|
if discussion != nil {
|
||||||
|
createdDiscussion = discussion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if createdDiscussion == nil {
|
||||||
|
return fmt.Errorf("Could not leave comment")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
payload := &bytes.Buffer{}
|
||||||
|
writer := multipart.NewWriter(payload)
|
||||||
|
_ = writer.WriteField("note", comment)
|
||||||
|
_ = writer.WriteField("path", "README.md")
|
||||||
|
_ = writer.WriteField("line", "3")
|
||||||
|
_ = writer.WriteField("line_type", "old")
|
||||||
|
err := writer.Close()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error making form data: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
url := fmt.Sprintf(commentUrl, c.projectId, strings.TrimSpace(sha))
|
||||||
|
client := &http.Client{}
|
||||||
|
req, err := http.NewRequest(http.MethodPost, url, payload)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error building request: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header.Add("PRIVATE-TOKEN", os.Getenv("GITLAB_TOKEN"))
|
||||||
|
req.Header.Set("Content-Type", writer.FormDataContentType())
|
||||||
|
|
||||||
|
res, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error making request: %w", err)
|
||||||
|
}
|
||||||
|
defer res.Body.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("Left Comment: " + comment[0:min(len(comment), 25)] + "...")
|
fmt.Println("Left Comment: " + comment[0:min(len(comment), 25)] + "...")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,3 +185,29 @@ func getMRVersions(projectId string, mergeId int) (e error, response *http.Respo
|
|||||||
|
|
||||||
return nil, response
|
return nil, response
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Client) OverviewComment() error {
|
||||||
|
lineNumber, fileName, comment, sha := os.Args[3], os.Args[4], os.Args[5], os.Args[6]
|
||||||
|
if lineNumber == "" || fileName == "" || comment == "" {
|
||||||
|
c.Usage("comment")
|
||||||
|
}
|
||||||
|
|
||||||
|
lineNumberInt, err := strconv.Atoi(lineNumber)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Not a valid line number: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
postCommitCommentOptions := gitlab.PostCommitCommentOptions{
|
||||||
|
Note: gitlab.String(comment),
|
||||||
|
Path: gitlab.String(fileName),
|
||||||
|
Line: &lineNumberInt,
|
||||||
|
LineType: gitlab.String("old"),
|
||||||
|
}
|
||||||
|
_, _, err = c.git.Commits.PostCommitComment(c.projectId, sha, &postCommitCommentOptions)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error leaving overview comment: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("Left Overview Comment: " + comment[0:min(len(comment), 25)] + "...")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ const (
|
|||||||
approve = "approve"
|
approve = "approve"
|
||||||
revoke = "revoke"
|
revoke = "revoke"
|
||||||
comment = "comment"
|
comment = "comment"
|
||||||
|
overviewComment = "overviewComment"
|
||||||
deleteComment = "deleteComment"
|
deleteComment = "deleteComment"
|
||||||
editComment = "editComment"
|
editComment = "editComment"
|
||||||
reply = "reply"
|
reply = "reply"
|
||||||
@@ -44,6 +45,8 @@ func main() {
|
|||||||
errCheck(c.DeleteComment())
|
errCheck(c.DeleteComment())
|
||||||
case editComment:
|
case editComment:
|
||||||
errCheck(c.EditComment())
|
errCheck(c.EditComment())
|
||||||
|
case overviewComment:
|
||||||
|
errCheck(c.OverviewComment())
|
||||||
case info:
|
case info:
|
||||||
errCheck(c.Info())
|
errCheck(c.Info())
|
||||||
case reply:
|
case reply:
|
||||||
|
|||||||
@@ -19,11 +19,43 @@ M.create_comment = function()
|
|||||||
keymaps.set_popup_keymaps(commentPopup, M.confirm_create_comment)
|
keymaps.set_popup_keymaps(commentPopup, M.confirm_create_comment)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
M.find_deletion_commit = function(file)
|
||||||
|
local current_line = vim.api.nvim_get_current_line()
|
||||||
|
local command = string.format("git log -S '%s' %s", current_line, file)
|
||||||
|
local handle = io.popen(command)
|
||||||
|
local output = handle:read("*line")
|
||||||
|
if output == nil then
|
||||||
|
notify("Error reading SHA of deletion commit", "error")
|
||||||
|
return ""
|
||||||
|
end
|
||||||
|
handle:close()
|
||||||
|
local words = {}
|
||||||
|
for word in output:gmatch("%S+") do
|
||||||
|
table.insert(words, word)
|
||||||
|
end
|
||||||
|
|
||||||
|
return words[2]
|
||||||
|
end
|
||||||
|
|
||||||
-- Sends the comment to Gitlab
|
-- Sends the comment to Gitlab
|
||||||
M.confirm_create_comment = function(text)
|
M.confirm_create_comment = function(text)
|
||||||
if u.base_invalid() then return end
|
if u.base_invalid() then return end
|
||||||
local relative_file_path = u.get_relative_file_path()
|
local relative_file_path = u.get_relative_file_path()
|
||||||
local current_line_number = u.get_current_line_number()
|
local current_line_number = u.get_current_line_number()
|
||||||
|
if relative_file_path == nil then return end
|
||||||
|
|
||||||
|
-- If leaving a comment on a deleted line, get hash value + proper filename
|
||||||
|
local sha = ""
|
||||||
|
local is_base_file = relative_file_path:find(".git")
|
||||||
|
if is_base_file then -- We are looking at a deletion.
|
||||||
|
local _, path = u.split_diff_view_filename(relative_file_path)
|
||||||
|
sha = M.find_deletion_commit(path)
|
||||||
|
if sha == "" then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
Job:new({
|
Job:new({
|
||||||
command = state.BIN,
|
command = state.BIN,
|
||||||
args = {
|
args = {
|
||||||
@@ -32,6 +64,7 @@ M.confirm_create_comment = function(text)
|
|||||||
current_line_number,
|
current_line_number,
|
||||||
relative_file_path,
|
relative_file_path,
|
||||||
text,
|
text,
|
||||||
|
sha
|
||||||
},
|
},
|
||||||
-- TODO: Render the tree after comment creation. Refresh?
|
-- TODO: Render the tree after comment creation. Refresh?
|
||||||
on_stdout = u.print_success,
|
on_stdout = u.print_success,
|
||||||
|
|||||||
@@ -215,6 +215,16 @@ local read_file = function(file_path)
|
|||||||
return file_contents
|
return file_contents
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local split_diff_view_filename = function(filename)
|
||||||
|
local hash, path = filename:match("://%.git/(/?[0-9a-f]+)(/.*)$")
|
||||||
|
if hash and path then
|
||||||
|
path = path:gsub("%.git/", ""):gsub("^/", "")
|
||||||
|
hash = hash:gsub("^/", "")
|
||||||
|
end
|
||||||
|
return hash, path
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
M.get_relative_file_path = get_relative_file_path
|
M.get_relative_file_path = get_relative_file_path
|
||||||
M.get_current_line_number = get_current_line_number
|
M.get_current_line_number = get_current_line_number
|
||||||
M.get_buffer_text = get_buffer_text
|
M.get_buffer_text = get_buffer_text
|
||||||
@@ -233,5 +243,6 @@ M.print_error = print_error
|
|||||||
M.create_popup_state = create_popup_state
|
M.create_popup_state = create_popup_state
|
||||||
M.exit = exit
|
M.exit = exit
|
||||||
M.read_file = read_file
|
M.read_file = read_file
|
||||||
|
M.split_diff_view_filename = split_diff_view_filename
|
||||||
M.P = P
|
M.P = P
|
||||||
return M
|
return M
|
||||||
|
|||||||
Reference in New Issue
Block a user