0
0
mirror of https://github.com/go-gitea/gitea.git synced 2026-01-14 17:36:42 +01:00

Merge branch 'main' into lunny/introduce_combined_status

This commit is contained in:
Lunny Xiao 2025-05-26 12:11:39 -07:00
commit 0fe62db756
18 changed files with 135 additions and 81 deletions

View File

@ -307,7 +307,7 @@ type CommitStatusIndex struct {
MaxIndex int64 `xorm:"index"`
}
var getBase = func(ctx context.Context, repoID int64, sha string) *xorm.Session {
func makeRepoCommitQuery(ctx context.Context, repoID int64, sha string) *xorm.Session {
return db.GetEngine(ctx).Table(&CommitStatus{}).
Where("repo_id = ?", repoID).And("sha = ?", sha)
}
@ -315,7 +315,7 @@ var getBase = func(ctx context.Context, repoID int64, sha string) *xorm.Session
// GetLatestCommitStatus returns all statuses with a unique context for a given commit.
func GetLatestCommitStatus(ctx context.Context, repoID int64, sha string, listOptions db.ListOptions) ([]*CommitStatus, error) {
indices := make([]int64, 0, 10)
sess := getBase(ctx, repoID, sha).
sess := makeRepoCommitQuery(ctx, repoID, sha).
Select("max( `index` ) as `index`").
GroupBy("context_hash").
OrderBy("max( `index` ) desc")
@ -330,11 +330,12 @@ func GetLatestCommitStatus(ctx context.Context, repoID int64, sha string, listOp
if len(indices) == 0 {
return statuses, nil
}
return statuses, getBase(ctx, repoID, sha).And(builder.In("`index`", indices)).Find(&statuses)
err := makeRepoCommitQuery(ctx, repoID, sha).And(builder.In("`index`", indices)).Find(&statuses)
return statuses, err
}
func CountLatestCommitStatus(ctx context.Context, repoID int64, sha string) (int64, error) {
return getBase(ctx, repoID, sha).
return makeRepoCommitQuery(ctx, repoID, sha).
Select("count(context_hash)").
GroupBy("context_hash").
Count()

View File

@ -26,7 +26,7 @@ func TestGetCommitStatuses(t *testing.T) {
repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
sha1 := "1234123412341234123412341234123412341234"
sha1 := "1234123412341234123412341234123412341234" // the mocked commit ID in test fixtures
statuses, maxResults, err := db.FindAndCount[git_model.CommitStatus](db.DefaultContext, &git_model.CommitStatusOptions{
ListOptions: db.ListOptions{Page: 1, PageSize: 50},
@ -248,7 +248,7 @@ func TestGetCountLatestCommitStatus(t *testing.T) {
repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
sha1 := "1234123412341234123412341234123412341234"
sha1 := "1234123412341234123412341234123412341234" // the mocked commit ID in test fixtures
commitStatuses, err := git_model.GetLatestCommitStatus(db.DefaultContext, repo1.ID, sha1, db.ListOptions{
Page: 1,

View File

@ -88,6 +88,8 @@ func applySorts(sess *xorm.Session, sortType string, priorityRepoID int64) {
sess.Asc("issue.created_unix").Asc("issue.id")
case "recentupdate":
sess.Desc("issue.updated_unix").Desc("issue.created_unix").Desc("issue.id")
case "recentclose":
sess.Desc("issue.closed_unix").Desc("issue.created_unix").Desc("issue.id")
case "leastupdate":
sess.Asc("issue.updated_unix").Asc("issue.created_unix").Asc("issue.id")
case "mostcomment":

View File

@ -152,7 +152,8 @@ func PullRequests(ctx context.Context, baseRepoID int64, opts *PullRequestsOptio
applySorts(findSession, opts.SortType, 0)
findSession = db.SetSessionPagination(findSession, opts)
prs := make([]*PullRequest, 0, opts.PageSize)
return prs, maxResults, findSession.Find(&prs)
found := findSession.Find(&prs)
return prs, maxResults, found
}
// PullRequestList defines a list of pull requests

View File

@ -14,6 +14,7 @@ import (
"code.gitea.io/gitea/modules/setting"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestPullRequest_LoadAttributes(t *testing.T) {
@ -76,6 +77,47 @@ func TestPullRequestsNewest(t *testing.T) {
}
}
func TestPullRequests_Closed_RecentSortType(t *testing.T) {
// Issue ID | Closed At. | Updated At
// 2 | 1707270001 | 1707270001
// 3 | 1707271000 | 1707279999
// 11 | 1707279999 | 1707275555
tests := []struct {
sortType string
expectedIssueIDOrder []int64
}{
{"recentupdate", []int64{3, 11, 2}},
{"recentclose", []int64{11, 3, 2}},
}
assert.NoError(t, unittest.PrepareTestDatabase())
_, err := db.Exec(db.DefaultContext, "UPDATE issue SET closed_unix = 1707270001, updated_unix = 1707270001, is_closed = true WHERE id = 2")
require.NoError(t, err)
_, err = db.Exec(db.DefaultContext, "UPDATE issue SET closed_unix = 1707271000, updated_unix = 1707279999, is_closed = true WHERE id = 3")
require.NoError(t, err)
_, err = db.Exec(db.DefaultContext, "UPDATE issue SET closed_unix = 1707279999, updated_unix = 1707275555, is_closed = true WHERE id = 11")
require.NoError(t, err)
for _, test := range tests {
t.Run(test.sortType, func(t *testing.T) {
prs, _, err := issues_model.PullRequests(db.DefaultContext, 1, &issues_model.PullRequestsOptions{
ListOptions: db.ListOptions{
Page: 1,
},
State: "closed",
SortType: test.sortType,
})
require.NoError(t, err)
if assert.Len(t, prs, len(test.expectedIssueIDOrder)) {
for i := range test.expectedIssueIDOrder {
assert.Equal(t, test.expectedIssueIDOrder[i], prs[i].IssueID)
}
}
})
}
}
func TestLoadRequestedReviewers(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())

View File

@ -132,18 +132,22 @@ func (r *BlameReader) Close() error {
}
// CreateBlameReader creates reader for given repository, commit and file
func CreateBlameReader(ctx context.Context, objectFormat ObjectFormat, repoPath string, commit *Commit, file string, bypassBlameIgnore bool) (*BlameReader, error) {
reader, stdout, err := os.Pipe()
if err != nil {
return nil, err
}
func CreateBlameReader(ctx context.Context, objectFormat ObjectFormat, repoPath string, commit *Commit, file string, bypassBlameIgnore bool) (rd *BlameReader, err error) {
var ignoreRevsFileName string
var ignoreRevsFileCleanup func()
defer func() {
if err != nil && ignoreRevsFileCleanup != nil {
ignoreRevsFileCleanup()
}
}()
cmd := NewCommandNoGlobals("blame", "--porcelain")
var ignoreRevsFileName string
var ignoreRevsFileCleanup func() // TODO: maybe it should check the returned err in a defer func to make sure the cleanup could always be executed correctly
if DefaultFeatures().CheckVersionAtLeast("2.23") && !bypassBlameIgnore {
ignoreRevsFileName, ignoreRevsFileCleanup = tryCreateBlameIgnoreRevsFile(commit)
ignoreRevsFileName, ignoreRevsFileCleanup, err = tryCreateBlameIgnoreRevsFile(commit)
if err != nil && !IsErrNotExist(err) {
return nil, err
}
if ignoreRevsFileName != "" {
// Possible improvement: use --ignore-revs-file /dev/stdin on unix
// There is no equivalent on Windows. May be implemented if Gitea uses an external git backend.
@ -154,6 +158,10 @@ func CreateBlameReader(ctx context.Context, objectFormat ObjectFormat, repoPath
cmd.AddDynamicArguments(commit.ID.String()).AddDashesAndList(file)
done := make(chan error, 1)
reader, stdout, err := os.Pipe()
if err != nil {
return nil, err
}
go func() {
stderr := bytes.Buffer{}
// TODO: it doesn't work for directories (the directories shouldn't be "blamed"), and the "err" should be returned by "Read" but not by "Close"
@ -182,33 +190,29 @@ func CreateBlameReader(ctx context.Context, objectFormat ObjectFormat, repoPath
}, nil
}
func tryCreateBlameIgnoreRevsFile(commit *Commit) (string, func()) {
func tryCreateBlameIgnoreRevsFile(commit *Commit) (string, func(), error) {
entry, err := commit.GetTreeEntryByPath(".git-blame-ignore-revs")
if err != nil {
log.Error("Unable to get .git-blame-ignore-revs file: GetTreeEntryByPath: %v", err)
return "", nil
return "", nil, err
}
r, err := entry.Blob().DataAsync()
if err != nil {
log.Error("Unable to get .git-blame-ignore-revs file data: DataAsync: %v", err)
return "", nil
return "", nil, err
}
defer r.Close()
f, cleanup, err := setting.AppDataTempDir("git-repo-content").CreateTempFileRandom("git-blame-ignore-revs")
if err != nil {
log.Error("Unable to get .git-blame-ignore-revs file data: CreateTempFileRandom: %v", err)
return "", nil
return "", nil, err
}
filename := f.Name()
_, err = io.Copy(f, r)
_ = f.Close()
if err != nil {
cleanup()
log.Error("Unable to get .git-blame-ignore-revs file data: Copy: %v", err)
return "", nil
return "", nil, err
}
return filename, cleanup
return filename, cleanup, nil
}

View File

@ -1190,7 +1190,7 @@ issues.context.edit=Modifica
issues.context.delete=Elimina
issues.reopen_issue=Riapri
issues.create_comment=Commento
issues.closed_at=`chiuso questo probleam <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.closed_at=`ha chiuso questo problema <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.reopened_at=`riaperto questo problema <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.commit_ref_at=`ha fatto riferimento a questa issue dal commit <a id="%[1]s" href="#%[1]s">%[2]s</a>`
issues.ref_issue_from=`<a href="%[3]s">ha fatto riferimento a questo problema %[4]s</a> <a id="%[1]s" href="#%[1]s">%[2]s</a>`

View File

@ -384,13 +384,13 @@ func (Action) CreateVariable(ctx *context.APIContext) {
// "$ref": "#/definitions/CreateVariableOption"
// responses:
// "201":
// description: response when creating an org-level variable
// "204":
// description: response when creating an org-level variable
// description: successfully created the org-level variable
// "400":
// "$ref": "#/responses/error"
// "404":
// "$ref": "#/responses/notFound"
// "409":
// description: variable name already exists.
// "500":
// "$ref": "#/responses/error"
opt := web.GetForm(ctx).(*api.CreateVariableOption)
@ -419,7 +419,7 @@ func (Action) CreateVariable(ctx *context.APIContext) {
return
}
ctx.Status(http.StatusNoContent)
ctx.Status(http.StatusCreated)
}
// UpdateVariable update an org-level variable

View File

@ -339,12 +339,12 @@ func (Action) CreateVariable(ctx *context.APIContext) {
// responses:
// "201":
// description: response when creating a repo-level variable
// "204":
// description: response when creating a repo-level variable
// "400":
// "$ref": "#/responses/error"
// "404":
// "$ref": "#/responses/notFound"
// "409":
// description: variable name already exists.
// "500":
// "$ref": "#/responses/error"
opt := web.GetForm(ctx).(*api.CreateVariableOption)
@ -373,7 +373,7 @@ func (Action) CreateVariable(ctx *context.APIContext) {
return
}
ctx.Status(http.StatusNoContent)
ctx.Status(http.StatusCreated)
}
// UpdateVariable update a repo-level variable

View File

@ -73,7 +73,7 @@ func ListPullRequests(ctx *context.APIContext) {
// in: query
// description: Type of sort
// type: string
// enum: [oldest, recentupdate, leastupdate, mostcomment, leastcomment, priority]
// enum: [oldest, recentupdate, recentclose, leastupdate, mostcomment, leastcomment, priority]
// - name: milestone
// in: query
// description: ID of the milestone

View File

@ -261,7 +261,7 @@ func GetCombinedCommitStatusByRef(ctx *context.APIContext) {
count, err := git_model.CountLatestCommitStatus(ctx, repo.ID, refCommit.Commit.ID.String())
if err != nil {
ctx.APIErrorInternal(fmt.Errorf("GetLatestCommitStatus[%s, %s]: %w", repo.FullName(), refCommit.CommitID, err))
ctx.APIErrorInternal(fmt.Errorf("CountLatestCommitStatus[%s, %s]: %w", repo.FullName(), refCommit.CommitID, err))
return
}
ctx.SetTotalCountHeader(count)

View File

@ -127,13 +127,11 @@ func CreateVariable(ctx *context.APIContext) {
// "$ref": "#/definitions/CreateVariableOption"
// responses:
// "201":
// description: response when creating a variable
// "204":
// description: response when creating a variable
// description: successfully created the user-level variable
// "400":
// "$ref": "#/responses/error"
// "404":
// "$ref": "#/responses/notFound"
// "409":
// description: variable name already exists.
opt := web.GetForm(ctx).(*api.CreateVariableOption)
@ -162,7 +160,7 @@ func CreateVariable(ctx *context.APIContext) {
return
}
ctx.Status(http.StatusNoContent)
ctx.Status(http.StatusCreated)
}
// UpdateVariable update a user-level variable which is created by current doer

View File

@ -84,7 +84,7 @@ func ParseCommitsWithStatus(ctx context.Context, oldCommits []*asymkey_model.Sig
commit := &git_model.SignCommitWithStatuses{
SignCommit: c,
}
statuses, err := git_model.GetLatestCommitStatus(ctx, repo.ID, commit.ID.String(), db.ListOptions{})
statuses, err := git_model.GetLatestCommitStatus(ctx, repo.ID, commit.ID.String(), db.ListOptionsAll)
if err != nil {
return nil, err
}

View File

@ -121,7 +121,7 @@ func (graph *Graph) LoadAndProcessCommits(ctx context.Context, repository *repo_
return repo_model.IsOwnerMemberCollaborator(ctx, repository, user.ID)
}, &keyMap)
statuses, err := git_model.GetLatestCommitStatus(ctx, repository.ID, c.Commit.ID.String(), db.ListOptions{})
statuses, err := git_model.GetLatestCommitStatus(ctx, repository.ID, c.Commit.ID.String(), db.ListOptionsAll)
if err != nil {
log.Error("GetLatestCommitStatus: %v", err)
} else {

View File

@ -36,15 +36,23 @@
<div class="run-list-meta">{{svg "octicon-calendar" 16}}{{DateUtils.TimeSince $run.Updated}}</div>
<div class="run-list-meta">{{svg "octicon-stopwatch" 16}}{{$run.Duration}}</div>
</div>
{{if and ($.AllowDeleteWorkflowRuns) ($run.Status.IsDone)}}
<button class="btn interact-bg link-action tw-p-2"
data-url="{{$run.Link}}/delete"
data-modal-confirm="{{ctx.Locale.Tr "actions.runs.delete.description"}}"
data-tooltip-content="{{ctx.Locale.Tr "actions.runs.delete"}}"
>
{{svg "octicon-trash"}}
</button>
{{end}}
<div class="ui dropdown jump tw-p-2">
{{svg "octicon-kebab-horizontal"}}
<div class="menu flex-items-menu">
<!-- TODO: This redundant link should be replaced by something else in future,
because have not figured out how to add "View Workflow" or anything similar to GitHub.
Related: https://github.com/go-gitea/gitea/pull/34530 -->
<a class="item" href="{{$run.Link}}">{{svg "octicon-play"}}{{ctx.Locale.Tr "view"}}</a>
{{if and $.AllowDeleteWorkflowRuns $run.Status.IsDone}}
<a class="item link-action"
data-url="{{$run.Link}}/delete"
data-modal-confirm="{{ctx.Locale.Tr "actions.runs.delete.description"}}"
>
{{svg "octicon-trash"}}{{ctx.Locale.Tr "actions.runs.delete"}}
</a>
{{end}}
</div>
</div>
</div>
</div>
{{end}}

View File

@ -2259,16 +2259,16 @@
],
"responses": {
"201": {
"description": "response when creating an org-level variable"
},
"204": {
"description": "response when creating an org-level variable"
"description": "successfully created the org-level variable"
},
"400": {
"$ref": "#/responses/error"
},
"404": {
"$ref": "#/responses/notFound"
"409": {
"description": "variable name already exists."
},
"500": {
"$ref": "#/responses/error"
}
}
},
@ -5263,14 +5263,14 @@
"201": {
"description": "response when creating a repo-level variable"
},
"204": {
"description": "response when creating a repo-level variable"
},
"400": {
"$ref": "#/responses/error"
},
"404": {
"$ref": "#/responses/notFound"
"409": {
"description": "variable name already exists."
},
"500": {
"$ref": "#/responses/error"
}
}
},
@ -12871,6 +12871,7 @@
"enum": [
"oldest",
"recentupdate",
"recentclose",
"leastupdate",
"mostcomment",
"leastcomment",
@ -17863,16 +17864,13 @@
],
"responses": {
"201": {
"description": "response when creating a variable"
},
"204": {
"description": "response when creating a variable"
"description": "successfully created the user-level variable"
},
"400": {
"$ref": "#/responses/error"
},
"404": {
"$ref": "#/responses/notFound"
"409": {
"description": "variable name already exists."
}
}
},

View File

@ -35,11 +35,11 @@ func TestAPIRepoVariables(t *testing.T) {
},
{
Name: "_",
ExpectedStatus: http.StatusNoContent,
ExpectedStatus: http.StatusCreated,
},
{
Name: "TEST_VAR",
ExpectedStatus: http.StatusNoContent,
ExpectedStatus: http.StatusCreated,
},
{
Name: "test_var",
@ -81,7 +81,7 @@ func TestAPIRepoVariables(t *testing.T) {
req := NewRequestWithJSON(t, "POST", url, api.CreateVariableOption{
Value: "initial_val",
}).AddTokenAuth(token)
MakeRequest(t, req, http.StatusNoContent)
MakeRequest(t, req, http.StatusCreated)
cases := []struct {
Name string
@ -138,7 +138,7 @@ func TestAPIRepoVariables(t *testing.T) {
req := NewRequestWithJSON(t, "POST", url, api.CreateVariableOption{
Value: "initial_val",
}).AddTokenAuth(token)
MakeRequest(t, req, http.StatusNoContent)
MakeRequest(t, req, http.StatusCreated)
req = NewRequest(t, "DELETE", url).AddTokenAuth(token)
MakeRequest(t, req, http.StatusNoContent)

View File

@ -29,11 +29,11 @@ func TestAPIUserVariables(t *testing.T) {
},
{
Name: "_",
ExpectedStatus: http.StatusNoContent,
ExpectedStatus: http.StatusCreated,
},
{
Name: "TEST_VAR",
ExpectedStatus: http.StatusNoContent,
ExpectedStatus: http.StatusCreated,
},
{
Name: "test_var",
@ -75,7 +75,7 @@ func TestAPIUserVariables(t *testing.T) {
req := NewRequestWithJSON(t, "POST", url, api.CreateVariableOption{
Value: "initial_val",
}).AddTokenAuth(token)
MakeRequest(t, req, http.StatusNoContent)
MakeRequest(t, req, http.StatusCreated)
cases := []struct {
Name string
@ -132,7 +132,7 @@ func TestAPIUserVariables(t *testing.T) {
req := NewRequestWithJSON(t, "POST", url, api.CreateVariableOption{
Value: "initial_val",
}).AddTokenAuth(token)
MakeRequest(t, req, http.StatusNoContent)
MakeRequest(t, req, http.StatusCreated)
req = NewRequest(t, "DELETE", url).AddTokenAuth(token)
MakeRequest(t, req, http.StatusNoContent)