mirror of
https://github.com/go-gitea/gitea.git
synced 2026-09-26 11:44:27 +02:00
enhance: allow auto-closing PRs from PRs (#39393)
On GitHub, one can close PRs via `Fixes: #123` references which was not possible on Gitea before, but now is. Verified fully that behaviour matches GH and ensured no regressions for external trackers.
This commit is contained in:
@@ -10,6 +10,7 @@ import (
|
||||
"gitea.dev/models/db"
|
||||
access_model "gitea.dev/models/perm/access"
|
||||
repo_model "gitea.dev/models/repo"
|
||||
"gitea.dev/models/unit"
|
||||
user_model "gitea.dev/models/user"
|
||||
"gitea.dev/modules/log"
|
||||
"gitea.dev/modules/references"
|
||||
@@ -202,8 +203,13 @@ func (issue *Issue) verifyReferencedIssue(stdCtx context.Context, ctx *crossRefe
|
||||
return nil, references.XRefActionNone, err
|
||||
}
|
||||
|
||||
// Close/reopen actions can only be set from pull requests to issues
|
||||
if refIssue.IsPull || !issue.IsPull {
|
||||
// Close/reopen actions can only be set from pull requests, reopen only to issues
|
||||
if !issue.IsPull || (refIssue.IsPull && refAction == references.XRefActionReopens) {
|
||||
refAction = references.XRefActionNone
|
||||
}
|
||||
|
||||
// With an external tracker, pull requests are referenced as "!N"
|
||||
if refAction != references.XRefActionNone && !ref.IsPull && refIssue.IsPull && refIssue.Repo.UnitEnabled(stdCtx, unit.TypeExternalTracker) {
|
||||
refAction = references.XRefActionNone
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"gitea.dev/models/db"
|
||||
issues_model "gitea.dev/models/issues"
|
||||
repo_model "gitea.dev/models/repo"
|
||||
"gitea.dev/models/unit"
|
||||
"gitea.dev/models/unittest"
|
||||
user_model "gitea.dev/models/user"
|
||||
"gitea.dev/modules/references"
|
||||
@@ -24,22 +25,26 @@ func TestXRef_AddCrossReferences(t *testing.T) {
|
||||
itarget := testCreateIssue(t, 1, 2, "title1", "content1", false)
|
||||
|
||||
// PR to close issue #1
|
||||
content := fmt.Sprintf("content2, closes #%d", itarget.Index)
|
||||
content := fmt.Sprintf("content2, closes #%d, fixes #2", itarget.Index)
|
||||
pr := testCreateIssue(t, 1, 2, "title2", content, true)
|
||||
ref := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{IssueID: itarget.ID, RefIssueID: pr.ID, RefCommentID: 0})
|
||||
assert.Equal(t, issues_model.CommentTypePullRef, ref.Type)
|
||||
assert.Equal(t, pr.RepoID, ref.RefRepoID)
|
||||
assert.True(t, ref.RefIsPull)
|
||||
assert.Equal(t, references.XRefActionCloses, ref.RefAction)
|
||||
ref = unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{IssueID: 2, RefIssueID: pr.ID, RefCommentID: 0})
|
||||
assert.Equal(t, references.XRefActionCloses, ref.RefAction)
|
||||
|
||||
// Comment on PR to reopen issue #1
|
||||
content = fmt.Sprintf("content2, reopens #%d", itarget.Index)
|
||||
content = fmt.Sprintf("content2, reopens #%d, reopens #2", itarget.Index)
|
||||
c := testCreateComment(t, 2, pr.ID, content)
|
||||
ref = unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{IssueID: itarget.ID, RefIssueID: pr.ID, RefCommentID: c.ID})
|
||||
assert.Equal(t, issues_model.CommentTypeCommentRef, ref.Type)
|
||||
assert.Equal(t, pr.RepoID, ref.RefRepoID)
|
||||
assert.True(t, ref.RefIsPull)
|
||||
assert.Equal(t, references.XRefActionReopens, ref.RefAction)
|
||||
ref = unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{IssueID: 2, RefIssueID: pr.ID, RefCommentID: c.ID})
|
||||
assert.Equal(t, references.XRefActionNone, ref.RefAction)
|
||||
|
||||
// Issue mentioning issue #1
|
||||
content = fmt.Sprintf("content3, mentions #%d", itarget.Index)
|
||||
@@ -66,6 +71,14 @@ func TestXRef_AddCrossReferences(t *testing.T) {
|
||||
content = fmt.Sprintf("content6, mentions org3/repo3#%d", itarget.Index)
|
||||
i = testCreateIssue(t, 4, 5, "title6", content, false)
|
||||
unittest.AssertNotExistsBean(t, &issues_model.Comment{IssueID: itarget.ID, RefIssueID: i.ID, RefCommentID: 0})
|
||||
|
||||
assert.NoError(t, db.DeleteBeans(t.Context(), &repo_model.RepoUnit{RepoID: 1, Type: unit.TypeIssues}))
|
||||
assert.NoError(t, db.Insert(t.Context(), &repo_model.RepoUnit{RepoID: 1, Type: unit.TypeExternalTracker, Config: &repo_model.ExternalTrackerConfig{}}))
|
||||
c = testCreateComment(t, 2, pr.ID, "fixes #2, fixes !3")
|
||||
ref = unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{IssueID: 2, RefIssueID: pr.ID, RefCommentID: c.ID})
|
||||
assert.Equal(t, references.XRefActionNone, ref.RefAction)
|
||||
ref = unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{IssueID: 3, RefIssueID: pr.ID, RefCommentID: c.ID})
|
||||
assert.Equal(t, references.XRefActionCloses, ref.RefAction)
|
||||
}
|
||||
|
||||
func TestXRef_NeuterCrossReferences(t *testing.T) {
|
||||
|
||||
@@ -85,6 +85,7 @@ type IssueReference struct {
|
||||
Index int64
|
||||
Owner string
|
||||
Name string
|
||||
IsPull bool
|
||||
Action XRefAction
|
||||
TimeLog string
|
||||
}
|
||||
@@ -123,6 +124,7 @@ func rawToIssueReferenceList(reflist []*rawReference) []IssueReference {
|
||||
Index: r.index,
|
||||
Owner: r.owner,
|
||||
Name: r.name,
|
||||
IsPull: r.isPull,
|
||||
Action: r.action,
|
||||
TimeLog: r.timeLog,
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
issues_model "gitea.dev/models/issues"
|
||||
access_model "gitea.dev/models/perm/access"
|
||||
repo_model "gitea.dev/models/repo"
|
||||
"gitea.dev/models/unit"
|
||||
user_model "gitea.dev/models/user"
|
||||
"gitea.dev/modules/container"
|
||||
"gitea.dev/modules/git"
|
||||
@@ -188,8 +189,8 @@ func UpdateIssuesCommit(ctx context.Context, doer *user_model.User, repo *repo_m
|
||||
return err
|
||||
}
|
||||
|
||||
// Only issues can be closed/reopened this way, and user needs the correct permissions
|
||||
if refIssue.IsPull || !canclose {
|
||||
// Only issues can be reopened this way, and user needs the correct permissions
|
||||
if !canclose || (refIssue.IsPull && ref.Action == references.XRefActionReopens) {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -198,6 +199,11 @@ func UpdateIssuesCommit(ctx context.Context, doer *user_model.User, repo *repo_m
|
||||
continue
|
||||
}
|
||||
|
||||
// With an external tracker, pull requests are referenced as "!N"
|
||||
if !ref.IsPull && refIssue.IsPull && refRepo.UnitEnabled(ctx, unit.TypeExternalTracker) {
|
||||
continue
|
||||
}
|
||||
|
||||
if !repo.CloseIssuesViaCommitInAnyBranch {
|
||||
// If the issue was specified to be in a particular branch, don't allow commits in other branches to close it
|
||||
if refIssue.Ref != "" {
|
||||
|
||||
@@ -42,7 +42,7 @@ func TestUpdateIssuesCommit(t *testing.T) {
|
||||
CommitterName: "User Two",
|
||||
AuthorEmail: "user2@example.com",
|
||||
AuthorName: "User Two",
|
||||
Message: "close #2",
|
||||
Message: "close #2, reopen #2",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ func TestUpdateIssuesCommit(t *testing.T) {
|
||||
PosterID: user.ID,
|
||||
IssueID: 1,
|
||||
}
|
||||
issueBean := &issues_model.Issue{RepoID: repo.ID, Index: 4}
|
||||
issueBean := &issues_model.Issue{RepoID: repo.ID, Index: 2}
|
||||
|
||||
unittest.AssertNotExistsBean(t, commentBean)
|
||||
unittest.AssertNotExistsBean(t, &issues_model.Issue{RepoID: repo.ID, Index: 2}, "is_closed=1")
|
||||
@@ -117,6 +117,13 @@ func TestUpdateIssuesCommit(t *testing.T) {
|
||||
unittest.AssertExistsAndLoadBean(t, commentBean)
|
||||
unittest.AssertExistsAndLoadBean(t, issueBean, "is_closed=1")
|
||||
unittest.CheckConsistencyFor(t, &activities_model.Action{})
|
||||
|
||||
repo = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 48})
|
||||
admin := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
|
||||
assert.NoError(t, UpdateIssuesCommit(t.Context(), admin, repo, []*repository.PushCommit{{Sha1: "abcdef4", Message: "close #1"}}, repo.DefaultBranch))
|
||||
unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{RepoID: repo.ID, Index: 1}, "is_closed=0")
|
||||
assert.NoError(t, UpdateIssuesCommit(t.Context(), admin, repo, []*repository.PushCommit{{Sha1: "abcdef5", Message: "close !1"}}, repo.DefaultBranch))
|
||||
unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{RepoID: repo.ID, Index: 1}, "is_closed=1")
|
||||
}
|
||||
|
||||
func TestUpdateIssuesCommit_Colon(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user