...

Source file src/edge-infra.dev/pkg/f8n/devinfra/jack/plugin/project_pruning/handler.go

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

     1  package projectpruning
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"time"
     7  
     8  	"github.com/google/go-github/v47/github"
     9  	"github.com/shurcooL/githubv4"
    10  
    11  	"edge-infra.dev/pkg/f8n/devinfra/jack/plugin"
    12  )
    13  
    14  const (
    15  	PluginName = "project_pruning"
    16  )
    17  
    18  type Card struct {
    19  	Title   string
    20  	ID      string
    21  	Content struct {
    22  		Issue struct {
    23  			State    string
    24  			ClosedAt string
    25  			Number   int
    26  		} `graphql:"... on Issue"`
    27  	}
    28  }
    29  
    30  func init() {
    31  	plugin.RegisterIssueHandler(PluginName, handleIssue)
    32  }
    33  
    34  func handleIssue(hp plugin.HandlerParams, ce github.IssuesEvent) {
    35  	hp.Log.WithName(PluginName)
    36  
    37  	switch action := ce.GetAction(); action {
    38  	case "opened":
    39  		client := hp.ClientV4
    40  
    41  		ctx := context.Background()
    42  
    43  		id, err := fetchProjectNodeID(ctx, client, "ncrvoyix-swt-retail", 12)
    44  		if err != nil {
    45  			hp.Log.Error(err, "Failed to fetch project node ID")
    46  		}
    47  
    48  		items, err := fetchProjectItems(ctx, client, id)
    49  		if err != nil {
    50  			hp.Log.Error(err, "Failed to fetch project items")
    51  		}
    52  		hp.Log.Info(fmt.Sprintf("there are currently %d items in the project\n", len(items)))
    53  
    54  		if len(items) < 1100 {
    55  			cardLimit := 1100 - len(items)
    56  			hp.Log.Info(fmt.Sprintf("Project cards under threshold. %d cards until pruning", cardLimit))
    57  			return
    58  		}
    59  		parseCards(ctx, hp, client, id, items)
    60  	}
    61  }
    62  
    63  func fetchProjectNodeID(ctx context.Context, c plugin.GithubV4ClientInterface, org string, id int) (string, error) {
    64  	var q struct {
    65  		Organization struct {
    66  			Project struct {
    67  				ID string
    68  			} `graphql:"projectNext(number: $id)"`
    69  		} `graphql:"organization(login: $org)"`
    70  	}
    71  
    72  	variables := map[string]interface{}{
    73  		"org": githubv4.String(org),
    74  		"id":  githubv4.Int(id), /* #nosec G115 */
    75  	}
    76  
    77  	err := c.Query(ctx, &q, variables)
    78  	return q.Organization.Project.ID, err
    79  }
    80  
    81  func fetchProjectItems(ctx context.Context, c plugin.GithubV4ClientInterface, id string) ([]Card, error) {
    82  	var q struct {
    83  		Node struct {
    84  			Project struct {
    85  				Items struct {
    86  					Nodes    []Card
    87  					PageInfo struct {
    88  						EndCursor   githubv4.String
    89  						HasNextPage bool
    90  					}
    91  				} `graphql:"items(first: 100, after: $cursor)"`
    92  			} `graphql:"... on ProjectNext"`
    93  		} `graphql:"node(id: $id)"`
    94  	}
    95  
    96  	variables := map[string]interface{}{
    97  		"id":     githubv4.ID(id),
    98  		"cursor": (*githubv4.String)(nil),
    99  	}
   100  
   101  	var cards []Card
   102  	for {
   103  		err := c.Query(ctx, &q, variables)
   104  		if err != nil {
   105  			return nil, err
   106  		}
   107  		cards = append(cards, q.Node.Project.Items.Nodes...)
   108  		if !q.Node.Project.Items.PageInfo.HasNextPage {
   109  			break
   110  		}
   111  		variables["cursor"] = githubv4.NewString(q.Node.Project.Items.PageInfo.EndCursor)
   112  	}
   113  
   114  	return cards, nil
   115  }
   116  
   117  func deleteCard(ctx context.Context, c plugin.GithubV4ClientInterface, pid, cid string) error {
   118  	var m struct {
   119  		DeleteProjectV2ItemInput struct {
   120  			DeletedItemID githubv4.ID
   121  		} `graphql:"deleteProjectV2Item(input: $input)"`
   122  	}
   123  
   124  	input := githubv4.DeleteProjectV2ItemInput{
   125  		ProjectID: githubv4.ID(pid),
   126  		ItemID:    githubv4.ID(cid),
   127  	}
   128  
   129  	return c.Mutate(ctx, &m, input, nil)
   130  }
   131  
   132  func parseCards(ctx context.Context, hp plugin.HandlerParams, client plugin.GithubV4ClientInterface, id string, items []Card) {
   133  	cutoff := time.Now().AddDate(0, 0, -14)
   134  	hp.Log.Info("(issues still exist, just being removed from the project")
   135  	var r []Card
   136  	for _, item := range items {
   137  		conditional := item.Content.Issue.State == string(githubv4.IssueStateClosed)
   138  		switch status := conditional; status {
   139  		case true:
   140  			closedTime, err := time.Parse(time.RFC3339, item.Content.Issue.ClosedAt)
   141  			if err != nil {
   142  				hp.Log.Error(err, "Failed to return time value")
   143  			}
   144  
   145  			// issueNum := item.Content.Issue.Number
   146  			// issue, _, err := hp.Client.Issues().Get(hp.Ctx, hp.Org, hp.Repo, issueNum)
   147  			// if err != nil {
   148  			// 	hp.Log.Error(err, "Failed to fetch issue")
   149  			// }
   150  
   151  			// check := true
   152  
   153  			// labels := issue.Labels
   154  			// for _, label := range labels {
   155  			// 	name := label.GetName()
   156  			// 	if name == string(constants.Epic) || name == string(constants.Capability) || name == string(constants.Feature) {
   157  			// 		check = false
   158  			// 	}
   159  			// }
   160  			// if err != nil {
   161  			// 	hp.Log.Error(err, "")
   162  			// }
   163  
   164  			//if closedTime.Before(cutoff) && check {
   165  			if closedTime.Before(cutoff) {
   166  				r = append(r, item)
   167  			}
   168  		}
   169  	}
   170  	hp.Log.Info(fmt.Sprintf("identified %d items for deletion\n", len(r)))
   171  
   172  	for _, item := range r {
   173  		if err := deleteCard(ctx, client, id, item.ID); err != nil {
   174  			hp.Log.Error(err, "Failed to delete card")
   175  		}
   176  
   177  		hp.Log.Info(fmt.Sprintf("%s: %s\n", item.Title, item.Content.Issue.ClosedAt))
   178  	}
   179  }
   180  

View as plain text