New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Plugin to add issue and PR to GitHub Project #10982
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
prow/plugins.yaml
Outdated
@@ -222,6 +222,15 @@ repo_milestone: | |||
maintainers_id: 2460384 | |||
maintainers_team: kubernetes-milestone-maintainers | |||
|
|||
repo_project: | |||
# Default maintainer for projects | |||
'': |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does this mean?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@stevekuznetsov for approach 2, does something like this work?
project:
- orgs:
- kubernetes:
# Org level config, which can be overriden by repo level configs under repos
maintainers_team_id: 2460384
maintainers_team_name: kubernetes-milestone-maintainers
columns:
- To do
- To triage
- Backlog
- repos:
- kubernetes:
maintainers_team_id: 2460384
maintainers_team_name: kubernetes-milestone-maintainers
columns:
- To do
- To triage
- Backlog
- kubeflow:
maintainers_team_id: 2460384
maintainers_team_name: kubernetes-milestone-maintainers
columns:
- To do
- To triage
- Backlog
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
or something like this? i think this might be more readable:
project:
# Org level config, which can be overriden by repo level configs under repos
kubernetes/*:
- maintainer_team_id: 2460384
maintainers_team_name: kubernetes-milestone-maintainers
columns:
- To do
- To triage
- Backlog
kubernetes/kubernetes:
- maintainer_team_id: 2460384
maintainers_team_name: kubernetes-milestone-maintainers
columns:
- To do
- To triage
- Backlog
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think your first mockup is more true to the branchprotection
config and I would rather see that one, but don't let me be the only opinion here!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@stevekuznetsov I see. I prefer the second one because it's easier to read IMHO
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Totally. @fejta thoughts? Another data point is (unfortunately) we have momentum behind the approach that already exists in our config, and keeping things similar would help everyone keep it straight
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@stevekuznetsov yeah I see. It makes sense to keep it consistent with existing configs
prow/plugins/config.go
Outdated
Label *Label `json:"label,omitempty"` | ||
Lgtm []Lgtm `json:"lgtm,omitempty"` | ||
RepoMilestone map[string]Milestone `json:"repo_milestone,omitempty"` | ||
ProjectConfigMap map[string]ProjectConfig `json:"repo_project,omitempty"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mapped by org/repo
?
prow/plugins/config.go
Outdated
@@ -372,6 +373,16 @@ type Milestone struct { | |||
MaintainersTeam string `json:"maintainers_team,omitempty"` | |||
} | |||
|
|||
// ProjectConfig contains the configuration options for the project plugin | |||
type ProjectConfig struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this a good config to emulate the branch-protection-style tree overrides?
prow/plugins/project/project.go
Outdated
Usage: "/project <board>, /project <board> <column>, or /project clear <board>", | ||
Description: "Updates the project board and column for an issue or PR", | ||
Featured: false, | ||
WhoCanUse: "Members of the project maintainers GitHub team can use the '/project' command.", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can this team be called out here with the config value?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Outstanding
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@stevekuznetsov i'm not sure what's the best way to call out the team value here, because I don't have access to the org and repo information
prow/plugins/project/project.go
Outdated
} | ||
|
||
// Get all projects for this org | ||
orgProjects, err := gc.GetOrgProjects(org) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should only make this call if we don't find the project we need in the repo
prow/plugins/project/project.go
Outdated
projectMap := buildProjectMap(projects) | ||
projectID, ok := projectMap[proposedProject] | ||
|
||
if proposedProject == clearKeyword { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this is the case, we shouldn't load project columns from GitHub
prow/plugins/project/project.go
Outdated
projectToClear := matches[2] | ||
projectToClearID, ok := projectMap[projectToClear] | ||
if ok { | ||
existingProjectCard, err := gc.GetProjectCard(projectToClearID, e.Number) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need his call if we already have the ID?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have the project ID but we need to project card ID in order to move the project card
prow/plugins/project/project.go
Outdated
} | ||
} | ||
|
||
// If proposedColumn is not found (or not provided), add to the "To do", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we should expose the default column as a config option and do nothing if it is not set?
// Move this issue/PR to the new column if there's already a project card for | ||
// this issue/PR in this project | ||
existingProjectCard, err := gc.GetProjectCard(projectID, e.Number) | ||
if err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will you get a not found error here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah probably will, in which case I shouldn't check for err here because we will move onto creating a new project card
please pass tests |
adcac06
to
394f49d
Compare
Dependent PR seems stalled. Asked for an ETA on it. |
I think it's fine to just commit the client changes in this PR and not depend on the other. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is looking really good! Some nits, some comments on how often we post back to GitHub
prow/plugins.yaml
Outdated
@@ -222,6 +222,15 @@ repo_milestone: | |||
maintainers_id: 2460384 | |||
maintainers_team: kubernetes-milestone-maintainers | |||
|
|||
repo_project: | |||
# Default maintainer for projects | |||
'': |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
prow/plugins/config.go
Outdated
ProjectMaintainersTeam string `json:"project_maintainers_team,omitempty"` | ||
|
||
// If no column is provided when adding an issue/PR to a project, the issue/PR | ||
// will be added to one of these columns (in the order they are specified) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wait, so when does it add to the second column in this list?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@stevekuznetsov if the first column in the default list (To do
) doesn't exist in the project, then it'll look for the second column (To triage
), and then the third Backlog
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this might be confusing or surprising behavior for users. @cblecker
prow/plugins/project/project.go
Outdated
Usage: "/project <board>, /project <board> <column>, or /project clear <board>", | ||
Description: "Updates the project board and column for an issue or PR", | ||
Featured: false, | ||
WhoCanUse: "Members of the project maintainers GitHub team can use the '/project' command.", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Outstanding
prow/plugins/project/project.go
Outdated
} | ||
|
||
pluginHelp := &pluginhelp.PluginHelp{ | ||
Description: "The project plugin allows members of a configurable GitHub team to set the project and column on an issue or pull request.", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/configurable //
prow/plugins/project/project.go
Outdated
pluginHelp := &pluginhelp.PluginHelp{ | ||
Description: "The project plugin allows members of a configurable GitHub team to set the project and column on an issue or pull request.", | ||
Config: func(repos []string) map[string]string { | ||
configMap := make(map[string]string) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we generally prefer
configMap := map[string]string{}
When no size is required
repoProjects, err := gc.GetRepoProjects(org, repo.Name) | ||
if err != nil { | ||
log.WithError(err).Errorf("Error listing all projects under repo %s", repo.Name) | ||
return err |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In these cases we should report some internal error to the user on GitHub and ask them to re-try their command
prow/plugins/project/project.go
Outdated
invalidNumArgs = "Please provide 2 or 3 arguments. Example usage: /project 0.5.0, /project 0.5.0 3999801, /project clear 0.4.0" | ||
projectTeamMsg = "The project maintainers team is the Github team %q with ID: %d." | ||
clearKeyword = "clear" | ||
projectNameToIDMap map[string]int |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's not use a global here, but we would need a project client similar to that which the other plugins have like ownersClient or slack client. Alternatively if we don't think it is necessary to have an in-memory cache in this plugin for projects, we can just forget about it and offload the caching to the ghproxy
. Sorry, I know I brought this up in the first place, but removing this might be the best approach for the first impl
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@stevekuznetsov I see. I think it might be overkill to have a client in this case. Could you point me to an example of how to offload the caching to ghproxy?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using ghproxy
will be invisible to this code -- we just pass the proxy as the URL to GitHub when we start up the binary. So, to use it we can just ignore the fact in the code and keep things simple here.
if proposedProject == clearKeyword { | ||
if err := gc.DeleteProjectCard(projectID); err != nil { | ||
log.WithError(err).Errorf("Error removing project card from project %s (ID %d) for %s/%s#%d.", projectToFind, projectID, org, repo, e.Number) | ||
return err |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be shown to the user?
prow/plugins/project/project.go
Outdated
log.WithError(err).Errorf("Error removing project card from project %s (ID %d) for %s/%s#%d.", projectToFind, projectID, org, repo, e.Number) | ||
return err | ||
} | ||
return nil |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we report a success to the user?
prow/plugins/project/project.go
Outdated
return err | ||
} | ||
|
||
// If proposedColumn is not found (or not provided), add to one of the default |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be documented in the configuration fields
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@cblecker is this surprising behavior? Should we just do the simple thing and error if we can't find the column that was requested?
a2b7729
to
0d701ee
Compare
a976635
to
d9e1dc2
Compare
37baeb4
to
1f80115
Compare
/test pull-test-infra-lint |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
project_config: | ||
project_org_configs: | ||
kubernetes: | ||
org_maintainers_team_id: 2460384 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: could be
org_maintainers_team:
id:
name:
With this, it looks like you could override the team ID but not the team name with a repo config? Is that something we want?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes that's true. When checking a user against the team only the team ID is used. I will remove the team name from the config
To do | ||
project_repo_configs: | ||
kubernetes: | ||
repo_maintainers_team_id: 2460384 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to specify these? They are the same as in the owning org
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's probably a bad example for me to use the same team here. They could be different and the repo setting will override the org setting
prow/plugins/project/project.go
Outdated
plugins.RegisterGenericCommentHandler(pluginName, handleGenericComment, helpProvider) | ||
} | ||
|
||
func getMaintainerTeam(pluginConfigs plugins.ProjectConfig, org string, repo string) (int, string) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: this could be a method on ProjectConfig
prow/plugins/project/project.go
Outdated
return 0, "" | ||
} | ||
|
||
func getColumnMap(pluginConfigs plugins.ProjectConfig, org string, repo string) map[string]string { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: this could be a method on ProjectConfig
prow/plugins/project/project.go
Outdated
return nil | ||
} | ||
|
||
func getOrgColumnMap(pluginConfigs plugins.ProjectConfig, org string, repo string) map[string]string { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: this could be a method on ProjectConfig
and does not need the repo
parameter
prow/plugins/project/project.go
Outdated
repos, err := gc.GetRepos(org, false) | ||
if err != nil { | ||
msg = fmt.Sprintf(errListingReposMsg, org) | ||
return gc.CreateComment(org, repo, e.Number, plugins.FormatResponseRaw(e.Body, e.HTMLURL, e.User.Login, msg)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So some of these errors are useful to users -- like if they try to use this command with a project that doesn't exist, or if they're not in the group. However, this call will fail only if there is an infrastructure error -- if we don't have permissions or can't hit the API or something. We should just return the error here
prow/plugins/project/project.go
Outdated
// specified in the project config and see if any of them exists on the | ||
// proposed project | ||
if proposedColumnName == "" { | ||
defaultColumn := "" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similar comment here, you could just call getColumnMap
and not need explicit default values
prow/plugins/project/project.go
Outdated
|
||
// Move this issue/PR to the new column if there's already a project card for | ||
// this issue/PR in this project | ||
existingProjectCard, _ := gc.GetColumnProjectCard(proposedColumnID, e.Number) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are we ignoring this error?
} | ||
} | ||
|
||
func TestParseCommand(t *testing.T) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This could use negative test cases
if proposedProject != test.proposedProject || | ||
proposedColumn != test.proposedColumn || | ||
shouldClear != test.shouldClear { | ||
t.Errorf("\nFor command %s, expected\n proposedProject = %s\n proposedColumn = %s\n shouldClear = %t\nbut got\n proposedProject = %s\n proposedColumn = %s\n shouldClear = %t\n", test.command, test.proposedProject, test.proposedColumn, test.shouldClear, proposedProject, proposedColumn, shouldClear) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would be nicer for the person who breaks this test to check them separately and do tailored messaged to each
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or wrap them in a struct and use ObjectReflectDiff or something
@stevekuznetsov Just realized the project name can be multiple words, for example a command like |
4f496e8
to
373b6b0
Compare
Talked to @stevekuznetsov on slack about the multi-word case for project name. We can try out this plugin with the single-word case and then have followup discussion on the command format to handle multi-word project and column names |
@cblecker would love to hear your input on the above ^^ |
d947930
to
96ff294
Compare
96ff294
to
60c3fbe
Compare
/lgtm |
LGTM label has been added. Git tree hash: 823cb0dd17bf83db215d7a22d83d5a1edb7d3f2c
|
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: stevekuznetsov, taragu The full list of commands accepted by this bot can be found here. The pull request process is described here
Needs approval from an approver in each of these files:
Approvers can indicate their approval by writing |
@taragu: Updated the
In response to this:
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository. |
Fixes #10514
Need to wait for PR #10401 to be merged
/kind feature
TODO:
prow/github/fakegithub/fakegithub.go
and unit tests (after PR WIP: Auto add PRs to project if they match a search query #10401 is merged)