Merge pull request '[gitea] week 13 cherry-pick' (#2769) from earl-warren/forgejo:wip-gitea-cherry-pick into forgejo

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/2769
Reviewed-by: oliverpool <oliverpool@noreply.codeberg.org>
This commit is contained in:
Earl Warren 2024-03-26 18:45:57 +00:00
commit 8339b24d61
302 changed files with 2569 additions and 5061 deletions

View file

@ -100,7 +100,7 @@ func ReadLogs(ctx context.Context, inStorage bool, filename string, offset, limi
}
if err := scanner.Err(); err != nil {
return nil, fmt.Errorf("scan: %w", err)
return nil, fmt.Errorf("ReadLogs scan: %w", err)
}
return rows, nil

View file

@ -41,6 +41,12 @@ func FullSteps(task *actions_model.ActionTask) []*actions_model.ActionTaskStep {
}
logIndex += preStep.LogLength
// lastHasRunStep is the last step that has run.
// For example,
// 1. preStep(Success) -> step1(Success) -> step2(Running) -> step3(Waiting) -> postStep(Waiting): lastHasRunStep is step1.
// 2. preStep(Success) -> step1(Success) -> step2(Success) -> step3(Success) -> postStep(Success): lastHasRunStep is step3.
// 3. preStep(Success) -> step1(Success) -> step2(Failure) -> step3 -> postStep(Waiting): lastHasRunStep is step2.
// So its Stopped is the Started of postStep when there are no more steps to run.
var lastHasRunStep *actions_model.ActionTaskStep
for _, step := range task.Steps {
if step.Status.HasRun() {
@ -56,11 +62,15 @@ func FullSteps(task *actions_model.ActionTask) []*actions_model.ActionTaskStep {
Name: postStepName,
Status: actions_model.StatusWaiting,
}
if task.Status.IsDone() {
// If the lastHasRunStep is the last step, or it has failed, postStep has started.
if lastHasRunStep.Status.IsFailure() || lastHasRunStep == task.Steps[len(task.Steps)-1] {
postStep.LogIndex = logIndex
postStep.LogLength = task.LogLength - postStep.LogIndex
postStep.Status = task.Status
postStep.Started = lastHasRunStep.Stopped
postStep.Status = actions_model.StatusRunning
}
if task.Status.IsDone() {
postStep.Status = task.Status
postStep.Stopped = task.Stopped
}
ret := make([]*actions_model.ActionTaskStep, 0, len(task.Steps)+2)

View file

@ -103,6 +103,40 @@ func TestFullSteps(t *testing.T) {
{Name: postStepName, Status: actions_model.StatusSuccess, LogIndex: 100, LogLength: 0, Started: 10100, Stopped: 10100},
},
},
{
name: "all steps finished but task is running",
task: &actions_model.ActionTask{
Steps: []*actions_model.ActionTaskStep{
{Status: actions_model.StatusSuccess, LogIndex: 10, LogLength: 80, Started: 10010, Stopped: 10090},
},
Status: actions_model.StatusRunning,
Started: 10000,
Stopped: 0,
LogLength: 100,
},
want: []*actions_model.ActionTaskStep{
{Name: preStepName, Status: actions_model.StatusSuccess, LogIndex: 0, LogLength: 10, Started: 10000, Stopped: 10010},
{Status: actions_model.StatusSuccess, LogIndex: 10, LogLength: 80, Started: 10010, Stopped: 10090},
{Name: postStepName, Status: actions_model.StatusRunning, LogIndex: 90, LogLength: 10, Started: 10090, Stopped: 0},
},
},
{
name: "skipped task",
task: &actions_model.ActionTask{
Steps: []*actions_model.ActionTaskStep{
{Status: actions_model.StatusSkipped, LogIndex: 0, LogLength: 0, Started: 0, Stopped: 0},
},
Status: actions_model.StatusSkipped,
Started: 0,
Stopped: 0,
LogLength: 0,
},
want: []*actions_model.ActionTaskStep{
{Name: preStepName, Status: actions_model.StatusSkipped, LogIndex: 0, LogLength: 0, Started: 0, Stopped: 0},
{Status: actions_model.StatusSkipped, LogIndex: 0, LogLength: 0, Started: 0, Stopped: 0},
{Name: postStepName, Status: actions_model.StatusSkipped, LogIndex: 0, LogLength: 0, Started: 0, Stopped: 0},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {

View file

@ -150,13 +150,16 @@ func TruncateString(str string, limit int) string {
// StringsToInt64s converts a slice of string to a slice of int64.
func StringsToInt64s(strs []string) ([]int64, error) {
ints := make([]int64, len(strs))
for i := range strs {
n, err := strconv.ParseInt(strs[i], 10, 64)
if strs == nil {
return nil, nil
}
ints := make([]int64, 0, len(strs))
for _, s := range strs {
n, err := strconv.ParseInt(s, 10, 64)
if err != nil {
return ints, err
return nil, err
}
ints[i] = n
ints = append(ints, n)
}
return ints, nil
}

View file

@ -138,12 +138,13 @@ func TestStringsToInt64s(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, expected, result)
}
testSuccess(nil, nil)
testSuccess([]string{}, []int64{})
testSuccess([]string{"-1234"}, []int64{-1234})
testSuccess([]string{"1", "4", "16", "64", "256"},
[]int64{1, 4, 16, 64, 256})
testSuccess([]string{"1", "4", "16", "64", "256"}, []int64{1, 4, 16, 64, 256})
_, err := StringsToInt64s([]string{"-1", "a", "$"})
ints, err := StringsToInt64s([]string{"-1", "a"})
assert.Len(t, ints, 0)
assert.Error(t, err)
}

View file

@ -9,6 +9,7 @@ import (
"bytes"
"context"
"errors"
"fmt"
"io"
"os/exec"
"strconv"
@ -390,6 +391,9 @@ func (c *Commit) GetSubModules() (*ObjectCache, error) {
}
}
}
if err = scanner.Err(); err != nil {
return nil, fmt.Errorf("GetSubModules scan: %w", err)
}
return c.submoduleCache, nil
}

View file

@ -283,7 +283,7 @@ type DivergeObject struct {
// GetDivergingCommits returns the number of commits a targetBranch is ahead or behind a baseBranch
func GetDivergingCommits(ctx context.Context, repoPath, baseBranch, targetBranch string) (do DivergeObject, err error) {
cmd := NewCommand(ctx, "rev-list", "--count", "--left-right").
AddDynamicArguments(baseBranch + "..." + targetBranch)
AddDynamicArguments(baseBranch + "..." + targetBranch).AddArguments("--")
stdout, _, err := cmd.RunStdString(&RunOpts{Dir: repoPath})
if err != nil {
return do, err

View file

@ -124,6 +124,10 @@ func (repo *Repository) GetCodeActivityStats(fromTime time.Time, branch string)
}
}
}
if err = scanner.Err(); err != nil {
_ = stdoutReader.Close()
return fmt.Errorf("GetCodeActivityStats scan: %w", err)
}
a := make([]*CodeActivityAuthor, 0, len(authors))
for _, v := range authors {
a = append(a, v)

View file

@ -59,8 +59,8 @@ func (g *Manager) start() {
go func() {
defer func() {
close(startupDone)
// Close the unused listeners and ignore the error here there's not much we can do with it, they're logged in the CloseProvidedListeners function
_ = CloseProvidedListeners()
// Close the unused listeners
closeProvidedListeners()
}()
// Wait for all servers to be created
g.createServerCond.L.Lock()

View file

@ -129,25 +129,17 @@ func getProvidedFDs() (savedErr error) {
return savedErr
}
// CloseProvidedListeners closes all unused provided listeners.
func CloseProvidedListeners() error {
// closeProvidedListeners closes all unused provided listeners.
func closeProvidedListeners() {
mutex.Lock()
defer mutex.Unlock()
var returnableError error
for _, l := range providedListeners {
err := l.Close()
if err != nil {
log.Error("Error in closing unused provided listener: %v", err)
if returnableError != nil {
returnableError = fmt.Errorf("%v & %w", returnableError, err)
} else {
returnableError = err
}
}
}
providedListeners = []net.Listener{}
return returnableError
}
// DefaultGetListener obtains a listener for the stream-oriented local network address:

View file

@ -39,6 +39,8 @@ import (
const (
unicodeNormalizeName = "unicodeNormalize"
maxBatchSize = 16
// fuzzyDenominator determines the levenshtein distance per each character of a keyword
fuzzyDenominator = 4
)
func addUnicodeNormalizeTokenFilter(m *mapping.IndexMappingImpl) error {
@ -239,15 +241,12 @@ func (b *Indexer) Search(ctx context.Context, opts *internal.SearchOptions) (int
keywordQuery query.Query
)
phraseQuery := bleve.NewMatchPhraseQuery(opts.Keyword)
phraseQuery.FieldVal = "Content"
phraseQuery.Analyzer = repoIndexerAnalyzer
keywordQuery = phraseQuery
if opts.IsKeywordFuzzy {
phraseQuery := bleve.NewMatchPhraseQuery(opts.Keyword)
phraseQuery.FieldVal = "Content"
phraseQuery.Analyzer = repoIndexerAnalyzer
keywordQuery = phraseQuery
} else {
prefixQuery := bleve.NewPrefixQuery(opts.Keyword)
prefixQuery.FieldVal = "Content"
keywordQuery = prefixQuery
phraseQuery.Fuzziness = len(opts.Keyword) / fuzzyDenominator
}
if len(opts.RepoIDs) > 0 {

View file

@ -20,17 +20,11 @@ func NumericEqualityQuery(value int64, field string) *query.NumericRangeQuery {
}
// MatchPhraseQuery generates a match phrase query for the given phrase, field and analyzer
func MatchPhraseQuery(matchPhrase, field, analyzer string) *query.MatchPhraseQuery {
func MatchPhraseQuery(matchPhrase, field, analyzer string, fuzziness int) *query.MatchPhraseQuery {
q := bleve.NewMatchPhraseQuery(matchPhrase)
q.FieldVal = field
q.Analyzer = analyzer
return q
}
// PrefixQuery generates a match prefix query for the given prefix and field
func PrefixQuery(matchPrefix, field string) *query.PrefixQuery {
q := bleve.NewPrefixQuery(matchPrefix)
q.FieldVal = field
q.Fuzziness = fuzziness
return q
}

View file

@ -35,7 +35,11 @@ func addUnicodeNormalizeTokenFilter(m *mapping.IndexMappingImpl) error {
})
}
const maxBatchSize = 16
const (
maxBatchSize = 16
// fuzzyDenominator determines the levenshtein distance per each character of a keyword
fuzzyDenominator = 4
)
// IndexerData an update to the issue indexer
type IndexerData internal.IndexerData
@ -156,19 +160,16 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (
var queries []query.Query
if options.Keyword != "" {
fuzziness := 0
if options.IsFuzzyKeyword {
queries = append(queries, bleve.NewDisjunctionQuery([]query.Query{
inner_bleve.MatchPhraseQuery(options.Keyword, "title", issueIndexerAnalyzer),
inner_bleve.MatchPhraseQuery(options.Keyword, "content", issueIndexerAnalyzer),
inner_bleve.MatchPhraseQuery(options.Keyword, "comments", issueIndexerAnalyzer),
}...))
} else {
queries = append(queries, bleve.NewDisjunctionQuery([]query.Query{
inner_bleve.PrefixQuery(options.Keyword, "title"),
inner_bleve.PrefixQuery(options.Keyword, "content"),
inner_bleve.PrefixQuery(options.Keyword, "comments"),
}...))
fuzziness = len(options.Keyword) / fuzzyDenominator
}
queries = append(queries, bleve.NewDisjunctionQuery([]query.Query{
inner_bleve.MatchPhraseQuery(options.Keyword, "title", issueIndexerAnalyzer, fuzziness),
inner_bleve.MatchPhraseQuery(options.Keyword, "content", issueIndexerAnalyzer, fuzziness),
inner_bleve.MatchPhraseQuery(options.Keyword, "comments", issueIndexerAnalyzer, fuzziness),
}...))
}
if len(options.RepoIDs) > 0 || options.AllPublic {

View file

@ -515,10 +515,8 @@ var cases = []*testIndexerCase{
{
Name: "SortByCreatedDesc",
SearchOptions: &internal.SearchOptions{
Paginator: &db.ListOptions{
ListAll: true,
},
SortBy: internal.SortByCreatedDesc,
Paginator: &db.ListOptionsAll,
SortBy: internal.SortByCreatedDesc,
},
Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) {
assert.Equal(t, len(data), len(result.Hits))
@ -533,10 +531,8 @@ var cases = []*testIndexerCase{
{
Name: "SortByUpdatedDesc",
SearchOptions: &internal.SearchOptions{
Paginator: &db.ListOptions{
ListAll: true,
},
SortBy: internal.SortByUpdatedDesc,
Paginator: &db.ListOptionsAll,
SortBy: internal.SortByUpdatedDesc,
},
Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) {
assert.Equal(t, len(data), len(result.Hits))
@ -551,10 +547,8 @@ var cases = []*testIndexerCase{
{
Name: "SortByCommentsDesc",
SearchOptions: &internal.SearchOptions{
Paginator: &db.ListOptions{
ListAll: true,
},
SortBy: internal.SortByCommentsDesc,
Paginator: &db.ListOptionsAll,
SortBy: internal.SortByCommentsDesc,
},
Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) {
assert.Equal(t, len(data), len(result.Hits))
@ -569,10 +563,8 @@ var cases = []*testIndexerCase{
{
Name: "SortByDeadlineDesc",
SearchOptions: &internal.SearchOptions{
Paginator: &db.ListOptions{
ListAll: true,
},
SortBy: internal.SortByDeadlineDesc,
Paginator: &db.ListOptionsAll,
SortBy: internal.SortByDeadlineDesc,
},
Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) {
assert.Equal(t, len(data), len(result.Hits))
@ -587,10 +579,8 @@ var cases = []*testIndexerCase{
{
Name: "SortByCreatedAsc",
SearchOptions: &internal.SearchOptions{
Paginator: &db.ListOptions{
ListAll: true,
},
SortBy: internal.SortByCreatedAsc,
Paginator: &db.ListOptionsAll,
SortBy: internal.SortByCreatedAsc,
},
Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) {
assert.Equal(t, len(data), len(result.Hits))
@ -605,10 +595,8 @@ var cases = []*testIndexerCase{
{
Name: "SortByUpdatedAsc",
SearchOptions: &internal.SearchOptions{
Paginator: &db.ListOptions{
ListAll: true,
},
SortBy: internal.SortByUpdatedAsc,
Paginator: &db.ListOptionsAll,
SortBy: internal.SortByUpdatedAsc,
},
Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) {
assert.Equal(t, len(data), len(result.Hits))
@ -623,10 +611,8 @@ var cases = []*testIndexerCase{
{
Name: "SortByCommentsAsc",
SearchOptions: &internal.SearchOptions{
Paginator: &db.ListOptions{
ListAll: true,
},
SortBy: internal.SortByCommentsAsc,
Paginator: &db.ListOptionsAll,
SortBy: internal.SortByCommentsAsc,
},
Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) {
assert.Equal(t, len(data), len(result.Hits))
@ -641,10 +627,8 @@ var cases = []*testIndexerCase{
{
Name: "SortByDeadlineAsc",
SearchOptions: &internal.SearchOptions{
Paginator: &db.ListOptions{
ListAll: true,
},
SortBy: internal.SortByDeadlineAsc,
Paginator: &db.ListOptionsAll,
SortBy: internal.SortByDeadlineAsc,
},
Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) {
assert.Equal(t, len(data), len(result.Hits))

View file

@ -61,9 +61,7 @@ func getIssueIndexerData(ctx context.Context, issueID int64) (*internal.IndexerD
)
{
reviews, err := issue_model.FindReviews(ctx, issue_model.FindReviewOptions{
ListOptions: db.ListOptions{
ListAll: true,
},
ListOptions: db.ListOptionsAll,
IssueID: issueID,
OfficialOnly: false,
})

View file

@ -6,6 +6,7 @@ package markup
import (
"bufio"
"bytes"
"fmt"
"html"
"io"
"regexp"
@ -123,6 +124,9 @@ func (Renderer) fallbackRender(input io.Reader, tmpBlock *bufio.Writer) error {
return err
}
}
if err = scan.Err(); err != nil {
return fmt.Errorf("fallbackRender scan: %w", err)
}
_, err = tmpBlock.WriteString("</pre>")
if err != nil {

View file

@ -54,7 +54,7 @@ func Sorted[S ~[]E, E cmp.Ordered](values S) S {
return values
}
// TODO: Replace with "maps.Values" once available
// TODO: Replace with "maps.Values" once available, current it only in golang.org/x/exp/maps but not in standard library
func ValuesOfMap[K comparable, V any](m map[K]V) []V {
values := make([]V, 0, len(m))
for _, v := range m {
@ -62,3 +62,12 @@ func ValuesOfMap[K comparable, V any](m map[K]V) []V {
}
return values
}
// TODO: Replace with "maps.Keys" once available, current it only in golang.org/x/exp/maps but not in standard library
func KeysOfMap[K comparable, V any](m map[K]V) []K {
keys := make([]K, 0, len(m))
for k := range m {
keys = append(keys, k)
}
return keys
}