Skip to content

Commit

Permalink
Merge pull request #30 from zapier/feat/delete-old-comments
Browse files Browse the repository at this point in the history
Delete old comments whenever we start a new TFC run
  • Loading branch information
Matt Morrison authored Jul 19, 2023
2 parents e18e1b2 + 0de705d commit 0467714
Show file tree
Hide file tree
Showing 7 changed files with 184 additions and 0 deletions.
4 changes: 4 additions & 0 deletions pkg/comment_formatter/tfc_status_update.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,3 +169,7 @@ var needToApplyFullWorkSpace = `
**Need to Apply Full Workspace Before Merging**
`

const OLD_RUN_BLOCK = `
### Previous TFC URLS
%s`
3 changes: 3 additions & 0 deletions pkg/mocks/mock_vcs.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

62 changes: 62 additions & 0 deletions pkg/utils/formatter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package utils

import (
"fmt"
"log"
"strings"
)

// We format the URL as **RUN URL**: <url> <br> under tfc_status_update.go
const (
URL_RUN_PREFIX = "**Run URL**: "
URL_RUN_SUFFIX = "<br>"
URL_RUN_GROUP_PREFIX = `<details><summary>
### Previous TFC Urls:
</summary>`
URL_RUN_GROUP_SUFFIX = "</details>"
URL_RUN_STATUS_PREFIX = "**Status**: "
)

func CaptureSubstring(body string, prefix string, suffix string) string {
startIndex := strings.Index(body, prefix)
if startIndex == -1 {
return ""
}

subBody := body[startIndex+len(prefix):]
endIndex := strings.Index(subBody, suffix)
if endIndex == -1 {
return ""
}

return subBody[:endIndex]
}

// AI generated these, may not be compresphenive
func FormatStatus(status string) string {
status = strings.Trim(status, "`")
log.Printf("Status: %s", status)
switch status {
case "applied":
return "✅ Applied"
case "planned":
return "📝 Planned"
case "policy_checked":
return "📝 Policy Checked"
case "policy_soft_failed":
return "📝 Policy Soft Failed"
case "errored":
return "❌ Errored"
case "canceled":
return "❌ Canceled"
case "discarded":
return "❌ Discarded"
case "planned_and_finished":
return "📝 Planned and Finished"
default:
// anything we can't match just preserve with the ticks
return fmt.Sprintf("`%s`", status)
}
}
53 changes: 53 additions & 0 deletions pkg/vcs/github/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,59 @@ func (c *Client) GetMergeRequestApprovals(id int, project string) (vcs.MRApprove
return pr, nil
}

// Go over all comments on a PR, trying to grab any old TFC run urls and deleting the bodies
func (c *Client) GetOldRunUrls(prID int, fullName string, rootCommentID int) (string, error) {
log.Debug().Msg("pruneComments")
projectParts, err := splitFullName(fullName)
if err != nil {
return "", utils.CreatePermanentError(err)
}
comments, _, err := c.client.Issues.ListComments(c.ctx, projectParts[0], projectParts[1], prID, &gogithub.IssueListCommentsOptions{})
if err != nil {
return "", utils.CreatePermanentError(err)
}

currentUser, _, err := c.client.Users.Get(context.Background(), "")
if err != nil {
return "", utils.CreatePermanentError(err)
}

var oldRunUrls []string
var oldRunBlock string
for _, comment := range comments {
// If this token user made the comment, and we're making a new comment, pick the TFC url out of the body and delete the comment
if comment.GetUser().GetLogin() == currentUser.GetLogin() {
runUrl := utils.CaptureSubstring(comment.GetBody(), utils.URL_RUN_PREFIX, utils.URL_RUN_SUFFIX)
runStatus := utils.CaptureSubstring(comment.GetBody(), utils.URL_RUN_STATUS_PREFIX, utils.URL_RUN_SUFFIX)
if runUrl != "" && runStatus != "" {
// Example: <tfc url> - ✅ Applied
oldRunUrls = append(oldRunUrls, fmt.Sprintf("%s - %s", runUrl, runStatus))
}

// Github orders comments from earliest -> latest via ID, so we check each comment and take the last match on an "old url" block
oldRunBlockTest := utils.CaptureSubstring(comment.GetBody(), utils.URL_RUN_GROUP_PREFIX, utils.URL_RUN_GROUP_SUFFIX)
if oldRunBlockTest != "" {
oldRunBlock = oldRunBlockTest
}

if os.Getenv("TFBUDDY_DELETE_OLD_COMMENTS") != "" && comment.GetID() != int64(rootCommentID) {
log.Debug().Msgf("Deleting comment %d", comment.GetID())
_, err := c.client.Issues.DeleteComment(c.ctx, projectParts[0], projectParts[1], comment.GetID())
if err != nil {
return "", utils.CreatePermanentError(err)
}
}
}
}

// If we found any old run urls, return them formatted
if len(oldRunUrls) > 0 {
// Try and find any exisitng groupings of old urls, else make a new one
return fmt.Sprintf("%s\n%s\n%s\n%s", utils.URL_RUN_GROUP_PREFIX, oldRunBlock, strings.Join(oldRunUrls, "\n"), utils.URL_RUN_GROUP_SUFFIX), nil
}
return oldRunBlock, nil
}

func (c *Client) CreateMergeRequestComment(prID int, fullName string, comment string) error {
_, err := c.PostIssueComment(prID, fullName, comment)
return err
Expand Down
48 changes: 48 additions & 0 deletions pkg/vcs/gitlab/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"net/url"
"os"
"strings"
"time"

"github.com/cenkalti/backoff/v4"
Expand Down Expand Up @@ -109,6 +110,52 @@ func (c *GitlabClient) GetCommitStatuses(projectID, commitSHA string) []*gogitla
return statuses
}

// Crawl the comments on this MR for tfbuddy comments, grab any TFC urls out of them, and delete them.
func (c *GitlabClient) GetOldRunUrls(mrIID int, project string, rootNoteID int) (string, error) {
log.Debug().Str("projectID", project).Int("mrIID", mrIID).Msg("pruning notes")
notes, _, err := c.client.Notes.ListMergeRequestNotes(project, mrIID, &gogitlab.ListMergeRequestNotesOptions{})
if err != nil {
return "", utils.CreatePermanentError(err)
}

currentUser, _, err := c.client.Users.CurrentUser()

if err != nil {
return "", utils.CreatePermanentError(err)
}

var oldRunUrls []string
var oldRunBlock string
for _, note := range notes {
if note.Author.Username == currentUser.Username {
runUrl := utils.CaptureSubstring(note.Body, utils.URL_RUN_PREFIX, utils.URL_RUN_SUFFIX)
runStatus := utils.CaptureSubstring(note.Body, utils.URL_RUN_STATUS_PREFIX, utils.URL_RUN_SUFFIX)
if runUrl != "" && runStatus != "" {
oldRunUrls = append(oldRunUrls, fmt.Sprintf("%s - %s", runUrl, utils.FormatStatus(runStatus)))
}

// Gitlab default sort is order by created by, so take the last match on this
oldRunBlockTest := utils.CaptureSubstring(note.Body, utils.URL_RUN_GROUP_PREFIX, utils.URL_RUN_GROUP_SUFFIX)
if oldRunBlockTest != "" {
oldRunBlock = oldRunBlockTest
}
if os.Getenv("TFBUDDY_DELETE_OLD_COMMENTS") != "" && note.ID != rootNoteID {
log.Debug().Str("projectID", project).Int("mrIID", mrIID).Msgf("deleting note %d", note.ID)
_, err := c.client.Notes.DeleteMergeRequestNote(project, mrIID, note.ID)
if err != nil {
return "", utils.CreatePermanentError(err)
}
}
}
}

// Add new urls into block
if len(oldRunUrls) > 0 {
return fmt.Sprintf("%s\n%s\n%s\n%s", utils.URL_RUN_GROUP_PREFIX, oldRunBlock, strings.Join(oldRunUrls, "\n"), utils.URL_RUN_GROUP_SUFFIX), nil
}
return oldRunBlock, nil
}

// CreateMergeRequestComment creates a comment on the merge request.
func (c *GitlabClient) CreateMergeRequestComment(mrIID int, projectID, comment string) error {
if comment != "" {
Expand Down Expand Up @@ -148,6 +195,7 @@ func (c *GitlabClient) CreateMergeRequestDiscussion(mrIID int, project, comment
if comment == "" {
return nil, errors.New("comment is empty")
}

return backoff.RetryWithData(func() (vcs.MRDiscussionNotes, error) {
log.Debug().Str("project", project).Int("mrIID", mrIID).Msg("create Gitlab discussion")
dis, _, err := c.client.Discussions.CreateMergeRequestDiscussion(project, mrIID, &gogitlab.CreateMergeRequestDiscussionOptions{
Expand Down
13 changes: 13 additions & 0 deletions pkg/vcs/gitlab/mr_comment_updater.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,19 @@ func (p *RunStatusUpdater) postRunStatusComment(run *tfe.Run, rmd runstream.RunM

commentBody, topLevelNoteBody, resolveDiscussion := comment_formatter.FormatRunStatusCommentBody(p.tfc, run, rmd)

var oldUrls string
var err error

if run.Status == tfe.RunErrored || run.Status == tfe.RunCanceled || run.Status == tfe.RunDiscarded || run.Status == tfe.RunPlannedAndFinished {
oldUrls, err = p.client.GetOldRunUrls(rmd.GetMRInternalID(), rmd.GetMRProjectNameWithNamespace(), int(rmd.GetRootNoteID()))
if err != nil {
log.Error().Err(err).Msg("could not retrieve old run urls")
}
if oldUrls != "" {
topLevelNoteBody = fmt.Sprintf("%s\n%s", oldUrls, topLevelNoteBody)
}
}
//oldURLBlock := utils.CaptureSubstring(, prefix string, suffix string)
if _, err := p.client.UpdateMergeRequestDiscussionNote(
rmd.GetMRInternalID(),
int(rmd.GetRootNoteID()),
Expand Down
1 change: 1 addition & 0 deletions pkg/vcs/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ type GitClient interface {
AddMergeRequestDiscussionReply(mrIID int, project, discussionID, comment string) (MRNote, error)
SetCommitStatus(projectWithNS string, commitSHA string, status CommitStatusOptions) (CommitStatus, error)
GetPipelinesForCommit(projectWithNS string, commitSHA string) ([]ProjectPipeline, error)
GetOldRunUrls(mrIID int, project string, rootCommentID int) (string, error)
}
type GitRepo interface {
FetchUpstreamBranch(string) error
Expand Down

0 comments on commit 0467714

Please sign in to comment.