191 lines
5.5 KiB
Go
191 lines
5.5 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"log"
|
|
"mime/multipart"
|
|
"net/http"
|
|
"os"
|
|
"strconv"
|
|
"time"
|
|
|
|
"github.com/xanzy/go-gitlab"
|
|
)
|
|
|
|
const mrVersionsUrl = "https://gitlab.com/api/v4/projects/%s/merge_requests/%d/versions"
|
|
|
|
type MRVersion struct {
|
|
ID int `json:"id"`
|
|
HeadCommitSHA string `json:"head_commit_sha"`
|
|
BaseCommitSHA string `json:"base_commit_sha"`
|
|
StartCommitSHA string `json:"start_commit_sha"`
|
|
CreatedAt time.Time `json:"created_at"`
|
|
MergeRequestID int `json:"merge_request_id"`
|
|
State string `json:"state"`
|
|
RealSize string `json:"real_size"`
|
|
}
|
|
|
|
func (c *Client) Comment() error {
|
|
if len(os.Args) < 6 {
|
|
c.Usage("comment")
|
|
}
|
|
|
|
lineNumber, fileName, comment := os.Args[3], os.Args[4], os.Args[5]
|
|
if lineNumber == "" || fileName == "" || comment == "" {
|
|
c.Usage("comment")
|
|
}
|
|
|
|
err, response := getMRVersions(c.projectId, c.mergeId)
|
|
if err != nil {
|
|
log.Fatalf("Error making diff thread: %s", err)
|
|
}
|
|
defer response.Body.Close()
|
|
|
|
body, err := ioutil.ReadAll(response.Body)
|
|
|
|
var diffVersionInfo []MRVersion
|
|
err = json.Unmarshal(body, &diffVersionInfo)
|
|
if err != nil {
|
|
return fmt.Errorf("Error unmarshalling version info JSON: %w", err)
|
|
}
|
|
|
|
/* This is necessary since we do not know whether the comment is on a line that
|
|
has been changed or not. Making all three of these API calls will let us leave
|
|
the comment regardless. I ran these in sequence vai a Sync.WaitGroup, but
|
|
it was successfully posting a comment to a modified twice, so now I'm running
|
|
them in sequence.
|
|
|
|
To clean this up we might try to detect more information about the change in our
|
|
Lua code and pass it to the Go code.
|
|
|
|
See the Gitlab documentation: https://docs.gitlab.com/ee/api/discussions.html#create-a-new-thread-in-the-merge-request-diff */
|
|
for i := 0; i < 3; i++ {
|
|
ii := i
|
|
_, err := c.CommentOnDeletion(lineNumber, fileName, comment, diffVersionInfo[0], ii)
|
|
if err == nil {
|
|
fmt.Println("Left Comment: " + comment[0:min(len(comment), 25)] + "...")
|
|
return nil
|
|
}
|
|
}
|
|
|
|
return fmt.Errorf("Could not leave comment")
|
|
|
|
}
|
|
|
|
func min(a int, b int) int {
|
|
if a < b {
|
|
return a
|
|
}
|
|
return b
|
|
}
|
|
|
|
/* Gets the latest merge request revision data */
|
|
func getMRVersions(projectId string, mergeId int) (e error, response *http.Response) {
|
|
|
|
gitlabToken := os.Getenv("GITLAB_TOKEN")
|
|
url := fmt.Sprintf(mrVersionsUrl, projectId, mergeId)
|
|
|
|
req, err := http.NewRequest(http.MethodGet, url, nil)
|
|
|
|
req.Header.Add("PRIVATE-TOKEN", gitlabToken)
|
|
|
|
if err != nil {
|
|
return err, nil
|
|
}
|
|
|
|
client := &http.Client{}
|
|
response, err = client.Do(req)
|
|
|
|
if err != nil {
|
|
return err, nil
|
|
}
|
|
|
|
if response.StatusCode != 200 {
|
|
return errors.New("Non-200 status code: " + response.Status), nil
|
|
}
|
|
|
|
return nil, response
|
|
}
|
|
|
|
/*
|
|
Creates a new merge request discussion https://docs.gitlab.com/ee/api/discussions.html#create-new-merge-request-thread
|
|
The go-gitlab client was not working for this API specifically 😢
|
|
*/
|
|
func (c *Client) CommentOnDeletion(lineNumber string, fileName string, comment string, diffVersionInfo MRVersion, i int) (*http.Response, error) {
|
|
|
|
deletionDiscussionUrl := fmt.Sprintf("https://gitlab.com/api/v4/projects/%s/merge_requests/%d/discussions", c.projectId, c.mergeId)
|
|
|
|
payload := &bytes.Buffer{}
|
|
writer := multipart.NewWriter(payload)
|
|
_ = writer.WriteField("body", comment)
|
|
_ = writer.WriteField("position[base_sha]", diffVersionInfo.BaseCommitSHA)
|
|
_ = writer.WriteField("position[start_sha]", diffVersionInfo.StartCommitSHA)
|
|
_ = writer.WriteField("position[head_sha]", diffVersionInfo.HeadCommitSHA)
|
|
_ = writer.WriteField("position[position_type]", "text")
|
|
|
|
/* We need to set these properties differently depending on whether we're commenting on a deleted line,
|
|
a modified line, an added line, or an unmodified line */
|
|
_ = writer.WriteField("position[old_path]", fileName)
|
|
_ = writer.WriteField("position[new_path]", fileName)
|
|
if i == 0 {
|
|
_ = writer.WriteField("position[old_line]", lineNumber)
|
|
} else if i == 1 {
|
|
_ = writer.WriteField("position[new_line]", lineNumber)
|
|
} else {
|
|
_ = writer.WriteField("position[old_line]", lineNumber)
|
|
_ = writer.WriteField("position[new_line]", lineNumber)
|
|
}
|
|
|
|
err := writer.Close()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("Error making form data: %w", err)
|
|
}
|
|
|
|
client := &http.Client{}
|
|
req, err := http.NewRequest(http.MethodPost, deletionDiscussionUrl, payload)
|
|
|
|
if err != nil {
|
|
return nil, 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 nil, fmt.Errorf("Error making request: %w", err)
|
|
}
|
|
|
|
defer res.Body.Close()
|
|
return res, nil
|
|
}
|
|
|
|
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
|
|
}
|