mirror of
https://github.com/go-gitea/gitea
synced 2025-12-07 13:28:25 +00:00
Merge branch 'main' into allow-force-push-protected-branches
This commit is contained in:
@@ -46,10 +46,13 @@ type ActionRun struct {
|
||||
TriggerEvent string // the trigger event defined in the `on` configuration of the triggered workflow
|
||||
Status Status `xorm:"index"`
|
||||
Version int `xorm:"version default 0"` // Status could be updated concomitantly, so an optimistic lock is needed
|
||||
Started timeutil.TimeStamp
|
||||
Stopped timeutil.TimeStamp
|
||||
Created timeutil.TimeStamp `xorm:"created"`
|
||||
Updated timeutil.TimeStamp `xorm:"updated"`
|
||||
// Started and Stopped is used for recording last run time, if rerun happened, they will be reset to 0
|
||||
Started timeutil.TimeStamp
|
||||
Stopped timeutil.TimeStamp
|
||||
// PreviousDuration is used for recording previous duration
|
||||
PreviousDuration time.Duration
|
||||
Created timeutil.TimeStamp `xorm:"created"`
|
||||
Updated timeutil.TimeStamp `xorm:"updated"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
@@ -118,7 +121,7 @@ func (run *ActionRun) LoadAttributes(ctx context.Context) error {
|
||||
}
|
||||
|
||||
func (run *ActionRun) Duration() time.Duration {
|
||||
return calculateDuration(run.Started, run.Stopped, run.Status)
|
||||
return calculateDuration(run.Started, run.Stopped, run.Status) + run.PreviousDuration
|
||||
}
|
||||
|
||||
func (run *ActionRun) GetPushEventPayload() (*api.PushPayload, error) {
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/gitrepo"
|
||||
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
@@ -65,7 +66,7 @@ func GetActivityStats(ctx context.Context, repo *repo_model.Repository, timeFrom
|
||||
return nil, fmt.Errorf("FillUnresolvedIssues: %w", err)
|
||||
}
|
||||
if code {
|
||||
gitRepo, closer, err := git.RepositoryFromContextOrOpen(ctx, repo.RepoPath())
|
||||
gitRepo, closer, err := gitrepo.RepositoryFromContextOrOpen(ctx, repo)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("OpenRepository: %w", err)
|
||||
}
|
||||
@@ -82,7 +83,7 @@ func GetActivityStats(ctx context.Context, repo *repo_model.Repository, timeFrom
|
||||
|
||||
// GetActivityStatsTopAuthors returns top author stats for git commits for all branches
|
||||
func GetActivityStatsTopAuthors(ctx context.Context, repo *repo_model.Repository, timeFrom time.Time, count int) ([]*ActivityAuthorData, error) {
|
||||
gitRepo, closer, err := git.RepositoryFromContextOrOpen(ctx, repo.RepoPath())
|
||||
gitRepo, closer, err := gitrepo.RepositoryFromContextOrOpen(ctx, repo)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("OpenRepository: %w", err)
|
||||
}
|
||||
|
||||
+21
-11
@@ -133,17 +133,21 @@ type FindOptionsOrder interface {
|
||||
|
||||
// Find represents a common find function which accept an options interface
|
||||
func Find[T any](ctx context.Context, opts FindOptions) ([]*T, error) {
|
||||
sess := GetEngine(ctx)
|
||||
sess := GetEngine(ctx).Where(opts.ToConds())
|
||||
|
||||
if joinOpt, ok := opts.(FindOptionsJoin); ok && len(joinOpt.ToJoins()) > 0 {
|
||||
if joinOpt, ok := opts.(FindOptionsJoin); ok {
|
||||
for _, joinFunc := range joinOpt.ToJoins() {
|
||||
if err := joinFunc(sess); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
if orderOpt, ok := opts.(FindOptionsOrder); ok {
|
||||
if order := orderOpt.ToOrders(); order != "" {
|
||||
sess.OrderBy(order)
|
||||
}
|
||||
}
|
||||
|
||||
sess = sess.Where(opts.ToConds())
|
||||
page, pageSize := opts.GetPage(), opts.GetPageSize()
|
||||
if !opts.IsListAll() && pageSize > 0 {
|
||||
if page == 0 {
|
||||
@@ -151,9 +155,6 @@ func Find[T any](ctx context.Context, opts FindOptions) ([]*T, error) {
|
||||
}
|
||||
sess.Limit(pageSize, (page-1)*pageSize)
|
||||
}
|
||||
if newOpt, ok := opts.(FindOptionsOrder); ok && newOpt.ToOrders() != "" {
|
||||
sess.OrderBy(newOpt.ToOrders())
|
||||
}
|
||||
|
||||
findPageSize := defaultFindSliceSize
|
||||
if pageSize > 0 {
|
||||
@@ -168,8 +169,8 @@ func Find[T any](ctx context.Context, opts FindOptions) ([]*T, error) {
|
||||
|
||||
// Count represents a common count function which accept an options interface
|
||||
func Count[T any](ctx context.Context, opts FindOptions) (int64, error) {
|
||||
sess := GetEngine(ctx)
|
||||
if joinOpt, ok := opts.(FindOptionsJoin); ok && len(joinOpt.ToJoins()) > 0 {
|
||||
sess := GetEngine(ctx).Where(opts.ToConds())
|
||||
if joinOpt, ok := opts.(FindOptionsJoin); ok {
|
||||
for _, joinFunc := range joinOpt.ToJoins() {
|
||||
if err := joinFunc(sess); err != nil {
|
||||
return 0, err
|
||||
@@ -178,7 +179,7 @@ func Count[T any](ctx context.Context, opts FindOptions) (int64, error) {
|
||||
}
|
||||
|
||||
var object T
|
||||
return sess.Where(opts.ToConds()).Count(&object)
|
||||
return sess.Count(&object)
|
||||
}
|
||||
|
||||
// FindAndCount represents a common findandcount function which accept an options interface
|
||||
@@ -188,8 +189,17 @@ func FindAndCount[T any](ctx context.Context, opts FindOptions) ([]*T, int64, er
|
||||
if !opts.IsListAll() && pageSize > 0 && page >= 1 {
|
||||
sess.Limit(pageSize, (page-1)*pageSize)
|
||||
}
|
||||
if newOpt, ok := opts.(FindOptionsOrder); ok && newOpt.ToOrders() != "" {
|
||||
sess.OrderBy(newOpt.ToOrders())
|
||||
if joinOpt, ok := opts.(FindOptionsJoin); ok {
|
||||
for _, joinFunc := range joinOpt.ToJoins() {
|
||||
if err := joinFunc(sess); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
}
|
||||
}
|
||||
if orderOpt, ok := opts.(FindOptionsOrder); ok {
|
||||
if order := orderOpt.ToOrders(); order != "" {
|
||||
sess.OrderBy(order)
|
||||
}
|
||||
}
|
||||
|
||||
findPageSize := defaultFindSliceSize
|
||||
|
||||
@@ -669,3 +669,10 @@
|
||||
type: 10
|
||||
config: "{}"
|
||||
created_unix: 946684810
|
||||
|
||||
-
|
||||
id: 101
|
||||
repo_id: 59
|
||||
type: 1
|
||||
config: "{}"
|
||||
created_unix: 946684810
|
||||
|
||||
@@ -1693,3 +1693,16 @@
|
||||
size: 0
|
||||
is_fsck_enabled: true
|
||||
close_issues_via_commit_in_any_branch: false
|
||||
|
||||
-
|
||||
id: 59
|
||||
owner_id: 2
|
||||
owner_name: user2
|
||||
lower_name: test_commit_revert
|
||||
name: test_commit_revert
|
||||
default_branch: main
|
||||
is_empty: false
|
||||
is_archived: false
|
||||
is_private: true
|
||||
status: 0
|
||||
num_issues: 0
|
||||
|
||||
@@ -66,7 +66,7 @@
|
||||
num_followers: 2
|
||||
num_following: 1
|
||||
num_stars: 2
|
||||
num_repos: 14
|
||||
num_repos: 15
|
||||
num_teams: 0
|
||||
num_members: 0
|
||||
visibility: 0
|
||||
|
||||
+20
-36
@@ -25,7 +25,6 @@ import (
|
||||
"code.gitea.io/gitea/modules/translation"
|
||||
|
||||
"xorm.io/builder"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
// CommitStatus holds a single Status of a single Commit
|
||||
@@ -38,7 +37,7 @@ type CommitStatus struct {
|
||||
SHA string `xorm:"VARCHAR(64) NOT NULL INDEX UNIQUE(repo_sha_index)"`
|
||||
TargetURL string `xorm:"TEXT"`
|
||||
Description string `xorm:"TEXT"`
|
||||
ContextHash string `xorm:"char(40) index"`
|
||||
ContextHash string `xorm:"VARCHAR(64) index"`
|
||||
Context string `xorm:"TEXT"`
|
||||
Creator *user_model.User `xorm:"-"`
|
||||
CreatorID int64
|
||||
@@ -221,57 +220,42 @@ func CalcCommitStatus(statuses []*CommitStatus) *CommitStatus {
|
||||
// CommitStatusOptions holds the options for query commit statuses
|
||||
type CommitStatusOptions struct {
|
||||
db.ListOptions
|
||||
RepoID int64
|
||||
SHA string
|
||||
State string
|
||||
SortType string
|
||||
}
|
||||
|
||||
// GetCommitStatuses returns all statuses for a given commit.
|
||||
func GetCommitStatuses(ctx context.Context, repo *repo_model.Repository, sha string, opts *CommitStatusOptions) ([]*CommitStatus, int64, error) {
|
||||
if opts.Page <= 0 {
|
||||
opts.Page = 1
|
||||
}
|
||||
if opts.PageSize <= 0 {
|
||||
opts.Page = setting.ItemsPerPage
|
||||
func (opts *CommitStatusOptions) ToConds() builder.Cond {
|
||||
var cond builder.Cond = builder.Eq{
|
||||
"repo_id": opts.RepoID,
|
||||
"sha": opts.SHA,
|
||||
}
|
||||
|
||||
countSession := listCommitStatusesStatement(ctx, repo, sha, opts)
|
||||
countSession = db.SetSessionPagination(countSession, opts)
|
||||
maxResults, err := countSession.Count(new(CommitStatus))
|
||||
if err != nil {
|
||||
log.Error("Count PRs: %v", err)
|
||||
return nil, maxResults, err
|
||||
}
|
||||
|
||||
statuses := make([]*CommitStatus, 0, opts.PageSize)
|
||||
findSession := listCommitStatusesStatement(ctx, repo, sha, opts)
|
||||
findSession = db.SetSessionPagination(findSession, opts)
|
||||
sortCommitStatusesSession(findSession, opts.SortType)
|
||||
return statuses, maxResults, findSession.Find(&statuses)
|
||||
}
|
||||
|
||||
func listCommitStatusesStatement(ctx context.Context, repo *repo_model.Repository, sha string, opts *CommitStatusOptions) *xorm.Session {
|
||||
sess := db.GetEngine(ctx).Where("repo_id = ?", repo.ID).And("sha = ?", sha)
|
||||
switch opts.State {
|
||||
case "pending", "success", "error", "failure", "warning":
|
||||
sess.And("state = ?", opts.State)
|
||||
cond = cond.And(builder.Eq{
|
||||
"state": opts.State,
|
||||
})
|
||||
}
|
||||
return sess
|
||||
|
||||
return cond
|
||||
}
|
||||
|
||||
func sortCommitStatusesSession(sess *xorm.Session, sortType string) {
|
||||
switch sortType {
|
||||
func (opts *CommitStatusOptions) ToOrders() string {
|
||||
switch opts.SortType {
|
||||
case "oldest":
|
||||
sess.Asc("created_unix")
|
||||
return "created_unix ASC"
|
||||
case "recentupdate":
|
||||
sess.Desc("updated_unix")
|
||||
return "updated_unix DESC"
|
||||
case "leastupdate":
|
||||
sess.Asc("updated_unix")
|
||||
return "updated_unix ASC"
|
||||
case "leastindex":
|
||||
sess.Desc("index")
|
||||
return "`index` DESC"
|
||||
case "highestindex":
|
||||
sess.Asc("index")
|
||||
return "`index` ASC"
|
||||
default:
|
||||
sess.Desc("created_unix")
|
||||
return "created_unix DESC"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,11 @@ func TestGetCommitStatuses(t *testing.T) {
|
||||
|
||||
sha1 := "1234123412341234123412341234123412341234"
|
||||
|
||||
statuses, maxResults, err := git_model.GetCommitStatuses(db.DefaultContext, repo1, sha1, &git_model.CommitStatusOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 50}})
|
||||
statuses, maxResults, err := db.FindAndCount[git_model.CommitStatus](db.DefaultContext, &git_model.CommitStatusOptions{
|
||||
ListOptions: db.ListOptions{Page: 1, PageSize: 50},
|
||||
RepoID: repo1.ID,
|
||||
SHA: sha1,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, int(maxResults), 5)
|
||||
assert.Len(t, statuses, 5)
|
||||
@@ -46,4 +50,128 @@ func TestGetCommitStatuses(t *testing.T) {
|
||||
assert.Equal(t, "deploy/awesomeness", statuses[4].Context)
|
||||
assert.Equal(t, structs.CommitStatusError, statuses[4].State)
|
||||
assert.Equal(t, "https://try.gitea.io/api/v1/repos/user2/repo1/statuses/1234123412341234123412341234123412341234", statuses[4].APIURL(db.DefaultContext))
|
||||
|
||||
statuses, maxResults, err = db.FindAndCount[git_model.CommitStatus](db.DefaultContext, &git_model.CommitStatusOptions{
|
||||
ListOptions: db.ListOptions{Page: 2, PageSize: 50},
|
||||
RepoID: repo1.ID,
|
||||
SHA: sha1,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, int(maxResults), 5)
|
||||
assert.Empty(t, statuses)
|
||||
}
|
||||
|
||||
func Test_CalcCommitStatus(t *testing.T) {
|
||||
kases := []struct {
|
||||
statuses []*git_model.CommitStatus
|
||||
expected *git_model.CommitStatus
|
||||
}{
|
||||
{
|
||||
statuses: []*git_model.CommitStatus{
|
||||
{
|
||||
State: structs.CommitStatusPending,
|
||||
},
|
||||
},
|
||||
expected: &git_model.CommitStatus{
|
||||
State: structs.CommitStatusPending,
|
||||
},
|
||||
},
|
||||
{
|
||||
statuses: []*git_model.CommitStatus{
|
||||
{
|
||||
State: structs.CommitStatusSuccess,
|
||||
},
|
||||
{
|
||||
State: structs.CommitStatusPending,
|
||||
},
|
||||
},
|
||||
expected: &git_model.CommitStatus{
|
||||
State: structs.CommitStatusPending,
|
||||
},
|
||||
},
|
||||
{
|
||||
statuses: []*git_model.CommitStatus{
|
||||
{
|
||||
State: structs.CommitStatusSuccess,
|
||||
},
|
||||
{
|
||||
State: structs.CommitStatusPending,
|
||||
},
|
||||
{
|
||||
State: structs.CommitStatusSuccess,
|
||||
},
|
||||
},
|
||||
expected: &git_model.CommitStatus{
|
||||
State: structs.CommitStatusPending,
|
||||
},
|
||||
},
|
||||
{
|
||||
statuses: []*git_model.CommitStatus{
|
||||
{
|
||||
State: structs.CommitStatusError,
|
||||
},
|
||||
{
|
||||
State: structs.CommitStatusPending,
|
||||
},
|
||||
{
|
||||
State: structs.CommitStatusSuccess,
|
||||
},
|
||||
},
|
||||
expected: &git_model.CommitStatus{
|
||||
State: structs.CommitStatusError,
|
||||
},
|
||||
},
|
||||
{
|
||||
statuses: []*git_model.CommitStatus{
|
||||
{
|
||||
State: structs.CommitStatusWarning,
|
||||
},
|
||||
{
|
||||
State: structs.CommitStatusPending,
|
||||
},
|
||||
{
|
||||
State: structs.CommitStatusSuccess,
|
||||
},
|
||||
},
|
||||
expected: &git_model.CommitStatus{
|
||||
State: structs.CommitStatusWarning,
|
||||
},
|
||||
},
|
||||
{
|
||||
statuses: []*git_model.CommitStatus{
|
||||
{
|
||||
State: structs.CommitStatusSuccess,
|
||||
},
|
||||
{
|
||||
State: structs.CommitStatusSuccess,
|
||||
},
|
||||
{
|
||||
State: structs.CommitStatusSuccess,
|
||||
},
|
||||
},
|
||||
expected: &git_model.CommitStatus{
|
||||
State: structs.CommitStatusSuccess,
|
||||
},
|
||||
},
|
||||
{
|
||||
statuses: []*git_model.CommitStatus{
|
||||
{
|
||||
State: structs.CommitStatusFailure,
|
||||
},
|
||||
{
|
||||
State: structs.CommitStatusError,
|
||||
},
|
||||
{
|
||||
State: structs.CommitStatusWarning,
|
||||
},
|
||||
},
|
||||
expected: &git_model.CommitStatus{
|
||||
State: structs.CommitStatusError,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, kase := range kases {
|
||||
assert.Equal(t, kase.expected, git_model.CalcCommitStatus(kase.statuses))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ import (
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/container"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/gitrepo"
|
||||
"code.gitea.io/gitea/modules/json"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/references"
|
||||
@@ -270,7 +270,7 @@ type Comment struct {
|
||||
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
|
||||
|
||||
// Reference issue in commit message
|
||||
CommitSHA string `xorm:"VARCHAR(40)"`
|
||||
CommitSHA string `xorm:"VARCHAR(64)"`
|
||||
|
||||
Attachments []*repo_model.Attachment `xorm:"-"`
|
||||
Reactions ReactionList `xorm:"-"`
|
||||
@@ -762,8 +762,7 @@ func (c *Comment) LoadPushCommits(ctx context.Context) (err error) {
|
||||
c.OldCommit = data.CommitIDs[0]
|
||||
c.NewCommit = data.CommitIDs[1]
|
||||
} else {
|
||||
repoPath := c.Issue.Repo.RepoPath()
|
||||
gitRepo, closer, err := git.RepositoryFromContextOrOpen(ctx, repoPath)
|
||||
gitRepo, closer, err := gitrepo.RepositoryFromContextOrOpen(ctx, c.Issue.Repo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -109,9 +109,11 @@ func findCodeComments(ctx context.Context, opts FindCommentsOptions, issue *Issu
|
||||
|
||||
var err error
|
||||
if comment.RenderedContent, err = markdown.RenderString(&markup.RenderContext{
|
||||
Ctx: ctx,
|
||||
URLPrefix: issue.Repo.Link(),
|
||||
Metas: issue.Repo.ComposeMetas(ctx),
|
||||
Ctx: ctx,
|
||||
Links: markup.Links{
|
||||
Base: issue.Repo.Link(),
|
||||
},
|
||||
Metas: issue.Repo.ComposeMetas(ctx),
|
||||
}, comment.Content); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/gitrepo"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
@@ -171,11 +172,11 @@ type PullRequest struct {
|
||||
HeadBranch string
|
||||
HeadCommitID string `xorm:"-"`
|
||||
BaseBranch string
|
||||
MergeBase string `xorm:"VARCHAR(40)"`
|
||||
MergeBase string `xorm:"VARCHAR(64)"`
|
||||
AllowMaintainerEdit bool `xorm:"NOT NULL DEFAULT false"`
|
||||
|
||||
HasMerged bool `xorm:"INDEX"`
|
||||
MergedCommitID string `xorm:"VARCHAR(40)"`
|
||||
MergedCommitID string `xorm:"VARCHAR(64)"`
|
||||
MergerID int64 `xorm:"INDEX"`
|
||||
Merger *user_model.User `xorm:"-"`
|
||||
MergedUnix timeutil.TimeStamp `xorm:"updated INDEX"`
|
||||
@@ -865,7 +866,7 @@ func PullRequestCodeOwnersReview(ctx context.Context, pull *Issue, pr *PullReque
|
||||
return err
|
||||
}
|
||||
|
||||
repo, err := git.OpenRepository(ctx, pr.BaseRepo.RepoPath())
|
||||
repo, err := gitrepo.OpenRepository(ctx, pr.BaseRepo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -116,7 +116,7 @@ type Review struct {
|
||||
Content string `xorm:"TEXT"`
|
||||
// Official is a review made by an assigned approver (counts towards approval)
|
||||
Official bool `xorm:"NOT NULL DEFAULT false"`
|
||||
CommitID string `xorm:"VARCHAR(40)"`
|
||||
CommitID string `xorm:"VARCHAR(64)"`
|
||||
Stale bool `xorm:"NOT NULL DEFAULT false"`
|
||||
Dismissed bool `xorm:"NOT NULL DEFAULT false"`
|
||||
|
||||
|
||||
@@ -340,7 +340,7 @@ func GetTrackedTimeByID(ctx context.Context, id int64) (*TrackedTime, error) {
|
||||
}
|
||||
|
||||
// GetIssueTotalTrackedTime returns the total tracked time for issues by given conditions.
|
||||
func GetIssueTotalTrackedTime(ctx context.Context, opts *IssuesOptions, isClosed bool) (int64, error) {
|
||||
func GetIssueTotalTrackedTime(ctx context.Context, opts *IssuesOptions, isClosed util.OptionalBool) (int64, error) {
|
||||
if len(opts.IssueIDs) <= MaxQueryParameters {
|
||||
return getIssueTotalTrackedTimeChunk(ctx, opts, isClosed, opts.IssueIDs)
|
||||
}
|
||||
@@ -363,7 +363,7 @@ func GetIssueTotalTrackedTime(ctx context.Context, opts *IssuesOptions, isClosed
|
||||
return accum, nil
|
||||
}
|
||||
|
||||
func getIssueTotalTrackedTimeChunk(ctx context.Context, opts *IssuesOptions, isClosed bool, issueIDs []int64) (int64, error) {
|
||||
func getIssueTotalTrackedTimeChunk(ctx context.Context, opts *IssuesOptions, isClosed util.OptionalBool, issueIDs []int64) (int64, error) {
|
||||
sumSession := func(opts *IssuesOptions, issueIDs []int64) *xorm.Session {
|
||||
sess := db.GetEngine(ctx).
|
||||
Table("tracked_time").
|
||||
@@ -377,7 +377,9 @@ func getIssueTotalTrackedTimeChunk(ctx context.Context, opts *IssuesOptions, isC
|
||||
Time int64
|
||||
}
|
||||
|
||||
return sumSession(opts, issueIDs).
|
||||
And("issue.is_closed = ?", isClosed).
|
||||
SumInt(new(trackedTime), "tracked_time.time")
|
||||
session := sumSession(opts, issueIDs)
|
||||
if !isClosed.IsNone() {
|
||||
session = session.And("issue.is_closed = ?", isClosed.IsTrue())
|
||||
}
|
||||
return session.SumInt(new(trackedTime), "tracked_time.time")
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
issues_model "code.gitea.io/gitea/models/issues"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
@@ -119,11 +120,15 @@ func TestTotalTimesForEachUser(t *testing.T) {
|
||||
func TestGetIssueTotalTrackedTime(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
ttt, err := issues_model.GetIssueTotalTrackedTime(db.DefaultContext, &issues_model.IssuesOptions{MilestoneIDs: []int64{1}}, false)
|
||||
ttt, err := issues_model.GetIssueTotalTrackedTime(db.DefaultContext, &issues_model.IssuesOptions{MilestoneIDs: []int64{1}}, util.OptionalBoolFalse)
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 3682, ttt)
|
||||
|
||||
ttt, err = issues_model.GetIssueTotalTrackedTime(db.DefaultContext, &issues_model.IssuesOptions{MilestoneIDs: []int64{1}}, true)
|
||||
ttt, err = issues_model.GetIssueTotalTrackedTime(db.DefaultContext, &issues_model.IssuesOptions{MilestoneIDs: []int64{1}}, util.OptionalBoolTrue)
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 0, ttt)
|
||||
|
||||
ttt, err = issues_model.GetIssueTotalTrackedTime(db.DefaultContext, &issues_model.IssuesOptions{MilestoneIDs: []int64{1}}, util.OptionalBoolNone)
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 3682, ttt)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
# type Repository struct {
|
||||
# ID int64 `xorm:"pk autoincr"`
|
||||
# }
|
||||
-
|
||||
id: 1
|
||||
-
|
||||
id: 2
|
||||
-
|
||||
id: 3
|
||||
-
|
||||
id: 10
|
||||
@@ -552,6 +552,12 @@ var migrations = []Migration{
|
||||
NewMigration("Add Index to pull_auto_merge.doer_id", v1_22.AddIndexToPullAutoMergeDoerID),
|
||||
// v283 -> v284
|
||||
NewMigration("Add combined Index to issue_user.uid and issue_id", v1_22.AddCombinedIndexToIssueUser),
|
||||
// v284 -> v285
|
||||
NewMigration("Add ignore stale approval column on branch table", v1_22.AddIgnoreStaleApprovalsColumnToProtectedBranchTable),
|
||||
// v285 -> v286
|
||||
NewMigration("Add PreviousDuration to ActionRun", v1_22.AddPreviousDurationToActionRun),
|
||||
// v286 -> v287
|
||||
NewMigration("Add support for SHA256 git repositories", v1_22.AdjustDBForSha256),
|
||||
}
|
||||
|
||||
// GetCurrentDBVersion returns the current db version
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_22 //nolint
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func AddPreviousDurationToActionRun(x *xorm.Engine) error {
|
||||
type ActionRun struct {
|
||||
PreviousDuration time.Duration
|
||||
}
|
||||
|
||||
return x.Sync(&ActionRun{})
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
package v1_22 //nolint
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func expandHashReferencesToSha256(x *xorm.Engine) error {
|
||||
alteredTables := [][2]string{
|
||||
{"commit_status", "context_hash"},
|
||||
{"comment", "commit_sha"},
|
||||
{"pull_request", "merge_base"},
|
||||
{"pull_request", "merged_commit_id"},
|
||||
{"review", "commit_id"},
|
||||
{"review_state", "commit_sha"},
|
||||
{"repo_archiver", "commit_id"},
|
||||
{"release", "sha1"},
|
||||
{"repo_indexer_status", "commit_sha"},
|
||||
}
|
||||
|
||||
db := x.NewSession()
|
||||
defer db.Close()
|
||||
|
||||
if err := db.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !setting.Database.Type.IsSQLite3() {
|
||||
if setting.Database.Type.IsMSSQL() {
|
||||
// drop indexes that need to be re-created afterwards
|
||||
droppedIndexes := []string{
|
||||
"DROP INDEX commit_status.IDX_commit_status_context_hash",
|
||||
"DROP INDEX review_state.UQE_review_state_pull_commit_user",
|
||||
"DROP INDEX repo_archiver.UQE_repo_archiver_s",
|
||||
}
|
||||
for _, s := range droppedIndexes {
|
||||
_, err := db.Exec(s)
|
||||
if err != nil {
|
||||
return errors.New(s + " " + err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, alts := range alteredTables {
|
||||
var err error
|
||||
if setting.Database.Type.IsMySQL() {
|
||||
_, err = db.Exec(fmt.Sprintf("ALTER TABLE `%s` MODIFY COLUMN `%s` VARCHAR(64)", alts[0], alts[1]))
|
||||
} else if setting.Database.Type.IsMSSQL() {
|
||||
_, err = db.Exec(fmt.Sprintf("ALTER TABLE `%s` ALTER COLUMN `%s` VARCHAR(64)", alts[0], alts[1]))
|
||||
} else {
|
||||
_, err = db.Exec(fmt.Sprintf("ALTER TABLE `%s` ALTER COLUMN `%s` TYPE VARCHAR(64)", alts[0], alts[1]))
|
||||
}
|
||||
if err != nil {
|
||||
return fmt.Errorf("alter column '%s' of table '%s' failed: %w", alts[1], alts[0], err)
|
||||
}
|
||||
}
|
||||
|
||||
if setting.Database.Type.IsMSSQL() {
|
||||
recreateIndexes := []string{
|
||||
"CREATE INDEX IDX_commit_status_context_hash ON commit_status(context_hash)",
|
||||
"CREATE UNIQUE INDEX UQE_review_state_pull_commit_user ON review_state(user_id, pull_id, commit_sha)",
|
||||
"CREATE UNIQUE INDEX UQE_repo_archiver_s ON repo_archiver(repo_id, type, commit_id)",
|
||||
}
|
||||
for _, s := range recreateIndexes {
|
||||
_, err := db.Exec(s)
|
||||
if err != nil {
|
||||
return errors.New(s + " " + err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
log.Debug("Updated database tables to hold SHA256 git hash references")
|
||||
|
||||
return db.Commit()
|
||||
}
|
||||
|
||||
func addObjectFormatNameToRepository(x *xorm.Engine) error {
|
||||
type Repository struct {
|
||||
ObjectFormatName string `xorm:"VARCHAR(6) NOT NULL DEFAULT 'sha1'"`
|
||||
}
|
||||
|
||||
if err := x.Sync(new(Repository)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Here to catch weird edge-cases where column constraints above are
|
||||
// not applied by the DB backend
|
||||
_, err := x.Exec("UPDATE repository set object_format_name = 'sha1' WHERE object_format_name = '' or object_format_name IS NULL")
|
||||
return err
|
||||
}
|
||||
|
||||
func AdjustDBForSha256(x *xorm.Engine) error {
|
||||
if err := expandHashReferencesToSha256(x); err != nil {
|
||||
return err
|
||||
}
|
||||
return addObjectFormatNameToRepository(x)
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_22 //nolint
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models/migrations/base"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func PrepareOldRepository(t *testing.T) (*xorm.Engine, func()) {
|
||||
type Repository struct { // old struct
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
}
|
||||
|
||||
// Prepare and load the testing database
|
||||
return base.PrepareTestEnv(t, 0, new(Repository))
|
||||
}
|
||||
|
||||
func Test_RepositoryFormat(t *testing.T) {
|
||||
x, deferable := PrepareOldRepository(t)
|
||||
defer deferable()
|
||||
|
||||
type Repository struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
ObjectFormatName string `xorg:"not null default('sha1')"`
|
||||
}
|
||||
|
||||
repo := new(Repository)
|
||||
|
||||
// check we have some records to migrate
|
||||
count, err := x.Count(new(Repository))
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 4, count)
|
||||
|
||||
assert.NoError(t, AdjustDBForSha256(x))
|
||||
|
||||
repo.ID = 20
|
||||
repo.ObjectFormatName = "sha256"
|
||||
_, err = x.Insert(repo)
|
||||
assert.NoError(t, err)
|
||||
|
||||
count, err = x.Count(new(Repository))
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 5, count)
|
||||
|
||||
repo = new(Repository)
|
||||
ok, err := x.ID(2).Get(repo)
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, true, ok)
|
||||
assert.EqualValues(t, "sha1", repo.ObjectFormatName)
|
||||
|
||||
repo = new(Repository)
|
||||
ok, err = x.ID(20).Get(repo)
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, true, ok)
|
||||
assert.EqualValues(t, "sha256", repo.ObjectFormatName)
|
||||
}
|
||||
@@ -191,18 +191,18 @@ type Package struct {
|
||||
func TryInsertPackage(ctx context.Context, p *Package) (*Package, error) {
|
||||
e := db.GetEngine(ctx)
|
||||
|
||||
key := &Package{
|
||||
OwnerID: p.OwnerID,
|
||||
Type: p.Type,
|
||||
LowerName: p.LowerName,
|
||||
}
|
||||
existing := &Package{}
|
||||
|
||||
has, err := e.Get(key)
|
||||
has, err := e.Where(builder.Eq{
|
||||
"owner_id": p.OwnerID,
|
||||
"type": p.Type,
|
||||
"lower_name": p.LowerName,
|
||||
}).Get(existing)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if has {
|
||||
return key, ErrDuplicatePackage
|
||||
return existing, ErrDuplicatePackage
|
||||
}
|
||||
if _, err = e.Insert(p); err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -41,12 +41,20 @@ type PackageBlob struct {
|
||||
func GetOrInsertBlob(ctx context.Context, pb *PackageBlob) (*PackageBlob, bool, error) {
|
||||
e := db.GetEngine(ctx)
|
||||
|
||||
has, err := e.Get(pb)
|
||||
existing := &PackageBlob{}
|
||||
|
||||
has, err := e.Where(builder.Eq{
|
||||
"size": pb.Size,
|
||||
"hash_md5": pb.HashMD5,
|
||||
"hash_sha1": pb.HashSHA1,
|
||||
"hash_sha256": pb.HashSHA256,
|
||||
"hash_sha512": pb.HashSHA512,
|
||||
}).Get(existing)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
if has {
|
||||
return pb, true, nil
|
||||
return existing, true, nil
|
||||
}
|
||||
if _, err = e.Insert(pb); err != nil {
|
||||
return nil, false, err
|
||||
|
||||
@@ -46,18 +46,18 @@ type PackageFile struct {
|
||||
func TryInsertFile(ctx context.Context, pf *PackageFile) (*PackageFile, error) {
|
||||
e := db.GetEngine(ctx)
|
||||
|
||||
key := &PackageFile{
|
||||
VersionID: pf.VersionID,
|
||||
LowerName: pf.LowerName,
|
||||
CompositeKey: pf.CompositeKey,
|
||||
}
|
||||
existing := &PackageFile{}
|
||||
|
||||
has, err := e.Get(key)
|
||||
has, err := e.Where(builder.Eq{
|
||||
"version_id": pf.VersionID,
|
||||
"lower_name": pf.LowerName,
|
||||
"composite_key": pf.CompositeKey,
|
||||
}).Get(existing)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if has {
|
||||
return pf, ErrDuplicatePackageFile
|
||||
return existing, ErrDuplicatePackageFile
|
||||
}
|
||||
if _, err = e.Insert(pf); err != nil {
|
||||
return nil, err
|
||||
@@ -93,13 +93,13 @@ func GetFileForVersionByName(ctx context.Context, versionID int64, name, key str
|
||||
return nil, ErrPackageFileNotExist
|
||||
}
|
||||
|
||||
pf := &PackageFile{
|
||||
VersionID: versionID,
|
||||
LowerName: strings.ToLower(name),
|
||||
CompositeKey: key,
|
||||
}
|
||||
pf := &PackageFile{}
|
||||
|
||||
has, err := db.GetEngine(ctx).Get(pf)
|
||||
has, err := db.GetEngine(ctx).Where(builder.Eq{
|
||||
"version_id": versionID,
|
||||
"lower_name": strings.ToLower(name),
|
||||
"composite_key": key,
|
||||
}).Get(pf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -39,17 +39,17 @@ type PackageVersion struct {
|
||||
func GetOrInsertVersion(ctx context.Context, pv *PackageVersion) (*PackageVersion, error) {
|
||||
e := db.GetEngine(ctx)
|
||||
|
||||
key := &PackageVersion{
|
||||
PackageID: pv.PackageID,
|
||||
LowerVersion: pv.LowerVersion,
|
||||
}
|
||||
existing := &PackageVersion{}
|
||||
|
||||
has, err := e.Get(key)
|
||||
has, err := e.Where(builder.Eq{
|
||||
"package_id": pv.PackageID,
|
||||
"lower_version": pv.LowerVersion,
|
||||
}).Get(existing)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if has {
|
||||
return key, ErrDuplicatePackageVersion
|
||||
return existing, ErrDuplicatePackageVersion
|
||||
}
|
||||
if _, err = e.Insert(pv); err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package rpm
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
packages_model "code.gitea.io/gitea/models/packages"
|
||||
rpm_module "code.gitea.io/gitea/modules/packages/rpm"
|
||||
)
|
||||
|
||||
// GetGroups gets all available groups
|
||||
func GetGroups(ctx context.Context, ownerID int64) ([]string, error) {
|
||||
return packages_model.GetDistinctPropertyValues(
|
||||
ctx,
|
||||
packages_model.TypeRpm,
|
||||
ownerID,
|
||||
packages_model.PropertyTypeFile,
|
||||
rpm_module.PropertyGroup,
|
||||
nil,
|
||||
)
|
||||
}
|
||||
@@ -39,7 +39,7 @@ type ReviewState struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
UserID int64 `xorm:"NOT NULL UNIQUE(pull_commit_user)"`
|
||||
PullID int64 `xorm:"NOT NULL INDEX UNIQUE(pull_commit_user) DEFAULT 0"` // Which PR was the review on?
|
||||
CommitSHA string `xorm:"NOT NULL VARCHAR(40) UNIQUE(pull_commit_user)"` // Which commit was the head commit for the review?
|
||||
CommitSHA string `xorm:"NOT NULL VARCHAR(64) UNIQUE(pull_commit_user)"` // Which commit was the head commit for the review?
|
||||
UpdatedFiles map[string]ViewedState `xorm:"NOT NULL LONGTEXT JSON"` // Stores for each of the changed files of a PR whether they have been viewed, changed since last viewed, or not viewed
|
||||
UpdatedUnix timeutil.TimeStamp `xorm:"updated"` // Is an accurate indicator of the order of commits as we do not expect it to be possible to make reviews on previous commits
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ type RepoArchiver struct { //revive:disable-line:exported
|
||||
RepoID int64 `xorm:"index unique(s)"`
|
||||
Type git.ArchiveType `xorm:"unique(s)"`
|
||||
Status ArchiverStatus
|
||||
CommitID string `xorm:"VARCHAR(40) unique(s)"`
|
||||
CommitID string `xorm:"VARCHAR(64) unique(s)"`
|
||||
CreatedUnix timeutil.TimeStamp `xorm:"INDEX NOT NULL created"`
|
||||
}
|
||||
|
||||
|
||||
@@ -121,8 +121,11 @@ func GetPushMirrorsSyncedOnCommit(ctx context.Context, repoID int64) ([]*PushMir
|
||||
// PushMirrorsIterate iterates all push-mirror repositories.
|
||||
func PushMirrorsIterate(ctx context.Context, limit int, f func(idx int, bean any) error) error {
|
||||
sess := db.GetEngine(ctx).
|
||||
Where("last_update + (`interval` / ?) <= ?", time.Second, time.Now().Unix()).
|
||||
And("`interval` != 0").
|
||||
Table("push_mirror").
|
||||
Join("INNER", "`repository`", "`repository`.id = `push_mirror`.repo_id").
|
||||
Where("`push_mirror`.last_update + (`push_mirror`.`interval` / ?) <= ?", time.Second, time.Now().Unix()).
|
||||
And("`push_mirror`.`interval` != 0").
|
||||
And("`repository`.is_archived = ?", false).
|
||||
OrderBy("last_update ASC")
|
||||
if limit > 0 {
|
||||
sess = sess.Limit(limit)
|
||||
|
||||
@@ -75,7 +75,7 @@ type Release struct {
|
||||
Target string
|
||||
TargetBehind string `xorm:"-"` // to handle non-existing or empty target
|
||||
Title string
|
||||
Sha1 string `xorm:"VARCHAR(40)"`
|
||||
Sha1 string `xorm:"VARCHAR(64)"`
|
||||
NumCommits int64
|
||||
NumCommitsBehind int64 `xorm:"-"`
|
||||
Note string `xorm:"TEXT"`
|
||||
|
||||
+10
-7
@@ -180,7 +180,7 @@ type Repository struct {
|
||||
IsFsckEnabled bool `xorm:"NOT NULL DEFAULT true"`
|
||||
CloseIssuesViaCommitInAnyBranch bool `xorm:"NOT NULL DEFAULT false"`
|
||||
Topics []string `xorm:"TEXT JSON"`
|
||||
ObjectFormatName string `xorm:"-"`
|
||||
ObjectFormatName string `xorm:"VARCHAR(6) NOT NULL DEFAULT 'sha1'"`
|
||||
|
||||
TrustModel TrustModelType
|
||||
|
||||
@@ -196,6 +196,14 @@ func init() {
|
||||
db.RegisterModel(new(Repository))
|
||||
}
|
||||
|
||||
func (repo *Repository) GetName() string {
|
||||
return repo.Name
|
||||
}
|
||||
|
||||
func (repo *Repository) GetOwnerName() string {
|
||||
return repo.OwnerName
|
||||
}
|
||||
|
||||
// SanitizedOriginalURL returns a sanitized OriginalURL
|
||||
func (repo *Repository) SanitizedOriginalURL() string {
|
||||
if repo.OriginalURL == "" {
|
||||
@@ -276,10 +284,6 @@ func (repo *Repository) AfterLoad() {
|
||||
repo.NumOpenMilestones = repo.NumMilestones - repo.NumClosedMilestones
|
||||
repo.NumOpenProjects = repo.NumProjects - repo.NumClosedProjects
|
||||
repo.NumOpenActionRuns = repo.NumActionRuns - repo.NumClosedActionRuns
|
||||
|
||||
// this is a temporary behaviour to support old repos, next step is to store the object format in the database
|
||||
// and read from database so this line could be removed. To not depend on git module, we use a constant variable here
|
||||
repo.ObjectFormatName = "sha1"
|
||||
}
|
||||
|
||||
// LoadAttributes loads attributes of the repository.
|
||||
@@ -584,8 +588,7 @@ func (repo *Repository) CanEnableEditor() bool {
|
||||
// DescriptionHTML does special handles to description and return HTML string.
|
||||
func (repo *Repository) DescriptionHTML(ctx context.Context) template.HTML {
|
||||
desc, err := markup.RenderDescriptionHTML(&markup.RenderContext{
|
||||
Ctx: ctx,
|
||||
URLPrefix: repo.HTMLURL(),
|
||||
Ctx: ctx,
|
||||
// Don't use Metas to speedup requests
|
||||
}, repo.Description)
|
||||
if err != nil {
|
||||
|
||||
@@ -27,7 +27,7 @@ const (
|
||||
type RepoIndexerStatus struct { //revive:disable-line:exported
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
RepoID int64 `xorm:"INDEX(s)"`
|
||||
CommitSha string `xorm:"VARCHAR(40)"`
|
||||
CommitSha string `xorm:"VARCHAR(64)"`
|
||||
IndexerType RepoIndexerType `xorm:"INDEX(s) NOT NULL DEFAULT 0"`
|
||||
}
|
||||
|
||||
|
||||
@@ -366,7 +366,7 @@ func syncTopicsInRepository(sess db.Engine, repoID int64) error {
|
||||
topicNames := make([]string, 0, 25)
|
||||
if err := sess.Table("topic").Cols("name").
|
||||
Join("INNER", "repo_topic", "repo_topic.topic_id = topic.id").
|
||||
Where("repo_topic.repo_id = ?", repoID).Desc("topic.repo_count").Find(&topicNames); err != nil {
|
||||
Where("repo_topic.repo_id = ?", repoID).Asc("topic.name").Find(&topicNames); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user