Skip to content

Commit 0f8aa9a

Browse files
authored
Merge branch 'main' into deps-88
2 parents 409af29 + 950abfe commit 0f8aa9a

File tree

24 files changed

+1298
-90
lines changed

24 files changed

+1298
-90
lines changed

cmd/migrate_storage.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ func migrateActionsLog(ctx context.Context, dstStorage storage.ObjectStorage) er
196196

197197
func migrateActionsArtifacts(ctx context.Context, dstStorage storage.ObjectStorage) error {
198198
return db.Iterate(ctx, nil, func(ctx context.Context, artifact *actions_model.ActionArtifact) error {
199-
if artifact.Status == int64(actions_model.ArtifactStatusExpired) {
199+
if artifact.Status == actions_model.ArtifactStatusExpired {
200200
return nil
201201
}
202202

models/actions/artifact.go

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ type ActionArtifact struct {
4848
ContentEncoding string // The content encoding of the artifact
4949
ArtifactPath string `xorm:"index unique(runid_name_path)"` // The path to the artifact when runner uploads it
5050
ArtifactName string `xorm:"index unique(runid_name_path)"` // The name of the artifact when runner uploads it
51-
Status int64 `xorm:"index"` // The status of the artifact, uploading, expired or need-delete
51+
Status ArtifactStatus `xorm:"index"` // The status of the artifact, uploading, expired or need-delete
5252
CreatedUnix timeutil.TimeStamp `xorm:"created"`
5353
UpdatedUnix timeutil.TimeStamp `xorm:"updated index"`
5454
ExpiredUnix timeutil.TimeStamp `xorm:"index"` // The time when the artifact will be expired
@@ -68,7 +68,7 @@ func CreateArtifact(ctx context.Context, t *ActionTask, artifactName, artifactPa
6868
RepoID: t.RepoID,
6969
OwnerID: t.OwnerID,
7070
CommitSHA: t.CommitSHA,
71-
Status: int64(ArtifactStatusUploadPending),
71+
Status: ArtifactStatusUploadPending,
7272
ExpiredUnix: timeutil.TimeStamp(time.Now().Unix() + timeutil.Day*expiredDays),
7373
}
7474
if _, err := db.GetEngine(ctx).Insert(artifact); err != nil {
@@ -108,10 +108,11 @@ func UpdateArtifactByID(ctx context.Context, id int64, art *ActionArtifact) erro
108108

109109
type FindArtifactsOptions struct {
110110
db.ListOptions
111-
RepoID int64
112-
RunID int64
113-
ArtifactName string
114-
Status int
111+
RepoID int64
112+
RunID int64
113+
ArtifactName string
114+
Status int
115+
FinalizedArtifactsV4 bool
115116
}
116117

117118
func (opts FindArtifactsOptions) ToOrders() string {
@@ -134,6 +135,10 @@ func (opts FindArtifactsOptions) ToConds() builder.Cond {
134135
if opts.Status > 0 {
135136
cond = cond.And(builder.Eq{"status": opts.Status})
136137
}
138+
if opts.FinalizedArtifactsV4 {
139+
cond = cond.And(builder.Eq{"status": ArtifactStatusUploadConfirmed}.Or(builder.Eq{"status": ArtifactStatusExpired}))
140+
cond = cond.And(builder.Eq{"content_encoding": "application/zip"})
141+
}
137142

138143
return cond
139144
}
@@ -172,18 +177,18 @@ func ListPendingDeleteArtifacts(ctx context.Context, limit int) ([]*ActionArtifa
172177

173178
// SetArtifactExpired sets an artifact to expired
174179
func SetArtifactExpired(ctx context.Context, artifactID int64) error {
175-
_, err := db.GetEngine(ctx).Where("id=? AND status = ?", artifactID, ArtifactStatusUploadConfirmed).Cols("status").Update(&ActionArtifact{Status: int64(ArtifactStatusExpired)})
180+
_, err := db.GetEngine(ctx).Where("id=? AND status = ?", artifactID, ArtifactStatusUploadConfirmed).Cols("status").Update(&ActionArtifact{Status: ArtifactStatusExpired})
176181
return err
177182
}
178183

179184
// SetArtifactNeedDelete sets an artifact to need-delete, cron job will delete it
180185
func SetArtifactNeedDelete(ctx context.Context, runID int64, name string) error {
181-
_, err := db.GetEngine(ctx).Where("run_id=? AND artifact_name=? AND status = ?", runID, name, ArtifactStatusUploadConfirmed).Cols("status").Update(&ActionArtifact{Status: int64(ArtifactStatusPendingDeletion)})
186+
_, err := db.GetEngine(ctx).Where("run_id=? AND artifact_name=? AND status = ?", runID, name, ArtifactStatusUploadConfirmed).Cols("status").Update(&ActionArtifact{Status: ArtifactStatusPendingDeletion})
182187
return err
183188
}
184189

185190
// SetArtifactDeleted sets an artifact to deleted
186191
func SetArtifactDeleted(ctx context.Context, artifactID int64) error {
187-
_, err := db.GetEngine(ctx).ID(artifactID).Cols("status").Update(&ActionArtifact{Status: int64(ArtifactStatusDeleted)})
192+
_, err := db.GetEngine(ctx).ID(artifactID).Cols("status").Update(&ActionArtifact{Status: ArtifactStatusDeleted})
188193
return err
189194
}

models/fixtures/action_artifact.yml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,3 +69,21 @@
6969
created_unix: 1730330775
7070
updated_unix: 1730330775
7171
expired_unix: 1738106775
72+
73+
-
74+
id: 23
75+
run_id: 793
76+
runner_id: 1
77+
repo_id: 2
78+
owner_id: 2
79+
commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0
80+
storage_path: "27/5/1730330775594233150.chunk"
81+
file_size: 1024
82+
file_compressed_size: 1024
83+
content_encoding: "application/zip"
84+
artifact_path: "artifact-v4-download.zip"
85+
artifact_name: "artifact-v4-download"
86+
status: 2
87+
created_unix: 1730330775
88+
updated_unix: 1730330775
89+
expired_unix: 1738106775

models/user/user.go

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1187,20 +1187,24 @@ func GetUsersByEmails(ctx context.Context, emails []string) (map[string]*User, e
11871187
for _, email := range emailAddresses {
11881188
userIDs.Add(email.UID)
11891189
}
1190-
users, err := GetUsersByIDs(ctx, userIDs.Values())
1190+
users, err := GetUsersMapByIDs(ctx, userIDs.Values())
11911191
if err != nil {
11921192
return nil, err
11931193
}
11941194

11951195
results := make(map[string]*User, len(emails))
1196-
for _, user := range users {
1197-
if user.KeepEmailPrivate {
1198-
results[user.LowerName+"@"+setting.Service.NoReplyAddress] = user
1199-
} else {
1200-
results[user.Email] = user
1196+
for _, email := range emailAddresses {
1197+
user := users[email.UID]
1198+
if user != nil {
1199+
if user.KeepEmailPrivate {
1200+
results[user.LowerName+"@"+setting.Service.NoReplyAddress] = user
1201+
} else {
1202+
results[email.Email] = user
1203+
}
12011204
}
12021205
}
1203-
users = make([]*User, 0, len(needCheckUserNames))
1206+
1207+
users = make(map[int64]*User, len(needCheckUserNames))
12041208
if err := db.GetEngine(ctx).In("lower_name", needCheckUserNames.Values()).Find(&users); err != nil {
12051209
return nil, err
12061210
}

modules/actions/artifacts.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// Copyright 2025 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package actions
5+
6+
import (
7+
"net/http"
8+
9+
actions_model "code.gitea.io/gitea/models/actions"
10+
"code.gitea.io/gitea/modules/setting"
11+
"code.gitea.io/gitea/modules/storage"
12+
"code.gitea.io/gitea/services/context"
13+
)
14+
15+
// Artifacts using the v4 backend are stored as a single combined zip file per artifact on the backend
16+
// The v4 backend ensures ContentEncoding is set to "application/zip", which is not the case for the old backend
17+
func IsArtifactV4(art *actions_model.ActionArtifact) bool {
18+
return art.ArtifactName+".zip" == art.ArtifactPath && art.ContentEncoding == "application/zip"
19+
}
20+
21+
func DownloadArtifactV4ServeDirectOnly(ctx *context.Base, art *actions_model.ActionArtifact) (bool, error) {
22+
if setting.Actions.ArtifactStorage.ServeDirect() {
23+
u, err := storage.ActionsArtifacts.URL(art.StoragePath, art.ArtifactPath, nil)
24+
if u != nil && err == nil {
25+
ctx.Redirect(u.String(), http.StatusFound)
26+
return true, nil
27+
}
28+
}
29+
return false, nil
30+
}
31+
32+
func DownloadArtifactV4Fallback(ctx *context.Base, art *actions_model.ActionArtifact) error {
33+
f, err := storage.ActionsArtifacts.Open(art.StoragePath)
34+
if err != nil {
35+
return err
36+
}
37+
defer f.Close()
38+
http.ServeContent(ctx.Resp, ctx.Req, art.ArtifactName+".zip", art.CreatedUnix.AsLocalTime(), f)
39+
return nil
40+
}
41+
42+
func DownloadArtifactV4(ctx *context.Base, art *actions_model.ActionArtifact) error {
43+
ok, err := DownloadArtifactV4ServeDirectOnly(ctx, art)
44+
if ok || err != nil {
45+
return err
46+
}
47+
return DownloadArtifactV4Fallback(ctx, art)
48+
}

modules/indexer/code/bleve/bleve.go

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -260,17 +260,28 @@ func (b *Indexer) Search(ctx context.Context, opts *internal.SearchOptions) (int
260260
var (
261261
indexerQuery query.Query
262262
keywordQuery query.Query
263+
contentQuery query.Query
263264
)
264265

265266
pathQuery := bleve.NewPrefixQuery(strings.ToLower(opts.Keyword))
266267
pathQuery.FieldVal = "Filename"
267268
pathQuery.SetBoost(10)
268269

269-
contentQuery := bleve.NewMatchQuery(opts.Keyword)
270-
contentQuery.FieldVal = "Content"
271-
272-
if opts.IsKeywordFuzzy {
273-
contentQuery.Fuzziness = inner_bleve.GuessFuzzinessByKeyword(opts.Keyword)
270+
keywordAsPhrase, isPhrase := internal.ParseKeywordAsPhrase(opts.Keyword)
271+
if isPhrase {
272+
q := bleve.NewMatchPhraseQuery(keywordAsPhrase)
273+
q.FieldVal = "Content"
274+
if opts.IsKeywordFuzzy {
275+
q.Fuzziness = inner_bleve.GuessFuzzinessByKeyword(keywordAsPhrase)
276+
}
277+
contentQuery = q
278+
} else {
279+
q := bleve.NewMatchQuery(opts.Keyword)
280+
q.FieldVal = "Content"
281+
if opts.IsKeywordFuzzy {
282+
q.Fuzziness = inner_bleve.GuessFuzzinessByKeyword(opts.Keyword)
283+
}
284+
contentQuery = q
274285
}
275286

276287
keywordQuery = bleve.NewDisjunctionQuery(contentQuery, pathQuery)

modules/indexer/code/elasticsearch/elasticsearch.go

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"code.gitea.io/gitea/modules/setting"
2525
"code.gitea.io/gitea/modules/timeutil"
2626
"code.gitea.io/gitea/modules/typesniffer"
27+
"code.gitea.io/gitea/modules/util"
2728

2829
"github.com/go-enry/go-enry/v2"
2930
"github.com/olivere/elastic/v7"
@@ -359,13 +360,19 @@ func extractAggs(searchResult *elastic.SearchResult) []*internal.SearchResultLan
359360

360361
// Search searches for codes and language stats by given conditions.
361362
func (b *Indexer) Search(ctx context.Context, opts *internal.SearchOptions) (int64, []*internal.SearchResult, []*internal.SearchResultLanguages, error) {
362-
searchType := esMultiMatchTypePhrasePrefix
363-
if opts.IsKeywordFuzzy {
364-
searchType = esMultiMatchTypeBestFields
363+
var contentQuery elastic.Query
364+
keywordAsPhrase, isPhrase := internal.ParseKeywordAsPhrase(opts.Keyword)
365+
if isPhrase {
366+
contentQuery = elastic.NewMatchPhraseQuery("content", keywordAsPhrase)
367+
} else {
368+
// TODO: this is the old logic, but not really using "fuzziness"
369+
// * IsKeywordFuzzy=true: "best_fields"
370+
// * IsKeywordFuzzy=false: "phrase_prefix"
371+
contentQuery = elastic.NewMultiMatchQuery("content", opts.Keyword).
372+
Type(util.Iif(opts.IsKeywordFuzzy, esMultiMatchTypeBestFields, esMultiMatchTypePhrasePrefix))
365373
}
366-
367374
kwQuery := elastic.NewBoolQuery().Should(
368-
elastic.NewMultiMatchQuery(opts.Keyword, "content").Type(searchType),
375+
contentQuery,
369376
elastic.NewMultiMatchQuery(opts.Keyword, "filename^10").Type(esMultiMatchTypePhrasePrefix),
370377
)
371378
query := elastic.NewBoolQuery()
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// Copyright 2025 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package gitgrep
5+
6+
import (
7+
"context"
8+
"fmt"
9+
"strings"
10+
11+
"code.gitea.io/gitea/modules/git"
12+
code_indexer "code.gitea.io/gitea/modules/indexer/code"
13+
"code.gitea.io/gitea/modules/setting"
14+
)
15+
16+
func indexSettingToGitGrepPathspecList() (list []string) {
17+
for _, expr := range setting.Indexer.IncludePatterns {
18+
list = append(list, ":(glob)"+expr.PatternString())
19+
}
20+
for _, expr := range setting.Indexer.ExcludePatterns {
21+
list = append(list, ":(glob,exclude)"+expr.PatternString())
22+
}
23+
return list
24+
}
25+
26+
func PerformSearch(ctx context.Context, page int, repoID int64, gitRepo *git.Repository, ref git.RefName, keyword string, isFuzzy bool) (searchResults []*code_indexer.Result, total int, err error) {
27+
// TODO: it should also respect ParseKeywordAsPhrase and clarify the "fuzzy" behavior
28+
res, err := git.GrepSearch(ctx, gitRepo, keyword, git.GrepOptions{
29+
ContextLineNumber: 1,
30+
IsFuzzy: isFuzzy,
31+
RefName: ref.String(),
32+
PathspecList: indexSettingToGitGrepPathspecList(),
33+
})
34+
if err != nil {
35+
// TODO: if no branch exists, it reports: exit status 128, fatal: this operation must be run in a work tree.
36+
return nil, 0, fmt.Errorf("git.GrepSearch: %w", err)
37+
}
38+
commitID, err := gitRepo.GetRefCommitID(ref.String())
39+
if err != nil {
40+
return nil, 0, fmt.Errorf("gitRepo.GetRefCommitID: %w", err)
41+
}
42+
43+
total = len(res)
44+
pageStart := min((page-1)*setting.UI.RepoSearchPagingNum, len(res))
45+
pageEnd := min(page*setting.UI.RepoSearchPagingNum, len(res))
46+
res = res[pageStart:pageEnd]
47+
for _, r := range res {
48+
searchResults = append(searchResults, &code_indexer.Result{
49+
RepoID: repoID,
50+
Filename: r.Filename,
51+
CommitID: commitID,
52+
// UpdatedUnix: not supported yet
53+
// Language: not supported yet
54+
// Color: not supported yet
55+
Lines: code_indexer.HighlightSearchResultCode(r.Filename, "", r.LineNumbers, strings.Join(r.LineCodes, "\n")),
56+
})
57+
}
58+
return searchResults, total, nil
59+
}

routers/web/repo/search_test.go renamed to modules/indexer/code/gitgrep/gitgrep_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Copyright 2024 The Gitea Authors. All rights reserved.
22
// SPDX-License-Identifier: MIT
33

4-
package repo
4+
package gitgrep
55

66
import (
77
"testing"

modules/indexer/code/indexer.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,11 @@ var (
2929
// When the real indexer is not ready, it will be a dummy indexer which will return error to explain it's not ready.
3030
// So it's always safe use it as *globalIndexer.Load() and call its methods.
3131
globalIndexer atomic.Pointer[internal.Indexer]
32-
dummyIndexer *internal.Indexer
3332
)
3433

3534
func init() {
36-
i := internal.NewDummyIndexer()
37-
dummyIndexer = &i
38-
globalIndexer.Store(dummyIndexer)
35+
dummyIndexer := internal.NewDummyIndexer()
36+
globalIndexer.Store(&dummyIndexer)
3937
}
4038

4139
func index(ctx context.Context, indexer internal.Indexer, repoID int64) error {

0 commit comments

Comments
 (0)