Skip to content

Commit 1416f20

Browse files
authored
Merge branch 'main' into feat/latest-run-view
2 parents ef35fcb + 42841aa commit 1416f20

File tree

31 files changed

+922
-73
lines changed

31 files changed

+922
-73
lines changed

.github/labeler.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,3 +75,8 @@ modifies/js:
7575
- any-glob-to-any-file:
7676
- "**/*.js"
7777
- "**/*.vue"
78+
79+
docs-update-needed:
80+
- changed-files:
81+
- any-glob-to-any-file:
82+
- "custom/conf/app.example.ini"

assets/go-licenses.json

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

custom/conf/app.example.ini

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2687,6 +2687,12 @@ LEVEL = Info
26872687
;DEFAULT_ACTIONS_URL = github
26882688
;; Logs retention time in days. Old logs will be deleted after this period.
26892689
;LOG_RETENTION_DAYS = 365
2690+
;; Log compression type, `none` for no compression, `zstd` for zstd compression.
2691+
;; Other compression types like `gzip` are NOT supported, since seekable stream is required for log view.
2692+
;; It's always recommended to use compression when using local disk as log storage if CPU or memory is not a bottleneck.
2693+
;; And for object storage services like S3, which is billed for requests, it would cause extra 2 times of get requests for each log view.
2694+
;; But it will save storage space and network bandwidth, so it's still recommended to use compression.
2695+
;LOG_COMPRESSION = none
26902696
;; Default artifact retention time in days. Artifacts could have their own retention periods by setting the `retention-days` option in `actions/upload-artifact` step.
26912697
;ARTIFACT_RETENTION_DAYS = 90
26922698
;; Timeout to stop the task which have running status, but haven't been updated for a long time

go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ require (
2020
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358
2121
github.com/ProtonMail/go-crypto v1.0.0
2222
github.com/PuerkitoBio/goquery v1.9.2
23+
github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.7.2
2324
github.com/alecthomas/chroma/v2 v2.14.0
2425
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb
2526
github.com/blevesearch/bleve/v2 v2.4.2
@@ -209,6 +210,7 @@ require (
209210
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
210211
github.com/golang/protobuf v1.5.4 // indirect
211212
github.com/golang/snappy v0.0.4 // indirect
213+
github.com/google/btree v1.1.2 // indirect
212214
github.com/google/go-querystring v1.1.0 // indirect
213215
github.com/google/go-tpm v0.9.0 // indirect
214216
github.com/gorilla/css v1.0.1 // indirect

go.sum

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ github.com/RoaringBitmap/roaring v0.4.23/go.mod h1:D0gp8kJQgE1A4LQ5wFLggQEyvDi06
8080
github.com/RoaringBitmap/roaring v0.7.1/go.mod h1:jdT9ykXwHFNdJbEtxePexlFYH9LXucApeS0/+/g+p1I=
8181
github.com/RoaringBitmap/roaring v1.9.4 h1:yhEIoH4YezLYT04s1nHehNO64EKFTop/wBhxv2QzDdQ=
8282
github.com/RoaringBitmap/roaring v1.9.4/go.mod h1:6AXUsoIEzDTFFQCe1RbGA6uFONMhvejWj5rqITANK90=
83+
github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.7.2 h1:cSXom2MoKJ9KPPw29RoZtHvUETY4F4n/kXl8m9btnQ0=
84+
github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.7.2/go.mod h1:JitQWJ8JuV4Y87l8VsHiiwhb3cgdyn68mX40s7NT6PA=
8385
github.com/alecthomas/assert/v2 v2.7.0 h1:QtqSACNS3tF7oasA8CU6A6sXZSBDqnm7RfpLl9bZqbE=
8486
github.com/alecthomas/assert/v2 v2.7.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
8587
github.com/alecthomas/chroma/v2 v2.2.0/go.mod h1:vf4zrexSH54oEjJ7EdB65tGNHmH3pGZmVkgTP5RHvAs=
@@ -395,6 +397,8 @@ github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEW
395397
github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
396398
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
397399
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
400+
github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
401+
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
398402
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
399403
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
400404
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=

models/actions/task.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -502,7 +502,13 @@ func convertTimestamp(timestamp *timestamppb.Timestamp) timeutil.TimeStamp {
502502
}
503503

504504
func logFileName(repoFullName string, taskID int64) string {
505-
return fmt.Sprintf("%s/%02x/%d.log", repoFullName, taskID%256, taskID)
505+
ret := fmt.Sprintf("%s/%02x/%d.log", repoFullName, taskID%256, taskID)
506+
507+
if setting.Actions.LogCompression.IsZstd() {
508+
ret += ".zst"
509+
}
510+
511+
return ret
506512
}
507513

508514
func getTaskIDFromCache(token string) int64 {

models/issues/comment.go

Lines changed: 50 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,13 @@ func (r RoleInRepo) LocaleHelper(lang translation.Locale) string {
222222
return lang.TrString("repo.issues.role." + string(r) + "_helper")
223223
}
224224

225+
// CommentMetaData stores metadata for a comment, these data will not be changed once inserted into database
226+
type CommentMetaData struct {
227+
ProjectColumnID int64 `json:"project_column_id,omitempty"`
228+
ProjectColumnTitle string `json:"project_column_title,omitempty"`
229+
ProjectTitle string `json:"project_title,omitempty"`
230+
}
231+
225232
// Comment represents a comment in commit and issue page.
226233
type Comment struct {
227234
ID int64 `xorm:"pk autoincr"`
@@ -295,6 +302,8 @@ type Comment struct {
295302
RefAction references.XRefAction `xorm:"SMALLINT"` // What happens if RefIssueID resolves
296303
RefIsPull bool
297304

305+
CommentMetaData *CommentMetaData `xorm:"JSON TEXT"` // put all non-index metadata in a single field
306+
298307
RefRepo *repo_model.Repository `xorm:"-"`
299308
RefIssue *Issue `xorm:"-"`
300309
RefComment *Comment `xorm:"-"`
@@ -797,6 +806,15 @@ func CreateComment(ctx context.Context, opts *CreateCommentOptions) (_ *Comment,
797806
LabelID = opts.Label.ID
798807
}
799808

809+
var commentMetaData *CommentMetaData
810+
if opts.ProjectColumnTitle != "" {
811+
commentMetaData = &CommentMetaData{
812+
ProjectColumnID: opts.ProjectColumnID,
813+
ProjectColumnTitle: opts.ProjectColumnTitle,
814+
ProjectTitle: opts.ProjectTitle,
815+
}
816+
}
817+
800818
comment := &Comment{
801819
Type: opts.Type,
802820
PosterID: opts.Doer.ID,
@@ -830,6 +848,7 @@ func CreateComment(ctx context.Context, opts *CreateCommentOptions) (_ *Comment,
830848
RefIsPull: opts.RefIsPull,
831849
IsForcePush: opts.IsForcePush,
832850
Invalidated: opts.Invalidated,
851+
CommentMetaData: commentMetaData,
833852
}
834853
if _, err = e.Insert(comment); err != nil {
835854
return nil, err
@@ -982,34 +1001,37 @@ type CreateCommentOptions struct {
9821001
Issue *Issue
9831002
Label *Label
9841003

985-
DependentIssueID int64
986-
OldMilestoneID int64
987-
MilestoneID int64
988-
OldProjectID int64
989-
ProjectID int64
990-
TimeID int64
991-
AssigneeID int64
992-
AssigneeTeamID int64
993-
RemovedAssignee bool
994-
OldTitle string
995-
NewTitle string
996-
OldRef string
997-
NewRef string
998-
CommitID int64
999-
CommitSHA string
1000-
Patch string
1001-
LineNum int64
1002-
TreePath string
1003-
ReviewID int64
1004-
Content string
1005-
Attachments []string // UUIDs of attachments
1006-
RefRepoID int64
1007-
RefIssueID int64
1008-
RefCommentID int64
1009-
RefAction references.XRefAction
1010-
RefIsPull bool
1011-
IsForcePush bool
1012-
Invalidated bool
1004+
DependentIssueID int64
1005+
OldMilestoneID int64
1006+
MilestoneID int64
1007+
OldProjectID int64
1008+
ProjectID int64
1009+
ProjectTitle string
1010+
ProjectColumnID int64
1011+
ProjectColumnTitle string
1012+
TimeID int64
1013+
AssigneeID int64
1014+
AssigneeTeamID int64
1015+
RemovedAssignee bool
1016+
OldTitle string
1017+
NewTitle string
1018+
OldRef string
1019+
NewRef string
1020+
CommitID int64
1021+
CommitSHA string
1022+
Patch string
1023+
LineNum int64
1024+
TreePath string
1025+
ReviewID int64
1026+
Content string
1027+
Attachments []string // UUIDs of attachments
1028+
RefRepoID int64
1029+
RefIssueID int64
1030+
RefCommentID int64
1031+
RefAction references.XRefAction
1032+
RefIsPull bool
1033+
IsForcePush bool
1034+
Invalidated bool
10131035
}
10141036

10151037
// GetCommentByID returns the comment by given ID.

models/issues/issue_list.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,7 @@ func (issues IssueList) loadComments(ctx context.Context, cond builder.Cond) (er
441441
Join("INNER", "issue", "issue.id = comment.issue_id").
442442
In("issue.id", issuesIDs[:limit]).
443443
Where(cond).
444+
NoAutoCondition().
444445
Rows(new(Comment))
445446
if err != nil {
446447
return err

models/migrations/migrations.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -597,6 +597,8 @@ var migrations = []Migration{
597597
NewMigration("Add skip_secondary_authorization option to oauth2 application table", v1_23.AddSkipSecondaryAuthColumnToOAuth2ApplicationTable),
598598
// v302 -> v303
599599
NewMigration("Add index to action_task stopped log_expired", v1_23.AddIndexToActionTaskStoppedLogExpired),
600+
// v303 -> v304
601+
NewMigration("Add metadata column for comment table", v1_23.AddCommentMetaDataColumn),
600602
}
601603

602604
// GetCurrentDBVersion returns the current db version

models/migrations/v1_23/v303.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Copyright 2024 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package v1_23 //nolint
5+
6+
import (
7+
"xorm.io/xorm"
8+
)
9+
10+
// CommentMetaData stores metadata for a comment, these data will not be changed once inserted into database
11+
type CommentMetaData struct {
12+
ProjectColumnID int64 `json:"project_column_id"`
13+
ProjectColumnTitle string `json:"project_column_title"`
14+
ProjectTitle string `json:"project_title"`
15+
}
16+
17+
func AddCommentMetaDataColumn(x *xorm.Engine) error {
18+
type Comment struct {
19+
CommentMetaData *CommentMetaData `xorm:"JSON TEXT"` // put all non-index metadata in a single field
20+
}
21+
22+
return x.Sync(new(Comment))
23+
}

models/project/issue.go

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -76,30 +76,6 @@ func (p *Project) NumOpenIssues(ctx context.Context) int {
7676
return int(c)
7777
}
7878

79-
// MoveIssuesOnProjectColumn moves or keeps issues in a column and sorts them inside that column
80-
func MoveIssuesOnProjectColumn(ctx context.Context, column *Column, sortedIssueIDs map[int64]int64) error {
81-
return db.WithTx(ctx, func(ctx context.Context) error {
82-
sess := db.GetEngine(ctx)
83-
issueIDs := util.ValuesOfMap(sortedIssueIDs)
84-
85-
count, err := sess.Table(new(ProjectIssue)).Where("project_id=?", column.ProjectID).In("issue_id", issueIDs).Count()
86-
if err != nil {
87-
return err
88-
}
89-
if int(count) != len(sortedIssueIDs) {
90-
return fmt.Errorf("all issues have to be added to a project first")
91-
}
92-
93-
for sorting, issueID := range sortedIssueIDs {
94-
_, err = sess.Exec("UPDATE `project_issue` SET project_board_id=?, sorting=? WHERE issue_id=?", column.ID, sorting, issueID)
95-
if err != nil {
96-
return err
97-
}
98-
}
99-
return nil
100-
})
101-
}
102-
10379
func (c *Column) moveIssuesToAnotherColumn(ctx context.Context, newColumn *Column) error {
10480
if c.ProjectID != newColumn.ProjectID {
10581
return fmt.Errorf("columns have to be in the same project")

modules/actions/log.go

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"code.gitea.io/gitea/models/dbfs"
1616
"code.gitea.io/gitea/modules/log"
1717
"code.gitea.io/gitea/modules/storage"
18+
"code.gitea.io/gitea/modules/zstd"
1819

1920
runnerv1 "code.gitea.io/actions-proto-go/runner/v1"
2021
"google.golang.org/protobuf/types/known/timestamppb"
@@ -28,6 +29,9 @@ const (
2829
defaultBufSize = MaxLineSize
2930
)
3031

32+
// WriteLogs appends logs to DBFS file for temporary storage.
33+
// It doesn't respect the file format in the filename like ".zst", since it's difficult to reopen a closed compressed file and append new content.
34+
// Why doesn't it store logs in object storage directly? Because it's not efficient to append content to object storage.
3135
func WriteLogs(ctx context.Context, filename string, offset int64, rows []*runnerv1.LogRow) ([]int, error) {
3236
flag := os.O_WRONLY
3337
if offset == 0 {
@@ -106,6 +110,17 @@ func ReadLogs(ctx context.Context, inStorage bool, filename string, offset, limi
106110
return rows, nil
107111
}
108112

113+
const (
114+
// logZstdBlockSize is the block size for zstd compression.
115+
// 128KB leads the compression ratio to be close to the regular zstd compression.
116+
// And it means each read from the underlying object storage will be at least 128KB*(compression ratio).
117+
// The compression ratio is about 30% for text files, so the actual read size is about 38KB, which should be acceptable.
118+
logZstdBlockSize = 128 * 1024 // 128KB
119+
)
120+
121+
// TransferLogs transfers logs from DBFS to object storage.
122+
// It happens when the file is complete and no more logs will be appended.
123+
// It respects the file format in the filename like ".zst", and compresses the content if needed.
109124
func TransferLogs(ctx context.Context, filename string) (func(), error) {
110125
name := DBFSPrefix + filename
111126
remove := func() {
@@ -119,7 +134,26 @@ func TransferLogs(ctx context.Context, filename string) (func(), error) {
119134
}
120135
defer f.Close()
121136

122-
if _, err := storage.Actions.Save(filename, f, -1); err != nil {
137+
var reader io.Reader = f
138+
if strings.HasSuffix(filename, ".zst") {
139+
r, w := io.Pipe()
140+
reader = r
141+
zstdWriter, err := zstd.NewSeekableWriter(w, logZstdBlockSize)
142+
if err != nil {
143+
return nil, fmt.Errorf("zstd NewSeekableWriter: %w", err)
144+
}
145+
go func() {
146+
defer func() {
147+
_ = w.CloseWithError(zstdWriter.Close())
148+
}()
149+
if _, err := io.Copy(zstdWriter, f); err != nil {
150+
_ = w.CloseWithError(err)
151+
return
152+
}
153+
}()
154+
}
155+
156+
if _, err := storage.Actions.Save(filename, reader, -1); err != nil {
123157
return nil, fmt.Errorf("storage save %q: %w", filename, err)
124158
}
125159
return remove, nil
@@ -150,11 +184,22 @@ func OpenLogs(ctx context.Context, inStorage bool, filename string) (io.ReadSeek
150184
}
151185
return f, nil
152186
}
187+
153188
f, err := storage.Actions.Open(filename)
154189
if err != nil {
155190
return nil, fmt.Errorf("storage open %q: %w", filename, err)
156191
}
157-
return f, nil
192+
193+
var reader io.ReadSeekCloser = f
194+
if strings.HasSuffix(filename, ".zst") {
195+
r, err := zstd.NewSeekableReader(f)
196+
if err != nil {
197+
return nil, fmt.Errorf("zstd NewSeekableReader: %w", err)
198+
}
199+
reader = r
200+
}
201+
202+
return reader, nil
158203
}
159204

160205
func FormatLog(timestamp time.Time, content string) string {

modules/git/repo_branch_gogit.go

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,28 +14,33 @@ import (
1414
"github.com/go-git/go-git/v5/plumbing/storer"
1515
)
1616

17-
// IsObjectExist returns true if given reference exists in the repository.
17+
// IsObjectExist returns true if the given object exists in the repository.
18+
// FIXME: Inconsistent behavior with nogogit edition
19+
// Unlike the implementation of IsObjectExist in nogogit edition, it does not support short hashes here.
20+
// For example, IsObjectExist("153f451") will return false, but it will return true in nogogit edition.
21+
// To fix this, the solution could be adding support for short hashes in gogit edition if it's really needed.
1822
func (repo *Repository) IsObjectExist(name string) bool {
1923
if name == "" {
2024
return false
2125
}
2226

23-
_, err := repo.gogitRepo.ResolveRevision(plumbing.Revision(name))
24-
27+
_, err := repo.gogitRepo.Object(plumbing.AnyObject, plumbing.NewHash(name))
2528
return err == nil
2629
}
2730

2831
// IsReferenceExist returns true if given reference exists in the repository.
32+
// FIXME: Inconsistent behavior with nogogit edition
33+
// Unlike the implementation of IsObjectExist in nogogit edition, it does not support blob hashes here.
34+
// For example, IsObjectExist([existing_blob_hash]) will return false, but it will return true in nogogit edition.
35+
// To fix this, the solution could be refusing to support blob hashes in nogogit edition since a blob hash is not a reference.
2936
func (repo *Repository) IsReferenceExist(name string) bool {
3037
if name == "" {
3138
return false
3239
}
3340

34-
reference, err := repo.gogitRepo.Reference(plumbing.ReferenceName(name), true)
35-
if err != nil {
36-
return false
37-
}
38-
return reference.Type() != plumbing.InvalidReference
41+
_, err := repo.gogitRepo.ResolveRevision(plumbing.Revision(name))
42+
43+
return err == nil
3944
}
4045

4146
// IsBranchExist returns true if given branch exists in current repository.

modules/git/repo_branch_nogogit.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import (
1616
"code.gitea.io/gitea/modules/log"
1717
)
1818

19-
// IsObjectExist returns true if given reference exists in the repository.
19+
// IsObjectExist returns true if the given object exists in the repository.
2020
func (repo *Repository) IsObjectExist(name string) bool {
2121
if name == "" {
2222
return false

0 commit comments

Comments
 (0)