...

Source file src/edge-infra.dev/pkg/f8n/devinfra/jack/plugin/list/comments.go

Documentation: edge-infra.dev/pkg/f8n/devinfra/jack/plugin/list

     1  package list
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"strconv"
     7  	"strings"
     8  
     9  	"github.com/google/go-github/v47/github"
    10  
    11  	"edge-infra.dev/pkg/f8n/devinfra/jack/constants"
    12  	guestservices "edge-infra.dev/pkg/f8n/devinfra/jack/guest_services"
    13  	"edge-infra.dev/pkg/f8n/devinfra/jack/plugin"
    14  	"edge-infra.dev/pkg/lib/logging"
    15  )
    16  
    17  // Adding issue(s) from an epic
    18  func addIssue(hp plugin.HandlerParams, event AddRemoveEvent, commands []string, isParent bool) error {
    19  	log := hp.Log
    20  	ctx := hp.Ctx
    21  	client := hp.Client
    22  	log.Info("Adding a list of issues")
    23  
    24  	epicNumber := event.EpicNumber
    25  	repoOwner := event.RepoOwner
    26  	repoName := event.RepoName
    27  
    28  	log.Info(fmt.Sprintf("%+v", commands))
    29  
    30  	hasParentLabel := false
    31  	for _, v := range event.Labels {
    32  		if guestservices.IsParentLabel(v.GetName()) {
    33  			log.Info(v.GetName())
    34  			hasParentLabel = true
    35  			log.Info("Issue already has a parent label")
    36  		}
    37  	}
    38  
    39  	isEpic := guestservices.HasEpicLabel(event.Labels)
    40  	// if the issue has the epic label and its attempting to add a parent stop it
    41  	if isEpic && isParent {
    42  		log.Info("Epics cant have parents silly goose")
    43  		msg := fmt.Sprintf("%s `%s`\n%s `%s` `%s` `%s`\n___\n%s",
    44  			"Cannot add parents to issues labeled with",
    45  			string(constants.Epic),
    46  			"Use a different parent label such as:",
    47  			string(constants.Capability),
    48  			string(constants.Feature),
    49  			string(constants.Story),
    50  			"[Check the docs for more info](https://docs.edge-infra.dev/dev/project/#hierarchy)")
    51  		issueComment := github.IssueComment{Body: &msg}
    52  
    53  		if _, _, err := client.Issues().CreateComment(ctx, repoOwner, repoName, epicNumber, &issueComment); err != nil {
    54  			log.Error(err, "Failed to comment on issue")
    55  			return err
    56  		}
    57  		return nil
    58  	}
    59  
    60  	// iterate over the list of issues
    61  	errorList, newParentBody := iterateOverListOfIssues(ctx, log, client, event, commands, isParent)
    62  
    63  	if !hasParentLabel && !isParent {
    64  		log.Info("adding epic label to new parent")
    65  		labels := []string{string(constants.Epic)}
    66  
    67  		var fl []*github.Label
    68  		fl = append(fl, &github.Label{Name: github.String(string(constants.Epic))})
    69  
    70  		newParent := guestservices.ParentChild{}
    71  		newParent.New(newParentBody, fl, constants.Preamble, constants.Postamble, event.RepoID)
    72  
    73  		githubIssueBody := &github.IssueRequest{Body: github.String(newParent.ToString()), Labels: &labels}
    74  		_, _, err := client.Issues().Edit(ctx, repoOwner, repoName, epicNumber, githubIssueBody)
    75  		if err != nil {
    76  			return err
    77  		}
    78  		return nil
    79  
    80  		// _, _, err := client.Issues.AddLabelsToIssue(ctx, repoOwner, repoName, epicNumber, labels)
    81  		// if err != nil {
    82  		// 	log.Error(err, "Failed to add kind/epic label")
    83  		// }
    84  	}
    85  
    86  	githubIssueBody := &github.IssueRequest{Body: github.String(newParentBody)}
    87  
    88  	_, _, err := client.Issues().Edit(ctx, repoOwner, repoName, epicNumber, githubIssueBody)
    89  	if err != nil {
    90  		log.Error(err, "Failed to update epic desc")
    91  		return err
    92  	}
    93  
    94  	if len(errorList) > 0 {
    95  		errorString := strings.Join(errorList, "\n")
    96  
    97  		log.Info("Commenting on epic that some issues were not added")
    98  		log.Info(fmt.Sprintf("%s\n%s", "Some issues could not be added:", errorString))
    99  
   100  		msg := fmt.Sprintf("%s\n%s", "Some issues could not be added:", errorString)
   101  		issueComment := github.IssueComment{Body: &msg}
   102  
   103  		if _, _, err := client.Issues().CreateComment(ctx, repoOwner, repoName, epicNumber, &issueComment); err != nil {
   104  			log.Error(err, "Failed to comment on issue")
   105  			return err
   106  		}
   107  	}
   108  	return nil
   109  }
   110  
   111  func iterateOverListOfIssues(ctx context.Context, log logging.EdgeLogger, client plugin.GithubClientInterface, event AddRemoveEvent,
   112  	commands []string, isParent bool) ([]string, string) {
   113  	epicTitle := event.EpicTitle
   114  	epicNumber := event.EpicNumber
   115  	labels := event.Labels
   116  	repoOwner := event.RepoOwner
   117  	repoName := event.RepoName
   118  	repoID := event.RepoID
   119  
   120  	pc := guestservices.ParentChild{}
   121  	pc.New(event.EpicBody, labels, constants.Preamble, constants.Postamble, repoID)
   122  	log.Info(pc.ToString())
   123  
   124  	errorList := []string{}
   125  
   126  	for _, v := range commands {
   127  		log.Info(v)
   128  
   129  		issueNumberRegex, err := strconv.Atoi(v)
   130  		if err != nil {
   131  			log.Info("#" + v + " - Failed to convert issue number to an int")
   132  			errorList = append(errorList, "#"+v+" - failed to convert issue number to an int")
   133  			continue
   134  		}
   135  
   136  		// Check if the user is attempting to add the issue to itself
   137  		if epicNumber == issueNumberRegex {
   138  			log.Info("#" + v + " - cannot add an issue to itself")
   139  			errorList = append(errorList, "#"+v+" - cannot add an issue to itself")
   140  			continue
   141  		}
   142  
   143  		// Get the issue
   144  		foundIssue, _, err := client.Issues().Get(ctx, repoOwner, repoName, issueNumberRegex)
   145  		if err != nil {
   146  			log.Error(err, "Failed to get issues")
   147  			errorList = append(errorList, "failed to get issue #"+v)
   148  			continue
   149  		}
   150  
   151  		// check if the issue is a PR
   152  		if foundIssue.IsPullRequest() {
   153  			log.Info("#" + v + " references a pull request")
   154  			errorList = append(errorList, "#"+v+" - pull requests cannot be linked to issues")
   155  			continue
   156  		}
   157  
   158  		foundIssueNumber := foundIssue.GetNumber()
   159  		foundIssueTitle := foundIssue.GetTitle()
   160  		foundIssueBody := foundIssue.GetBody()
   161  		foundLabels := foundIssue.Labels
   162  
   163  		secondPC := &guestservices.ParentChild{}
   164  		secondPC.New(foundIssueBody, foundLabels, constants.Preamble, constants.Postamble, repoID)
   165  
   166  		log.Info(fmt.Sprintf("Adding epic %v to issue %v", epicNumber, foundIssueNumber))
   167  
   168  		// check if the issue being added is already on the list
   169  		foundParentIndex := pc.FindParent(foundIssueNumber, repoID)
   170  		foundChildIndex := pc.FindChild(foundIssueNumber, repoID)
   171  
   172  		// if a parent was found with the same issue number and the command is /child, swap
   173  		if foundParentIndex != -1 && !isParent {
   174  			log.Info(fmt.Sprintf("This issue was already added to the parent list #%d", epicNumber))
   175  			log.Info(fmt.Sprintf("attempting to swap child to parent %d", foundIssueNumber))
   176  			success := pc.SwapParentToChild(foundIssueNumber, repoID)
   177  			if success == -1 {
   178  				log.Info(fmt.Sprintf("failed to swap #%d from parent to child on upstream list", foundIssueNumber))
   179  			}
   180  
   181  			success = secondPC.SwapChildToParent(epicNumber, repoID)
   182  			if success == -1 {
   183  				log.Info(fmt.Sprintf("failed to swap #%d from child to parent on downstream list", epicNumber))
   184  			}
   185  		}
   186  
   187  		// if a child was found, its a parent command, and the downstream issue is a parent then swap
   188  		p, _ := guestservices.CheckForParentLabel("", foundIssue.Labels)
   189  		if foundChildIndex != -1 && isParent && p {
   190  			log.Info(fmt.Sprintf("This issue was already added to the child list on #%d", epicNumber))
   191  			log.Info(fmt.Sprintf("attempting to swap child to parent %d", foundIssueNumber))
   192  			success := pc.SwapChildToParent(foundIssueNumber, repoID)
   193  			if success == -1 {
   194  				log.Info(fmt.Sprintf("failed to swap #%d from child to parent on upstream list", foundIssueNumber))
   195  			}
   196  			log.Info(fmt.Sprintf("attempting to swap parent to child %d %d", foundIssueNumber, epicNumber))
   197  			success = secondPC.SwapParentToChild(epicNumber, repoID)
   198  			if success == -1 {
   199  				log.Info(fmt.Sprintf("failed to swap #%d from parent to child on downstream list", epicNumber))
   200  			}
   201  		}
   202  
   203  		// if there are no matches add like normal
   204  		if foundChildIndex == -1 && foundParentIndex == -1 {
   205  			// Create the new epic to add
   206  			epicString := guestservices.ListItem{Number: epicNumber, Title: epicTitle, RepoID: repoID}
   207  			// Create the issue and add the epic to its list
   208  			if isParent {
   209  				secondPC.AddChildItem(epicString)
   210  			} else {
   211  				secondPC.AddParentItem(epicString)
   212  			}
   213  
   214  			issueString := guestservices.ListItem{Number: foundIssueNumber, Title: foundIssueTitle, RepoID: repoID}
   215  			if isParent {
   216  				pc.AddParentItem(issueString)
   217  			} else {
   218  				pc.AddChildItem(issueString)
   219  			}
   220  		}
   221  
   222  		newIssueBody := secondPC.ToString()
   223  
   224  		// Update the issue with the new body
   225  		githubIssueBody := &github.IssueRequest{Body: github.String(newIssueBody)}
   226  		_, _, err = client.Issues().Edit(ctx, repoOwner, repoName, foundIssueNumber, githubIssueBody)
   227  		if err != nil {
   228  			log.Error(err, "Failed to update issue body")
   229  			errorList = append(errorList, "#"+v+" - failed to update issue body")
   230  			continue
   231  		}
   232  
   233  		log.Info(fmt.Sprintf("Added issue %v to epic %v", foundIssueNumber, epicNumber))
   234  	}
   235  
   236  	return errorList, pc.ToString()
   237  }
   238  
   239  // Removing issue(s) from an epic
   240  func removeIssue(hp plugin.HandlerParams, event AddRemoveEvent, commands []string, isParent bool) error {
   241  	log := hp.Log
   242  	ctx := hp.Ctx
   243  	client := hp.Client
   244  
   245  	log.Info("Removing a list of issues")
   246  
   247  	repoOwner := event.RepoOwner
   248  	repoName := event.RepoName
   249  	repoID := event.RepoID
   250  	senderNumber := event.EpicNumber
   251  	senderBody := event.EpicBody
   252  	senderLabels := event.Labels
   253  
   254  	sender := guestservices.ParentChild{}
   255  	sender.New(senderBody, senderLabels, constants.Preamble, constants.Postamble, repoID)
   256  
   257  	errorList := []string{}
   258  	for _, v := range commands {
   259  		issueNumberRegex, err := strconv.Atoi(v)
   260  		if err != nil {
   261  			log.Info(v + " Failed to convert issue number to an int")
   262  			errorList = append(errorList, "#"+v+" - failed to convert issue number to an int")
   263  			continue
   264  		}
   265  
   266  		// Get the issue
   267  		receivingIssue, _, err := client.Issues().Get(ctx, repoOwner, repoName, issueNumberRegex)
   268  		if err != nil {
   269  			log.Error(err, "Failed to get issues")
   270  			errorList = append(errorList, "#"+v+" - failed to retrieve issue")
   271  			continue
   272  		}
   273  
   274  		if receivingIssue.IsPullRequest() {
   275  			log.Info("#" + v + " references a pull request")
   276  			errorList = append(errorList, "#"+v+" - pull requests cannot be linked to issues")
   277  			continue
   278  		}
   279  
   280  		log.Info("Removing an epic from an issues list")
   281  
   282  		receiverNumber := receivingIssue.GetNumber()
   283  		receiverBody := receivingIssue.GetBody()
   284  		receiverLabels := receivingIssue.Labels
   285  
   286  		log.Info(fmt.Sprintf("Removing epic %v from issue %v", senderNumber, receiverNumber))
   287  
   288  		receiver := guestservices.ParentChild{}
   289  		receiver.New(receiverBody, receiverLabels, constants.Preamble, constants.Postamble, repoID)
   290  
   291  		if isParent {
   292  			receiver.RemoveChild(senderNumber, repoID)
   293  		} else {
   294  			receiver.RemoveParent(senderNumber, repoID)
   295  		}
   296  		newReceiverBody := receiver.ToString()
   297  
   298  		// Set its new body
   299  		githubIssueBody := &github.IssueRequest{Body: github.String(newReceiverBody)}
   300  		_, _, err = client.Issues().Edit(ctx, repoOwner, repoName, receiverNumber, githubIssueBody)
   301  		if err != nil {
   302  			log.Error(err, "Failed to update issue body")
   303  			errorList = append(errorList, "#"+v+" - failed to update issue body")
   304  			continue
   305  		}
   306  		if isParent {
   307  			sender.RemoveParent(receiverNumber, repoID)
   308  		} else {
   309  			sender.RemoveChild(receiverNumber, repoID)
   310  		}
   311  		log.Info(fmt.Sprintf("Removed issue %v from epic %v", receiverNumber, senderNumber))
   312  	}
   313  
   314  	newSenderBody := sender.ToString()
   315  	githubIssueBody := &github.IssueRequest{Body: github.String(newSenderBody)}
   316  	_, _, err := client.Issues().Edit(ctx, repoOwner, repoName, senderNumber, githubIssueBody)
   317  	if err != nil {
   318  		log.Error(err, "Failed to update epic desc")
   319  		return err
   320  	}
   321  
   322  	// If any errors were caught when adding issues add them to a comment on the epic
   323  	if len(errorList) > 0 {
   324  		errorString := strings.Join(errorList, "\n")
   325  
   326  		log.Info("Commenting on epic that some issues were not removed")
   327  		log.Info(fmt.Sprintf("%s\n%s", "Some issues could not be removed:", errorString))
   328  
   329  		msg := fmt.Sprintf("%s\n%s", "Some issues could not be removed:", errorString)
   330  		issueComment := github.IssueComment{Body: &msg}
   331  
   332  		if _, _, err := client.Issues().CreateComment(ctx, repoOwner, repoName, senderNumber, &issueComment); err != nil {
   333  			log.Error(err, "Failed to comment on issue")
   334  			return err
   335  		}
   336  	}
   337  
   338  	return nil
   339  }
   340  

View as plain text