Merge remote-tracking branch 'upstream/main' into limit-repo-size

This commit is contained in:
DmitryFrolovTri
2023-06-29 08:04:23 +00:00
359 changed files with 5718 additions and 3433 deletions
+7 -6
View File
@@ -36,12 +36,13 @@ type ActionRun struct {
TriggerUser *user_model.User `xorm:"-"`
Ref string
CommitSHA string
IsForkPullRequest bool // If this is triggered by a PR from a forked repository or an untrusted user, we need to check if it is approved and limit permissions when running the workflow.
NeedApproval bool // may need approval if it's a fork pull request
ApprovedBy int64 `xorm:"index"` // who approved
Event webhook_module.HookEventType
EventPayload string `xorm:"LONGTEXT"`
Status Status `xorm:"index"`
IsForkPullRequest bool // If this is triggered by a PR from a forked repository or an untrusted user, we need to check if it is approved and limit permissions when running the workflow.
NeedApproval bool // may need approval if it's a fork pull request
ApprovedBy int64 `xorm:"index"` // who approved
Event webhook_module.HookEventType // the webhook event that causes the workflow to run
EventPayload string `xorm:"LONGTEXT"`
TriggerEvent string // the trigger event defined in the `on` configuration of the triggered workflow
Status Status `xorm:"index"`
Started timeutil.TimeStamp
Stopped timeutil.TimeStamp
Created timeutil.TimeStamp `xorm:"created"`
+35
View File
@@ -71,6 +71,7 @@ type FindRunOptions struct {
WorkflowFileName string
TriggerUserID int64
Approved bool // not util.OptionalBool, it works only when it's true
Status Status
}
func (opts FindRunOptions) toConds() builder.Cond {
@@ -90,6 +91,9 @@ func (opts FindRunOptions) toConds() builder.Cond {
if opts.Approved {
cond = cond.And(builder.Gt{"approved_by": 0})
}
if opts.Status > StatusUnknown {
cond = cond.And(builder.Eq{"status": opts.Status})
}
return cond
}
@@ -106,3 +110,34 @@ func FindRuns(ctx context.Context, opts FindRunOptions) (RunList, int64, error)
func CountRuns(ctx context.Context, opts FindRunOptions) (int64, error) {
return db.GetEngine(ctx).Where(opts.toConds()).Count(new(ActionRun))
}
type StatusInfo struct {
Status int
DisplayedStatus string
}
// GetStatusInfoList returns a slice of StatusInfo
func GetStatusInfoList(ctx context.Context) []StatusInfo {
// same as those in aggregateJobStatus
allStatus := []Status{StatusSuccess, StatusFailure, StatusWaiting, StatusRunning}
statusInfoList := make([]StatusInfo, 0, 4)
for _, s := range allStatus {
statusInfoList = append(statusInfoList, StatusInfo{
Status: int(s),
DisplayedStatus: s.String(),
})
}
return statusInfoList
}
// GetActors returns a slice of Actors
func GetActors(ctx context.Context, repoID int64) ([]*user_model.User, error) {
actors := make([]*user_model.User, 0, 10)
return actors, db.GetEngine(ctx).Where(builder.In("id", builder.Select("`action_run`.trigger_user_id").From("`action_run`").
GroupBy("`action_run`.trigger_user_id").
Where(builder.Eq{"`action_run`.repo_id": repoID}))).
Cols("id", "name", "full_name", "avatar", "avatar_email", "use_custom_avatar").
OrderBy(user_model.GetOrderByName()).
Find(&actors)
}
+9
View File
@@ -344,6 +344,9 @@ func UpdateTask(ctx context.Context, task *ActionTask, cols ...string) error {
return err
}
// UpdateTaskByState updates the task by the state.
// It will always update the task if the state is not final, even there is no change.
// So it will update ActionTask.Updated to avoid the task being judged as a zombie task.
func UpdateTaskByState(ctx context.Context, state *runnerv1.TaskState) (*ActionTask, error) {
stepStates := map[int64]*runnerv1.StepState{}
for _, v := range state.Steps {
@@ -384,6 +387,12 @@ func UpdateTaskByState(ctx context.Context, state *runnerv1.TaskState) (*ActionT
}, nil); err != nil {
return nil, err
}
} else {
// Force update ActionTask.Updated to avoid the task being judged as a zombie task
task.Updated = timeutil.TimeStampNow()
if err := UpdateTask(ctx, task, "updated"); err != nil {
return nil, err
}
}
if err := task.LoadAttributes(ctx); err != nil {
+97
View File
@@ -0,0 +1,97 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package actions
import (
"context"
"errors"
"fmt"
"strings"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util"
"xorm.io/builder"
)
type ActionVariable struct {
ID int64 `xorm:"pk autoincr"`
OwnerID int64 `xorm:"UNIQUE(owner_repo_name)"`
RepoID int64 `xorm:"INDEX UNIQUE(owner_repo_name)"`
Name string `xorm:"UNIQUE(owner_repo_name) NOT NULL"`
Data string `xorm:"LONGTEXT NOT NULL"`
CreatedUnix timeutil.TimeStamp `xorm:"created NOT NULL"`
UpdatedUnix timeutil.TimeStamp `xorm:"updated"`
}
func init() {
db.RegisterModel(new(ActionVariable))
}
func (v *ActionVariable) Validate() error {
if v.OwnerID == 0 && v.RepoID == 0 {
return errors.New("the variable is not bound to any scope")
}
return nil
}
func InsertVariable(ctx context.Context, ownerID, repoID int64, name, data string) (*ActionVariable, error) {
variable := &ActionVariable{
OwnerID: ownerID,
RepoID: repoID,
Name: strings.ToUpper(name),
Data: data,
}
if err := variable.Validate(); err != nil {
return variable, err
}
return variable, db.Insert(ctx, variable)
}
type FindVariablesOpts struct {
db.ListOptions
OwnerID int64
RepoID int64
}
func (opts *FindVariablesOpts) toConds() builder.Cond {
cond := builder.NewCond()
if opts.OwnerID > 0 {
cond = cond.And(builder.Eq{"owner_id": opts.OwnerID})
}
if opts.RepoID > 0 {
cond = cond.And(builder.Eq{"repo_id": opts.RepoID})
}
return cond
}
func FindVariables(ctx context.Context, opts FindVariablesOpts) ([]*ActionVariable, error) {
var variables []*ActionVariable
sess := db.GetEngine(ctx)
if opts.PageSize != 0 {
sess = db.SetSessionPagination(sess, &opts.ListOptions)
}
return variables, sess.Where(opts.toConds()).Find(&variables)
}
func GetVariableByID(ctx context.Context, variableID int64) (*ActionVariable, error) {
var variable ActionVariable
has, err := db.GetEngine(ctx).Where("id=?", variableID).Get(&variable)
if err != nil {
return nil, err
} else if !has {
return nil, fmt.Errorf("variable with id %d: %w", variableID, util.ErrNotExist)
}
return &variable, nil
}
func UpdateVariable(ctx context.Context, variable *ActionVariable) (bool, error) {
count, err := db.GetEngine(ctx).ID(variable.ID).Cols("name", "data").
Update(&ActionVariable{
Name: variable.Name,
Data: variable.Data,
})
return count != 0, err
}
+4 -1
View File
@@ -123,7 +123,10 @@ func newXORMEngine() (*xorm.Engine, error) {
// SyncAllTables sync the schemas of all tables, is required by unit test code
func SyncAllTables() error {
return x.StoreEngine("InnoDB").Sync2(tables...)
_, err := x.StoreEngine("InnoDB").SyncWithOptions(xorm.SyncOptions{
WarnIfDatabaseColumnMissed: true,
}, tables...)
return err
}
// InitEngine initializes the xorm.Engine and sets it as db.DefaultContext
+4
View File
@@ -20,6 +20,10 @@ const (
SearchOrderByNewest SearchOrderBy = "created_unix DESC"
SearchOrderBySize SearchOrderBy = "size ASC"
SearchOrderBySizeReverse SearchOrderBy = "size DESC"
SearchOrderByGitSize SearchOrderBy = "git_size ASC"
SearchOrderByGitSizeReverse SearchOrderBy = "git_size DESC"
SearchOrderByLFSSize SearchOrderBy = "lfs_size ASC"
SearchOrderByLFSSizeReverse SearchOrderBy = "lfs_size DESC"
SearchOrderByID SearchOrderBy = "id ASC"
SearchOrderByIDReverse SearchOrderBy = "id DESC"
SearchOrderByStars SearchOrderBy = "num_stars ASC"
+18
View File
@@ -7,6 +7,7 @@ import (
"context"
"errors"
"io"
"io/fs"
"os"
"path/filepath"
"strconv"
@@ -21,6 +22,7 @@ var defaultFileBlockSize int64 = 32 * 1024
type File interface {
io.ReadWriteCloser
io.Seeker
fs.File
}
type file struct {
@@ -193,10 +195,26 @@ func (f *file) Close() error {
return nil
}
func (f *file) Stat() (os.FileInfo, error) {
if f.metaID == 0 {
return nil, os.ErrInvalid
}
fileMeta, err := findFileMetaByID(f.ctx, f.metaID)
if err != nil {
return nil, err
}
return fileMeta, nil
}
func timeToFileTimestamp(t time.Time) int64 {
return t.UnixMicro()
}
func fileTimestampToTime(timestamp int64) time.Time {
return time.UnixMicro(timestamp)
}
func (f *file) loadMetaByPath() (*dbfsMeta, error) {
var fileMeta dbfsMeta
if ok, err := db.GetEngine(f.ctx).Where("full_path = ?", f.fullPath).Get(&fileMeta); err != nil {
+29
View File
@@ -5,7 +5,10 @@ package dbfs
import (
"context"
"io/fs"
"os"
"path"
"time"
"code.gitea.io/gitea/models/db"
)
@@ -100,3 +103,29 @@ func Remove(ctx context.Context, name string) error {
defer f.Close()
return f.delete()
}
var _ fs.FileInfo = (*dbfsMeta)(nil)
func (m *dbfsMeta) Name() string {
return path.Base(m.FullPath)
}
func (m *dbfsMeta) Size() int64 {
return m.FileSize
}
func (m *dbfsMeta) Mode() fs.FileMode {
return os.ModePerm
}
func (m *dbfsMeta) ModTime() time.Time {
return fileTimestampToTime(m.ModifyTimestamp)
}
func (m *dbfsMeta) IsDir() bool {
return false
}
func (m *dbfsMeta) Sys() any {
return nil
}
+13
View File
@@ -111,6 +111,19 @@ func TestDbfsBasic(t *testing.T) {
_, err = OpenFile(db.DefaultContext, "test2.txt", os.O_RDONLY)
assert.Error(t, err)
// test stat
f, err = OpenFile(db.DefaultContext, "test/test.txt", os.O_RDWR|os.O_CREATE)
assert.NoError(t, err)
stat, err := f.Stat()
assert.NoError(t, err)
assert.EqualValues(t, "test.txt", stat.Name())
assert.EqualValues(t, 0, stat.Size())
_, err = f.Write([]byte("0123456789"))
assert.NoError(t, err)
stat, err = f.Stat()
assert.NoError(t, err)
assert.EqualValues(t, 10, stat.Size())
}
func TestDbfsReadWrite(t *testing.T) {
+1 -1
View File
@@ -167,7 +167,7 @@ func AsCommentType(typeName string) CommentType {
func (t CommentType) HasContentSupport() bool {
switch t {
case CommentTypeComment, CommentTypeCode, CommentTypeReview:
case CommentTypeComment, CommentTypeCode, CommentTypeReview, CommentTypeDismissReview:
return true
}
return false
+10 -8
View File
@@ -18,11 +18,11 @@ import (
type CodeComments map[string]map[int64][]*Comment
// FetchCodeComments will return a 2d-map: ["Path"]["Line"] = Comments at line
func FetchCodeComments(ctx context.Context, issue *Issue, currentUser *user_model.User) (CodeComments, error) {
return fetchCodeCommentsByReview(ctx, issue, currentUser, nil)
func FetchCodeComments(ctx context.Context, issue *Issue, currentUser *user_model.User, showOutdatedComments bool) (CodeComments, error) {
return fetchCodeCommentsByReview(ctx, issue, currentUser, nil, showOutdatedComments)
}
func fetchCodeCommentsByReview(ctx context.Context, issue *Issue, currentUser *user_model.User, review *Review) (CodeComments, error) {
func fetchCodeCommentsByReview(ctx context.Context, issue *Issue, currentUser *user_model.User, review *Review, showOutdatedComments bool) (CodeComments, error) {
pathToLineToComment := make(CodeComments)
if review == nil {
review = &Review{ID: 0}
@@ -33,7 +33,7 @@ func fetchCodeCommentsByReview(ctx context.Context, issue *Issue, currentUser *u
ReviewID: review.ID,
}
comments, err := findCodeComments(ctx, opts, issue, currentUser, review)
comments, err := findCodeComments(ctx, opts, issue, currentUser, review, showOutdatedComments)
if err != nil {
return nil, err
}
@@ -47,15 +47,17 @@ func fetchCodeCommentsByReview(ctx context.Context, issue *Issue, currentUser *u
return pathToLineToComment, nil
}
func findCodeComments(ctx context.Context, opts FindCommentsOptions, issue *Issue, currentUser *user_model.User, review *Review) ([]*Comment, error) {
func findCodeComments(ctx context.Context, opts FindCommentsOptions, issue *Issue, currentUser *user_model.User, review *Review, showOutdatedComments bool) ([]*Comment, error) {
var comments CommentList
if review == nil {
review = &Review{ID: 0}
}
conds := opts.ToConds()
if review.ID == 0 {
if !showOutdatedComments && review.ID == 0 {
conds = conds.And(builder.Eq{"invalidated": false})
}
e := db.GetEngine(ctx)
if err := e.Where(conds).
Asc("comment.created_unix").
@@ -118,12 +120,12 @@ func findCodeComments(ctx context.Context, opts FindCommentsOptions, issue *Issu
}
// FetchCodeCommentsByLine fetches the code comments for a given treePath and line number
func FetchCodeCommentsByLine(ctx context.Context, issue *Issue, currentUser *user_model.User, treePath string, line int64) ([]*Comment, error) {
func FetchCodeCommentsByLine(ctx context.Context, issue *Issue, currentUser *user_model.User, treePath string, line int64, showOutdatedComments bool) ([]*Comment, error) {
opts := FindCommentsOptions{
Type: CommentTypeCode,
IssueID: issue.ID,
TreePath: treePath,
Line: line,
}
return findCodeComments(ctx, opts, issue, currentUser, nil)
return findCodeComments(ctx, opts, issue, currentUser, nil, showOutdatedComments)
}
+2 -2
View File
@@ -50,7 +50,7 @@ func TestFetchCodeComments(t *testing.T) {
issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 2})
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
res, err := issues_model.FetchCodeComments(db.DefaultContext, issue, user)
res, err := issues_model.FetchCodeComments(db.DefaultContext, issue, user, false)
assert.NoError(t, err)
assert.Contains(t, res, "README.md")
assert.Contains(t, res["README.md"], int64(4))
@@ -58,7 +58,7 @@ func TestFetchCodeComments(t *testing.T) {
assert.Equal(t, int64(4), res["README.md"][4][0].ID)
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
res, err = issues_model.FetchCodeComments(db.DefaultContext, issue, user2)
res, err = issues_model.FetchCodeComments(db.DefaultContext, issue, user2, false)
assert.NoError(t, err)
assert.Len(t, res, 1)
}
+20 -18
View File
@@ -229,39 +229,41 @@ func (issues IssueList) loadMilestones(ctx context.Context) error {
return nil
}
func (issues IssueList) getProjectIDs() []int64 {
ids := make(container.Set[int64], len(issues))
for _, issue := range issues {
ids.Add(issue.ProjectID())
}
return ids.Values()
}
func (issues IssueList) LoadProjects(ctx context.Context) error {
issueIDs := issues.getIssueIDs()
projectMaps := make(map[int64]*project_model.Project, len(issues))
left := len(issueIDs)
func (issues IssueList) loadProjects(ctx context.Context) error {
projectIDs := issues.getProjectIDs()
if len(projectIDs) == 0 {
return nil
type projectWithIssueID struct {
*project_model.Project `xorm:"extends"`
IssueID int64
}
projectMaps := make(map[int64]*project_model.Project, len(projectIDs))
left := len(projectIDs)
for left > 0 {
limit := db.DefaultMaxInSize
if left < limit {
limit = left
}
projects := make([]*projectWithIssueID, 0, limit)
err := db.GetEngine(ctx).
In("id", projectIDs[:limit]).
Find(&projectMaps)
Table("project").
Select("project.*, project_issue.issue_id").
Join("INNER", "project_issue", "project.id = project_issue.project_id").
In("project_issue.issue_id", issueIDs[:limit]).
Find(&projects)
if err != nil {
return err
}
for _, project := range projects {
projectMaps[project.IssueID] = project.Project
}
left -= limit
projectIDs = projectIDs[limit:]
issueIDs = issueIDs[limit:]
}
for _, issue := range issues {
issue.Project = projectMaps[issue.ProjectID()]
issue.Project = projectMaps[issue.ID]
}
return nil
}
@@ -541,7 +543,7 @@ func (issues IssueList) loadAttributes(ctx context.Context) error {
return fmt.Errorf("issue.loadAttributes: loadMilestones: %w", err)
}
if err := issues.loadProjects(ctx); err != nil {
if err := issues.LoadProjects(ctx); err != nil {
return fmt.Errorf("issue.loadAttributes: loadProjects: %w", err)
}
+4
View File
@@ -66,8 +66,12 @@ func TestIssueList_LoadAttributes(t *testing.T) {
}
if issue.ID == int64(1) {
assert.Equal(t, int64(400), issue.TotalTrackedTime)
assert.NotNil(t, issue.Project)
} else if issue.ID == int64(2) {
assert.Equal(t, int64(3682), issue.TotalTrackedTime)
assert.Nil(t, issue.Project)
} else {
assert.Nil(t, issue.Project)
}
}
}
-5
View File
@@ -27,11 +27,6 @@ func (issue *Issue) LoadProject(ctx context.Context) (err error) {
return err
}
// ProjectID return project id if issue was assigned to one
func (issue *Issue) ProjectID() int64 {
return issue.projectID(db.DefaultContext)
}
func (issue *Issue) projectID(ctx context.Context) int64 {
var ip project_model.ProjectIssue
has, err := db.GetEngine(ctx).Where("issue_id=?", issue.ID).Get(&ip)
+1 -1
View File
@@ -141,7 +141,7 @@ func (r *Review) LoadCodeComments(ctx context.Context) (err error) {
if err = r.loadIssue(ctx); err != nil {
return
}
r.CodeComments, err = fetchCodeCommentsByReview(ctx, r.Issue, nil, r)
r.CodeComments, err = fetchCodeCommentsByReview(ctx, r.Issue, nil, r, false)
return err
}
+23 -16
View File
@@ -6,6 +6,7 @@ package issues
import (
"context"
"errors"
"fmt"
"time"
"code.gitea.io/gitea/models/db"
@@ -173,10 +174,12 @@ func AddTime(user *user_model.User, issue *Issue, amount int64, created time.Tim
}
if _, err := CreateComment(ctx, &CreateCommentOptions{
Issue: issue,
Repo: issue.Repo,
Doer: user,
Content: util.SecToTime(amount),
Issue: issue,
Repo: issue.Repo,
Doer: user,
// Content before v1.21 did store the formated string instead of seconds,
// so use "|" as delimeter to mark the new format
Content: fmt.Sprintf("|%d", amount),
Type: CommentTypeAddTimeManual,
TimeID: t.ID,
}); err != nil {
@@ -199,8 +202,8 @@ func addTime(ctx context.Context, user *user_model.User, issue *Issue, amount in
return tt, db.Insert(ctx, tt)
}
// TotalTimes returns the spent time for each user by an issue
func TotalTimes(options *FindTrackedTimesOptions) (map[*user_model.User]string, error) {
// TotalTimes returns the spent time in seconds for each user by an issue
func TotalTimes(options *FindTrackedTimesOptions) (map[*user_model.User]int64, error) {
trackedTimes, err := GetTrackedTimes(db.DefaultContext, options)
if err != nil {
return nil, err
@@ -211,7 +214,7 @@ func TotalTimes(options *FindTrackedTimesOptions) (map[*user_model.User]string,
totalTimesByUser[t.UserID] += t.Time
}
totalTimes := make(map[*user_model.User]string)
totalTimes := make(map[*user_model.User]int64)
// Fetching User and making time human readable
for userID, total := range totalTimesByUser {
user, err := user_model.GetUserByID(db.DefaultContext, userID)
@@ -221,7 +224,7 @@ func TotalTimes(options *FindTrackedTimesOptions) (map[*user_model.User]string,
}
return nil, err
}
totalTimes[user] = util.SecToTime(total)
totalTimes[user] = total
}
return totalTimes, nil
}
@@ -251,10 +254,12 @@ func DeleteIssueUserTimes(issue *Issue, user *user_model.User) error {
return err
}
if _, err := CreateComment(ctx, &CreateCommentOptions{
Issue: issue,
Repo: issue.Repo,
Doer: user,
Content: "- " + util.SecToTime(removedTime),
Issue: issue,
Repo: issue.Repo,
Doer: user,
// Content before v1.21 did store the formated string instead of seconds,
// so use "|" as delimeter to mark the new format
Content: fmt.Sprintf("|%d", removedTime),
Type: CommentTypeDeleteTimeManual,
}); err != nil {
return err
@@ -280,10 +285,12 @@ func DeleteTime(t *TrackedTime) error {
}
if _, err := CreateComment(ctx, &CreateCommentOptions{
Issue: t.Issue,
Repo: t.Issue.Repo,
Doer: t.User,
Content: "- " + util.SecToTime(t.Time),
Issue: t.Issue,
Repo: t.Issue.Repo,
Doer: t.User,
// Content before v1.21 did store the formated string instead of seconds,
// so use "|" as delimeter to mark the new format
Content: fmt.Sprintf("|%d", t.Time),
Type: CommentTypeDeleteTimeManual,
}); err != nil {
return err
+7 -7
View File
@@ -35,7 +35,7 @@ func TestAddTime(t *testing.T) {
assert.Equal(t, int64(3661), tt.Time)
comment := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{Type: issues_model.CommentTypeAddTimeManual, PosterID: 3, IssueID: 1})
assert.Equal(t, "1 hour 1 minute", comment.Content)
assert.Equal(t, "|3661", comment.Content)
}
func TestGetTrackedTimes(t *testing.T) {
@@ -86,8 +86,8 @@ func TestTotalTimes(t *testing.T) {
assert.NoError(t, err)
assert.Len(t, total, 1)
for user, time := range total {
assert.Equal(t, int64(1), user.ID)
assert.Equal(t, "6 minutes 40 seconds", time)
assert.EqualValues(t, 1, user.ID)
assert.EqualValues(t, 400, time)
}
total, err = issues_model.TotalTimes(&issues_model.FindTrackedTimesOptions{IssueID: 2})
@@ -95,9 +95,9 @@ func TestTotalTimes(t *testing.T) {
assert.Len(t, total, 2)
for user, time := range total {
if user.ID == 2 {
assert.Equal(t, "1 hour 1 minute", time)
assert.EqualValues(t, 3662, time)
} else if user.ID == 1 {
assert.Equal(t, "20 seconds", time)
assert.EqualValues(t, 20, time)
} else {
assert.Error(t, assert.AnError)
}
@@ -107,8 +107,8 @@ func TestTotalTimes(t *testing.T) {
assert.NoError(t, err)
assert.Len(t, total, 1)
for user, time := range total {
assert.Equal(t, int64(2), user.ID)
assert.Equal(t, "1 second", time)
assert.EqualValues(t, 2, user.ID)
assert.EqualValues(t, 1, time)
}
total, err = issues_model.TotalTimes(&issues_model.FindTrackedTimesOptions{IssueID: 4})
+1 -1
View File
@@ -147,9 +147,9 @@ func MainTest(m *testing.M) {
os.Exit(1)
}
setting.CustomPath = filepath.Join(setting.AppWorkPath, "custom")
setting.AppDataPath = tmpDataPath
setting.SetCustomPathAndConf("", "", "")
unittest.InitSettings()
if err = git.InitFull(context.Background()); err != nil {
fmt.Printf("Unable to InitFull: %v\n", err)
+6
View File
@@ -503,6 +503,12 @@ var migrations = []Migration{
// v260 -> v261
NewMigration("Drop custom_labels column of action_runner table", v1_21.DropCustomLabelsColumnOfActionRunner),
// v261 -> v262
NewMigration("Add variable table", v1_21.CreateVariableTable),
// v262 -> v263
NewMigration("Add TriggerEvent to action_run table", v1_21.AddTriggerEventToActionRun),
// v263 -> v264
NewMigration("Add git_size and lfs_size columns to repository table", v1_21.AddGitSizeAndLFSSizeToRepositoryTable),
// to modify later
NewMigration("Add size limit on repository", v1_21.AddSizeLimitOnRepo),
}
+24
View File
@@ -0,0 +1,24 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package v1_21 //nolint
import (
"code.gitea.io/gitea/modules/timeutil"
"xorm.io/xorm"
)
func CreateVariableTable(x *xorm.Engine) error {
type ActionVariable struct {
ID int64 `xorm:"pk autoincr"`
OwnerID int64 `xorm:"UNIQUE(owner_repo_name)"`
RepoID int64 `xorm:"INDEX UNIQUE(owner_repo_name)"`
Name string `xorm:"UNIQUE(owner_repo_name) NOT NULL"`
Data string `xorm:"LONGTEXT NOT NULL"`
CreatedUnix timeutil.TimeStamp `xorm:"created NOT NULL"`
UpdatedUnix timeutil.TimeStamp `xorm:"updated"`
}
return x.Sync(new(ActionVariable))
}
+16
View File
@@ -0,0 +1,16 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package v1_21 //nolint
import (
"xorm.io/xorm"
)
func AddTriggerEventToActionRun(x *xorm.Engine) error {
type ActionRun struct {
TriggerEvent string
}
return x.Sync(new(ActionRun))
}
+41
View File
@@ -0,0 +1,41 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package v1_21 //nolint
import (
"fmt"
"xorm.io/xorm"
)
// AddGitSizeAndLFSSizeToRepositoryTable: add GitSize and LFSSize columns to Repository
func AddGitSizeAndLFSSizeToRepositoryTable(x *xorm.Engine) error {
type Repository struct {
GitSize int64 `xorm:"NOT NULL DEFAULT 0"`
LFSSize int64 `xorm:"NOT NULL DEFAULT 0"`
}
sess := x.NewSession()
defer sess.Close()
if err := sess.Begin(); err != nil {
return err
}
if err := sess.Sync2(new(Repository)); err != nil {
return fmt.Errorf("Sync2: %w", err)
}
_, err := sess.Exec(`UPDATE repository SET lfs_size=(SELECT SUM(size) FROM lfs_meta_object WHERE lfs_meta_object.repository_id=repository.ID) WHERE EXISTS (SELECT 1 FROM lfs_meta_object WHERE lfs_meta_object.repository_id=repository.ID)`)
if err != nil {
return err
}
_, err = sess.Exec(`UPDATE repository SET git_size = size - lfs_size`)
if err != nil {
return err
}
return sess.Commit()
}
+39
View File
@@ -16,6 +16,7 @@ import (
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/markup"
"code.gitea.io/gitea/modules/setting"
@@ -165,6 +166,8 @@ type Repository struct {
SizeLimit int64 `xorm:"NOT NULL DEFAULT 0"`
Size int64 `xorm:"NOT NULL DEFAULT 0"`
EnableSizeLimit bool `xorm:"NOT NULL DEFAULT true"`
GitSize int64 `xorm:"NOT NULL DEFAULT 0"`
LFSSize int64 `xorm:"NOT NULL DEFAULT 0"`
CodeIndexerStatus *RepoIndexerStatus `xorm:"-"`
StatsIndexerStatus *RepoIndexerStatus `xorm:"-"`
IsFsckEnabled bool `xorm:"NOT NULL DEFAULT true"`
@@ -198,6 +201,42 @@ func (repo *Repository) SanitizedOriginalURL() string {
return u.String()
}
// text representations to be returned in SizeDetail.Name
const (
SizeDetailNameGit = "git"
SizeDetailNameLFS = "lfs"
)
type SizeDetail struct {
Name string
Size int64
}
// SizeDetails forms a struct with various size details about repository
func (repo *Repository) SizeDetails() []SizeDetail {
sizeDetails := []SizeDetail{
{
Name: SizeDetailNameGit,
Size: repo.GitSize,
},
{
Name: SizeDetailNameLFS,
Size: repo.LFSSize,
},
}
return sizeDetails
}
// SizeDetailsString returns a concatenation of all repository size details as a string
func (repo *Repository) SizeDetailsString() string {
var str strings.Builder
sizeDetails := repo.SizeDetails()
for _, detail := range sizeDetails {
str.WriteString(fmt.Sprintf("%s: %s, ", detail.Name, base.FileSize(detail.Size)))
}
return strings.TrimSuffix(str.String(), ", ")
}
func (repo *Repository) LogString() string {
if repo == nil {
return "<Repository nil>"
+5 -3
View File
@@ -185,9 +185,11 @@ func ChangeRepositoryName(doer *user_model.User, repo *Repository, newRepoName s
}
// UpdateRepoSize updates the repository size, calculating it using getDirectorySize
func UpdateRepoSize(ctx context.Context, repoID, size int64) error {
_, err := db.GetEngine(ctx).ID(repoID).Cols("size").NoAutoTime().Update(&Repository{
Size: size,
func UpdateRepoSize(ctx context.Context, repoID, gitSize, lfsSize int64) error {
_, err := db.GetEngine(ctx).ID(repoID).Cols("size", "git_size", "lfs_size").NoAutoTime().Update(&Repository{
Size: gitSize + lfsSize,
GitSize: gitSize,
LFSSize: lfsSize,
})
return err
}
+4 -38
View File
@@ -5,38 +5,17 @@ package secret
import (
"context"
"fmt"
"regexp"
"errors"
"strings"
"code.gitea.io/gitea/models/db"
secret_module "code.gitea.io/gitea/modules/secret"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util"
"xorm.io/builder"
)
type ErrSecretInvalidValue struct {
Name *string
Data *string
}
func (err ErrSecretInvalidValue) Error() string {
if err.Name != nil {
return fmt.Sprintf("secret name %q is invalid", *err.Name)
}
if err.Data != nil {
return fmt.Sprintf("secret data %q is invalid", *err.Data)
}
return util.ErrInvalidArgument.Error()
}
func (err ErrSecretInvalidValue) Unwrap() error {
return util.ErrInvalidArgument
}
// Secret represents a secret
type Secret struct {
ID int64
@@ -74,24 +53,11 @@ func init() {
db.RegisterModel(new(Secret))
}
var (
secretNameReg = regexp.MustCompile("^[A-Z_][A-Z0-9_]*$")
forbiddenSecretPrefixReg = regexp.MustCompile("^GIT(EA|HUB)_")
)
// Validate validates the required fields and formats.
func (s *Secret) Validate() error {
switch {
case len(s.Name) == 0 || len(s.Name) > 50:
return ErrSecretInvalidValue{Name: &s.Name}
case len(s.Data) == 0:
return ErrSecretInvalidValue{Data: &s.Data}
case !secretNameReg.MatchString(s.Name) ||
forbiddenSecretPrefixReg.MatchString(s.Name):
return ErrSecretInvalidValue{Name: &s.Name}
default:
return nil
if s.OwnerID == 0 && s.RepoID == 0 {
return errors.New("the secret is not bound to any scope")
}
return nil
}
type FindSecretsOptions struct {
+8 -6
View File
@@ -42,12 +42,14 @@ func fatalTestError(fmtStr string, args ...interface{}) {
os.Exit(1)
}
// InitSettings initializes config provider and load common setttings for tests
// InitSettings initializes config provider and load common settings for tests
func InitSettings(extraConfigs ...string) {
setting.Init(&setting.Options{
AllowEmpty: true,
ExtraConfig: strings.Join(extraConfigs, "\n"),
})
if setting.CustomConf == "" {
setting.CustomConf = filepath.Join(setting.CustomPath, "conf/app-unittest-tmp.ini")
_ = os.Remove(setting.CustomConf)
}
setting.InitCfgProvider(setting.CustomConf, strings.Join(extraConfigs, "\n"))
setting.LoadCommonSettings()
if err := setting.PrepareAppDataPath(); err != nil {
log.Fatalf("Can not prepare APP_DATA_PATH: %v", err)
@@ -69,7 +71,7 @@ type TestOptions struct {
// MainTest a reusable TestMain(..) function for unit tests that need to use a
// test database. Creates the test database, and sets necessary settings.
func MainTest(m *testing.M, testOpts *TestOptions) {
setting.SetCustomPathAndConf("", "", "")
setting.CustomPath = filepath.Join(testOpts.GiteaRootPath, "custom")
InitSettings()
var err error
+2
View File
@@ -8,6 +8,8 @@ const (
SettingsKeyHiddenCommentTypes = "issue.hidden_comment_types"
// SettingsKeyDiffWhitespaceBehavior is the setting key for whitespace behavior of diff
SettingsKeyDiffWhitespaceBehavior = "diff.whitespace_behaviour"
// SettingsKeyShowOutdatedComments is the setting key wether or not to show outdated comments in PRs
SettingsKeyShowOutdatedComments = "comment_code.show_outdated"
// UserActivityPubPrivPem is user's private key
UserActivityPubPrivPem = "activitypub.priv_pem"
// UserActivityPubPubPem is user's public key