aboutsummaryrefslogtreecommitdiffstats
path: root/models
diff options
context:
space:
mode:
authorkolaente <konrad@kola-entertainments.de>2018-07-17 23:23:58 +0200
committertechknowlogick <techknowlogick@users.noreply.github.com>2018-07-17 17:23:58 -0400
commit1bff02de55331e11de3627d5c5628feb2cd97387 (patch)
treed6d6ace5f246c1555b294bf096763260f7d74d7b /models
parent7be5935c55dcdf198efdf1306bbeb2b54aa0b900 (diff)
downloadgitea-1bff02de55331e11de3627d5c5628feb2cd97387.tar.gz
gitea-1bff02de55331e11de3627d5c5628feb2cd97387.zip
Added dependencies for issues (#2196) (#2531)
Diffstat (limited to 'models')
-rw-r--r--models/action.go4
-rw-r--r--models/error.go85
-rw-r--r--models/issue.go44
-rw-r--r--models/issue_comment.go137
-rw-r--r--models/issue_dependency.go137
-rw-r--r--models/issue_dependency_test.go57
-rw-r--r--models/migrations/migrations.go2
-rw-r--r--models/migrations/v70.go100
-rw-r--r--models/models.go1
-rw-r--r--models/repo.go6
-rw-r--r--models/repo_unit.go2
11 files changed, 530 insertions, 45 deletions
diff --git a/models/action.go b/models/action.go
index a6fb209a41..adf30bb88b 100644
--- a/models/action.go
+++ b/models/action.go
@@ -477,6 +477,10 @@ func UpdateIssuesCommit(doer *User, repo *Repository, commits []*PushCommit) err
}
if err = issue.ChangeStatus(doer, repo, true); err != nil {
+ // Don't return an error when dependencies are open as this would let the push fail
+ if IsErrDependenciesLeft(err) {
+ return nil
+ }
return err
}
}
diff --git a/models/error.go b/models/error.go
index 0757044475..029c33aba4 100644
--- a/models/error.go
+++ b/models/error.go
@@ -1259,3 +1259,88 @@ func IsErrU2FRegistrationNotExist(err error) bool {
_, ok := err.(ErrU2FRegistrationNotExist)
return ok
}
+
+// .___ ________ .___ .__
+// | | ______ ________ __ ____ \______ \ ____ ______ ____ ____ __| _/____ ____ ____ |__| ____ ______
+// | |/ ___// ___/ | \_/ __ \ | | \_/ __ \\____ \_/ __ \ / \ / __ |/ __ \ / \_/ ___\| |/ __ \ / ___/
+// | |\___ \ \___ \| | /\ ___/ | ` \ ___/| |_> > ___/| | \/ /_/ \ ___/| | \ \___| \ ___/ \___ \
+// |___/____ >____ >____/ \___ >_______ /\___ > __/ \___ >___| /\____ |\___ >___| /\___ >__|\___ >____ >
+// \/ \/ \/ \/ \/|__| \/ \/ \/ \/ \/ \/ \/ \/
+
+// ErrDependencyExists represents a "DependencyAlreadyExists" kind of error.
+type ErrDependencyExists struct {
+ IssueID int64
+ DependencyID int64
+}
+
+// IsErrDependencyExists checks if an error is a ErrDependencyExists.
+func IsErrDependencyExists(err error) bool {
+ _, ok := err.(ErrDependencyExists)
+ return ok
+}
+
+func (err ErrDependencyExists) Error() string {
+ return fmt.Sprintf("issue dependency does already exist [issue id: %d, dependency id: %d]", err.IssueID, err.DependencyID)
+}
+
+// ErrDependencyNotExists represents a "DependencyAlreadyExists" kind of error.
+type ErrDependencyNotExists struct {
+ IssueID int64
+ DependencyID int64
+}
+
+// IsErrDependencyNotExists checks if an error is a ErrDependencyExists.
+func IsErrDependencyNotExists(err error) bool {
+ _, ok := err.(ErrDependencyNotExists)
+ return ok
+}
+
+func (err ErrDependencyNotExists) Error() string {
+ return fmt.Sprintf("issue dependency does not exist [issue id: %d, dependency id: %d]", err.IssueID, err.DependencyID)
+}
+
+// ErrCircularDependency represents a "DependencyCircular" kind of error.
+type ErrCircularDependency struct {
+ IssueID int64
+ DependencyID int64
+}
+
+// IsErrCircularDependency checks if an error is a ErrCircularDependency.
+func IsErrCircularDependency(err error) bool {
+ _, ok := err.(ErrCircularDependency)
+ return ok
+}
+
+func (err ErrCircularDependency) Error() string {
+ return fmt.Sprintf("circular dependencies exists (two issues blocking each other) [issue id: %d, dependency id: %d]", err.IssueID, err.DependencyID)
+}
+
+// ErrDependenciesLeft represents an error where the issue you're trying to close still has dependencies left.
+type ErrDependenciesLeft struct {
+ IssueID int64
+}
+
+// IsErrDependenciesLeft checks if an error is a ErrDependenciesLeft.
+func IsErrDependenciesLeft(err error) bool {
+ _, ok := err.(ErrDependenciesLeft)
+ return ok
+}
+
+func (err ErrDependenciesLeft) Error() string {
+ return fmt.Sprintf("issue has open dependencies [issue id: %d]", err.IssueID)
+}
+
+// ErrUnknownDependencyType represents an error where an unknown dependency type was passed
+type ErrUnknownDependencyType struct {
+ Type DependencyType
+}
+
+// IsErrUnknownDependencyType checks if an error is ErrUnknownDependencyType
+func IsErrUnknownDependencyType(err error) bool {
+ _, ok := err.(ErrUnknownDependencyType)
+ return ok
+}
+
+func (err ErrUnknownDependencyType) Error() string {
+ return fmt.Sprintf("unknown dependency type [type: %d]", err.Type)
+}
diff --git a/models/issue.go b/models/issue.go
index d97266b4ed..c89ffa7d0f 100644
--- a/models/issue.go
+++ b/models/issue.go
@@ -649,6 +649,20 @@ func (issue *Issue) changeStatus(e *xorm.Session, doer *User, repo *Repository,
if issue.IsClosed == isClosed {
return nil
}
+
+ // Check for open dependencies
+ if isClosed && issue.Repo.IsDependenciesEnabled() {
+ // only check if dependencies are enabled and we're about to close an issue, otherwise reopening an issue would fail when there are unsatisfied dependencies
+ noDeps, err := IssueNoDependenciesLeft(issue)
+ if err != nil {
+ return err
+ }
+
+ if !noDeps {
+ return ErrDependenciesLeft{issue.ID}
+ }
+ }
+
issue.IsClosed = isClosed
if isClosed {
issue.ClosedUnix = util.TimeStampNow()
@@ -1598,3 +1612,33 @@ func UpdateIssueDeadline(issue *Issue, deadlineUnix util.TimeStamp, doer *User)
return sess.Commit()
}
+
+// Get Blocked By Dependencies, aka all issues this issue is blocked by.
+func (issue *Issue) getBlockedByDependencies(e Engine) (issueDeps []*Issue, err error) {
+ return issueDeps, e.
+ Table("issue_dependency").
+ Select("issue.*").
+ Join("INNER", "issue", "issue.id = issue_dependency.dependency_id").
+ Where("issue_id = ?", issue.ID).
+ Find(&issueDeps)
+}
+
+// Get Blocking Dependencies, aka all issues this issue blocks.
+func (issue *Issue) getBlockingDependencies(e Engine) (issueDeps []*Issue, err error) {
+ return issueDeps, e.
+ Table("issue_dependency").
+ Select("issue.*").
+ Join("INNER", "issue", "issue.id = issue_dependency.issue_id").
+ Where("dependency_id = ?", issue.ID).
+ Find(&issueDeps)
+}
+
+// BlockedByDependencies finds all Dependencies an issue is blocked by
+func (issue *Issue) BlockedByDependencies() ([]*Issue, error) {
+ return issue.getBlockedByDependencies(x)
+}
+
+// BlockingDependencies returns all blocking dependencies, aka all other issues a given issue blocks
+func (issue *Issue) BlockingDependencies() ([]*Issue, error) {
+ return issue.getBlockingDependencies(x)
+}
diff --git a/models/issue_comment.go b/models/issue_comment.go
index 1c7c57dd06..ad276e61f9 100644
--- a/models/issue_comment.go
+++ b/models/issue_comment.go
@@ -66,6 +66,10 @@ const (
CommentTypeModifiedDeadline
// Removed a due date
CommentTypeRemovedDeadline
+ // Dependency added
+ CommentTypeAddDependency
+ //Dependency removed
+ CommentTypeRemoveDependency
)
// CommentTag defines comment tag type
@@ -81,23 +85,25 @@ const (
// Comment represents a comment in commit and issue page.
type Comment struct {
- ID int64 `xorm:"pk autoincr"`
- Type CommentType
- PosterID int64 `xorm:"INDEX"`
- Poster *User `xorm:"-"`
- IssueID int64 `xorm:"INDEX"`
- Issue *Issue `xorm:"-"`
- LabelID int64
- Label *Label `xorm:"-"`
- OldMilestoneID int64
- MilestoneID int64
- OldMilestone *Milestone `xorm:"-"`
- Milestone *Milestone `xorm:"-"`
- AssigneeID int64
- RemovedAssignee bool
- Assignee *User `xorm:"-"`
- OldTitle string
- NewTitle string
+ ID int64 `xorm:"pk autoincr"`
+ Type CommentType
+ PosterID int64 `xorm:"INDEX"`
+ Poster *User `xorm:"-"`
+ IssueID int64 `xorm:"INDEX"`
+ Issue *Issue `xorm:"-"`
+ LabelID int64
+ Label *Label `xorm:"-"`
+ OldMilestoneID int64
+ MilestoneID int64
+ OldMilestone *Milestone `xorm:"-"`
+ Milestone *Milestone `xorm:"-"`
+ AssigneeID int64
+ RemovedAssignee bool
+ Assignee *User `xorm:"-"`
+ OldTitle string
+ NewTitle string
+ DependentIssueID int64
+ DependentIssue *Issue `xorm:"-"`
CommitID int64
Line int64
@@ -281,6 +287,15 @@ func (c *Comment) LoadAssigneeUser() error {
return nil
}
+// LoadDepIssueDetails loads Dependent Issue Details
+func (c *Comment) LoadDepIssueDetails() (err error) {
+ if c.DependentIssueID <= 0 || c.DependentIssue != nil {
+ return nil
+ }
+ c.DependentIssue, err = getIssueByID(x, c.DependentIssueID)
+ return err
+}
+
// MailParticipants sends new comment emails to repository watchers
// and mentioned people.
func (c *Comment) MailParticipants(e Engine, opType ActionType, issue *Issue) (err error) {
@@ -332,22 +347,24 @@ func createComment(e *xorm.Session, opts *CreateCommentOptions) (_ *Comment, err
if opts.Label != nil {
LabelID = opts.Label.ID
}
+
comment := &Comment{
- Type: opts.Type,
- PosterID: opts.Doer.ID,
- Poster: opts.Doer,
- IssueID: opts.Issue.ID,
- LabelID: LabelID,
- OldMilestoneID: opts.OldMilestoneID,
- MilestoneID: opts.MilestoneID,
- RemovedAssignee: opts.RemovedAssignee,
- AssigneeID: opts.AssigneeID,
- CommitID: opts.CommitID,
- CommitSHA: opts.CommitSHA,
- Line: opts.LineNum,
- Content: opts.Content,
- OldTitle: opts.OldTitle,
- NewTitle: opts.NewTitle,
+ Type: opts.Type,
+ PosterID: opts.Doer.ID,
+ Poster: opts.Doer,
+ IssueID: opts.Issue.ID,
+ LabelID: LabelID,
+ OldMilestoneID: opts.OldMilestoneID,
+ MilestoneID: opts.MilestoneID,
+ RemovedAssignee: opts.RemovedAssignee,
+ AssigneeID: opts.AssigneeID,
+ CommitID: opts.CommitID,
+ CommitSHA: opts.CommitSHA,
+ Line: opts.LineNum,
+ Content: opts.Content,
+ OldTitle: opts.OldTitle,
+ NewTitle: opts.NewTitle,
+ DependentIssueID: opts.DependentIssueID,
}
if _, err = e.Insert(comment); err != nil {
return nil, err
@@ -549,6 +566,39 @@ func createDeleteBranchComment(e *xorm.Session, doer *User, repo *Repository, is
})
}
+// Creates issue dependency comment
+func createIssueDependencyComment(e *xorm.Session, doer *User, issue *Issue, dependentIssue *Issue, add bool) (err error) {
+ cType := CommentTypeAddDependency
+ if !add {
+ cType = CommentTypeRemoveDependency
+ }
+
+ // Make two comments, one in each issue
+ _, err = createComment(e, &CreateCommentOptions{
+ Type: cType,
+ Doer: doer,
+ Repo: issue.Repo,
+ Issue: issue,
+ DependentIssueID: dependentIssue.ID,
+ })
+ if err != nil {
+ return
+ }
+
+ _, err = createComment(e, &CreateCommentOptions{
+ Type: cType,
+ Doer: doer,
+ Repo: issue.Repo,
+ Issue: dependentIssue,
+ DependentIssueID: issue.ID,
+ })
+ if err != nil {
+ return
+ }
+
+ return
+}
+
// CreateCommentOptions defines options for creating comment
type CreateCommentOptions struct {
Type CommentType
@@ -557,17 +607,18 @@ type CreateCommentOptions struct {
Issue *Issue
Label *Label
- OldMilestoneID int64
- MilestoneID int64
- AssigneeID int64
- RemovedAssignee bool
- OldTitle string
- NewTitle string
- CommitID int64
- CommitSHA string
- LineNum int64
- Content string
- Attachments []string // UUIDs of attachments
+ DependentIssueID int64
+ OldMilestoneID int64
+ MilestoneID int64
+ AssigneeID int64
+ RemovedAssignee bool
+ OldTitle string
+ NewTitle string
+ CommitID int64
+ CommitSHA string
+ LineNum int64
+ Content string
+ Attachments []string // UUIDs of attachments
}
// CreateComment creates comment of issue or commit.
diff --git a/models/issue_dependency.go b/models/issue_dependency.go
new file mode 100644
index 0000000000..157e9257c4
--- /dev/null
+++ b/models/issue_dependency.go
@@ -0,0 +1,137 @@
+// Copyright 2018 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package models
+
+import (
+ "code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/util"
+)
+
+// IssueDependency represents an issue dependency
+type IssueDependency struct {
+ ID int64 `xorm:"pk autoincr"`
+ UserID int64 `xorm:"NOT NULL"`
+ IssueID int64 `xorm:"UNIQUE(issue_dependency) NOT NULL"`
+ DependencyID int64 `xorm:"UNIQUE(issue_dependency) NOT NULL"`
+ CreatedUnix util.TimeStamp `xorm:"created"`
+ UpdatedUnix util.TimeStamp `xorm:"updated"`
+}
+
+// DependencyType Defines Dependency Type Constants
+type DependencyType int
+
+// Define Dependency Types
+const (
+ DependencyTypeBlockedBy DependencyType = iota
+ DependencyTypeBlocking
+)
+
+// CreateIssueDependency creates a new dependency for an issue
+func CreateIssueDependency(user *User, issue, dep *Issue) error {
+ sess := x.NewSession()
+ defer sess.Close()
+ if err := sess.Begin(); err != nil {
+ return err
+ }
+
+ // Check if it aleready exists
+ exists, err := issueDepExists(sess, issue.ID, dep.ID)
+ if err != nil {
+ return err
+ }
+ if exists {
+ return ErrDependencyExists{issue.ID, dep.ID}
+ }
+ // And if it would be circular
+ circular, err := issueDepExists(sess, dep.ID, issue.ID)
+ if err != nil {
+ return err
+ }
+ if circular {
+ return ErrCircularDependency{issue.ID, dep.ID}
+ }
+
+ if _, err := sess.Insert(&IssueDependency{
+ UserID: user.ID,
+ IssueID: issue.ID,
+ DependencyID: dep.ID,
+ }); err != nil {
+ return err
+ }
+
+ // Add comment referencing the new dependency
+ if err = createIssueDependencyComment(sess, user, issue, dep, true); err != nil {
+ return err
+ }
+
+ return sess.Commit()
+}
+
+// RemoveIssueDependency removes a dependency from an issue
+func RemoveIssueDependency(user *User, issue *Issue, dep *Issue, depType DependencyType) (err error) {
+ sess := x.NewSession()
+ defer sess.Close()
+ if err = sess.Begin(); err != nil {
+ return err
+ }
+
+ var issueDepToDelete IssueDependency
+
+ switch depType {
+ case DependencyTypeBlockedBy:
+ issueDepToDelete = IssueDependency{IssueID: issue.ID, DependencyID: dep.ID}
+ case DependencyTypeBlocking:
+ issueDepToDelete = IssueDependency{IssueID: dep.ID, DependencyID: issue.ID}
+ default:
+ return ErrUnknownDependencyType{depType}
+ }
+
+ affected, err := sess.Delete(&issueDepToDelete)
+ if err != nil {
+ return err
+ }
+
+ // If we deleted nothing, the dependency did not exist
+ if affected <= 0 {
+ return ErrDependencyNotExists{issue.ID, dep.ID}
+ }
+
+ // Add comment referencing the removed dependency
+ if err = createIssueDependencyComment(sess, user, issue, dep, false); err != nil {
+ return err
+ }
+ return sess.Commit()
+}
+
+// Check if the dependency already exists
+func issueDepExists(e Engine, issueID int64, depID int64) (bool, error) {
+ return e.Where("(issue_id = ? AND dependency_id = ?)", issueID, depID).Exist(&IssueDependency{})
+}
+
+// IssueNoDependenciesLeft checks if issue can be closed
+func IssueNoDependenciesLeft(issue *Issue) (bool, error) {
+
+ exists, err := x.
+ Table("issue_dependency").
+ Select("issue.*").
+ Join("INNER", "issue", "issue.id = issue_dependency.dependency_id").
+ Where("issue_dependency.issue_id = ?", issue.ID).
+ And("issue.is_closed = ?", "0").
+ Exist(&Issue{})
+
+ return !exists, err
+}
+
+// IsDependenciesEnabled returns if dependecies are enabled and returns the default setting if not set.
+func (repo *Repository) IsDependenciesEnabled() bool {
+ var u *RepoUnit
+ var err error
+ if u, err = repo.GetUnit(UnitTypeIssues); err != nil {
+ log.Trace("%s", err)
+ return setting.Service.DefaultEnableDependencies
+ }
+ return u.IssuesConfig().EnableDependencies
+}
diff --git a/models/issue_dependency_test.go b/models/issue_dependency_test.go
new file mode 100644
index 0000000000..571bce3184
--- /dev/null
+++ b/models/issue_dependency_test.go
@@ -0,0 +1,57 @@
+// Copyright 2018 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package models
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestCreateIssueDependency(t *testing.T) {
+ // Prepare
+ assert.NoError(t, PrepareTestDatabase())
+
+ user1, err := GetUserByID(1)
+ assert.NoError(t, err)
+
+ issue1, err := GetIssueByID(1)
+ assert.NoError(t, err)
+ issue2, err := GetIssueByID(2)
+ assert.NoError(t, err)
+
+ // Create a dependency and check if it was successful
+ err = CreateIssueDependency(user1, issue1, issue2)
+ assert.NoError(t, err)
+
+ // Do it again to see if it will check if the dependency already exists
+ err = CreateIssueDependency(user1, issue1, issue2)
+ assert.Error(t, err)
+ assert.True(t, IsErrDependencyExists(err))
+
+ // Check for circular dependencies
+ err = CreateIssueDependency(user1, issue2, issue1)
+ assert.Error(t, err)
+ assert.True(t, IsErrCircularDependency(err))
+
+ _ = AssertExistsAndLoadBean(t, &Comment{Type: CommentTypeAddDependency, PosterID: user1.ID, IssueID: issue1.ID})
+
+ // Check if dependencies left is correct
+ left, err := IssueNoDependenciesLeft(issue1)
+ assert.NoError(t, err)
+ assert.False(t, left)
+
+ // Close #2 and check again
+ err = issue2.ChangeStatus(user1, issue2.Repo, true)
+ assert.NoError(t, err)
+
+ left, err = IssueNoDependenciesLeft(issue1)
+ assert.NoError(t, err)
+ assert.True(t, left)
+
+ // Test removing the dependency
+ err = RemoveIssueDependency(user1, issue1, issue2, DependencyTypeBlockedBy)
+ assert.NoError(t, err)
+}
diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go
index cc262d8102..48c4228fe1 100644
--- a/models/migrations/migrations.go
+++ b/models/migrations/migrations.go
@@ -192,6 +192,8 @@ var migrations = []Migration{
NewMigration("Reformat and remove incorrect topics", reformatAndRemoveIncorrectTopics),
// v69 -> v70
NewMigration("move team units to team_unit table", moveTeamUnitsToTeamUnitTable),
+ // v70 -> v71
+ NewMigration("add issue_dependencies", addIssueDependencies),
}
// Migrate database to current version
diff --git a/models/migrations/v70.go b/models/migrations/v70.go
new file mode 100644
index 0000000000..4ce1d4ee53
--- /dev/null
+++ b/models/migrations/v70.go
@@ -0,0 +1,100 @@
+// Copyright 2018 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package migrations
+
+import (
+ "fmt"
+ "time"
+
+ "code.gitea.io/gitea/modules/setting"
+
+ "github.com/go-xorm/xorm"
+)
+
+func addIssueDependencies(x *xorm.Engine) (err error) {
+
+ type IssueDependency struct {
+ ID int64 `xorm:"pk autoincr"`
+ UserID int64 `xorm:"NOT NULL"`
+ IssueID int64 `xorm:"NOT NULL"`
+ DependencyID int64 `xorm:"NOT NULL"`
+ Created time.Time `xorm:"-"`
+ CreatedUnix int64 `xorm:"created"`
+ Updated time.Time `xorm:"-"`
+ UpdatedUnix int64 `xorm:"updated"`
+ }
+
+ if err = x.Sync(new(IssueDependency)); err != nil {
+ return fmt.Errorf("Error creating issue_dependency_table column definition: %v", err)
+ }
+
+ // Update Comment definition
+ // This (copied) struct does only contain fields used by xorm as the only use here is to update the database
+
+ // CommentType defines the comment type
+ type CommentType int
+
+ // TimeStamp defines a timestamp
+ type TimeStamp int64
+
+ type Comment struct {
+ ID int64 `xorm:"pk autoincr"`
+ Type CommentType
+ PosterID int64 `xorm:"INDEX"`
+ IssueID int64 `xorm:"INDEX"`
+ LabelID int64
+ OldMilestoneID int64
+ MilestoneID int64
+ OldAssigneeID int64
+ AssigneeID int64
+ OldTitle string
+ NewTitle string
+ DependentIssueID int64
+
+ CommitID int64
+ Line int64
+ Content string `xorm:"TEXT"`
+
+ CreatedUnix TimeStamp `xorm:"INDEX created"`
+ UpdatedUnix TimeStamp `xorm:"INDEX updated"`
+
+ // Reference issue in commit message
+ CommitSHA string `xorm:"VARCHAR(40)"`
+ }
+
+ if err = x.Sync(new(Comment)); err != nil {
+ return fmt.Errorf("Error updating issue_comment table column definition: %v", err)
+ }
+
+ // RepoUnit describes all units of a repository
+ type RepoUnit struct {
+ ID int64
+ RepoID int64 `xorm:"INDEX(s)"`
+ Type int `xorm:"INDEX(s)"`
+ Config map[string]interface{} `xorm:"JSON"`
+ CreatedUnix int64 `xorm:"INDEX CREATED"`
+ Created time.Time `xorm:"-"`
+ }
+
+ //Updating existing issue units
+ units := make([]*RepoUnit, 0, 100)
+ err = x.Where("`type` = ?", V16UnitTypeIssues).Find(&units)
+ if err != nil {
+ return fmt.Errorf("Query repo units: %v", err)
+ }
+ for _, unit := range units {
+ if unit.Config == nil {
+ unit.Config = make(map[string]interface{})
+ }
+ if _, ok := unit.Config["EnableDependencies"]; !ok {
+ unit.Config["EnableDependencies"] = setting.Service.DefaultEnableDependencies
+ }
+ if _, err := x.ID(unit.ID).Cols("config").Update(unit); err != nil {
+ return err
+ }
+ }
+
+ return err
+}
diff --git a/models/models.go b/models/models.go
index aaf1370fd4..9477b6950a 100644
--- a/models/models.go
+++ b/models/models.go
@@ -118,6 +118,7 @@ func init() {
new(TrackedTime),
new(DeletedBranch),
new(RepoIndexerStatus),
+ new(IssueDependency),
new(LFSLock),
new(Reaction),
new(IssueAssignees),
diff --git a/models/repo.go b/models/repo.go
index c795deee8d..e87883969d 100644
--- a/models/repo.go
+++ b/models/repo.go
@@ -1345,7 +1345,11 @@ func createRepository(e *xorm.Session, doer, u *User, repo *Repository) (err err
units = append(units, RepoUnit{
RepoID: repo.ID,
Type: tp,
- Config: &IssuesConfig{EnableTimetracker: setting.Service.DefaultEnableTimetracking, AllowOnlyContributorsToTrackTime: setting.Service.DefaultAllowOnlyContributorsToTrackTime},
+ Config: &IssuesConfig{
+ EnableTimetracker: setting.Service.DefaultEnableTimetracking,
+ AllowOnlyContributorsToTrackTime: setting.Service.DefaultAllowOnlyContributorsToTrackTime,
+ EnableDependencies: setting.Service.DefaultEnableDependencies,
+ },
})
} else if tp == UnitTypePullRequests {
units = append(units, RepoUnit{
diff --git a/models/repo_unit.go b/models/repo_unit.go
index 49b62ec9cd..1e1778356a 100644
--- a/models/repo_unit.go
+++ b/models/repo_unit.go
@@ -73,6 +73,7 @@ func (cfg *ExternalTrackerConfig) ToDB() ([]byte, error) {
type IssuesConfig struct {
EnableTimetracker bool
AllowOnlyContributorsToTrackTime bool
+ EnableDependencies bool
}
// FromDB fills up a IssuesConfig from serialized format.
@@ -165,7 +166,6 @@ func (r *RepoUnit) IssuesConfig() *IssuesConfig {
func (r *RepoUnit) ExternalTrackerConfig() *ExternalTrackerConfig {
return r.Config.(*ExternalTrackerConfig)
}
-
func getUnitsByRepoID(e Engine, repoID int64) (units []*RepoUnit, err error) {
return units, e.Where("repo_id = ?", repoID).Find(&units)
}