Skip to content
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

Reference issues from pull requests and other issues #8137

Merged
merged 37 commits into from
Sep 20, 2019
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
78c3778
Update ref comment
guillep2k Sep 9, 2019
b4d5dd3
Generate comment on simple ref
guillep2k Sep 10, 2019
235694a
Make fmt + remove unneeded repo load
guillep2k Sep 10, 2019
64e4b42
Add TODO comments
guillep2k Sep 10, 2019
7edef31
Add ref-check in issue creation; re-arrange template
guillep2k Sep 10, 2019
2fbdf4c
Merge branch 'master' of github.com:go-gitea/gitea into xref-issues
guillep2k Sep 11, 2019
11eb10c
Make unit tests pass; rearrange code
guillep2k Sep 11, 2019
e4c21f6
Make fmt
guillep2k Sep 11, 2019
9f85400
Filter out xref comment if user can't see the referencing issue
guillep2k Sep 11, 2019
dd8cbe2
Add TODOs
guillep2k Sep 11, 2019
272aeda
Merge branch 'master' of github.com:go-gitea/gitea into xref-issues
guillep2k Sep 11, 2019
ecba703
Add cross reference
guillep2k Sep 11, 2019
cc85b0f
Rearrange code; add cross-repository references
guillep2k Sep 12, 2019
c92b919
Merge branch 'master' of github.com:go-gitea/gitea into xref-issues
guillep2k Sep 12, 2019
145e1a9
Striketrhough obsolete references
guillep2k Sep 12, 2019
3fd20eb
Remove unnecesary TODO
guillep2k Sep 12, 2019
ba36bc6
Add "not supported" note
guillep2k Sep 12, 2019
0bf9216
Support for edits and deletes, and issue title
guillep2k Sep 12, 2019
fefd405
Revert changes to go.mod
guillep2k Sep 12, 2019
7f4aedf
Fix fmt
guillep2k Sep 12, 2019
7fef0b7
Merge branch 'master' of github.com:go-gitea/gitea into xref-issues
guillep2k Sep 12, 2019
4f24ee9
Add support for xref from API
guillep2k Sep 12, 2019
2c83866
Add first integration test
guillep2k Sep 12, 2019
c4fcdd6
Add integration tests
guillep2k Sep 12, 2019
31304f5
Correct formatting
guillep2k Sep 12, 2019
8b2b0d6
Fix add comment test
guillep2k Sep 12, 2019
fcc6bf0
Add migration
guillep2k Sep 12, 2019
610291c
Remove outdated comments; fix typo
guillep2k Sep 12, 2019
5312037
Some code refactoring and rearranging
guillep2k Sep 14, 2019
ca2f7bf
Merge branch 'master' into xref-issues
guillep2k Sep 14, 2019
f7ffc51
Rename findCrossReferences to createCrossReferences
guillep2k Sep 18, 2019
9ffb74b
Delete xrefs when repository is deleted
guillep2k Sep 18, 2019
66392e1
Corrections as suggested by @lafriks
guillep2k Sep 18, 2019
49bbe79
Prepare for merge
guillep2k Sep 18, 2019
d157ea8
Merge master into xref-issues
guillep2k Sep 18, 2019
92246b5
Fix log for errors
guillep2k Sep 19, 2019
ec51461
Merge branch 'master' into xref-issues
guillep2k Sep 19, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 75 additions & 2 deletions models/issue.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"path"
"regexp"
"sort"
"strconv"
"strings"

"code.gitea.io/gitea/modules/base"
Expand Down Expand Up @@ -69,6 +70,12 @@ type Issue struct {
var (
issueTasksPat *regexp.Regexp
issueTasksDonePat *regexp.Regexp

// issueNumericPattern matches string that references to a numeric issue, e.g. #1287
issueNumericPattern = regexp.MustCompile(`(?:\s|^|\(|\[)(#[0-9]+)(?:\s|$|\)|\]|:|\.(\s|$))`)
guillep2k marked this conversation as resolved.
Show resolved Hide resolved
// crossReferenceIssueNumericPattern matches string that references a numeric issue in a different repository
// e.g. gogits/gogs#12345
// crossReferenceIssueNumericPattern = regexp.MustCompile(`(?:\s|^|\(|\[)([0-9a-zA-Z-_\.]+/[0-9a-zA-Z-_\.]+#[0-9]+)(?:\s|$|\)|\]|\.(\s|$))`)
)

const issueTasksRegexpStr = `(^\s*[-*]\s\[[\sx]\]\s.)|(\n\s*[-*]\s\[[\sx]\]\s.)`
Expand Down Expand Up @@ -1171,8 +1178,15 @@ func newIssue(e *xorm.Session, doer *User, opts NewIssueOptions) (err error) {
}
}
}

return opts.Issue.loadAttributes(e)
if err = opts.Issue.loadAttributes(e); err != nil {
return err
}
refopts := &ParseReferencesOptions{
Type: CommentTypeIssueRef,
Doer: doer,
OrigIssue: opts.Issue,
}
return opts.Issue.parseCommentReferences(e, refopts, opts.Issue.Content)
}

// NewIssue creates new issue with labels for repository.
Expand Down Expand Up @@ -1863,3 +1877,62 @@ func (issue *Issue) updateClosedNum(e Engine) (err error) {
}
return
}

// ParseReferencesOptions represents a comment or issue that might make references to other issues
type ParseReferencesOptions struct {
Type CommentType
Doer *User
OrigIssue *Issue
OrigComment *Comment
}

func (issue *Issue) parseCommentReferences(e *xorm.Session, refopts *ParseReferencesOptions, content string) error {
// TODO: Check for multiple references to the same Issue
// Issues in the same repository
// FIXME: Should we support IssueNameStyleAlphanumeric?
matches := issueNumericPattern.FindAllStringSubmatch(content, -1)
for _, match := range matches {
if i, err := strconv.ParseInt(match[1][1:], 10, 64); err == nil {
if err = issue.loadRepo(e); err != nil {
return err
}
if err = issue.checkCommentReference(e, refopts, issue.RepoID, i); err != nil {
return err
}
}
}
// Issues in other repositories
// GAP: TODO: use crossReferenceIssueNumericPattern to parse references to other repos; take units into account (?)
return nil
}

func (issue *Issue) checkCommentReference(e *xorm.Session, refopts *ParseReferencesOptions, repoID int64, index int64) error {
if refopts.OrigIssue.RepoID == repoID && refopts.OrigIssue.Index == index {
return nil
}
refIssue := &Issue{RepoID: repoID, Index: index}
if has, _ := e.Get(refIssue); !has {
return nil
}
addCommentReference(e, refopts, refIssue)
return nil
}

func addCommentReference(e *xorm.Session, refopts *ParseReferencesOptions, referred *Issue) error {
if err := referred.loadRepo(e); err != nil {
return err
}
var refCommentID int64
if refopts.OrigComment != nil {
refCommentID = refopts.OrigComment.ID
}
_, err := createComment(e, &CreateCommentOptions{
Type: refopts.Type,
Doer: refopts.Doer,
Repo: referred.Repo,
Issue: referred,
RefIssueID: refopts.OrigIssue.ID,
RefCommentID: refCommentID,
})
return err
}
84 changes: 84 additions & 0 deletions models/issue_comment.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,12 @@ type Comment struct {
Review *Review `xorm:"-"`
ReviewID int64 `xorm:"index"`
Invalidated bool

// Reference issue and pull from comment
RefIssueID int64 `xorm:"index"`
RefCommentID int64 `xorm:"index"`
RefIssue *Issue `xorm:"-"`
RefComment *Comment `xorm:"-"`
}

// LoadIssue loads issue from database
Expand Down Expand Up @@ -281,6 +287,64 @@ func (c *Comment) EventTag() string {
return "event-" + com.ToStr(c.ID)
}

// LoadRefComment loads comment that created this reference from database
func (c *Comment) LoadRefComment() (err error) {
if c.RefComment != nil {
return nil
}
c.RefComment, err = GetCommentByID(c.RefCommentID)
return
}

// LoadRefIssue loads comment that created this reference from database
func (c *Comment) LoadRefIssue() (err error) {
if c.RefIssue != nil {
return nil
}
c.RefIssue, err = GetIssueByID(c.RefIssueID)
if err == nil {
err = c.RefIssue.loadRepo(x)
}
return
}

// RefCommentHTMLURL returns the HTML URL for the comment that created this reference
func (c *Comment) RefCommentHTMLURL() string {
if err := c.LoadRefComment(); err != nil { // Silently dropping errors :unamused:
log.Error("LoadRefComment(%d): %v", c.RefCommentID, err)
return ""
}
return c.RefComment.HTMLURL()
}

// RefIssueHTMLURL returns the HTML URL of the issue where this reference was created
func (c *Comment) RefIssueHTMLURL() string {
if err := c.LoadRefIssue(); err != nil { // Silently dropping errors :unamused:
log.Error("LoadRefIssue(%d): %v", c.RefCommentID, err)
return ""
}
return c.RefIssue.HTMLURL()
}

// RefIssueTitle returns the title of the issue where this reference was created
func (c *Comment) RefIssueTitle() string {
if err := c.LoadRefIssue(); err != nil { // Silently dropping errors :unamused:
log.Error("LoadRefIssue(%d): %v", c.RefCommentID, err)
return ""
}
return c.RefIssue.Title
}

// RefIssueIdent returns the user friendly identity (e.g. "#1234") of the issue where this reference was created
func (c *Comment) RefIssueIdent() string {
if err := c.LoadRefIssue(); err != nil { // Silently dropping errors :unamused:
log.Error("LoadRefIssue(%d): %v", c.RefCommentID, err)
return ""
}
// FIXME: check this name for cross-repository references (#7901 if it gets merged)
return "#" + com.ToStr(c.RefIssue.Index)
}

// LoadLabel if comment.Type is CommentTypeLabel, then load Label
func (c *Comment) LoadLabel() error {
var label Label
Expand Down Expand Up @@ -527,6 +591,8 @@ func createComment(e *xorm.Session, opts *CreateCommentOptions) (_ *Comment, err
TreePath: opts.TreePath,
ReviewID: opts.ReviewID,
Patch: opts.Patch,
RefIssueID: opts.RefIssueID,
RefCommentID: opts.RefCommentID,
}
if _, err = e.Insert(comment); err != nil {
return nil, err
Expand All @@ -540,6 +606,22 @@ func createComment(e *xorm.Session, opts *CreateCommentOptions) (_ *Comment, err
return nil, err
}

// Check references to other issues/pulls
if opts.Type == CommentTypeCode || opts.Type == CommentTypeComment {
if err := comment.LoadIssue(); err != nil {
return nil, err
}
refopts := &ParseReferencesOptions{
Type: CommentTypeCommentRef,
Doer: opts.Doer,
OrigIssue: comment.Issue,
OrigComment: comment,
}
if err = comment.Issue.parseCommentReferences(e, refopts, opts.Content); err != nil {
return nil, err
}
}

return comment, nil
}

Expand Down Expand Up @@ -794,6 +876,8 @@ type CreateCommentOptions struct {
ReviewID int64
Content string
Attachments []string // UUIDs of attachments
RefIssueID int64
RefCommentID int64
}

// CreateComment creates comment of issue or commit.
Expand Down
2 changes: 2 additions & 0 deletions options/locale/locale_en-US.ini
Original file line number Diff line number Diff line change
Expand Up @@ -835,6 +835,8 @@ issues.create_comment = Comment
issues.closed_at = `closed <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.reopened_at = `reopened <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.commit_ref_at = `referenced this issue from a commit <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_issue_at = `referenced this issue %[1]s`
issues.ref_pull_at = `referenced this pull request %[1]s`
issues.poster = Poster
issues.collaborator = Collaborator
issues.owner = Owner
Expand Down
18 changes: 18 additions & 0 deletions templates/repo/issue/view_content/comments.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,24 @@
</a>
<span class="text grey"><a href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a> {{$.i18n.Tr "repo.issues.closed_at" .EventTag $createdStr | Safe}}</span>
</div>
{{else if or (eq .Type 3) (eq .Type 5) (eq .Type 6)}}
<div class="event">
<span class="octicon octicon-bookmark"></span>
<a class="ui avatar image" href="{{.Poster.HomeLink}}">
<img src="{{.Poster.RelAvatarLink}}">
</a>
<span class="text grey"><a href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a>
{{if .Issue.IsPull}}
<a href="{{.RefCommentHTMLURL}}">{{$.i18n.Tr "repo.issues.ref_pull_at" $createdStr | Safe}}</a>
{{else}}
<a href="{{.RefCommentHTMLURL}}">{{$.i18n.Tr "repo.issues.ref_issue_at" $createdStr | Safe}}</a>
{{end}}
</span>

<div class="detail">
<span class="text grey"><a href="{{.RefIssueHTMLURL}}"><b>{{.RefIssueTitle | Str2html}}</b> {{.RefIssueIdent | Str2html}}</a></span>
</div>
</div>
{{else if eq .Type 4}}
<div class="event">
<span class="octicon octicon-bookmark"></span>
Expand Down