mirror of
https://github.com/go-gitea/gitea.git
synced 2025-07-21 12:24:38 +02:00
Merge branch 'main' into non-text-edit
This commit is contained in:
commit
35d85dcf09
2
.github/workflows/pull-db-tests.yml
vendored
2
.github/workflows/pull-db-tests.yml
vendored
@ -17,7 +17,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
services:
|
||||
pgsql:
|
||||
image: postgres:12
|
||||
image: postgres:14
|
||||
env:
|
||||
POSTGRES_DB: test
|
||||
POSTGRES_PASSWORD: postgres
|
||||
|
@ -7,6 +7,7 @@
|
||||
target_url: https://example.com/builds/
|
||||
description: My awesome CI-service
|
||||
context: ci/awesomeness
|
||||
context_hash: c65f4d64a3b14a3eced0c9b36799e66e1bd5ced7
|
||||
creator_id: 2
|
||||
|
||||
-
|
||||
@ -18,6 +19,7 @@
|
||||
target_url: https://example.com/converage/
|
||||
description: My awesome Coverage service
|
||||
context: cov/awesomeness
|
||||
context_hash: 3929ac7bccd3fa1bf9b38ddedb77973b1b9a8cfe
|
||||
creator_id: 2
|
||||
|
||||
-
|
||||
@ -29,6 +31,7 @@
|
||||
target_url: https://example.com/converage/
|
||||
description: My awesome Coverage service
|
||||
context: cov/awesomeness
|
||||
context_hash: 3929ac7bccd3fa1bf9b38ddedb77973b1b9a8cfe
|
||||
creator_id: 2
|
||||
|
||||
-
|
||||
@ -40,6 +43,7 @@
|
||||
target_url: https://example.com/builds/
|
||||
description: My awesome CI-service
|
||||
context: ci/awesomeness
|
||||
context_hash: c65f4d64a3b14a3eced0c9b36799e66e1bd5ced7
|
||||
creator_id: 2
|
||||
|
||||
-
|
||||
@ -51,4 +55,5 @@
|
||||
target_url: https://example.com/builds/
|
||||
description: My awesome deploy service
|
||||
context: deploy/awesomeness
|
||||
context_hash: ae9547713a6665fc4261d0756904932085a41cf2
|
||||
creator_id: 2
|
||||
|
@ -230,18 +230,24 @@ func (status *CommitStatus) HideActionsURL(ctx context.Context) {
|
||||
|
||||
// CalcCommitStatus returns commit status state via some status, the commit statues should order by id desc
|
||||
func CalcCommitStatus(statuses []*CommitStatus) *CommitStatus {
|
||||
// This function is widely used, but it is not quite right.
|
||||
// Ideally it should return something like "CommitStatusSummary" with properly aggregated state.
|
||||
// GitHub's behavior: if all statuses are "skipped", GitHub will return "success" as the combined status.
|
||||
var lastStatus *CommitStatus
|
||||
state := api.CommitStatusSuccess
|
||||
for _, status := range statuses {
|
||||
if status.State.NoBetterThan(state) {
|
||||
if state == status.State || status.State.HasHigherPriorityThan(state) {
|
||||
state = status.State
|
||||
lastStatus = status
|
||||
}
|
||||
}
|
||||
if lastStatus == nil {
|
||||
if len(statuses) > 0 {
|
||||
// FIXME: a bad case: Gitea just returns the first commit status, its status is "skipped" in this case.
|
||||
lastStatus = statuses[0]
|
||||
} else {
|
||||
// FIXME: another bad case: if the "statuses" slice is empty, the returned value is an invalid CommitStatus, all its fields are empty.
|
||||
// Frontend code (tmpl&vue) sometimes depend on the empty fields to skip rendering commit status elements (need to double check in the future)
|
||||
lastStatus = &CommitStatus{}
|
||||
}
|
||||
}
|
||||
@ -298,27 +304,37 @@ type CommitStatusIndex struct {
|
||||
MaxIndex int64 `xorm:"index"`
|
||||
}
|
||||
|
||||
func makeRepoCommitQuery(ctx context.Context, repoID int64, sha string) *xorm.Session {
|
||||
return db.GetEngine(ctx).Table(&CommitStatus{}).
|
||||
Where("repo_id = ?", repoID).And("sha = ?", sha)
|
||||
}
|
||||
|
||||
// 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, int64, error) {
|
||||
getBase := func() *xorm.Session {
|
||||
return db.GetEngine(ctx).Table(&CommitStatus{}).
|
||||
Where("repo_id = ?", repoID).And("sha = ?", sha)
|
||||
}
|
||||
func GetLatestCommitStatus(ctx context.Context, repoID int64, sha string, listOptions db.ListOptions) ([]*CommitStatus, error) {
|
||||
indices := make([]int64, 0, 10)
|
||||
sess := getBase().Select("max( `index` ) as `index`").
|
||||
GroupBy("context_hash").OrderBy("max( `index` ) desc")
|
||||
sess := makeRepoCommitQuery(ctx, repoID, sha).
|
||||
Select("max( `index` ) as `index`").
|
||||
GroupBy("context_hash").
|
||||
OrderBy("max( `index` ) desc")
|
||||
if !listOptions.IsListAll() {
|
||||
sess = db.SetSessionPagination(sess, &listOptions)
|
||||
}
|
||||
count, err := sess.FindAndCount(&indices)
|
||||
if err != nil {
|
||||
return nil, count, err
|
||||
if err := sess.Find(&indices); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
statuses := make([]*CommitStatus, 0, len(indices))
|
||||
if len(indices) == 0 {
|
||||
return statuses, count, nil
|
||||
return statuses, nil
|
||||
}
|
||||
return statuses, count, getBase().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 makeRepoCommitQuery(ctx, repoID, sha).
|
||||
Select("count(context_hash)").
|
||||
GroupBy("context_hash").
|
||||
Count()
|
||||
}
|
||||
|
||||
// GetLatestCommitStatusForPairs returns all statuses with a unique context for a given list of repo-sha pairs
|
||||
|
@ -55,11 +55,15 @@ func GetLatestCommitStatusForRepoAndSHAs(ctx context.Context, repoSHAs []RepoSHA
|
||||
}
|
||||
|
||||
func UpdateCommitStatusSummary(ctx context.Context, repoID int64, sha string) error {
|
||||
commitStatuses, _, err := GetLatestCommitStatus(ctx, repoID, sha, db.ListOptionsAll)
|
||||
commitStatuses, err := GetLatestCommitStatus(ctx, repoID, sha, db.ListOptionsAll)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
state := CalcCommitStatus(commitStatuses)
|
||||
// it guarantees that commitStatuses is not empty because this function is always called after a commit status is created
|
||||
if len(commitStatuses) == 0 {
|
||||
setting.PanicInDevOrTesting("no commit statuses found for repo %d and sha %s", repoID, sha)
|
||||
}
|
||||
state := CalcCommitStatus(commitStatuses) // non-empty commitStatuses is guaranteed
|
||||
// mysql will return 0 when update a record which state hasn't been changed which behaviour is different from other database,
|
||||
// so we need to use insert in on duplicate
|
||||
if setting.Database.Type.IsMySQL() {
|
||||
|
@ -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},
|
||||
@ -256,3 +256,26 @@ func TestCommitStatusesHideActionsURL(t *testing.T) {
|
||||
assert.Empty(t, statuses[0].TargetURL)
|
||||
assert.Equal(t, "https://mycicd.org/1", statuses[1].TargetURL)
|
||||
}
|
||||
|
||||
func TestGetCountLatestCommitStatus(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
|
||||
|
||||
sha1 := "1234123412341234123412341234123412341234" // the mocked commit ID in test fixtures
|
||||
|
||||
commitStatuses, err := git_model.GetLatestCommitStatus(db.DefaultContext, repo1.ID, sha1, db.ListOptions{
|
||||
Page: 1,
|
||||
PageSize: 2,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, commitStatuses, 2)
|
||||
assert.Equal(t, structs.CommitStatusFailure, commitStatuses[0].State)
|
||||
assert.Equal(t, "ci/awesomeness", commitStatuses[0].Context)
|
||||
assert.Equal(t, structs.CommitStatusError, commitStatuses[1].State)
|
||||
assert.Equal(t, "deploy/awesomeness", commitStatuses[1].Context)
|
||||
|
||||
count, err := git_model.CountLatestCommitStatus(db.DefaultContext, repo1.ID, sha1)
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 3, count)
|
||||
}
|
||||
|
@ -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":
|
||||
|
@ -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
|
||||
|
@ -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())
|
||||
|
||||
|
@ -137,7 +137,7 @@ func (opts *SearchUserOptions) toSearchQueryBase(ctx context.Context) *xorm.Sess
|
||||
|
||||
// SearchUsers takes options i.e. keyword and part of user name to search,
|
||||
// it returns results in given range and number of total results.
|
||||
func SearchUsers(ctx context.Context, opts *SearchUserOptions) (users []*User, _ int64, _ error) {
|
||||
func SearchUsers(ctx context.Context, opts SearchUserOptions) (users []*User, _ int64, _ error) {
|
||||
sessCount := opts.toSearchQueryBase(ctx)
|
||||
defer sessCount.Close()
|
||||
count, err := sessCount.Count(new(User))
|
||||
@ -152,7 +152,7 @@ func SearchUsers(ctx context.Context, opts *SearchUserOptions) (users []*User, _
|
||||
sessQuery := opts.toSearchQueryBase(ctx).OrderBy(opts.OrderBy.String())
|
||||
defer sessQuery.Close()
|
||||
if opts.Page > 0 {
|
||||
sessQuery = db.SetSessionPagination(sessQuery, opts)
|
||||
sessQuery = db.SetSessionPagination(sessQuery, &opts)
|
||||
}
|
||||
|
||||
// the sql may contain JOIN, so we must only select User related columns
|
||||
|
@ -88,7 +88,7 @@ func TestCanCreateOrganization(t *testing.T) {
|
||||
|
||||
func TestSearchUsers(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
testSuccess := func(opts *user_model.SearchUserOptions, expectedUserOrOrgIDs []int64) {
|
||||
testSuccess := func(opts user_model.SearchUserOptions, expectedUserOrOrgIDs []int64) {
|
||||
users, _, err := user_model.SearchUsers(db.DefaultContext, opts)
|
||||
assert.NoError(t, err)
|
||||
cassText := fmt.Sprintf("ids: %v, opts: %v", expectedUserOrOrgIDs, opts)
|
||||
@ -100,61 +100,61 @@ func TestSearchUsers(t *testing.T) {
|
||||
}
|
||||
|
||||
// test orgs
|
||||
testOrgSuccess := func(opts *user_model.SearchUserOptions, expectedOrgIDs []int64) {
|
||||
testOrgSuccess := func(opts user_model.SearchUserOptions, expectedOrgIDs []int64) {
|
||||
opts.Type = user_model.UserTypeOrganization
|
||||
testSuccess(opts, expectedOrgIDs)
|
||||
}
|
||||
|
||||
testOrgSuccess(&user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1, PageSize: 2}},
|
||||
testOrgSuccess(user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1, PageSize: 2}},
|
||||
[]int64{3, 6})
|
||||
|
||||
testOrgSuccess(&user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 2, PageSize: 2}},
|
||||
testOrgSuccess(user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 2, PageSize: 2}},
|
||||
[]int64{7, 17})
|
||||
|
||||
testOrgSuccess(&user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 3, PageSize: 2}},
|
||||
testOrgSuccess(user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 3, PageSize: 2}},
|
||||
[]int64{19, 25})
|
||||
|
||||
testOrgSuccess(&user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 4, PageSize: 2}},
|
||||
testOrgSuccess(user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 4, PageSize: 2}},
|
||||
[]int64{26, 41})
|
||||
|
||||
testOrgSuccess(&user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 5, PageSize: 2}},
|
||||
testOrgSuccess(user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 5, PageSize: 2}},
|
||||
[]int64{42})
|
||||
|
||||
testOrgSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 6, PageSize: 2}},
|
||||
testOrgSuccess(user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 6, PageSize: 2}},
|
||||
[]int64{})
|
||||
|
||||
// test users
|
||||
testUserSuccess := func(opts *user_model.SearchUserOptions, expectedUserIDs []int64) {
|
||||
testUserSuccess := func(opts user_model.SearchUserOptions, expectedUserIDs []int64) {
|
||||
opts.Type = user_model.UserTypeIndividual
|
||||
testSuccess(opts, expectedUserIDs)
|
||||
}
|
||||
|
||||
testUserSuccess(&user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1}},
|
||||
testUserSuccess(user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1}},
|
||||
[]int64{1, 2, 4, 5, 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, 20, 21, 24, 27, 28, 29, 30, 32, 34, 37, 38, 39, 40})
|
||||
|
||||
testUserSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsActive: optional.Some(false)},
|
||||
testUserSuccess(user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsActive: optional.Some(false)},
|
||||
[]int64{9})
|
||||
|
||||
testUserSuccess(&user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1}, IsActive: optional.Some(true)},
|
||||
testUserSuccess(user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1}, IsActive: optional.Some(true)},
|
||||
[]int64{1, 2, 4, 5, 8, 10, 11, 12, 13, 14, 15, 16, 18, 20, 21, 24, 27, 28, 29, 30, 32, 34, 37, 38, 39, 40})
|
||||
|
||||
testUserSuccess(&user_model.SearchUserOptions{Keyword: "user1", OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1}, IsActive: optional.Some(true)},
|
||||
testUserSuccess(user_model.SearchUserOptions{Keyword: "user1", OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1}, IsActive: optional.Some(true)},
|
||||
[]int64{1, 10, 11, 12, 13, 14, 15, 16, 18})
|
||||
|
||||
// order by name asc default
|
||||
testUserSuccess(&user_model.SearchUserOptions{Keyword: "user1", ListOptions: db.ListOptions{Page: 1}, IsActive: optional.Some(true)},
|
||||
testUserSuccess(user_model.SearchUserOptions{Keyword: "user1", ListOptions: db.ListOptions{Page: 1}, IsActive: optional.Some(true)},
|
||||
[]int64{1, 10, 11, 12, 13, 14, 15, 16, 18})
|
||||
|
||||
testUserSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsAdmin: optional.Some(true)},
|
||||
testUserSuccess(user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsAdmin: optional.Some(true)},
|
||||
[]int64{1})
|
||||
|
||||
testUserSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsRestricted: optional.Some(true)},
|
||||
testUserSuccess(user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsRestricted: optional.Some(true)},
|
||||
[]int64{29})
|
||||
|
||||
testUserSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsProhibitLogin: optional.Some(true)},
|
||||
testUserSuccess(user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsProhibitLogin: optional.Some(true)},
|
||||
[]int64{37})
|
||||
|
||||
testUserSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsTwoFactorEnabled: optional.Some(true)},
|
||||
testUserSuccess(user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsTwoFactorEnabled: optional.Some(true)},
|
||||
[]int64{24})
|
||||
}
|
||||
|
||||
|
@ -43,21 +43,23 @@ func IsWorkflow(path string) bool {
|
||||
return strings.HasPrefix(path, ".gitea/workflows") || strings.HasPrefix(path, ".github/workflows")
|
||||
}
|
||||
|
||||
func ListWorkflows(commit *git.Commit) (git.Entries, error) {
|
||||
tree, err := commit.SubTree(".gitea/workflows")
|
||||
func ListWorkflows(commit *git.Commit) (string, git.Entries, error) {
|
||||
rpath := ".gitea/workflows"
|
||||
tree, err := commit.SubTree(rpath)
|
||||
if _, ok := err.(git.ErrNotExist); ok {
|
||||
tree, err = commit.SubTree(".github/workflows")
|
||||
rpath = ".github/workflows"
|
||||
tree, err = commit.SubTree(rpath)
|
||||
}
|
||||
if _, ok := err.(git.ErrNotExist); ok {
|
||||
return nil, nil
|
||||
return "", nil, nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
entries, err := tree.ListEntriesRecursiveFast()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
ret := make(git.Entries, 0, len(entries))
|
||||
@ -66,7 +68,7 @@ func ListWorkflows(commit *git.Commit) (git.Entries, error) {
|
||||
ret = append(ret, entry)
|
||||
}
|
||||
}
|
||||
return ret, nil
|
||||
return rpath, ret, nil
|
||||
}
|
||||
|
||||
func GetContentFromEntry(entry *git.TreeEntry) ([]byte, error) {
|
||||
@ -102,7 +104,7 @@ func DetectWorkflows(
|
||||
payload api.Payloader,
|
||||
detectSchedule bool,
|
||||
) ([]*DetectedWorkflow, []*DetectedWorkflow, error) {
|
||||
entries, err := ListWorkflows(commit)
|
||||
_, entries, err := ListWorkflows(commit)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@ -147,7 +149,7 @@ func DetectWorkflows(
|
||||
}
|
||||
|
||||
func DetectScheduledWorkflows(gitRepo *git.Repository, commit *git.Commit) ([]*DetectedWorkflow, error) {
|
||||
entries, err := ListWorkflows(commit)
|
||||
_, entries, err := ListWorkflows(commit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ type Commit struct {
|
||||
// CommitSignature represents a git commit signature part.
|
||||
type CommitSignature struct {
|
||||
Signature string
|
||||
Payload string // TODO check if can be reconstruct from the rest of commit information to not have duplicate data
|
||||
Payload string
|
||||
}
|
||||
|
||||
// Message returns the commit message. Same as retrieving CommitMessage directly.
|
||||
|
@ -6,10 +6,44 @@ package git
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
commitHeaderGpgsig = "gpgsig"
|
||||
commitHeaderGpgsigSha256 = "gpgsig-sha256"
|
||||
)
|
||||
|
||||
func assignCommitFields(gitRepo *Repository, commit *Commit, headerKey string, headerValue []byte) error {
|
||||
if len(headerValue) > 0 && headerValue[len(headerValue)-1] == '\n' {
|
||||
headerValue = headerValue[:len(headerValue)-1] // remove trailing newline
|
||||
}
|
||||
switch headerKey {
|
||||
case "tree":
|
||||
objID, err := NewIDFromString(string(headerValue))
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid tree ID %q: %w", string(headerValue), err)
|
||||
}
|
||||
commit.Tree = *NewTree(gitRepo, objID)
|
||||
case "parent":
|
||||
objID, err := NewIDFromString(string(headerValue))
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid parent ID %q: %w", string(headerValue), err)
|
||||
}
|
||||
commit.Parents = append(commit.Parents, objID)
|
||||
case "author":
|
||||
commit.Author.Decode(headerValue)
|
||||
case "committer":
|
||||
commit.Committer.Decode(headerValue)
|
||||
case commitHeaderGpgsig, commitHeaderGpgsigSha256:
|
||||
// if there are duplicate "gpgsig" and "gpgsig-sha256" headers, then the signature must have already been invalid
|
||||
// so we don't need to handle duplicate headers here
|
||||
commit.Signature = &CommitSignature{Signature: string(headerValue)}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CommitFromReader will generate a Commit from a provided reader
|
||||
// We need this to interpret commits from cat-file or cat-file --batch
|
||||
//
|
||||
@ -21,90 +55,46 @@ func CommitFromReader(gitRepo *Repository, objectID ObjectID, reader io.Reader)
|
||||
Committer: &Signature{},
|
||||
}
|
||||
|
||||
payloadSB := new(strings.Builder)
|
||||
signatureSB := new(strings.Builder)
|
||||
messageSB := new(strings.Builder)
|
||||
message := false
|
||||
pgpsig := false
|
||||
|
||||
bufReader, ok := reader.(*bufio.Reader)
|
||||
if !ok {
|
||||
bufReader = bufio.NewReader(reader)
|
||||
}
|
||||
|
||||
readLoop:
|
||||
bufReader := bufio.NewReader(reader)
|
||||
inHeader := true
|
||||
var payloadSB, messageSB bytes.Buffer
|
||||
var headerKey string
|
||||
var headerValue []byte
|
||||
for {
|
||||
line, err := bufReader.ReadBytes('\n')
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
if message {
|
||||
_, _ = messageSB.Write(line)
|
||||
if err != nil && err != io.EOF {
|
||||
return nil, fmt.Errorf("unable to read commit %q: %w", objectID.String(), err)
|
||||
}
|
||||
if len(line) == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
if inHeader {
|
||||
inHeader = !(len(line) == 1 && line[0] == '\n') // still in header if line is not just a newline
|
||||
k, v, _ := bytes.Cut(line, []byte{' '})
|
||||
if len(k) != 0 || !inHeader {
|
||||
if headerKey != "" {
|
||||
if err = assignCommitFields(gitRepo, commit, headerKey, headerValue); err != nil {
|
||||
return nil, fmt.Errorf("unable to parse commit %q: %w", objectID.String(), err)
|
||||
}
|
||||
}
|
||||
_, _ = payloadSB.Write(line)
|
||||
break readLoop
|
||||
headerKey = string(k) // it also resets the headerValue to empty string if not inHeader
|
||||
headerValue = v
|
||||
} else {
|
||||
headerValue = append(headerValue, v...)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
if pgpsig {
|
||||
if len(line) > 0 && line[0] == ' ' {
|
||||
_, _ = signatureSB.Write(line[1:])
|
||||
continue
|
||||
}
|
||||
pgpsig = false
|
||||
}
|
||||
|
||||
if !message {
|
||||
// This is probably not correct but is copied from go-gits interpretation...
|
||||
trimmed := bytes.TrimSpace(line)
|
||||
if len(trimmed) == 0 {
|
||||
message = true
|
||||
if headerKey != commitHeaderGpgsig && headerKey != commitHeaderGpgsigSha256 {
|
||||
_, _ = payloadSB.Write(line)
|
||||
continue
|
||||
}
|
||||
|
||||
split := bytes.SplitN(trimmed, []byte{' '}, 2)
|
||||
var data []byte
|
||||
if len(split) > 1 {
|
||||
data = split[1]
|
||||
}
|
||||
|
||||
switch string(split[0]) {
|
||||
case "tree":
|
||||
commit.Tree = *NewTree(gitRepo, MustIDFromString(string(data)))
|
||||
_, _ = payloadSB.Write(line)
|
||||
case "parent":
|
||||
commit.Parents = append(commit.Parents, MustIDFromString(string(data)))
|
||||
_, _ = payloadSB.Write(line)
|
||||
case "author":
|
||||
commit.Author = &Signature{}
|
||||
commit.Author.Decode(data)
|
||||
_, _ = payloadSB.Write(line)
|
||||
case "committer":
|
||||
commit.Committer = &Signature{}
|
||||
commit.Committer.Decode(data)
|
||||
_, _ = payloadSB.Write(line)
|
||||
case "encoding":
|
||||
_, _ = payloadSB.Write(line)
|
||||
case "gpgsig":
|
||||
fallthrough
|
||||
case "gpgsig-sha256": // FIXME: no intertop, so only 1 exists at present.
|
||||
_, _ = signatureSB.Write(data)
|
||||
_ = signatureSB.WriteByte('\n')
|
||||
pgpsig = true
|
||||
}
|
||||
} else {
|
||||
_, _ = messageSB.Write(line)
|
||||
_, _ = payloadSB.Write(line)
|
||||
}
|
||||
}
|
||||
commit.CommitMessage = messageSB.String()
|
||||
commit.Signature = &CommitSignature{
|
||||
Signature: signatureSB.String(),
|
||||
Payload: payloadSB.String(),
|
||||
}
|
||||
if len(commit.Signature.Signature) == 0 {
|
||||
commit.Signature = nil
|
||||
}
|
||||
|
||||
commit.CommitMessage = messageSB.String()
|
||||
if commit.Signature != nil {
|
||||
commit.Signature.Payload = payloadSB.String()
|
||||
}
|
||||
return commit, nil
|
||||
}
|
||||
|
@ -60,8 +60,7 @@ func TestGetFullCommitIDErrorSha256(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCommitFromReaderSha256(t *testing.T) {
|
||||
commitString := `9433b2a62b964c17a4485ae180f45f595d3e69d31b786087775e28c6b6399df0 commit 1114
|
||||
tree e7f9e96dd79c09b078cac8b303a7d3b9d65ff9b734e86060a4d20409fd379f9e
|
||||
commitString := `tree e7f9e96dd79c09b078cac8b303a7d3b9d65ff9b734e86060a4d20409fd379f9e
|
||||
parent 26e9ccc29fad747e9c5d9f4c9ddeb7eff61cc45ef6a8dc258cbeb181afc055e8
|
||||
author Adam Majer <amajer@suse.de> 1698676906 +0100
|
||||
committer Adam Majer <amajer@suse.de> 1698676906 +0100
|
||||
@ -112,8 +111,7 @@ VAEUo6ecdDxSpyt2naeg9pKus/BRi7P6g4B1hkk/zZstUX/QP4IQuAJbXjkvsC+X
|
||||
HKRr3NlRM/DygzTyj0gN74uoa0goCIbyAQhiT42nm0cuhM7uN/W0ayrlZjGF1cbR
|
||||
8NCJUL2Nwj0ywKIavC99Ipkb8AsFwpVT6U6effs6
|
||||
=xybZ
|
||||
-----END PGP SIGNATURE-----
|
||||
`, commitFromReader.Signature.Signature)
|
||||
-----END PGP SIGNATURE-----`, commitFromReader.Signature.Signature)
|
||||
assert.Equal(t, `tree e7f9e96dd79c09b078cac8b303a7d3b9d65ff9b734e86060a4d20409fd379f9e
|
||||
parent 26e9ccc29fad747e9c5d9f4c9ddeb7eff61cc45ef6a8dc258cbeb181afc055e8
|
||||
author Adam Majer <amajer@suse.de> 1698676906 +0100
|
||||
|
@ -59,8 +59,7 @@ func TestGetFullCommitIDError(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCommitFromReader(t *testing.T) {
|
||||
commitString := `feaf4ba6bc635fec442f46ddd4512416ec43c2c2 commit 1074
|
||||
tree f1a6cb52b2d16773290cefe49ad0684b50a4f930
|
||||
commitString := `tree f1a6cb52b2d16773290cefe49ad0684b50a4f930
|
||||
parent 37991dec2c8e592043f47155ce4808d4580f9123
|
||||
author silverwind <me@silverwind.io> 1563741793 +0200
|
||||
committer silverwind <me@silverwind.io> 1563741793 +0200
|
||||
@ -108,8 +107,7 @@ sD53z/f0J+We4VZjY+pidvA9BGZPFVdR3wd3xGs8/oH6UWaLJAMGkLG6dDb3qDLm
|
||||
mfeFhT57UbE4qukTDIQ0Y0WM40UYRTakRaDY7ubhXgLgx09Cnp9XTVMsHgT6j9/i
|
||||
1pxsB104XLWjQHTjr1JtiaBQEwFh9r2OKTcpvaLcbNtYpo7CzOs=
|
||||
=FRsO
|
||||
-----END PGP SIGNATURE-----
|
||||
`, commitFromReader.Signature.Signature)
|
||||
-----END PGP SIGNATURE-----`, commitFromReader.Signature.Signature)
|
||||
assert.Equal(t, `tree f1a6cb52b2d16773290cefe49ad0684b50a4f930
|
||||
parent 37991dec2c8e592043f47155ce4808d4580f9123
|
||||
author silverwind <me@silverwind.io> 1563741793 +0200
|
||||
@ -126,8 +124,7 @@ empty commit`, commitFromReader.Signature.Payload)
|
||||
}
|
||||
|
||||
func TestCommitWithEncodingFromReader(t *testing.T) {
|
||||
commitString := `feaf4ba6bc635fec442f46ddd4512416ec43c2c2 commit 1074
|
||||
tree ca3fad42080dd1a6d291b75acdfc46e5b9b307e5
|
||||
commitString := `tree ca3fad42080dd1a6d291b75acdfc46e5b9b307e5
|
||||
parent 47b24e7ab977ed31c5a39989d570847d6d0052af
|
||||
author KN4CK3R <admin@oldschoolhack.me> 1711702962 +0100
|
||||
committer KN4CK3R <admin@oldschoolhack.me> 1711702962 +0100
|
||||
@ -172,8 +169,7 @@ SONRzusmu5n3DgV956REL7x62h7JuqmBz/12HZkr0z0zgXkcZ04q08pSJATX5N1F
|
||||
yN+tWxTsWg+zhDk96d5Esdo9JMjcFvPv0eioo30GAERaz1hoD7zCMT4jgUFTQwgz
|
||||
jw4YcO5u
|
||||
=r3UU
|
||||
-----END PGP SIGNATURE-----
|
||||
`, commitFromReader.Signature.Signature)
|
||||
-----END PGP SIGNATURE-----`, commitFromReader.Signature.Signature)
|
||||
assert.Equal(t, `tree ca3fad42080dd1a6d291b75acdfc46e5b9b307e5
|
||||
parent 47b24e7ab977ed31c5a39989d570847d6d0052af
|
||||
author KN4CK3R <admin@oldschoolhack.me> 1711702962 +0100
|
||||
|
@ -18,6 +18,8 @@ const (
|
||||
CommitStatusFailure CommitStatusState = "failure"
|
||||
// CommitStatusWarning is for when the CommitStatus is Warning
|
||||
CommitStatusWarning CommitStatusState = "warning"
|
||||
// CommitStatusSkipped is for when CommitStatus is Skipped
|
||||
CommitStatusSkipped CommitStatusState = "skipped"
|
||||
)
|
||||
|
||||
var commitStatusPriorities = map[CommitStatusState]int{
|
||||
@ -26,25 +28,17 @@ var commitStatusPriorities = map[CommitStatusState]int{
|
||||
CommitStatusWarning: 2,
|
||||
CommitStatusPending: 3,
|
||||
CommitStatusSuccess: 4,
|
||||
CommitStatusSkipped: 5,
|
||||
}
|
||||
|
||||
func (css CommitStatusState) String() string {
|
||||
return string(css)
|
||||
}
|
||||
|
||||
// NoBetterThan returns true if this State is no better than the given State
|
||||
// This function only handles the states defined in CommitStatusPriorities
|
||||
func (css CommitStatusState) NoBetterThan(css2 CommitStatusState) bool {
|
||||
// NoBetterThan only handles the 5 states above
|
||||
if _, exist := commitStatusPriorities[css]; !exist {
|
||||
return false
|
||||
}
|
||||
|
||||
if _, exist := commitStatusPriorities[css2]; !exist {
|
||||
return false
|
||||
}
|
||||
|
||||
return commitStatusPriorities[css] <= commitStatusPriorities[css2]
|
||||
// HasHigherPriorityThan returns true if this state has higher priority than the other
|
||||
// Undefined states are considered to have the highest priority like CommitStatusError(0)
|
||||
func (css CommitStatusState) HasHigherPriorityThan(other CommitStatusState) bool {
|
||||
return commitStatusPriorities[css] < commitStatusPriorities[other]
|
||||
}
|
||||
|
||||
// IsPending represents if commit status state is pending
|
||||
|
@ -10,165 +10,21 @@ import (
|
||||
)
|
||||
|
||||
func TestNoBetterThan(t *testing.T) {
|
||||
type args struct {
|
||||
css CommitStatusState
|
||||
css2 CommitStatusState
|
||||
}
|
||||
var unExpectedState CommitStatusState
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want bool
|
||||
s1, s2 CommitStatusState
|
||||
higher bool
|
||||
}{
|
||||
{
|
||||
name: "success is no better than success",
|
||||
args: args{
|
||||
css: CommitStatusSuccess,
|
||||
css2: CommitStatusSuccess,
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "success is no better than pending",
|
||||
args: args{
|
||||
css: CommitStatusSuccess,
|
||||
css2: CommitStatusPending,
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "success is no better than failure",
|
||||
args: args{
|
||||
css: CommitStatusSuccess,
|
||||
css2: CommitStatusFailure,
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "success is no better than error",
|
||||
args: args{
|
||||
css: CommitStatusSuccess,
|
||||
css2: CommitStatusError,
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "pending is no better than success",
|
||||
args: args{
|
||||
css: CommitStatusPending,
|
||||
css2: CommitStatusSuccess,
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "pending is no better than pending",
|
||||
args: args{
|
||||
css: CommitStatusPending,
|
||||
css2: CommitStatusPending,
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "pending is no better than failure",
|
||||
args: args{
|
||||
css: CommitStatusPending,
|
||||
css2: CommitStatusFailure,
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "pending is no better than error",
|
||||
args: args{
|
||||
css: CommitStatusPending,
|
||||
css2: CommitStatusError,
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "failure is no better than success",
|
||||
args: args{
|
||||
css: CommitStatusFailure,
|
||||
css2: CommitStatusSuccess,
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "failure is no better than pending",
|
||||
args: args{
|
||||
css: CommitStatusFailure,
|
||||
css2: CommitStatusPending,
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "failure is no better than failure",
|
||||
args: args{
|
||||
css: CommitStatusFailure,
|
||||
css2: CommitStatusFailure,
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "failure is no better than error",
|
||||
args: args{
|
||||
css: CommitStatusFailure,
|
||||
css2: CommitStatusError,
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "error is no better than success",
|
||||
args: args{
|
||||
css: CommitStatusError,
|
||||
css2: CommitStatusSuccess,
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "error is no better than pending",
|
||||
args: args{
|
||||
css: CommitStatusError,
|
||||
css2: CommitStatusPending,
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "error is no better than failure",
|
||||
args: args{
|
||||
css: CommitStatusError,
|
||||
css2: CommitStatusFailure,
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "error is no better than error",
|
||||
args: args{
|
||||
css: CommitStatusError,
|
||||
css2: CommitStatusError,
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "unExpectedState is no better than success",
|
||||
args: args{
|
||||
css: unExpectedState,
|
||||
css2: CommitStatusSuccess,
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "unExpectedState is no better than unExpectedState",
|
||||
args: args{
|
||||
css: unExpectedState,
|
||||
css2: unExpectedState,
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
{CommitStatusError, CommitStatusFailure, true},
|
||||
{CommitStatusFailure, CommitStatusWarning, true},
|
||||
{CommitStatusWarning, CommitStatusPending, true},
|
||||
{CommitStatusPending, CommitStatusSuccess, true},
|
||||
{CommitStatusSuccess, CommitStatusSkipped, true},
|
||||
|
||||
{CommitStatusError, "unknown-xxx", false},
|
||||
{"unknown-xxx", CommitStatusFailure, true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := tt.args.css.NoBetterThan(tt.args.css2)
|
||||
assert.Equal(t, tt.want, result)
|
||||
})
|
||||
assert.Equal(t, tt.higher, tt.s1.HasHigherPriorityThan(tt.s2), "s1=%s, s2=%s, expected=%v", tt.s1, tt.s2, tt.higher)
|
||||
}
|
||||
assert.False(t, CommitStatusError.HasHigherPriorityThan(CommitStatusError))
|
||||
}
|
||||
|
@ -3817,6 +3817,7 @@ runs.expire_log_message = Logs have been purged because they were too old.
|
||||
runs.delete = Delete workflow run
|
||||
runs.delete.description = Are you sure you want to permanently delete this workflow run? This action cannot be undone.
|
||||
runs.not_done = This workflow run is not done.
|
||||
runs.view_workflow_file = View workflow file
|
||||
|
||||
workflow.disable = Disable Workflow
|
||||
workflow.disable_success = Workflow '%s' disabled successfully.
|
||||
|
@ -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>`
|
||||
|
@ -3809,6 +3809,9 @@ runs.no_workflows.documentation=Gitea Actions の詳細については、<a targ
|
||||
runs.no_runs=ワークフローはまだ実行されていません。
|
||||
runs.empty_commit_message=(空のコミットメッセージ)
|
||||
runs.expire_log_message=ログは古すぎるため消去されています。
|
||||
runs.delete=ワークフローの実行を削除
|
||||
runs.delete.description=このワークフローを完全に削除してもよろしいですか?この操作は元に戻せません。
|
||||
runs.not_done=このワークフローの実行は完了していません。
|
||||
|
||||
workflow.disable=ワークフローを無効にする
|
||||
workflow.disable_success=ワークフロー '%s' が無効になりました。
|
||||
|
@ -1523,7 +1523,7 @@ issues.remove_labels=removeu os rótulos %s %s
|
||||
issues.add_remove_labels=adicionou o(s) rótulo(s) %s e removeu %s %s
|
||||
issues.add_milestone_at=`adicionou esta questão à etapa <b>%s</b> %s`
|
||||
issues.add_project_at=`adicionou esta questão ao planeamento <b>%s</b> %s`
|
||||
issues.move_to_column_of_project=`isto foi movido para %s dentro de %s em %s`
|
||||
issues.move_to_column_of_project=`moveu isto para %s em %s %s`
|
||||
issues.change_milestone_at=`modificou a etapa de <b>%s</b> para <b>%s</b> %s`
|
||||
issues.change_project_at=`modificou o planeamento de <b>%s</b> para <b>%s</b> %s`
|
||||
issues.remove_milestone_at=`removeu esta questão da etapa <b>%s</b> %s`
|
||||
|
@ -101,7 +101,7 @@ func GetAllOrgs(ctx *context.APIContext) {
|
||||
|
||||
listOptions := utils.GetListOptions(ctx)
|
||||
|
||||
users, maxResults, err := user_model.SearchUsers(ctx, &user_model.SearchUserOptions{
|
||||
users, maxResults, err := user_model.SearchUsers(ctx, user_model.SearchUserOptions{
|
||||
Actor: ctx.Doer,
|
||||
Type: user_model.UserTypeOrganization,
|
||||
OrderBy: db.SearchOrderByAlphabetically,
|
||||
|
@ -423,7 +423,7 @@ func SearchUsers(ctx *context.APIContext) {
|
||||
|
||||
listOptions := utils.GetListOptions(ctx)
|
||||
|
||||
users, maxResults, err := user_model.SearchUsers(ctx, &user_model.SearchUserOptions{
|
||||
users, maxResults, err := user_model.SearchUsers(ctx, user_model.SearchUserOptions{
|
||||
Actor: ctx.Doer,
|
||||
Type: user_model.UserTypeIndividual,
|
||||
LoginName: ctx.FormTrim("login_name"),
|
||||
|
@ -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
|
||||
|
@ -201,7 +201,7 @@ func GetAll(ctx *context.APIContext) {
|
||||
|
||||
listOptions := utils.GetListOptions(ctx)
|
||||
|
||||
publicOrgs, maxResults, err := user_model.SearchUsers(ctx, &user_model.SearchUserOptions{
|
||||
publicOrgs, maxResults, err := user_model.SearchUsers(ctx, user_model.SearchUserOptions{
|
||||
Actor: ctx.Doer,
|
||||
ListOptions: listOptions,
|
||||
Type: user_model.UserTypeOrganization,
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -258,19 +258,24 @@ func GetCombinedCommitStatusByRef(ctx *context.APIContext) {
|
||||
|
||||
repo := ctx.Repo.Repository
|
||||
|
||||
statuses, count, err := git_model.GetLatestCommitStatus(ctx, repo.ID, refCommit.Commit.ID.String(), utils.GetListOptions(ctx))
|
||||
statuses, err := git_model.GetLatestCommitStatus(ctx, repo.ID, refCommit.Commit.ID.String(), utils.GetListOptions(ctx))
|
||||
if err != nil {
|
||||
ctx.APIErrorInternal(fmt.Errorf("GetLatestCommitStatus[%s, %s]: %w", repo.FullName(), refCommit.CommitID, err))
|
||||
return
|
||||
}
|
||||
|
||||
count, err := git_model.CountLatestCommitStatus(ctx, repo.ID, refCommit.Commit.ID.String())
|
||||
if err != nil {
|
||||
ctx.APIErrorInternal(fmt.Errorf("CountLatestCommitStatus[%s, %s]: %w", repo.FullName(), refCommit.CommitID, err))
|
||||
return
|
||||
}
|
||||
ctx.SetTotalCountHeader(count)
|
||||
|
||||
if len(statuses) == 0 {
|
||||
ctx.JSON(http.StatusOK, &api.CombinedStatus{})
|
||||
return
|
||||
}
|
||||
|
||||
combiStatus := convert.ToCombinedStatus(ctx, statuses, convert.ToRepo(ctx, repo, ctx.Repo.Permission))
|
||||
|
||||
ctx.SetTotalCountHeader(count)
|
||||
ctx.JSON(http.StatusOK, combiStatus)
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -73,7 +73,7 @@ func Search(ctx *context.APIContext) {
|
||||
if ctx.PublicOnly {
|
||||
visible = []structs.VisibleType{structs.VisibleTypePublic}
|
||||
}
|
||||
users, maxResults, err = user_model.SearchUsers(ctx, &user_model.SearchUserOptions{
|
||||
users, maxResults, err = user_model.SearchUsers(ctx, user_model.SearchUserOptions{
|
||||
Actor: ctx.Doer,
|
||||
Keyword: ctx.FormTrim("q"),
|
||||
UID: uid,
|
||||
|
@ -27,7 +27,7 @@ func Organizations(ctx *context.Context) {
|
||||
ctx.SetFormString("sort", UserSearchDefaultAdminSort)
|
||||
}
|
||||
|
||||
explore.RenderUserSearch(ctx, &user_model.SearchUserOptions{
|
||||
explore.RenderUserSearch(ctx, user_model.SearchUserOptions{
|
||||
Actor: ctx.Doer,
|
||||
Type: user_model.UserTypeOrganization,
|
||||
IncludeReserved: true, // administrator needs to list all accounts include reserved
|
||||
|
@ -64,7 +64,7 @@ func Users(ctx *context.Context) {
|
||||
"SortType": sortType,
|
||||
}
|
||||
|
||||
explore.RenderUserSearch(ctx, &user_model.SearchUserOptions{
|
||||
explore.RenderUserSearch(ctx, user_model.SearchUserOptions{
|
||||
Actor: ctx.Doer,
|
||||
Type: user_model.UserTypeIndividual,
|
||||
ListOptions: db.ListOptions{
|
||||
|
@ -44,7 +44,7 @@ func Organizations(ctx *context.Context) {
|
||||
ctx.SetFormString("sort", sortOrder)
|
||||
}
|
||||
|
||||
RenderUserSearch(ctx, &user_model.SearchUserOptions{
|
||||
RenderUserSearch(ctx, user_model.SearchUserOptions{
|
||||
Actor: ctx.Doer,
|
||||
Type: user_model.UserTypeOrganization,
|
||||
ListOptions: db.ListOptions{PageSize: setting.UI.ExplorePagingNum},
|
||||
|
@ -32,7 +32,7 @@ func isKeywordValid(keyword string) bool {
|
||||
}
|
||||
|
||||
// RenderUserSearch render user search page
|
||||
func RenderUserSearch(ctx *context.Context, opts *user_model.SearchUserOptions, tplName templates.TplName) {
|
||||
func RenderUserSearch(ctx *context.Context, opts user_model.SearchUserOptions, tplName templates.TplName) {
|
||||
// Sitemap index for sitemap paths
|
||||
opts.Page = int(ctx.PathParamInt64("idx"))
|
||||
isSitemap := ctx.PathParam("idx") != ""
|
||||
@ -151,7 +151,7 @@ func Users(ctx *context.Context) {
|
||||
ctx.SetFormString("sort", sortOrder)
|
||||
}
|
||||
|
||||
RenderUserSearch(ctx, &user_model.SearchUserOptions{
|
||||
RenderUserSearch(ctx, user_model.SearchUserOptions{
|
||||
Actor: ctx.Doer,
|
||||
Type: user_model.UserTypeIndividual,
|
||||
ListOptions: db.ListOptions{PageSize: setting.UI.ExplorePagingNum},
|
||||
|
@ -68,7 +68,7 @@ func Home(ctx *context.Context) {
|
||||
func HomeSitemap(ctx *context.Context) {
|
||||
m := sitemap.NewSitemapIndex()
|
||||
if !setting.Service.Explore.DisableUsersPage {
|
||||
_, cnt, err := user_model.SearchUsers(ctx, &user_model.SearchUserOptions{
|
||||
_, cnt, err := user_model.SearchUsers(ctx, user_model.SearchUserOptions{
|
||||
Type: user_model.UserTypeIndividual,
|
||||
ListOptions: db.ListOptions{PageSize: 1},
|
||||
IsActive: optional.Some(true),
|
||||
|
@ -126,7 +126,7 @@ func prepareWorkflowDispatchTemplate(ctx *context.Context, commit *git.Commit) (
|
||||
|
||||
var curWorkflow *model.Workflow
|
||||
|
||||
entries, err := actions.ListWorkflows(commit)
|
||||
_, entries, err := actions.ListWorkflows(commit)
|
||||
if err != nil {
|
||||
ctx.ServerError("ListWorkflows", err)
|
||||
return nil
|
||||
|
@ -64,6 +64,36 @@ func View(ctx *context_module.Context) {
|
||||
ctx.HTML(http.StatusOK, tplViewActions)
|
||||
}
|
||||
|
||||
func ViewWorkflowFile(ctx *context_module.Context) {
|
||||
runIndex := getRunIndex(ctx)
|
||||
run, err := actions_model.GetRunByIndex(ctx, ctx.Repo.Repository.ID, runIndex)
|
||||
if err != nil {
|
||||
ctx.NotFoundOrServerError("GetRunByIndex", func(err error) bool {
|
||||
return errors.Is(err, util.ErrNotExist)
|
||||
}, err)
|
||||
return
|
||||
}
|
||||
commit, err := ctx.Repo.GitRepo.GetCommit(run.CommitSHA)
|
||||
if err != nil {
|
||||
ctx.NotFoundOrServerError("GetCommit", func(err error) bool {
|
||||
return errors.Is(err, util.ErrNotExist)
|
||||
}, err)
|
||||
return
|
||||
}
|
||||
rpath, entries, err := actions.ListWorkflows(commit)
|
||||
if err != nil {
|
||||
ctx.ServerError("ListWorkflows", err)
|
||||
return
|
||||
}
|
||||
for _, entry := range entries {
|
||||
if entry.Name() == run.WorkflowID {
|
||||
ctx.Redirect(fmt.Sprintf("%s/src/commit/%s/%s/%s", ctx.Repo.RepoLink, url.PathEscape(run.CommitSHA), util.PathEscapeSegments(rpath), util.PathEscapeSegments(run.WorkflowID)))
|
||||
return
|
||||
}
|
||||
}
|
||||
ctx.NotFound(nil)
|
||||
}
|
||||
|
||||
type LogCursor struct {
|
||||
Step int `json:"step"`
|
||||
Cursor int64 `json:"cursor"`
|
||||
|
@ -377,7 +377,7 @@ func Diff(ctx *context.Context) {
|
||||
ctx.Data["FileIconPoolHTML"] = renderedIconPool.RenderToHTML()
|
||||
}
|
||||
|
||||
statuses, _, err := git_model.GetLatestCommitStatus(ctx, ctx.Repo.Repository.ID, commitID, db.ListOptionsAll)
|
||||
statuses, err := git_model.GetLatestCommitStatus(ctx, ctx.Repo.Repository.ID, commitID, db.ListOptionsAll)
|
||||
if err != nil {
|
||||
log.Error("GetLatestCommitStatus: %v", err)
|
||||
}
|
||||
|
@ -291,7 +291,7 @@ func prepareMergedViewPullInfo(ctx *context.Context, issue *issues_model.Issue)
|
||||
|
||||
if len(compareInfo.Commits) != 0 {
|
||||
sha := compareInfo.Commits[0].ID.String()
|
||||
commitStatuses, _, err := git_model.GetLatestCommitStatus(ctx, ctx.Repo.Repository.ID, sha, db.ListOptionsAll)
|
||||
commitStatuses, err := git_model.GetLatestCommitStatus(ctx, ctx.Repo.Repository.ID, sha, db.ListOptionsAll)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetLatestCommitStatus", err)
|
||||
return nil
|
||||
@ -358,7 +358,7 @@ func prepareViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *git.C
|
||||
ctx.ServerError(fmt.Sprintf("GetRefCommitID(%s)", pull.GetGitRefName()), err)
|
||||
return nil
|
||||
}
|
||||
commitStatuses, _, err := git_model.GetLatestCommitStatus(ctx, repo.ID, sha, db.ListOptionsAll)
|
||||
commitStatuses, err := git_model.GetLatestCommitStatus(ctx, repo.ID, sha, db.ListOptionsAll)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetLatestCommitStatus", err)
|
||||
return nil
|
||||
@ -454,7 +454,7 @@ func prepareViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *git.C
|
||||
return nil
|
||||
}
|
||||
|
||||
commitStatuses, _, err := git_model.GetLatestCommitStatus(ctx, repo.ID, sha, db.ListOptionsAll)
|
||||
commitStatuses, err := git_model.GetLatestCommitStatus(ctx, repo.ID, sha, db.ListOptionsAll)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetLatestCommitStatus", err)
|
||||
return nil
|
||||
|
@ -130,7 +130,7 @@ func getReleaseInfos(ctx *context.Context, opts *repo_model.FindReleasesOptions)
|
||||
}
|
||||
|
||||
if canReadActions {
|
||||
statuses, _, err := git_model.GetLatestCommitStatus(ctx, r.Repo.ID, r.Sha1, db.ListOptionsAll)
|
||||
statuses, err := git_model.GetLatestCommitStatus(ctx, r.Repo.ID, r.Sha1, db.ListOptionsAll)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -131,7 +131,7 @@ func loadLatestCommitData(ctx *context.Context, latestCommit *git.Commit) bool {
|
||||
ctx.Data["LatestCommitVerification"] = verification
|
||||
ctx.Data["LatestCommitUser"] = user_model.ValidateCommitWithEmail(ctx, latestCommit)
|
||||
|
||||
statuses, _, err := git_model.GetLatestCommitStatus(ctx, ctx.Repo.Repository.ID, latestCommit.ID.String(), db.ListOptionsAll)
|
||||
statuses, err := git_model.GetLatestCommitStatus(ctx, ctx.Repo.Repository.ID, latestCommit.ID.String(), db.ListOptionsAll)
|
||||
if err != nil {
|
||||
log.Error("GetLatestCommitStatus: %v", err)
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ import (
|
||||
|
||||
// SearchCandidates searches candidate users for dropdown list
|
||||
func SearchCandidates(ctx *context.Context) {
|
||||
users, _, err := user_model.SearchUsers(ctx, &user_model.SearchUserOptions{
|
||||
users, _, err := user_model.SearchUsers(ctx, user_model.SearchUserOptions{
|
||||
Actor: ctx.Doer,
|
||||
Keyword: ctx.FormTrim("q"),
|
||||
Type: user_model.UserTypeIndividual,
|
||||
|
@ -1445,6 +1445,7 @@ func registerWebRoutes(m *web.Router) {
|
||||
m.Post("/rerun", reqRepoActionsWriter, actions.Rerun)
|
||||
m.Get("/logs", actions.Logs)
|
||||
})
|
||||
m.Get("/workflow", actions.ViewWorkflowFile)
|
||||
m.Post("/cancel", reqRepoActionsWriter, actions.Cancel)
|
||||
m.Post("/approve", reqRepoActionsWriter, actions.Approve)
|
||||
m.Post("/delete", reqRepoActionsWriter, actions.Delete)
|
||||
|
@ -92,7 +92,7 @@ func createCommitStatus(ctx context.Context, job *actions_model.ActionRunJob) er
|
||||
}
|
||||
ctxname := fmt.Sprintf("%s / %s (%s)", runName, job.Name, event)
|
||||
state := toCommitStatus(job.Status)
|
||||
if statuses, _, err := git_model.GetLatestCommitStatus(ctx, repo.ID, sha, db.ListOptionsAll); err == nil {
|
||||
if statuses, err := git_model.GetLatestCommitStatus(ctx, repo.ID, sha, db.ListOptionsAll); err == nil {
|
||||
for _, v := range statuses {
|
||||
if v.Context == ctxname {
|
||||
if v.State == state {
|
||||
@ -149,12 +149,14 @@ func createCommitStatus(ctx context.Context, job *actions_model.ActionRunJob) er
|
||||
|
||||
func toCommitStatus(status actions_model.Status) api.CommitStatusState {
|
||||
switch status {
|
||||
case actions_model.StatusSuccess, actions_model.StatusSkipped:
|
||||
case actions_model.StatusSuccess:
|
||||
return api.CommitStatusSuccess
|
||||
case actions_model.StatusFailure, actions_model.StatusCancelled:
|
||||
return api.CommitStatusFailure
|
||||
case actions_model.StatusWaiting, actions_model.StatusBlocked, actions_model.StatusRunning:
|
||||
return api.CommitStatusPending
|
||||
case actions_model.StatusSkipped:
|
||||
return api.CommitStatusSkipped
|
||||
default:
|
||||
return api.CommitStatusError
|
||||
}
|
||||
|
@ -31,16 +31,6 @@ import (
|
||||
"github.com/nektos/act/pkg/model"
|
||||
)
|
||||
|
||||
func getActionWorkflowPath(commit *git.Commit) string {
|
||||
paths := []string{".gitea/workflows", ".github/workflows"}
|
||||
for _, treePath := range paths {
|
||||
if _, err := commit.SubTree(treePath); err == nil {
|
||||
return treePath
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func getActionWorkflowEntry(ctx *context.APIContext, commit *git.Commit, folder string, entry *git.TreeEntry) *api.ActionWorkflow {
|
||||
cfgUnit := ctx.Repo.Repository.MustGetUnit(ctx, unit.TypeActions)
|
||||
cfg := cfgUnit.ActionsConfig()
|
||||
@ -109,14 +99,12 @@ func ListActionWorkflows(ctx *context.APIContext) ([]*api.ActionWorkflow, error)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
entries, err := actions.ListWorkflows(defaultBranchCommit)
|
||||
folder, entries, err := actions.ListWorkflows(defaultBranchCommit)
|
||||
if err != nil {
|
||||
ctx.APIError(http.StatusNotFound, err.Error())
|
||||
return nil, err
|
||||
}
|
||||
|
||||
folder := getActionWorkflowPath(defaultBranchCommit)
|
||||
|
||||
workflows := make([]*api.ActionWorkflow, len(entries))
|
||||
for i, entry := range entries {
|
||||
workflows[i] = getActionWorkflowEntry(ctx, defaultBranchCommit, folder, entry)
|
||||
@ -185,7 +173,7 @@ func DispatchActionWorkflow(ctx reqctx.RequestContext, doer *user_model.User, re
|
||||
}
|
||||
|
||||
// get workflow entry from runTargetCommit
|
||||
entries, err := actions.ListWorkflows(runTargetCommit)
|
||||
_, entries, err := actions.ListWorkflows(runTargetCommit)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -42,13 +42,14 @@ func ToCombinedStatus(ctx context.Context, statuses []*git_model.CommitStatus, r
|
||||
SHA: statuses[0].SHA,
|
||||
TotalCount: len(statuses),
|
||||
Repository: repo,
|
||||
URL: "",
|
||||
URL: "", // never set or used?
|
||||
State: api.CommitStatusSuccess,
|
||||
}
|
||||
|
||||
retStatus.Statuses = make([]*api.CommitStatus, 0, len(statuses))
|
||||
for _, status := range statuses {
|
||||
retStatus.Statuses = append(retStatus.Statuses, ToCommitStatus(ctx, status))
|
||||
if retStatus.State == "" || status.State.NoBetterThan(retStatus.State) {
|
||||
if status.State.HasHigherPriorityThan(retStatus.State) {
|
||||
retStatus.State = status.State
|
||||
}
|
||||
}
|
||||
@ -57,9 +58,13 @@ func ToCombinedStatus(ctx context.Context, statuses []*git_model.CommitStatus, r
|
||||
// > failure if any of the contexts report as error or failure
|
||||
// > pending if there are no statuses or a context is pending
|
||||
// > success if the latest status for all contexts is success
|
||||
if retStatus.State.IsError() {
|
||||
retStatus.State = api.CommitStatusFailure
|
||||
switch retStatus.State {
|
||||
case api.CommitStatusSkipped:
|
||||
retStatus.State = api.CommitStatusSuccess // all skipped means success
|
||||
case api.CommitStatusPending, api.CommitStatusSuccess:
|
||||
// use the current state for pending or success
|
||||
default:
|
||||
retStatus.State = api.CommitStatusFailure // otherwise, it is a failure
|
||||
}
|
||||
|
||||
return retStatus
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -54,6 +54,8 @@ func ToggleAssigneeWithNotify(ctx context.Context, issue *issues_model.Issue, do
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
issue.AssigneeID = assigneeID
|
||||
issue.Assignee = assignee
|
||||
|
||||
notify_service.IssueChangeAssignee(ctx, doer, issue, assignee, removed, comment)
|
||||
|
||||
|
@ -180,11 +180,15 @@ func (c *CodeCommitDownloader) GetPullRequests(ctx context.Context, page, perPag
|
||||
continue
|
||||
}
|
||||
target := orig.PullRequestTargets[0]
|
||||
description := ""
|
||||
if orig.Description != nil {
|
||||
description = *orig.Description
|
||||
}
|
||||
pr := &base.PullRequest{
|
||||
Number: number,
|
||||
Title: *orig.Title,
|
||||
PosterName: c.getUsernameFromARN(*orig.AuthorArn),
|
||||
Content: *orig.Description,
|
||||
Content: description,
|
||||
State: "open",
|
||||
Created: *orig.CreationDate,
|
||||
Updated: *orig.LastActivityDate,
|
||||
|
@ -46,59 +46,33 @@ func MergeRequiredContextsCommitStatus(commitStatuses []*git_model.CommitStatus,
|
||||
|
||||
// If required rule not match any action, then it is pending
|
||||
if targetStatus == "" {
|
||||
if structs.CommitStatusPending.NoBetterThan(returnedStatus) {
|
||||
if structs.CommitStatusPending.HasHigherPriorityThan(returnedStatus) {
|
||||
returnedStatus = structs.CommitStatusPending
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
if targetStatus.NoBetterThan(returnedStatus) {
|
||||
if targetStatus.HasHigherPriorityThan(returnedStatus) {
|
||||
returnedStatus = targetStatus
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if matchedCount == 0 && returnedStatus == structs.CommitStatusSuccess {
|
||||
status := git_model.CalcCommitStatus(commitStatuses)
|
||||
if status != nil {
|
||||
return status.State
|
||||
if len(commitStatuses) == 0 {
|
||||
// "no statuses" should mean "pending"
|
||||
return structs.CommitStatusPending
|
||||
}
|
||||
return structs.CommitStatusSuccess
|
||||
status := git_model.CalcCommitStatus(commitStatuses)
|
||||
if status.State == structs.CommitStatusSkipped {
|
||||
return structs.CommitStatusSuccess // if all statuses are skipped, return success
|
||||
}
|
||||
return status.State
|
||||
}
|
||||
|
||||
return returnedStatus
|
||||
}
|
||||
|
||||
// IsCommitStatusContextSuccess returns true if all required status check contexts succeed.
|
||||
func IsCommitStatusContextSuccess(commitStatuses []*git_model.CommitStatus, requiredContexts []string) bool {
|
||||
// If no specific context is required, require that last commit status is a success
|
||||
if len(requiredContexts) == 0 {
|
||||
status := git_model.CalcCommitStatus(commitStatuses)
|
||||
if status == nil || status.State != structs.CommitStatusSuccess {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
for _, ctx := range requiredContexts {
|
||||
var found bool
|
||||
for _, commitStatus := range commitStatuses {
|
||||
if commitStatus.Context == ctx {
|
||||
if commitStatus.State != structs.CommitStatusSuccess {
|
||||
return false
|
||||
}
|
||||
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// IsPullCommitStatusPass returns if all required status checks PASS
|
||||
func IsPullCommitStatusPass(ctx context.Context, pr *issues_model.PullRequest) (bool, error) {
|
||||
pb, err := git_model.GetFirstMatchProtectedBranchRule(ctx, pr.BaseRepoID, pr.BaseBranch)
|
||||
@ -151,7 +125,7 @@ func GetPullRequestCommitStatusState(ctx context.Context, pr *issues_model.PullR
|
||||
return "", errors.Wrap(err, "LoadBaseRepo")
|
||||
}
|
||||
|
||||
commitStatuses, _, err := git_model.GetLatestCommitStatus(ctx, pr.BaseRepo.ID, sha, db.ListOptionsAll)
|
||||
commitStatuses, err := git_model.GetLatestCommitStatus(ctx, pr.BaseRepo.ID, sha, db.ListOptionsAll)
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, "GetLatestCommitStatus")
|
||||
}
|
||||
|
@ -14,52 +14,70 @@ import (
|
||||
)
|
||||
|
||||
func TestMergeRequiredContextsCommitStatus(t *testing.T) {
|
||||
testCases := [][]*git_model.CommitStatus{
|
||||
cases := []struct {
|
||||
commitStatuses []*git_model.CommitStatus
|
||||
requiredContexts []string
|
||||
expected structs.CommitStatusState
|
||||
}{
|
||||
{
|
||||
{Context: "Build 1", State: structs.CommitStatusSuccess},
|
||||
{Context: "Build 2", State: structs.CommitStatusSuccess},
|
||||
{Context: "Build 3", State: structs.CommitStatusSuccess},
|
||||
commitStatuses: []*git_model.CommitStatus{},
|
||||
requiredContexts: []string{},
|
||||
expected: structs.CommitStatusPending,
|
||||
},
|
||||
{
|
||||
{Context: "Build 1", State: structs.CommitStatusSuccess},
|
||||
{Context: "Build 2", State: structs.CommitStatusSuccess},
|
||||
{Context: "Build 2t", State: structs.CommitStatusPending},
|
||||
commitStatuses: []*git_model.CommitStatus{
|
||||
{Context: "Build xxx", State: structs.CommitStatusSkipped},
|
||||
},
|
||||
requiredContexts: []string{"Build*"},
|
||||
expected: structs.CommitStatusSuccess,
|
||||
},
|
||||
{
|
||||
{Context: "Build 1", State: structs.CommitStatusSuccess},
|
||||
{Context: "Build 2", State: structs.CommitStatusSuccess},
|
||||
{Context: "Build 2t", State: structs.CommitStatusFailure},
|
||||
commitStatuses: []*git_model.CommitStatus{
|
||||
{Context: "Build 1", State: structs.CommitStatusSkipped},
|
||||
{Context: "Build 2", State: structs.CommitStatusSuccess},
|
||||
{Context: "Build 3", State: structs.CommitStatusSuccess},
|
||||
},
|
||||
requiredContexts: []string{"Build*"},
|
||||
expected: structs.CommitStatusSuccess,
|
||||
},
|
||||
{
|
||||
{Context: "Build 1", State: structs.CommitStatusSuccess},
|
||||
{Context: "Build 2", State: structs.CommitStatusSuccess},
|
||||
{Context: "Build 2t", State: structs.CommitStatusSuccess},
|
||||
commitStatuses: []*git_model.CommitStatus{
|
||||
{Context: "Build 1", State: structs.CommitStatusSuccess},
|
||||
{Context: "Build 2", State: structs.CommitStatusSuccess},
|
||||
{Context: "Build 2t", State: structs.CommitStatusPending},
|
||||
},
|
||||
requiredContexts: []string{"Build*", "Build 2t*"},
|
||||
expected: structs.CommitStatusPending,
|
||||
},
|
||||
{
|
||||
{Context: "Build 1", State: structs.CommitStatusSuccess},
|
||||
{Context: "Build 2", State: structs.CommitStatusSuccess},
|
||||
{Context: "Build 2t", State: structs.CommitStatusSuccess},
|
||||
commitStatuses: []*git_model.CommitStatus{
|
||||
{Context: "Build 1", State: structs.CommitStatusSuccess},
|
||||
{Context: "Build 2", State: structs.CommitStatusSuccess},
|
||||
{Context: "Build 2t", State: structs.CommitStatusFailure},
|
||||
},
|
||||
requiredContexts: []string{"Build*", "Build 2t*"},
|
||||
expected: structs.CommitStatusFailure,
|
||||
},
|
||||
{
|
||||
commitStatuses: []*git_model.CommitStatus{
|
||||
{Context: "Build 1", State: structs.CommitStatusSuccess},
|
||||
{Context: "Build 2", State: structs.CommitStatusSuccess},
|
||||
{Context: "Build 2t", State: structs.CommitStatusSuccess},
|
||||
},
|
||||
requiredContexts: []string{"Build*", "Build 2t*", "Build 3*"},
|
||||
expected: structs.CommitStatusPending,
|
||||
},
|
||||
{
|
||||
commitStatuses: []*git_model.CommitStatus{
|
||||
{Context: "Build 1", State: structs.CommitStatusSuccess},
|
||||
{Context: "Build 2", State: structs.CommitStatusSuccess},
|
||||
{Context: "Build 2t", State: structs.CommitStatusSuccess},
|
||||
},
|
||||
requiredContexts: []string{"Build*", "Build *", "Build 2t*", "Build 1*"},
|
||||
expected: structs.CommitStatusSuccess,
|
||||
},
|
||||
}
|
||||
testCasesRequiredContexts := [][]string{
|
||||
{"Build*"},
|
||||
{"Build*", "Build 2t*"},
|
||||
{"Build*", "Build 2t*"},
|
||||
{"Build*", "Build 2t*", "Build 3*"},
|
||||
{"Build*", "Build *", "Build 2t*", "Build 1*"},
|
||||
}
|
||||
|
||||
testCasesExpected := []structs.CommitStatusState{
|
||||
structs.CommitStatusSuccess,
|
||||
structs.CommitStatusPending,
|
||||
structs.CommitStatusFailure,
|
||||
structs.CommitStatusPending,
|
||||
structs.CommitStatusSuccess,
|
||||
}
|
||||
|
||||
for i, commitStatuses := range testCases {
|
||||
if MergeRequiredContextsCommitStatus(commitStatuses, testCasesRequiredContexts[i]) != testCasesExpected[i] {
|
||||
assert.Fail(t, "Test case failed", "Test case %d failed", i+1)
|
||||
}
|
||||
for i, c := range cases {
|
||||
assert.Equal(t, c.expected, MergeRequiredContextsCommitStatus(c.commitStatuses, c.requiredContexts), "case %d", i)
|
||||
}
|
||||
}
|
||||
|
@ -1004,7 +1004,7 @@ func getAllCommitStatus(ctx context.Context, gitRepo *git.Repository, pr *issues
|
||||
return nil, nil, shaErr
|
||||
}
|
||||
|
||||
statuses, _, err = git_model.GetLatestCommitStatus(ctx, pr.BaseRepo.ID, sha, db.ListOptionsAll)
|
||||
statuses, err = git_model.GetLatestCommitStatus(ctx, pr.BaseRepo.ID, sha, db.ListOptionsAll)
|
||||
lastStatus = git_model.CalcCommitStatus(statuses)
|
||||
return statuses, lastStatus, err
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -36,15 +36,20 @@
|
||||
<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">
|
||||
<a class="item" href="{{$run.Link}}/workflow">{{svg "octicon-play"}}{{ctx.Locale.Tr "actions.runs.view_workflow_file"}}</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}}
|
||||
|
@ -14,3 +14,6 @@
|
||||
{{if eq .State "warning"}}
|
||||
{{svg "gitea-exclamation" 18 "commit-status icon text yellow"}}
|
||||
{{end}}
|
||||
{{if eq .State "skipped"}}
|
||||
{{svg "octicon-skip" 18 "commit-status icon text grey"}}
|
||||
{{end}}
|
||||
|
32
templates/swagger/v1_json.tmpl
generated
32
templates/swagger/v1_json.tmpl
generated
@ -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."
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -633,7 +633,7 @@ jobs:
|
||||
assert.NotEmpty(t, addFileResp)
|
||||
sha = addFileResp.Commit.SHA
|
||||
assert.Eventually(t, func() bool {
|
||||
latestCommitStatuses, _, err := git_model.GetLatestCommitStatus(db.DefaultContext, repo.ID, sha, db.ListOptionsAll)
|
||||
latestCommitStatuses, err := git_model.GetLatestCommitStatus(db.DefaultContext, repo.ID, sha, db.ListOptionsAll)
|
||||
assert.NoError(t, err)
|
||||
if len(latestCommitStatuses) == 0 {
|
||||
return false
|
||||
@ -676,7 +676,7 @@ jobs:
|
||||
}
|
||||
|
||||
func checkCommitStatusAndInsertFakeStatus(t *testing.T, repo *repo_model.Repository, sha string) {
|
||||
latestCommitStatuses, _, err := git_model.GetLatestCommitStatus(db.DefaultContext, repo.ID, sha, db.ListOptionsAll)
|
||||
latestCommitStatuses, err := git_model.GetLatestCommitStatus(db.DefaultContext, repo.ID, sha, db.ListOptionsAll)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, latestCommitStatuses, 1)
|
||||
assert.Equal(t, api.CommitStatusPending, latestCommitStatuses[0].State)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -151,6 +151,15 @@ func testNewIssue(t *testing.T, session *TestSession, user, repo, title, content
|
||||
return issueURL
|
||||
}
|
||||
|
||||
func testIssueAssign(t *testing.T, session *TestSession, repoLink string, issueID, assigneeID int64) {
|
||||
req := NewRequestWithValues(t, "POST", fmt.Sprintf(repoLink+"/issues/assignee?issue_ids=%d", issueID), map[string]string{
|
||||
"_csrf": GetUserCSRFToken(t, session),
|
||||
"id": strconv.FormatInt(assigneeID, 10),
|
||||
"action": "", // empty action means assign
|
||||
})
|
||||
session.MakeRequest(t, req, http.StatusOK)
|
||||
}
|
||||
|
||||
func testIssueAddComment(t *testing.T, session *TestSession, issueURL, content, status string) int64 {
|
||||
req := NewRequest(t, "GET", issueURL)
|
||||
resp := session.MakeRequest(t, req, http.StatusOK)
|
||||
|
@ -131,19 +131,19 @@ func (m *mockWebhookProvider) Close() {
|
||||
}
|
||||
|
||||
func Test_WebhookCreate(t *testing.T) {
|
||||
var payloads []api.CreatePayload
|
||||
var triggeredEvent string
|
||||
provider := newMockWebhookProvider(func(r *http.Request) {
|
||||
content, _ := io.ReadAll(r.Body)
|
||||
var payload api.CreatePayload
|
||||
err := json.Unmarshal(content, &payload)
|
||||
assert.NoError(t, err)
|
||||
payloads = append(payloads, payload)
|
||||
triggeredEvent = string(webhook_module.HookEventCreate)
|
||||
}, http.StatusOK)
|
||||
defer provider.Close()
|
||||
|
||||
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
|
||||
var payloads []api.CreatePayload
|
||||
var triggeredEvent string
|
||||
provider := newMockWebhookProvider(func(r *http.Request) {
|
||||
content, _ := io.ReadAll(r.Body)
|
||||
var payload api.CreatePayload
|
||||
err := json.Unmarshal(content, &payload)
|
||||
assert.NoError(t, err)
|
||||
payloads = append(payloads, payload)
|
||||
triggeredEvent = string(webhook_module.HookEventCreate)
|
||||
}, http.StatusOK)
|
||||
defer provider.Close()
|
||||
|
||||
// 1. create a new webhook with special webhook for repo1
|
||||
session := loginUser(t, "user2")
|
||||
|
||||
@ -163,19 +163,19 @@ func Test_WebhookCreate(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_WebhookDelete(t *testing.T) {
|
||||
var payloads []api.DeletePayload
|
||||
var triggeredEvent string
|
||||
provider := newMockWebhookProvider(func(r *http.Request) {
|
||||
content, _ := io.ReadAll(r.Body)
|
||||
var payload api.DeletePayload
|
||||
err := json.Unmarshal(content, &payload)
|
||||
assert.NoError(t, err)
|
||||
payloads = append(payloads, payload)
|
||||
triggeredEvent = "delete"
|
||||
}, http.StatusOK)
|
||||
defer provider.Close()
|
||||
|
||||
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
|
||||
var payloads []api.DeletePayload
|
||||
var triggeredEvent string
|
||||
provider := newMockWebhookProvider(func(r *http.Request) {
|
||||
content, _ := io.ReadAll(r.Body)
|
||||
var payload api.DeletePayload
|
||||
err := json.Unmarshal(content, &payload)
|
||||
assert.NoError(t, err)
|
||||
payloads = append(payloads, payload)
|
||||
triggeredEvent = "delete"
|
||||
}, http.StatusOK)
|
||||
defer provider.Close()
|
||||
|
||||
// 1. create a new webhook with special webhook for repo1
|
||||
session := loginUser(t, "user2")
|
||||
|
||||
@ -196,19 +196,19 @@ func Test_WebhookDelete(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_WebhookFork(t *testing.T) {
|
||||
var payloads []api.ForkPayload
|
||||
var triggeredEvent string
|
||||
provider := newMockWebhookProvider(func(r *http.Request) {
|
||||
content, _ := io.ReadAll(r.Body)
|
||||
var payload api.ForkPayload
|
||||
err := json.Unmarshal(content, &payload)
|
||||
assert.NoError(t, err)
|
||||
payloads = append(payloads, payload)
|
||||
triggeredEvent = "fork"
|
||||
}, http.StatusOK)
|
||||
defer provider.Close()
|
||||
|
||||
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
|
||||
var payloads []api.ForkPayload
|
||||
var triggeredEvent string
|
||||
provider := newMockWebhookProvider(func(r *http.Request) {
|
||||
content, _ := io.ReadAll(r.Body)
|
||||
var payload api.ForkPayload
|
||||
err := json.Unmarshal(content, &payload)
|
||||
assert.NoError(t, err)
|
||||
payloads = append(payloads, payload)
|
||||
triggeredEvent = "fork"
|
||||
}, http.StatusOK)
|
||||
defer provider.Close()
|
||||
|
||||
// 1. create a new webhook with special webhook for repo1
|
||||
session := loginUser(t, "user1")
|
||||
|
||||
@ -228,19 +228,19 @@ func Test_WebhookFork(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_WebhookIssueComment(t *testing.T) {
|
||||
var payloads []api.IssueCommentPayload
|
||||
var triggeredEvent string
|
||||
provider := newMockWebhookProvider(func(r *http.Request) {
|
||||
content, _ := io.ReadAll(r.Body)
|
||||
var payload api.IssueCommentPayload
|
||||
err := json.Unmarshal(content, &payload)
|
||||
assert.NoError(t, err)
|
||||
payloads = append(payloads, payload)
|
||||
triggeredEvent = "issue_comment"
|
||||
}, http.StatusOK)
|
||||
defer provider.Close()
|
||||
|
||||
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
|
||||
var payloads []api.IssueCommentPayload
|
||||
var triggeredEvent string
|
||||
provider := newMockWebhookProvider(func(r *http.Request) {
|
||||
content, _ := io.ReadAll(r.Body)
|
||||
var payload api.IssueCommentPayload
|
||||
err := json.Unmarshal(content, &payload)
|
||||
assert.NoError(t, err)
|
||||
payloads = append(payloads, payload)
|
||||
triggeredEvent = "issue_comment"
|
||||
}, http.StatusOK)
|
||||
defer provider.Close()
|
||||
|
||||
// 1. create a new webhook with special webhook for repo1
|
||||
session := loginUser(t, "user2")
|
||||
|
||||
@ -312,19 +312,19 @@ func Test_WebhookIssueComment(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_WebhookRelease(t *testing.T) {
|
||||
var payloads []api.ReleasePayload
|
||||
var triggeredEvent string
|
||||
provider := newMockWebhookProvider(func(r *http.Request) {
|
||||
content, _ := io.ReadAll(r.Body)
|
||||
var payload api.ReleasePayload
|
||||
err := json.Unmarshal(content, &payload)
|
||||
assert.NoError(t, err)
|
||||
payloads = append(payloads, payload)
|
||||
triggeredEvent = "release"
|
||||
}, http.StatusOK)
|
||||
defer provider.Close()
|
||||
|
||||
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
|
||||
var payloads []api.ReleasePayload
|
||||
var triggeredEvent string
|
||||
provider := newMockWebhookProvider(func(r *http.Request) {
|
||||
content, _ := io.ReadAll(r.Body)
|
||||
var payload api.ReleasePayload
|
||||
err := json.Unmarshal(content, &payload)
|
||||
assert.NoError(t, err)
|
||||
payloads = append(payloads, payload)
|
||||
triggeredEvent = "release"
|
||||
}, http.StatusOK)
|
||||
defer provider.Close()
|
||||
|
||||
// 1. create a new webhook with special webhook for repo1
|
||||
session := loginUser(t, "user2")
|
||||
|
||||
@ -345,19 +345,19 @@ func Test_WebhookRelease(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_WebhookPush(t *testing.T) {
|
||||
var payloads []api.PushPayload
|
||||
var triggeredEvent string
|
||||
provider := newMockWebhookProvider(func(r *http.Request) {
|
||||
content, _ := io.ReadAll(r.Body)
|
||||
var payload api.PushPayload
|
||||
err := json.Unmarshal(content, &payload)
|
||||
assert.NoError(t, err)
|
||||
payloads = append(payloads, payload)
|
||||
triggeredEvent = "push"
|
||||
}, http.StatusOK)
|
||||
defer provider.Close()
|
||||
|
||||
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
|
||||
var payloads []api.PushPayload
|
||||
var triggeredEvent string
|
||||
provider := newMockWebhookProvider(func(r *http.Request) {
|
||||
content, _ := io.ReadAll(r.Body)
|
||||
var payload api.PushPayload
|
||||
err := json.Unmarshal(content, &payload)
|
||||
assert.NoError(t, err)
|
||||
payloads = append(payloads, payload)
|
||||
triggeredEvent = "push"
|
||||
}, http.StatusOK)
|
||||
defer provider.Close()
|
||||
|
||||
// 1. create a new webhook with special webhook for repo1
|
||||
session := loginUser(t, "user2")
|
||||
|
||||
@ -416,19 +416,19 @@ func Test_WebhookPushDevBranch(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_WebhookIssue(t *testing.T) {
|
||||
var payloads []api.IssuePayload
|
||||
var triggeredEvent string
|
||||
provider := newMockWebhookProvider(func(r *http.Request) {
|
||||
content, _ := io.ReadAll(r.Body)
|
||||
var payload api.IssuePayload
|
||||
err := json.Unmarshal(content, &payload)
|
||||
assert.NoError(t, err)
|
||||
payloads = append(payloads, payload)
|
||||
triggeredEvent = "issues"
|
||||
}, http.StatusOK)
|
||||
defer provider.Close()
|
||||
|
||||
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
|
||||
var payloads []api.IssuePayload
|
||||
var triggeredEvent string
|
||||
provider := newMockWebhookProvider(func(r *http.Request) {
|
||||
content, _ := io.ReadAll(r.Body)
|
||||
var payload api.IssuePayload
|
||||
err := json.Unmarshal(content, &payload)
|
||||
assert.NoError(t, err)
|
||||
payloads = append(payloads, payload)
|
||||
triggeredEvent = "issues"
|
||||
}, http.StatusOK)
|
||||
defer provider.Close()
|
||||
|
||||
// 1. create a new webhook with special webhook for repo1
|
||||
session := loginUser(t, "user2")
|
||||
|
||||
@ -445,6 +445,45 @@ func Test_WebhookIssue(t *testing.T) {
|
||||
assert.Equal(t, "user2/repo1", payloads[0].Issue.Repo.FullName)
|
||||
assert.Equal(t, "Title1", payloads[0].Issue.Title)
|
||||
assert.Equal(t, "Description1", payloads[0].Issue.Body)
|
||||
assert.Positive(t, payloads[0].Issue.Created.Unix())
|
||||
assert.Positive(t, payloads[0].Issue.Updated.Unix())
|
||||
})
|
||||
}
|
||||
|
||||
func Test_WebhookIssueAssign(t *testing.T) {
|
||||
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
|
||||
var payloads []api.PullRequestPayload
|
||||
var triggeredEvent string
|
||||
provider := newMockWebhookProvider(func(r *http.Request) {
|
||||
content, _ := io.ReadAll(r.Body)
|
||||
var payload api.PullRequestPayload
|
||||
err := json.Unmarshal(content, &payload)
|
||||
assert.NoError(t, err)
|
||||
payloads = append(payloads, payload)
|
||||
triggeredEvent = "pull_request_assign"
|
||||
}, http.StatusOK)
|
||||
defer provider.Close()
|
||||
|
||||
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
|
||||
repo1 := unittest.AssertExistsAndLoadBean(t, &repo.Repository{ID: 1})
|
||||
|
||||
// 1. create a new webhook with special webhook for repo1
|
||||
session := loginUser(t, "user2")
|
||||
|
||||
testAPICreateWebhookForRepo(t, session, "user2", "repo1", provider.URL(), "pull_request_assign")
|
||||
|
||||
// 2. trigger the webhook, issue 2 is a pull request
|
||||
testIssueAssign(t, session, repo1.Link(), 2, user2.ID)
|
||||
|
||||
// 3. validate the webhook is triggered
|
||||
assert.Equal(t, "pull_request_assign", triggeredEvent)
|
||||
assert.Len(t, payloads, 1)
|
||||
assert.EqualValues(t, "assigned", payloads[0].Action)
|
||||
assert.Equal(t, "repo1", payloads[0].PullRequest.Base.Repository.Name)
|
||||
assert.Equal(t, "user2/repo1", payloads[0].PullRequest.Base.Repository.FullName)
|
||||
assert.Equal(t, "issue2", payloads[0].PullRequest.Title)
|
||||
assert.Equal(t, "content for the second issue", payloads[0].PullRequest.Body)
|
||||
assert.Equal(t, user2.ID, payloads[0].PullRequest.Assignee.ID)
|
||||
})
|
||||
}
|
||||
|
||||
@ -521,19 +560,19 @@ func Test_WebhookIssueMilestone(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_WebhookPullRequest(t *testing.T) {
|
||||
var payloads []api.PullRequestPayload
|
||||
var triggeredEvent string
|
||||
provider := newMockWebhookProvider(func(r *http.Request) {
|
||||
content, _ := io.ReadAll(r.Body)
|
||||
var payload api.PullRequestPayload
|
||||
err := json.Unmarshal(content, &payload)
|
||||
assert.NoError(t, err)
|
||||
payloads = append(payloads, payload)
|
||||
triggeredEvent = "pull_request"
|
||||
}, http.StatusOK)
|
||||
defer provider.Close()
|
||||
|
||||
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
|
||||
var payloads []api.PullRequestPayload
|
||||
var triggeredEvent string
|
||||
provider := newMockWebhookProvider(func(r *http.Request) {
|
||||
content, _ := io.ReadAll(r.Body)
|
||||
var payload api.PullRequestPayload
|
||||
err := json.Unmarshal(content, &payload)
|
||||
assert.NoError(t, err)
|
||||
payloads = append(payloads, payload)
|
||||
triggeredEvent = "pull_request"
|
||||
}, http.StatusOK)
|
||||
defer provider.Close()
|
||||
|
||||
// 1. create a new webhook with special webhook for repo1
|
||||
session := loginUser(t, "user2")
|
||||
|
||||
@ -558,19 +597,19 @@ func Test_WebhookPullRequest(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_WebhookPullRequestComment(t *testing.T) {
|
||||
var payloads []api.IssueCommentPayload
|
||||
var triggeredEvent string
|
||||
provider := newMockWebhookProvider(func(r *http.Request) {
|
||||
content, _ := io.ReadAll(r.Body)
|
||||
var payload api.IssueCommentPayload
|
||||
err := json.Unmarshal(content, &payload)
|
||||
assert.NoError(t, err)
|
||||
payloads = append(payloads, payload)
|
||||
triggeredEvent = "pull_request_comment"
|
||||
}, http.StatusOK)
|
||||
defer provider.Close()
|
||||
|
||||
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
|
||||
var payloads []api.IssueCommentPayload
|
||||
var triggeredEvent string
|
||||
provider := newMockWebhookProvider(func(r *http.Request) {
|
||||
content, _ := io.ReadAll(r.Body)
|
||||
var payload api.IssueCommentPayload
|
||||
err := json.Unmarshal(content, &payload)
|
||||
assert.NoError(t, err)
|
||||
payloads = append(payloads, payload)
|
||||
triggeredEvent = "pull_request_comment"
|
||||
}, http.StatusOK)
|
||||
defer provider.Close()
|
||||
|
||||
// 1. create a new webhook with special webhook for repo1
|
||||
session := loginUser(t, "user2")
|
||||
|
||||
@ -596,19 +635,19 @@ func Test_WebhookPullRequestComment(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_WebhookWiki(t *testing.T) {
|
||||
var payloads []api.WikiPayload
|
||||
var triggeredEvent string
|
||||
provider := newMockWebhookProvider(func(r *http.Request) {
|
||||
content, _ := io.ReadAll(r.Body)
|
||||
var payload api.WikiPayload
|
||||
err := json.Unmarshal(content, &payload)
|
||||
assert.NoError(t, err)
|
||||
payloads = append(payloads, payload)
|
||||
triggeredEvent = "wiki"
|
||||
}, http.StatusOK)
|
||||
defer provider.Close()
|
||||
|
||||
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
|
||||
var payloads []api.WikiPayload
|
||||
var triggeredEvent string
|
||||
provider := newMockWebhookProvider(func(r *http.Request) {
|
||||
content, _ := io.ReadAll(r.Body)
|
||||
var payload api.WikiPayload
|
||||
err := json.Unmarshal(content, &payload)
|
||||
assert.NoError(t, err)
|
||||
payloads = append(payloads, payload)
|
||||
triggeredEvent = "wiki"
|
||||
}, http.StatusOK)
|
||||
defer provider.Close()
|
||||
|
||||
// 1. create a new webhook with special webhook for repo1
|
||||
session := loginUser(t, "user2")
|
||||
|
||||
@ -628,19 +667,19 @@ func Test_WebhookWiki(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_WebhookRepository(t *testing.T) {
|
||||
var payloads []api.RepositoryPayload
|
||||
var triggeredEvent string
|
||||
provider := newMockWebhookProvider(func(r *http.Request) {
|
||||
content, _ := io.ReadAll(r.Body)
|
||||
var payload api.RepositoryPayload
|
||||
err := json.Unmarshal(content, &payload)
|
||||
assert.NoError(t, err)
|
||||
payloads = append(payloads, payload)
|
||||
triggeredEvent = "repository"
|
||||
}, http.StatusOK)
|
||||
defer provider.Close()
|
||||
|
||||
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
|
||||
var payloads []api.RepositoryPayload
|
||||
var triggeredEvent string
|
||||
provider := newMockWebhookProvider(func(r *http.Request) {
|
||||
content, _ := io.ReadAll(r.Body)
|
||||
var payload api.RepositoryPayload
|
||||
err := json.Unmarshal(content, &payload)
|
||||
assert.NoError(t, err)
|
||||
payloads = append(payloads, payload)
|
||||
triggeredEvent = "repository"
|
||||
}, http.StatusOK)
|
||||
defer provider.Close()
|
||||
|
||||
// 1. create a new webhook with special webhook for repo1
|
||||
session := loginUser(t, "user1")
|
||||
|
||||
@ -660,19 +699,19 @@ func Test_WebhookRepository(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_WebhookPackage(t *testing.T) {
|
||||
var payloads []api.PackagePayload
|
||||
var triggeredEvent string
|
||||
provider := newMockWebhookProvider(func(r *http.Request) {
|
||||
content, _ := io.ReadAll(r.Body)
|
||||
var payload api.PackagePayload
|
||||
err := json.Unmarshal(content, &payload)
|
||||
assert.NoError(t, err)
|
||||
payloads = append(payloads, payload)
|
||||
triggeredEvent = "package"
|
||||
}, http.StatusOK)
|
||||
defer provider.Close()
|
||||
|
||||
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
|
||||
var payloads []api.PackagePayload
|
||||
var triggeredEvent string
|
||||
provider := newMockWebhookProvider(func(r *http.Request) {
|
||||
content, _ := io.ReadAll(r.Body)
|
||||
var payload api.PackagePayload
|
||||
err := json.Unmarshal(content, &payload)
|
||||
assert.NoError(t, err)
|
||||
payloads = append(payloads, payload)
|
||||
triggeredEvent = "package"
|
||||
}, http.StatusOK)
|
||||
defer provider.Close()
|
||||
|
||||
// 1. create a new webhook with special webhook for repo1
|
||||
session := loginUser(t, "user1")
|
||||
|
||||
@ -697,24 +736,24 @@ func Test_WebhookPackage(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_WebhookStatus(t *testing.T) {
|
||||
var payloads []api.CommitStatusPayload
|
||||
var triggeredEvent string
|
||||
provider := newMockWebhookProvider(func(r *http.Request) {
|
||||
assert.Contains(t, r.Header["X-Github-Event-Type"], "status", "X-GitHub-Event-Type should contain status")
|
||||
assert.Contains(t, r.Header["X-Github-Hook-Installation-Target-Type"], "repository", "X-GitHub-Hook-Installation-Target-Type should contain repository")
|
||||
assert.Contains(t, r.Header["X-Gitea-Event-Type"], "status", "X-Gitea-Event-Type should contain status")
|
||||
assert.Contains(t, r.Header["X-Gitea-Hook-Installation-Target-Type"], "repository", "X-Gitea-Hook-Installation-Target-Type should contain repository")
|
||||
assert.Contains(t, r.Header["X-Gogs-Event-Type"], "status", "X-Gogs-Event-Type should contain status")
|
||||
content, _ := io.ReadAll(r.Body)
|
||||
var payload api.CommitStatusPayload
|
||||
err := json.Unmarshal(content, &payload)
|
||||
assert.NoError(t, err)
|
||||
payloads = append(payloads, payload)
|
||||
triggeredEvent = "status"
|
||||
}, http.StatusOK)
|
||||
defer provider.Close()
|
||||
|
||||
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
|
||||
var payloads []api.CommitStatusPayload
|
||||
var triggeredEvent string
|
||||
provider := newMockWebhookProvider(func(r *http.Request) {
|
||||
assert.Contains(t, r.Header["X-Github-Event-Type"], "status", "X-GitHub-Event-Type should contain status")
|
||||
assert.Contains(t, r.Header["X-Github-Hook-Installation-Target-Type"], "repository", "X-GitHub-Hook-Installation-Target-Type should contain repository")
|
||||
assert.Contains(t, r.Header["X-Gitea-Event-Type"], "status", "X-Gitea-Event-Type should contain status")
|
||||
assert.Contains(t, r.Header["X-Gitea-Hook-Installation-Target-Type"], "repository", "X-Gitea-Hook-Installation-Target-Type should contain repository")
|
||||
assert.Contains(t, r.Header["X-Gogs-Event-Type"], "status", "X-Gogs-Event-Type should contain status")
|
||||
content, _ := io.ReadAll(r.Body)
|
||||
var payload api.CommitStatusPayload
|
||||
err := json.Unmarshal(content, &payload)
|
||||
assert.NoError(t, err)
|
||||
payloads = append(payloads, payload)
|
||||
triggeredEvent = "status"
|
||||
}, http.StatusOK)
|
||||
defer provider.Close()
|
||||
|
||||
// 1. create a new webhook with special webhook for repo1
|
||||
session := loginUser(t, "user2")
|
||||
|
||||
@ -750,16 +789,16 @@ func Test_WebhookStatus(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_WebhookStatus_NoWrongTrigger(t *testing.T) {
|
||||
var trigger string
|
||||
provider := newMockWebhookProvider(func(r *http.Request) {
|
||||
assert.NotContains(t, r.Header["X-Github-Event-Type"], "status", "X-GitHub-Event-Type should not contain status")
|
||||
assert.NotContains(t, r.Header["X-Gitea-Event-Type"], "status", "X-Gitea-Event-Type should not contain status")
|
||||
assert.NotContains(t, r.Header["X-Gogs-Event-Type"], "status", "X-Gogs-Event-Type should not contain status")
|
||||
trigger = "push"
|
||||
}, http.StatusOK)
|
||||
defer provider.Close()
|
||||
|
||||
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
|
||||
var trigger string
|
||||
provider := newMockWebhookProvider(func(r *http.Request) {
|
||||
assert.NotContains(t, r.Header["X-Github-Event-Type"], "status", "X-GitHub-Event-Type should not contain status")
|
||||
assert.NotContains(t, r.Header["X-Gitea-Event-Type"], "status", "X-Gitea-Event-Type should not contain status")
|
||||
assert.NotContains(t, r.Header["X-Gogs-Event-Type"], "status", "X-Gogs-Event-Type should not contain status")
|
||||
trigger = "push"
|
||||
}, http.StatusOK)
|
||||
defer provider.Close()
|
||||
|
||||
// 1. create a new webhook with special webhook for repo1
|
||||
session := loginUser(t, "user2")
|
||||
|
||||
@ -775,22 +814,22 @@ func Test_WebhookStatus_NoWrongTrigger(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_WebhookWorkflowJob(t *testing.T) {
|
||||
var payloads []api.WorkflowJobPayload
|
||||
var triggeredEvent string
|
||||
provider := newMockWebhookProvider(func(r *http.Request) {
|
||||
assert.Contains(t, r.Header["X-Github-Event-Type"], "workflow_job", "X-GitHub-Event-Type should contain workflow_job")
|
||||
assert.Contains(t, r.Header["X-Gitea-Event-Type"], "workflow_job", "X-Gitea-Event-Type should contain workflow_job")
|
||||
assert.Contains(t, r.Header["X-Gogs-Event-Type"], "workflow_job", "X-Gogs-Event-Type should contain workflow_job")
|
||||
content, _ := io.ReadAll(r.Body)
|
||||
var payload api.WorkflowJobPayload
|
||||
err := json.Unmarshal(content, &payload)
|
||||
assert.NoError(t, err)
|
||||
payloads = append(payloads, payload)
|
||||
triggeredEvent = "workflow_job"
|
||||
}, http.StatusOK)
|
||||
defer provider.Close()
|
||||
|
||||
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
|
||||
var payloads []api.WorkflowJobPayload
|
||||
var triggeredEvent string
|
||||
provider := newMockWebhookProvider(func(r *http.Request) {
|
||||
assert.Contains(t, r.Header["X-Github-Event-Type"], "workflow_job", "X-GitHub-Event-Type should contain workflow_job")
|
||||
assert.Contains(t, r.Header["X-Gitea-Event-Type"], "workflow_job", "X-Gitea-Event-Type should contain workflow_job")
|
||||
assert.Contains(t, r.Header["X-Gogs-Event-Type"], "workflow_job", "X-Gogs-Event-Type should contain workflow_job")
|
||||
content, _ := io.ReadAll(r.Body)
|
||||
var payload api.WorkflowJobPayload
|
||||
err := json.Unmarshal(content, &payload)
|
||||
assert.NoError(t, err)
|
||||
payloads = append(payloads, payload)
|
||||
triggeredEvent = "workflow_job"
|
||||
}, http.StatusOK)
|
||||
defer provider.Close()
|
||||
|
||||
// 1. create a new webhook with special webhook for repo1
|
||||
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
|
||||
session := loginUser(t, "user2")
|
||||
|
@ -815,10 +815,6 @@ overflow-menu .ui.label {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.code-view .lines-num span::after {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.lines-type-marker {
|
||||
vertical-align: top;
|
||||
white-space: nowrap;
|
||||
@ -855,39 +851,13 @@ overflow-menu .ui.label {
|
||||
.lines-escape {
|
||||
width: 0;
|
||||
white-space: nowrap;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.lines-code {
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
.file-view tr.active {
|
||||
color: inherit !important;
|
||||
background: inherit !important;
|
||||
}
|
||||
|
||||
.file-view tr.active .lines-num,
|
||||
.file-view tr.active .lines-code {
|
||||
background: var(--color-highlight-bg) !important;
|
||||
}
|
||||
|
||||
.file-view tr.active:last-of-type .lines-code {
|
||||
border-bottom-right-radius: var(--border-radius);
|
||||
}
|
||||
|
||||
.file-view tr.active .lines-num {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.file-view tr.active .lines-num::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
width: 2px;
|
||||
height: 100%;
|
||||
background: var(--color-highlight-fg);
|
||||
}
|
||||
|
||||
.code-inner {
|
||||
font: 12px var(--fonts-monospace);
|
||||
white-space: pre-wrap;
|
||||
@ -938,12 +908,12 @@ overflow-menu .ui.label {
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.top-line-blame {
|
||||
tr.top-line-blame {
|
||||
border-top: 1px solid var(--color-secondary);
|
||||
}
|
||||
|
||||
.code-view tr.top-line-blame:first-of-type {
|
||||
border-top: none;
|
||||
tr.top-line-blame:first-of-type {
|
||||
border-top: none; /* merge code lines belonging to the same commit into one block */
|
||||
}
|
||||
|
||||
.lines-code .bottom-line,
|
||||
@ -951,15 +921,6 @@ overflow-menu .ui.label {
|
||||
border-bottom: 1px solid var(--color-secondary);
|
||||
}
|
||||
|
||||
.code-view {
|
||||
background: var(--color-code-bg);
|
||||
border-radius: var(--border-radius);
|
||||
}
|
||||
|
||||
.code-view table {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.migrate .svg.gitea-git {
|
||||
color: var(--color-git);
|
||||
}
|
||||
|
@ -62,7 +62,7 @@
|
||||
@import "./repo/issue-label.css";
|
||||
@import "./repo/issue-list.css";
|
||||
@import "./repo/list-header.css";
|
||||
@import "./repo/linebutton.css";
|
||||
@import "./repo/file-view.css";
|
||||
@import "./repo/wiki.css";
|
||||
@import "./repo/header.css";
|
||||
@import "./repo/home.css";
|
||||
|
@ -309,10 +309,18 @@
|
||||
box-sizing: initial;
|
||||
}
|
||||
|
||||
.file-view.markup {
|
||||
padding: 1em 2em;
|
||||
}
|
||||
|
||||
.file-view.markup:has(.file-not-rendered-prompt) {
|
||||
padding: 0; /* let the file-not-rendered-prompt layout itself */
|
||||
}
|
||||
|
||||
/* this background ensures images can break <hr>. We can only do this on
|
||||
cases where the background is known and not transparent. */
|
||||
.markup.file-view img,
|
||||
.markup.file-view video,
|
||||
.file-view.markup img,
|
||||
.file-view.markup video,
|
||||
.comment-body .markup img, /* regular comment */
|
||||
.comment-body .markup video,
|
||||
.comment-content .markup img, /* code comment */
|
||||
|
@ -1238,21 +1238,6 @@ td .commit-summary {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.file-view.markup {
|
||||
padding: 1em 2em;
|
||||
}
|
||||
|
||||
.file-view.markup:has(.file-not-rendered-prompt) {
|
||||
padding: 0; /* let the file-not-rendered-prompt layout itself */
|
||||
}
|
||||
|
||||
.file-not-rendered-prompt {
|
||||
padding: 1rem;
|
||||
text-align: center;
|
||||
font-size: 1rem !important; /* use consistent styles for various containers (code, markup, etc) */
|
||||
line-height: var(--line-height-default) !important; /* same as above */
|
||||
}
|
||||
|
||||
.repository .activity-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
58
web_src/css/repo/file-view.css
Normal file
58
web_src/css/repo/file-view.css
Normal file
@ -0,0 +1,58 @@
|
||||
.file-view tr.active {
|
||||
background: var(--color-highlight-bg);
|
||||
}
|
||||
|
||||
.file-view tr.active:last-of-type .lines-code {
|
||||
border-bottom-right-radius: var(--border-radius);
|
||||
}
|
||||
|
||||
.file-view tr.active .lines-num {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.file-view tr.active .lines-num::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
width: 2px;
|
||||
height: 100%;
|
||||
background: var(--color-highlight-fg);
|
||||
}
|
||||
|
||||
.file-view .file-not-rendered-prompt {
|
||||
padding: 1rem;
|
||||
text-align: center;
|
||||
font-size: 1rem !important; /* use consistent styles for various containers (code, markup, etc) */
|
||||
line-height: var(--line-height-default) !important; /* same as above */
|
||||
}
|
||||
|
||||
/* ".code-view" is always used with ".file-view", to show the code of a file */
|
||||
.file-view.code-view {
|
||||
background: var(--color-code-bg);
|
||||
border-radius: var(--border-radius);
|
||||
}
|
||||
|
||||
.file-view.code-view table {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.file-view.code-view .lines-num span::after {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.file-view.code-view .lines-num:hover {
|
||||
color: var(--color-text-dark);
|
||||
}
|
||||
|
||||
.file-view.code-view .ui.button.code-line-button {
|
||||
border: 1px solid var(--color-secondary);
|
||||
padding: 1px 4px;
|
||||
margin: 0;
|
||||
min-height: 0;
|
||||
position: absolute;
|
||||
left: 6px;
|
||||
}
|
||||
|
||||
.file-view.code-view .ui.button.code-line-button:hover {
|
||||
background: var(--color-secondary);
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
.code-view .lines-num:hover {
|
||||
color: var(--color-text-dark) !important;
|
||||
}
|
||||
|
||||
.ui.button.code-line-button {
|
||||
border: 1px solid var(--color-secondary);
|
||||
padding: 1px 4px;
|
||||
margin: 0;
|
||||
min-height: 0;
|
||||
position: absolute;
|
||||
left: 6px;
|
||||
}
|
||||
|
||||
.ui.button.code-line-button:hover {
|
||||
background: var(--color-secondary);
|
||||
}
|
@ -6,7 +6,7 @@ import {fomanticQuery} from '../modules/fomantic/base.ts';
|
||||
|
||||
const {appSubUrl, assetUrlPrefix, pageData} = window.config;
|
||||
|
||||
type CommitStatus = 'pending' | 'success' | 'error' | 'failure' | 'warning';
|
||||
type CommitStatus = 'pending' | 'success' | 'error' | 'failure' | 'warning' | 'skipped';
|
||||
|
||||
type CommitStatusMap = {
|
||||
[status in CommitStatus]: {
|
||||
@ -22,6 +22,7 @@ const commitStatus: CommitStatusMap = {
|
||||
error: {name: 'gitea-exclamation', color: 'red'},
|
||||
failure: {name: 'octicon-x', color: 'red'},
|
||||
warning: {name: 'gitea-exclamation', color: 'yellow'},
|
||||
skipped: {name: 'octicon-skip', color: 'grey'},
|
||||
};
|
||||
|
||||
export default defineComponent({
|
||||
|
Loading…
x
Reference in New Issue
Block a user