Allow multiple projects per issue and pull requests (#36784)

Add ability to add and remove multiple projects per issue
and pull request.

Resolve #12974

---------

Signed-off-by: Icy Avocado <avocado@ovacoda.com>
Co-authored-by: Tyrone Yeh <siryeh@gmail.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: OpenCode (gpt-5.2-codex) <opencode@openai.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: Claude (Opus 4.7) <noreply@anthropic.com>
This commit is contained in:
Icy Avocado
2026-04-30 08:38:05 -06:00
committed by GitHub
parent 52d6baf5a8
commit 81692ceafa
58 changed files with 1597 additions and 430 deletions
+8 -18
View File
@@ -102,28 +102,18 @@ func Test_Projects(t *testing.T) {
assert.NoError(t, err)
}()
column1 := project_model.Column{
Title: "column 1",
ProjectID: project1.ID,
}
err = project_model.NewColumn(t.Context(), &column1)
assert.NoError(t, err)
column2 := project_model.Column{
Title: "column 2",
ProjectID: project1.ID,
}
err = project_model.NewColumn(t.Context(), &column2)
// Get the default column created by the template (issues will be assigned here)
defaultColumn, err := project1.MustDefaultColumn(t.Context())
assert.NoError(t, err)
// issue 6 belongs to private repo 3 under org 3
issue6 := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 6})
err = issues_model.IssueAssignOrRemoveProject(t.Context(), issue6, user2, project1.ID, column1.ID)
err = issues_model.IssueAssignOrRemoveProject(t.Context(), issue6, user2, []int64{project1.ID})
assert.NoError(t, err)
// issue 16 belongs to public repo 16 under org 3
issue16 := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 16})
err = issues_model.IssueAssignOrRemoveProject(t.Context(), issue16, user2, project1.ID, column1.ID)
err = issues_model.IssueAssignOrRemoveProject(t.Context(), issue16, user2, []int64{project1.ID})
assert.NoError(t, err)
projects, err := db.Find[project_model.Project](t.Context(), project_model.SearchOptions{
@@ -139,8 +129,8 @@ func Test_Projects(t *testing.T) {
Doer: userAdmin,
})
assert.NoError(t, err)
assert.Len(t, columnIssues, 1) // column1 has 2 issues, 6 will not contains here because 0 issues
assert.Len(t, columnIssues[column1.ID], 2) // user2 can visit both issues, one from public repository one from private repository
assert.Len(t, columnIssues, 1) // default column has 2 issues
assert.Len(t, columnIssues[defaultColumn.ID], 2) // admin can visit both issues, one from public repository one from private repository
})
t.Run("Anonymous user", func(t *testing.T) {
@@ -149,7 +139,7 @@ func Test_Projects(t *testing.T) {
})
assert.NoError(t, err)
assert.Len(t, columnIssues, 1)
assert.Len(t, columnIssues[column1.ID], 1) // anonymous user can only visit public repo issues
assert.Len(t, columnIssues[defaultColumn.ID], 1) // anonymous user can only visit public repo issues
})
t.Run("Authenticated user with no permission to the private repo", func(t *testing.T) {
@@ -159,7 +149,7 @@ func Test_Projects(t *testing.T) {
})
assert.NoError(t, err)
assert.Len(t, columnIssues, 1)
assert.Len(t, columnIssues[column1.ID], 1) // user4 can only visit public repo issues
assert.Len(t, columnIssues[defaultColumn.ID], 1) // user2 can only visit public repo issues
})
})