package project import ( "context" "fmt" "edge-infra.dev/pkg/lib/logging" "github.com/google/go-github/v47/github" "github.com/shurcooL/githubv4" "edge-infra.dev/pkg/f8n/devinfra/jack/constants" gs "edge-infra.dev/pkg/f8n/devinfra/jack/guest_services" "edge-infra.dev/pkg/f8n/devinfra/jack/plugin" ) func addIssueToProject(hp plugin.HandlerParams, event github.IssuesEvent, id int64) error { log := hp.Log ctx := hp.Ctx client := hp.Client clientv4 := hp.ClientV4 log.Info("Adding a new issue to the default project") labels := gs.NewLabels(event.Issue.Labels) // if the issue is labeled with the incident label dont add to the project if labels.Kind == constants.KindIncident { return nil } // Get all the projects based on the org org := event.GetRepo().GetOwner().GetLogin() issueID := event.GetIssue().GetNodeID() orgs, _, err := client.Organizations().ListProjects(ctx, org, &github.ProjectListOptions{}) if err != nil { log.Error(err, "Failed to get orgs") return err } // Find the project number that matches the one defined in the config project := github.Project{} for _, x := range orgs { if int64(x.GetNumber()) == id { project = *x } } // if the project wasnt found in the list try the v4 api before returning // the nodeid of the new card isnt really relevant but it may come in handy later if (github.Project{}) == project { log.Info("cant find project associated with the given number") log.Info("trying the v4 api") betaProjID, err := attemptV4Connection(ctx, clientv4, org, id, log, issueID) if err != nil { log.Error(err, "v4 connection failed") return err } log.Info("added via v4 api -- card nodeid " + betaProjID) return nil } // Get the project projects, _, err := client.Projects().GetProject(ctx, project.GetID()) if err != nil { log.Error(err, "Failed to get the orgs projects") return err } // Get the columns of the project columns, _, err := client.Projects().ListProjectColumns(ctx, projects.GetID(), &github.ListOptions{}) if err != nil { log.Error(err, "Failed to get the projects columns") return err } // check that the project has columns if len(columns) <= 0 { log.Info("not enough columns on the given project") return nil } // Get the first project column ID, generally the backlog is in the first column // Add a new card to the first column projectColumnID := columns[0].GetID() githubProjectCard := &github.ProjectCardOptions{ContentID: event.GetIssue().GetID(), ContentType: "Issue"} _, _, err = client.Projects().CreateProjectCard(ctx, projectColumnID, githubProjectCard) if err != nil { log.Error(err, fmt.Sprintf("Failed to create a new project card for project %s", projects.GetName())) return err } log.Info(fmt.Sprintf("issue %v was added to the %v project", event.GetIssue().GetNumber(), projects.GetName())) return nil } // attemptV4Connection tries grabbing the nodeid of a given project // based off the nodeid we can assume if it is or isnt a beta project func attemptV4Connection(ctx context.Context, c plugin.GithubV4ClientInterface, org string, id int64, log logging.EdgeLogger, inid string) (string, error) { pnid, err := fetchProjectNodeID(ctx, c, org, id) if err != nil { log.Error(err, "Failed to get orgs") return "", err } return addIssueToV4Project(ctx, c, pnid, inid) // attempt to add the issue to a beta project } // fetchProjectNodeID attempt to grab the nodeid of a given project id // more or less airlifted from cmd/ghpm/main.go func fetchProjectNodeID(ctx context.Context, c plugin.GithubV4ClientInterface, org string, id int64) (string, error) { var q struct { Organization struct { Project struct { ID string } `graphql:"projectNext(number: $id)"` } `graphql:"organization(login: $org)"` } variables := map[string]interface{}{ "org": githubv4.String(org), "id": githubv4.Int(id), /* #nosec G115 */ } err := c.Query(ctx, &q, variables) return q.Organization.Project.ID, err } // addIssueToV4Project tries to add an issue to a beta project // returns the new cards nodeid func addIssueToV4Project(ctx context.Context, c plugin.GithubV4ClientInterface, pnid string, inid string) (string, error) { var m struct { AddProjectV2ItemByID struct { Item struct { ID string } } `graphql:"AddProjectV2ItemById(input: $input)"` } input := githubv4.AddProjectV2ItemByIdInput{ ProjectID: githubv4.ID(pnid), // project nodeid ContentID: githubv4.ID(inid), // issue nodeid } err := c.Mutate(ctx, &m, input, nil) if err != nil { return "", err } return m.AddProjectV2ItemByID.Item.ID, nil }