diff --git a/.eslintrc.yaml b/.eslintrc.yaml index 91019cde84..e553499691 100644 --- a/.eslintrc.yaml +++ b/.eslintrc.yaml @@ -4,6 +4,7 @@ reportUnusedDisableDirectives: true ignorePatterns: - /web_src/js/vendor - /web_src/fomantic + - /public/assets/js parserOptions: sourceType: module diff --git a/Makefile b/Makefile index f882fdd026..bb56f82eb5 100644 --- a/Makefile +++ b/Makefile @@ -767,7 +767,7 @@ generate-backend: $(TAGS_PREREQ) generate-go .PHONY: generate-go generate-go: $(TAGS_PREREQ) @echo "Running go generate..." - @CC= GOOS= GOARCH= $(GO) generate -tags '$(TAGS)' ./... + @CC= GOOS= GOARCH= CGO_ENABLED=0 $(GO) generate -tags '$(TAGS)' ./... .PHONY: merge-locales merge-locales: diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini index 4eee2cd1ff..1d00c38816 100644 --- a/custom/conf/app.example.ini +++ b/custom/conf/app.example.ini @@ -1471,7 +1471,7 @@ LEVEL = Info ;; Batch size to send for batched queues ;BATCH_LENGTH = 20 ;; -;; Connection string for redis queues this will store the redis or redis-cluster connection string. +;; Connection string for redis queues this will store the redis (or Redis cluster) connection string. ;; When `TYPE` is `persistable-channel`, this provides a directory for the underlying leveldb ;; or additional options of the form `leveldb://path/to/db?option=value&....`, and will override `DATADIR`. ;CONN_STR = "redis://127.0.0.1:6379/0" @@ -1756,9 +1756,8 @@ LEVEL = Info ;; For "memory" only, GC interval in seconds, default is 60 ;INTERVAL = 60 ;; -;; For "redis", "redis-cluster" and "memcache", connection host address -;; redis: `redis://127.0.0.1:6379/0?pool_size=100&idle_timeout=180s` -;; redis-cluster: `redis+cluster://127.0.0.1:6379/0?pool_size=100&idle_timeout=180s` +;; For "redis" and "memcache", connection host address +;; redis: `redis://127.0.0.1:6379/0?pool_size=100&idle_timeout=180s` (or `redis+cluster://127.0.0.1:6379/0?pool_size=100&idle_timeout=180s` for a Redis cluster) ;; memcache: `127.0.0.1:11211` ;; twoqueue: `{"size":50000,"recent_ratio":0.25,"ghost_ratio":0.5}` or `50000` ;HOST = @@ -1788,15 +1787,14 @@ LEVEL = Info ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; -;; Either "memory", "file", "redis", "redis-cluster", "db", "mysql", "couchbase", "memcache" or "postgres" +;; Either "memory", "file", "redis", "db", "mysql", "couchbase", "memcache" or "postgres" ;; Default is "memory". "db" will reuse the configuration in [database] ;PROVIDER = memory ;; ;; Provider config options ;; memory: doesn't have any config yet ;; file: session file path, e.g. `data/sessions` -;; redis: `redis://127.0.0.1:6379/0?pool_size=100&idle_timeout=180s` -;; redis-cluster: `redis+cluster://127.0.0.1:6379/0?pool_size=100&idle_timeout=180s` +;; redis: `redis://127.0.0.1:6379/0?pool_size=100&idle_timeout=180s` (or `redis+cluster://127.0.0.1:6379/0?pool_size=100&idle_timeout=180s` for a Redis cluster) ;; mysql: go-sql-driver/mysql dsn config string, e.g. `root:password@/session_table` ;PROVIDER_CONFIG = data/sessions ; Relative paths will be made absolute against _`AppWorkPath`_. ;; diff --git a/go.mod b/go.mod index 0fbb93baf8..4c72a35b97 100644 --- a/go.mod +++ b/go.mod @@ -57,6 +57,7 @@ require ( github.com/google/uuid v1.6.0 github.com/gorilla/feeds v1.1.2 github.com/gorilla/sessions v1.2.2 + github.com/h2non/gock v1.2.0 github.com/hashicorp/go-version v1.6.0 github.com/hashicorp/golang-lru/v2 v2.0.7 github.com/huandu/xstrings v1.4.0 @@ -203,6 +204,7 @@ require ( github.com/gorilla/handlers v1.5.2 // indirect github.com/gorilla/mux v1.8.1 // indirect github.com/gorilla/securecookie v1.1.2 // indirect + github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-retryablehttp v0.7.5 // indirect github.com/hashicorp/hcl v1.0.0 // indirect diff --git a/go.sum b/go.sum index fc0aa2ac2a..f9831db472 100644 --- a/go.sum +++ b/go.sum @@ -474,6 +474,10 @@ github.com/gorilla/sessions v1.1.1/go.mod h1:8KCfur6+4Mqcc6S0FEfKuN15Vl5MgXW92AE github.com/gorilla/sessions v1.2.0/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= github.com/gorilla/sessions v1.2.2 h1:lqzMYz6bOfvn2WriPUjNByzeXIlVzURcPmgMczkmTjY= github.com/gorilla/sessions v1.2.2/go.mod h1:ePLdVu+jbEgHH+KWw8I1z2wqd0BAdAQh/8LRvBeoNcQ= +github.com/h2non/gock v1.2.0 h1:K6ol8rfrRkUOefooBC8elXoaNGYkpp7y2qcxGG6BzUE= +github.com/h2non/gock v1.2.0/go.mod h1:tNhoxHYW2W42cYkYb1WqzdbYIieALC99kpYr7rH/BQk= +github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw= +github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= @@ -638,6 +642,8 @@ github.com/mschoch/smat v0.2.0 h1:8imxQsjDm8yFEAVBe7azKmKSgzSkZXDuKkSq9374khM= github.com/mschoch/smat v0.2.0/go.mod h1:kc9mz7DoBKqDyiRL7VZN8KvXQMWeTaVnttLRXOlotKw= github.com/msteinert/pam v1.2.0 h1:mYfjlvN2KYs2Pb9G6nb/1f/nPfAttT/Jee5Sq9r3bGE= github.com/msteinert/pam v1.2.0/go.mod h1:d2n0DCUK8rGecChV3JzvmsDjOY4R7AYbsNxAT+ftQl0= +github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 h1:W6apQkHrMkS0Muv8G/TipAy/FJl/rCYT0+EuS8+Z0z4= +github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms= github.com/niklasfasching/go-org v1.7.0 h1:vyMdcMWWTe/XmANk19F4k8XGBYg0GQ/gJGMimOjGMek= github.com/niklasfasching/go-org v1.7.0/go.mod h1:WuVm4d45oePiE0eX25GqTDQIt/qPW1T9DGkRscqLW5o= github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= diff --git a/models/git/commit_status.go b/models/git/commit_status.go index 3f2172e50a..d975f0572c 100644 --- a/models/git/commit_status.go +++ b/models/git/commit_status.go @@ -362,36 +362,16 @@ func GetLatestCommitStatusForRepoCommitIDs(ctx context.Context, repoID int64, co // FindRepoRecentCommitStatusContexts returns repository's recent commit status contexts func FindRepoRecentCommitStatusContexts(ctx context.Context, repoID int64, before time.Duration) ([]string, error) { - type result struct { - Index int64 - SHA string - } - getBase := func() *xorm.Session { - return db.GetEngine(ctx).Table(&CommitStatus{}).Where("repo_id = ?", repoID) - } - start := timeutil.TimeStampNow().AddDuration(-before) - results := make([]result, 0, 10) - sess := getBase().And("updated_unix >= ?", start). - Select("max( `index` ) as `index`, sha"). - GroupBy("context_hash, sha").OrderBy("max( `index` ) desc") - - err := sess.Find(&results) - if err != nil { + var contexts []string + if err := db.GetEngine(ctx).Table("commit_status"). + Where("repo_id = ?", repoID).And("updated_unix >= ?", start). + Cols("context").Distinct().Find(&contexts); err != nil { return nil, err } - contexts := make([]string, 0, len(results)) - if len(results) == 0 { - return contexts, nil - } - - conds := make([]builder.Cond, 0, len(results)) - for _, result := range results { - conds = append(conds, builder.Eq{"`index`": result.Index, "sha": result.SHA}) - } - return contexts, getBase().And(builder.Or(conds...)).Select("context").Find(&contexts) + return contexts, nil } // NewCommitStatusOptions holds options for creating a CommitStatus diff --git a/models/git/commit_status_test.go b/models/git/commit_status_test.go index 94c8d3776c..2ada8b3724 100644 --- a/models/git/commit_status_test.go +++ b/models/git/commit_status_test.go @@ -5,11 +5,15 @@ package git_test import ( "testing" + "time" "code.gitea.io/gitea/models/db" git_model "code.gitea.io/gitea/models/git" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" + user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/structs" "github.com/stretchr/testify/assert" @@ -183,3 +187,55 @@ func Test_CalcCommitStatus(t *testing.T) { assert.Equal(t, kase.expected, git_model.CalcCommitStatus(kase.statuses)) } } + +func TestFindRepoRecentCommitStatusContexts(t *testing.T) { + assert.NoError(t, unittest.PrepareTestDatabase()) + + repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}) + user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) + gitRepo, err := gitrepo.OpenRepository(git.DefaultContext, repo2) + assert.NoError(t, err) + defer gitRepo.Close() + + commit, err := gitRepo.GetBranchCommit(repo2.DefaultBranch) + assert.NoError(t, err) + + defer func() { + _, err := db.DeleteByBean(db.DefaultContext, &git_model.CommitStatus{ + RepoID: repo2.ID, + CreatorID: user2.ID, + SHA: commit.ID.String(), + }) + assert.NoError(t, err) + }() + + err = git_model.NewCommitStatus(db.DefaultContext, git_model.NewCommitStatusOptions{ + Repo: repo2, + Creator: user2, + SHA: commit.ID, + CommitStatus: &git_model.CommitStatus{ + State: structs.CommitStatusFailure, + TargetURL: "https://example.com/tests/", + Context: "compliance/lint-backend", + }, + }) + assert.NoError(t, err) + + err = git_model.NewCommitStatus(db.DefaultContext, git_model.NewCommitStatusOptions{ + Repo: repo2, + Creator: user2, + SHA: commit.ID, + CommitStatus: &git_model.CommitStatus{ + State: structs.CommitStatusSuccess, + TargetURL: "https://example.com/tests/", + Context: "compliance/lint-backend", + }, + }) + assert.NoError(t, err) + + contexts, err := git_model.FindRepoRecentCommitStatusContexts(db.DefaultContext, repo2.ID, time.Hour) + assert.NoError(t, err) + if assert.Len(t, contexts, 1) { + assert.Equal(t, "compliance/lint-backend", contexts[0]) + } +} diff --git a/models/issues/issue_update.go b/models/issues/issue_update.go index 78e1f8e030..c3debac92e 100644 --- a/models/issues/issue_update.go +++ b/models/issues/issue_update.go @@ -450,65 +450,6 @@ func UpdateIssueMentions(ctx context.Context, issueID int64, mentions []*user_mo return nil } -// UpdateIssueByAPI updates all allowed fields of given issue. -// If the issue status is changed a statusChangeComment is returned -// similarly if the title is changed the titleChanged bool is set to true -func UpdateIssueByAPI(ctx context.Context, issue *Issue, doer *user_model.User) (statusChangeComment *Comment, titleChanged bool, err error) { - ctx, committer, err := db.TxContext(ctx) - if err != nil { - return nil, false, err - } - defer committer.Close() - - if err := issue.LoadRepo(ctx); err != nil { - return nil, false, fmt.Errorf("loadRepo: %w", err) - } - - // Reload the issue - currentIssue, err := GetIssueByID(ctx, issue.ID) - if err != nil { - return nil, false, err - } - - sess := db.GetEngine(ctx).ID(issue.ID) - cols := []string{"name", "content", "milestone_id", "priority", "deadline_unix", "is_locked"} - if issue.NoAutoTime { - cols = append(cols, "updated_unix") - sess.NoAutoTime() - } - if _, err := sess.Cols(cols...).Update(issue); err != nil { - return nil, false, err - } - - titleChanged = currentIssue.Title != issue.Title - if titleChanged { - opts := &CreateCommentOptions{ - Type: CommentTypeChangeTitle, - Doer: doer, - Repo: issue.Repo, - Issue: issue, - OldTitle: currentIssue.Title, - NewTitle: issue.Title, - } - _, err := CreateComment(ctx, opts) - if err != nil { - return nil, false, fmt.Errorf("createComment: %w", err) - } - } - - if currentIssue.IsClosed != issue.IsClosed { - statusChangeComment, err = doChangeIssueStatus(ctx, issue, doer, false) - if err != nil { - return nil, false, err - } - } - - if err := issue.AddCrossReferences(ctx, doer, true); err != nil { - return nil, false, err - } - return statusChangeComment, titleChanged, committer.Commit() -} - // UpdateIssueDeadline updates an issue deadline and adds comments. Setting a deadline to 0 means deleting it. func UpdateIssueDeadline(ctx context.Context, issue *Issue, deadlineUnix timeutil.TimeStamp, doer *user_model.User) (err error) { // if the deadline hasn't changed do nothing diff --git a/models/issues/issue_xref_test.go b/models/issues/issue_xref_test.go index 5bcaf75518..f1b1bb2a6b 100644 --- a/models/issues/issue_xref_test.go +++ b/models/issues/issue_xref_test.go @@ -34,7 +34,7 @@ func TestXRef_AddCrossReferences(t *testing.T) { // Comment on PR to reopen issue #1 content = fmt.Sprintf("content2, reopens #%d", itarget.Index) - c := testCreateComment(t, 1, 2, pr.ID, content) + c := testCreateComment(t, 2, pr.ID, content) ref = unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{IssueID: itarget.ID, RefIssueID: pr.ID, RefCommentID: c.ID}) assert.Equal(t, issues_model.CommentTypeCommentRef, ref.Type) assert.Equal(t, pr.RepoID, ref.RefRepoID) @@ -104,18 +104,18 @@ func TestXRef_ResolveCrossReferences(t *testing.T) { pr := testCreatePR(t, 1, 2, "titlepr", fmt.Sprintf("closes #%d", i1.Index)) rp := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{IssueID: i1.ID, RefIssueID: pr.Issue.ID, RefCommentID: 0}) - c1 := testCreateComment(t, 1, 2, pr.Issue.ID, fmt.Sprintf("closes #%d", i2.Index)) + c1 := testCreateComment(t, 2, pr.Issue.ID, fmt.Sprintf("closes #%d", i2.Index)) r1 := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{IssueID: i2.ID, RefIssueID: pr.Issue.ID, RefCommentID: c1.ID}) // Must be ignored - c2 := testCreateComment(t, 1, 2, pr.Issue.ID, fmt.Sprintf("mentions #%d", i2.Index)) + c2 := testCreateComment(t, 2, pr.Issue.ID, fmt.Sprintf("mentions #%d", i2.Index)) unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{IssueID: i2.ID, RefIssueID: pr.Issue.ID, RefCommentID: c2.ID}) // Must be superseded by c4/r4 - c3 := testCreateComment(t, 1, 2, pr.Issue.ID, fmt.Sprintf("reopens #%d", i3.Index)) + c3 := testCreateComment(t, 2, pr.Issue.ID, fmt.Sprintf("reopens #%d", i3.Index)) unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{IssueID: i3.ID, RefIssueID: pr.Issue.ID, RefCommentID: c3.ID}) - c4 := testCreateComment(t, 1, 2, pr.Issue.ID, fmt.Sprintf("closes #%d", i3.Index)) + c4 := testCreateComment(t, 2, pr.Issue.ID, fmt.Sprintf("closes #%d", i3.Index)) r4 := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{IssueID: i3.ID, RefIssueID: pr.Issue.ID, RefCommentID: c4.ID}) refs, err := pr.ResolveCrossReferences(db.DefaultContext) @@ -168,7 +168,7 @@ func testCreatePR(t *testing.T, repo, doer int64, title, content string) *issues return pr } -func testCreateComment(t *testing.T, repo, doer, issue int64, content string) *issues_model.Comment { +func testCreateComment(t *testing.T, doer, issue int64, content string) *issues_model.Comment { d := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: doer}) i := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: issue}) c := &issues_model.Comment{Type: issues_model.CommentTypeComment, PosterID: doer, Poster: d, IssueID: issue, Issue: i, Content: content} diff --git a/models/organization/org_test.go b/models/organization/org_test.go index 5e40dd4190..23ef22e2fb 100644 --- a/models/organization/org_test.go +++ b/models/organization/org_test.go @@ -291,15 +291,15 @@ func TestAccessibleReposEnv_CountRepos(t *testing.T) { func TestAccessibleReposEnv_RepoIDs(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) org := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 3}) - testSuccess := func(userID, _, pageSize int64, expectedRepoIDs []int64) { + testSuccess := func(userID int64, expectedRepoIDs []int64) { env, err := organization.AccessibleReposEnv(db.DefaultContext, org, userID) assert.NoError(t, err) repoIDs, err := env.RepoIDs(1, 100) assert.NoError(t, err) assert.Equal(t, expectedRepoIDs, repoIDs) } - testSuccess(2, 1, 100, []int64{3, 5, 32}) - testSuccess(4, 0, 100, []int64{3, 32}) + testSuccess(2, []int64{3, 5, 32}) + testSuccess(4, []int64{3, 32}) } func TestAccessibleReposEnv_Repos(t *testing.T) { diff --git a/models/repo/user_repo.go b/models/repo/user_repo.go index d3fbd961bd..6790ee1da9 100644 --- a/models/repo/user_repo.go +++ b/models/repo/user_repo.go @@ -95,7 +95,10 @@ func GetRepoAssignees(ctx context.Context, repo *Repository) (_ []*user_model.Us // and just waste 1 unit is cheaper than re-allocate memory once. users := make([]*user_model.User, 0, len(uniqueUserIDs)+1) if len(userIDs) > 0 { - if err = e.In("id", uniqueUserIDs.Values()).OrderBy(user_model.GetOrderByName()).Find(&users); err != nil { + if err = e.In("id", uniqueUserIDs.Values()). + Where(builder.Eq{"`user`.is_active": true}). + OrderBy(user_model.GetOrderByName()). + Find(&users); err != nil { return nil, err } } @@ -117,7 +120,8 @@ func GetReviewers(ctx context.Context, repo *Repository, doerID, posterID int64) return nil, err } - cond := builder.And(builder.Neq{"`user`.id": posterID}) + cond := builder.And(builder.Neq{"`user`.id": posterID}). + And(builder.Eq{"`user`.is_active": true}) if repo.IsPrivate || repo.Owner.Visibility == api.VisibleTypePrivate { // This a private repository: diff --git a/models/repo/user_repo_test.go b/models/repo/user_repo_test.go index ad794beb9b..0433ff83d8 100644 --- a/models/repo/user_repo_test.go +++ b/models/repo/user_repo_test.go @@ -26,10 +26,17 @@ func TestRepoAssignees(t *testing.T) { repo21 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 21}) users, err = repo_model.GetRepoAssignees(db.DefaultContext, repo21) assert.NoError(t, err) - assert.Len(t, users, 3) - assert.Equal(t, users[0].ID, int64(15)) - assert.Equal(t, users[1].ID, int64(18)) - assert.Equal(t, users[2].ID, int64(16)) + if assert.Len(t, users, 3) { + assert.ElementsMatch(t, []int64{15, 16, 18}, []int64{users[0].ID, users[1].ID, users[2].ID}) + } + + // do not return deactivated users + assert.NoError(t, user_model.UpdateUserCols(db.DefaultContext, &user_model.User{ID: 15, IsActive: false}, "is_active")) + users, err = repo_model.GetRepoAssignees(db.DefaultContext, repo21) + assert.NoError(t, err) + if assert.Len(t, users, 2) { + assert.NotContains(t, []int64{users[0].ID, users[1].ID}, 15) + } } func TestRepoGetReviewers(t *testing.T) { @@ -41,17 +48,19 @@ func TestRepoGetReviewers(t *testing.T) { ctx := db.DefaultContext reviewers, err := repo_model.GetReviewers(ctx, repo1, 2, 2) assert.NoError(t, err) - assert.Len(t, reviewers, 4) + if assert.Len(t, reviewers, 3) { + assert.ElementsMatch(t, []int64{1, 4, 11}, []int64{reviewers[0].ID, reviewers[1].ID, reviewers[2].ID}) + } // should include doer if doer is not PR poster. reviewers, err = repo_model.GetReviewers(ctx, repo1, 11, 2) assert.NoError(t, err) - assert.Len(t, reviewers, 4) + assert.Len(t, reviewers, 3) // should not include PR poster, if PR poster would be otherwise eligible reviewers, err = repo_model.GetReviewers(ctx, repo1, 11, 4) assert.NoError(t, err) - assert.Len(t, reviewers, 3) + assert.Len(t, reviewers, 2) // test private user repo repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}) diff --git a/modules/actions/workflows.go b/modules/actions/workflows.go index c103aa1928..8677e1fd55 100644 --- a/modules/actions/workflows.go +++ b/modules/actions/workflows.go @@ -211,14 +211,14 @@ func detectMatched(gitRepo *git.Repository, commit *git.Commit, triggedEvent web webhook_module.HookEventIssueAssign, webhook_module.HookEventIssueLabel, webhook_module.HookEventIssueMilestone: - return matchIssuesEvent(commit, payload.(*api.IssuePayload), evt) + return matchIssuesEvent(payload.(*api.IssuePayload), evt) case // issue_comment webhook_module.HookEventIssueComment, // `pull_request_comment` is same as `issue_comment` // See https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_comment-use-issue_comment webhook_module.HookEventPullRequestComment: - return matchIssueCommentEvent(commit, payload.(*api.IssueCommentPayload), evt) + return matchIssueCommentEvent(payload.(*api.IssueCommentPayload), evt) case // pull_request webhook_module.HookEventPullRequest, @@ -232,19 +232,19 @@ func detectMatched(gitRepo *git.Repository, commit *git.Commit, triggedEvent web case // pull_request_review webhook_module.HookEventPullRequestReviewApproved, webhook_module.HookEventPullRequestReviewRejected: - return matchPullRequestReviewEvent(commit, payload.(*api.PullRequestPayload), evt) + return matchPullRequestReviewEvent(payload.(*api.PullRequestPayload), evt) case // pull_request_review_comment webhook_module.HookEventPullRequestReviewComment: - return matchPullRequestReviewCommentEvent(commit, payload.(*api.PullRequestPayload), evt) + return matchPullRequestReviewCommentEvent(payload.(*api.PullRequestPayload), evt) case // release webhook_module.HookEventRelease: - return matchReleaseEvent(commit, payload.(*api.ReleasePayload), evt) + return matchReleaseEvent(payload.(*api.ReleasePayload), evt) case // registry_package webhook_module.HookEventPackage: - return matchPackageEvent(commit, payload.(*api.PackagePayload), evt) + return matchPackageEvent(payload.(*api.PackagePayload), evt) default: log.Warn("unsupported event %q", triggedEvent) @@ -350,7 +350,7 @@ func matchPushEvent(commit *git.Commit, pushPayload *api.PushPayload, evt *jobpa return matchTimes == len(evt.Acts()) } -func matchIssuesEvent(commit *git.Commit, issuePayload *api.IssuePayload, evt *jobparser.Event) bool { +func matchIssuesEvent(issuePayload *api.IssuePayload, evt *jobparser.Event) bool { // with no special filter parameters if len(evt.Acts()) == 0 { return true @@ -498,7 +498,7 @@ func matchPullRequestEvent(gitRepo *git.Repository, commit *git.Commit, prPayloa return activityTypeMatched && matchTimes == len(evt.Acts()) } -func matchIssueCommentEvent(commit *git.Commit, issueCommentPayload *api.IssueCommentPayload, evt *jobparser.Event) bool { +func matchIssueCommentEvent(issueCommentPayload *api.IssueCommentPayload, evt *jobparser.Event) bool { // with no special filter parameters if len(evt.Acts()) == 0 { return true @@ -530,7 +530,7 @@ func matchIssueCommentEvent(commit *git.Commit, issueCommentPayload *api.IssueCo return matchTimes == len(evt.Acts()) } -func matchPullRequestReviewEvent(commit *git.Commit, prPayload *api.PullRequestPayload, evt *jobparser.Event) bool { +func matchPullRequestReviewEvent(prPayload *api.PullRequestPayload, evt *jobparser.Event) bool { // with no special filter parameters if len(evt.Acts()) == 0 { return true @@ -579,7 +579,7 @@ func matchPullRequestReviewEvent(commit *git.Commit, prPayload *api.PullRequestP return matchTimes == len(evt.Acts()) } -func matchPullRequestReviewCommentEvent(commit *git.Commit, prPayload *api.PullRequestPayload, evt *jobparser.Event) bool { +func matchPullRequestReviewCommentEvent(prPayload *api.PullRequestPayload, evt *jobparser.Event) bool { // with no special filter parameters if len(evt.Acts()) == 0 { return true @@ -628,7 +628,7 @@ func matchPullRequestReviewCommentEvent(commit *git.Commit, prPayload *api.PullR return matchTimes == len(evt.Acts()) } -func matchReleaseEvent(commit *git.Commit, payload *api.ReleasePayload, evt *jobparser.Event) bool { +func matchReleaseEvent(payload *api.ReleasePayload, evt *jobparser.Event) bool { // with no special filter parameters if len(evt.Acts()) == 0 { return true @@ -665,7 +665,7 @@ func matchReleaseEvent(commit *git.Commit, payload *api.ReleasePayload, evt *job return matchTimes == len(evt.Acts()) } -func matchPackageEvent(commit *git.Commit, payload *api.PackagePayload, evt *jobparser.Event) bool { +func matchPackageEvent(payload *api.PackagePayload, evt *jobparser.Event) bool { // with no special filter parameters if len(evt.Acts()) == 0 { return true diff --git a/modules/auth/password/pwn/pwn_test.go b/modules/auth/password/pwn/pwn_test.go index a2a6b3a174..b3e7734c3f 100644 --- a/modules/auth/password/pwn/pwn_test.go +++ b/modules/auth/password/pwn/pwn_test.go @@ -4,12 +4,11 @@ package pwn import ( - "math/rand/v2" "net/http" - "strings" "testing" "time" + "github.com/h2non/gock" "github.com/stretchr/testify/assert" ) @@ -18,86 +17,34 @@ var client = New(WithHTTP(&http.Client{ })) func TestPassword(t *testing.T) { - // Check input error - _, err := client.CheckPassword("", false) + defer gock.Off() + + count, err := client.CheckPassword("", false) assert.ErrorIs(t, err, ErrEmptyPassword, "blank input should return ErrEmptyPassword") + assert.Equal(t, -1, count) - // Should fail - fail := "password1234" - count, err := client.CheckPassword(fail, false) - assert.NotEmpty(t, count, "%s should fail as a password", fail) + gock.New("https://api.pwnedpasswords.com").Get("/range/5c1d8").Times(1).Reply(200).BodyString("EAF2F254732680E8AC339B84F3266ECCBB5:1\r\nFC446EB88938834178CB9322C1EE273C2A7:2") + count, err = client.CheckPassword("pwned", false) assert.NoError(t, err) + assert.Equal(t, 1, count) - // Should fail (with padding) - failPad := "administrator" - count, err = client.CheckPassword(failPad, true) - assert.NotEmpty(t, count, "%s should fail as a password", failPad) + gock.New("https://api.pwnedpasswords.com").Get("/range/ba189").Times(1).Reply(200).BodyString("FD4CB34F0378BCB15D23F6FFD28F0775C9E:3\r\nFDF342FCD8C3611DAE4D76E8A992A3E4169:4") + count, err = client.CheckPassword("notpwned", false) assert.NoError(t, err) + assert.Equal(t, 0, count) - // Checking for a "good" password isn't going to be perfect, but we can give it a good try - // with hopefully minimal error. Try five times? - assert.Condition(t, func() bool { - for i := 0; i <= 5; i++ { - count, err = client.CheckPassword(testPassword(), false) - assert.NoError(t, err) - if count == 0 { - return true - } - } - return false - }, "no generated passwords passed. there is a chance this is a fluke") + gock.New("https://api.pwnedpasswords.com").Get("/range/a1733").Times(1).Reply(200).BodyString("C4CE0F1F0062B27B9E2F41AF0C08218017C:1\r\nFC446EB88938834178CB9322C1EE273C2A7:2\r\nFE81480327C992FE62065A827429DD1318B:0") + count, err = client.CheckPassword("paddedpwned", true) + assert.NoError(t, err) + assert.Equal(t, 1, count) - // Again, but with padded responses - assert.Condition(t, func() bool { - for i := 0; i <= 5; i++ { - count, err = client.CheckPassword(testPassword(), true) - assert.NoError(t, err) - if count == 0 { - return true - } - } - return false - }, "no generated passwords passed. there is a chance this is a fluke") -} - -// Credit to https://golangbyexample.com/generate-random-password-golang/ -// DO NOT USE THIS FOR AN ACTUAL PASSWORD GENERATOR -var ( - lowerCharSet = "abcdedfghijklmnopqrst" - upperCharSet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - specialCharSet = "!@#$%&*" - numberSet = "0123456789" - allCharSet = lowerCharSet + upperCharSet + specialCharSet + numberSet -) - -func testPassword() string { - var password strings.Builder - - // Set special character - for i := 0; i < 5; i++ { - random := rand.IntN(len(specialCharSet)) - password.WriteString(string(specialCharSet[random])) - } - - // Set numeric - for i := 0; i < 5; i++ { - random := rand.IntN(len(numberSet)) - password.WriteString(string(numberSet[random])) - } - - // Set uppercase - for i := 0; i < 5; i++ { - random := rand.IntN(len(upperCharSet)) - password.WriteString(string(upperCharSet[random])) - } - - for i := 0; i < 5; i++ { - random := rand.IntN(len(allCharSet)) - password.WriteString(string(allCharSet[random])) - } - inRune := []rune(password.String()) - rand.Shuffle(len(inRune), func(i, j int) { - inRune[i], inRune[j] = inRune[j], inRune[i] - }) - return string(inRune) + gock.New("https://api.pwnedpasswords.com").Get("/range/5617b").Times(1).Reply(200).BodyString("FD4CB34F0378BCB15D23F6FFD28F0775C9E:3\r\nFDF342FCD8C3611DAE4D76E8A992A3E4169:4\r\nFE81480327C992FE62065A827429DD1318B:0") + count, err = client.CheckPassword("paddednotpwned", true) + assert.NoError(t, err) + assert.Equal(t, 0, count) + + gock.New("https://api.pwnedpasswords.com").Get("/range/79082").Times(1).Reply(200).BodyString("FDF342FCD8C3611DAE4D76E8A992A3E4169:4\r\nFE81480327C992FE62065A827429DD1318B:0\r\nAFEF386F56EB0B4BE314E07696E5E6E6536:0") + count, err = client.CheckPassword("paddednotpwnedzero", true) + assert.NoError(t, err) + assert.Equal(t, 0, count) } diff --git a/modules/git/commit_info_nogogit.go b/modules/git/commit_info_nogogit.go index a5d18694f7..7c369b07f9 100644 --- a/modules/git/commit_info_nogogit.go +++ b/modules/git/commit_info_nogogit.go @@ -29,7 +29,7 @@ func (tes Entries) GetCommitsInfo(ctx context.Context, commit *Commit, treePath var revs map[string]*Commit if commit.repo.LastCommitCache != nil { var unHitPaths []string - revs, unHitPaths, err = getLastCommitForPathsByCache(ctx, commit.ID.String(), treePath, entryPaths, commit.repo.LastCommitCache) + revs, unHitPaths, err = getLastCommitForPathsByCache(commit.ID.String(), treePath, entryPaths, commit.repo.LastCommitCache) if err != nil { return nil, nil, err } @@ -97,7 +97,7 @@ func (tes Entries) GetCommitsInfo(ctx context.Context, commit *Commit, treePath return commitsInfo, treeCommit, nil } -func getLastCommitForPathsByCache(ctx context.Context, commitID, treePath string, paths []string, cache *LastCommitCache) (map[string]*Commit, []string, error) { +func getLastCommitForPathsByCache(commitID, treePath string, paths []string, cache *LastCommitCache) (map[string]*Commit, []string, error) { var unHitEntryPaths []string results := make(map[string]*Commit) for _, p := range paths { diff --git a/modules/git/parse_gogit.go b/modules/git/parse_gogit.go index d1fdd346e4..74d258de8e 100644 --- a/modules/git/parse_gogit.go +++ b/modules/git/parse_gogit.go @@ -18,7 +18,7 @@ import ( ) // ParseTreeEntries parses the output of a `git ls-tree -l` command. -func ParseTreeEntries(h ObjectFormat, data []byte) ([]*TreeEntry, error) { +func ParseTreeEntries(data []byte) ([]*TreeEntry, error) { return parseTreeEntries(data, nil) } diff --git a/modules/git/parse_gogit_test.go b/modules/git/parse_gogit_test.go index d9e5b4441f..3e171d7e56 100644 --- a/modules/git/parse_gogit_test.go +++ b/modules/git/parse_gogit_test.go @@ -67,7 +67,7 @@ func TestParseTreeEntries(t *testing.T) { } for _, testCase := range testCases { - entries, err := ParseTreeEntries(Sha1ObjectFormat, []byte(testCase.Input)) + entries, err := ParseTreeEntries([]byte(testCase.Input)) assert.NoError(t, err) if len(entries) > 1 { fmt.Println(testCase.Expected[0].ID) diff --git a/modules/git/parse_nogogit.go b/modules/git/parse_nogogit.go index 225342cc5a..546b38be37 100644 --- a/modules/git/parse_nogogit.go +++ b/modules/git/parse_nogogit.go @@ -17,13 +17,13 @@ import ( ) // ParseTreeEntries parses the output of a `git ls-tree -l` command. -func ParseTreeEntries(objectFormat ObjectFormat, data []byte) ([]*TreeEntry, error) { - return parseTreeEntries(objectFormat, data, nil) +func ParseTreeEntries(data []byte) ([]*TreeEntry, error) { + return parseTreeEntries(data, nil) } var sepSpace = []byte{' '} -func parseTreeEntries(objectFormat ObjectFormat, data []byte, ptree *Tree) ([]*TreeEntry, error) { +func parseTreeEntries(data []byte, ptree *Tree) ([]*TreeEntry, error) { var err error entries := make([]*TreeEntry, 0, bytes.Count(data, []byte{'\n'})+1) for pos := 0; pos < len(data); { diff --git a/modules/git/parse_nogogit_test.go b/modules/git/parse_nogogit_test.go index f037fd7a2e..23fddb014c 100644 --- a/modules/git/parse_nogogit_test.go +++ b/modules/git/parse_nogogit_test.go @@ -12,8 +12,6 @@ import ( ) func TestParseTreeEntriesLong(t *testing.T) { - objectFormat := Sha1ObjectFormat - testCases := []struct { Input string Expected []*TreeEntry @@ -56,7 +54,7 @@ func TestParseTreeEntriesLong(t *testing.T) { }, } for _, testCase := range testCases { - entries, err := ParseTreeEntries(objectFormat, []byte(testCase.Input)) + entries, err := ParseTreeEntries([]byte(testCase.Input)) assert.NoError(t, err) assert.Len(t, entries, len(testCase.Expected)) for i, entry := range entries { @@ -66,8 +64,6 @@ func TestParseTreeEntriesLong(t *testing.T) { } func TestParseTreeEntriesShort(t *testing.T) { - objectFormat := Sha1ObjectFormat - testCases := []struct { Input string Expected []*TreeEntry @@ -91,7 +87,7 @@ func TestParseTreeEntriesShort(t *testing.T) { }, } for _, testCase := range testCases { - entries, err := ParseTreeEntries(objectFormat, []byte(testCase.Input)) + entries, err := ParseTreeEntries([]byte(testCase.Input)) assert.NoError(t, err) assert.Len(t, entries, len(testCase.Expected)) for i, entry := range entries { @@ -102,7 +98,7 @@ func TestParseTreeEntriesShort(t *testing.T) { func TestParseTreeEntriesInvalid(t *testing.T) { // there was a panic: "runtime error: slice bounds out of range" when the input was invalid: #20315 - entries, err := ParseTreeEntries(Sha1ObjectFormat, []byte("100644 blob ea0d83c9081af9500ac9f804101b3fd0a5c293af")) + entries, err := ParseTreeEntries([]byte("100644 blob ea0d83c9081af9500ac9f804101b3fd0a5c293af")) assert.Error(t, err) assert.Len(t, entries, 0) } diff --git a/modules/git/tree_nogogit.go b/modules/git/tree_nogogit.go index a591485082..e0a72de5b8 100644 --- a/modules/git/tree_nogogit.go +++ b/modules/git/tree_nogogit.go @@ -77,11 +77,8 @@ func (t *Tree) ListEntries() (Entries, error) { return nil, runErr } - objectFormat, err := t.repo.GetObjectFormat() - if err != nil { - return nil, err - } - t.entries, err = parseTreeEntries(objectFormat, stdout, t) + var err error + t.entries, err = parseTreeEntries(stdout, t) if err == nil { t.entriesParsed = true } @@ -104,11 +101,8 @@ func (t *Tree) listEntriesRecursive(extraArgs TrustedCmdArgs) (Entries, error) { return nil, runErr } - objectFormat, err := t.repo.GetObjectFormat() - if err != nil { - return nil, err - } - t.entriesRecursive, err = parseTreeEntries(objectFormat, stdout, t) + var err error + t.entriesRecursive, err = parseTreeEntries(stdout, t) if err == nil { t.entriesRecursiveParsed = true } diff --git a/modules/httplib/serve.go b/modules/httplib/serve.go index a193ed901c..6e147d76f5 100644 --- a/modules/httplib/serve.go +++ b/modules/httplib/serve.go @@ -17,11 +17,14 @@ import ( "time" charsetModule "code.gitea.io/gitea/modules/charset" + "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/httpcache" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/typesniffer" "code.gitea.io/gitea/modules/util" + + "github.com/klauspost/compress/gzhttp" ) type ServeHeaderOptions struct { @@ -38,6 +41,11 @@ type ServeHeaderOptions struct { func ServeSetHeaders(w http.ResponseWriter, opts *ServeHeaderOptions) { header := w.Header() + skipCompressionExts := container.SetOf(".gz", ".bz2", ".zip", ".xz", ".zst", ".deb", ".apk", ".jar", ".png", ".jpg", ".webp") + if skipCompressionExts.Contains(strings.ToLower(path.Ext(opts.Filename))) { + w.Header().Add(gzhttp.HeaderNoCompression, "1") + } + contentType := typesniffer.ApplicationOctetStream if opts.ContentType != "" { if opts.ContentTypeCharset != "" { diff --git a/modules/indexer/code/git.go b/modules/indexer/code/git.go index 2905a540e5..bc345f2325 100644 --- a/modules/indexer/code/git.go +++ b/modules/indexer/code/git.go @@ -62,8 +62,8 @@ func isIndexable(entry *git.TreeEntry) bool { } // parseGitLsTreeOutput parses the output of a `git ls-tree -r --full-name` command -func parseGitLsTreeOutput(objectFormat git.ObjectFormat, stdout []byte) ([]internal.FileUpdate, error) { - entries, err := git.ParseTreeEntries(objectFormat, stdout) +func parseGitLsTreeOutput(stdout []byte) ([]internal.FileUpdate, error) { + entries, err := git.ParseTreeEntries(stdout) if err != nil { return nil, err } @@ -91,10 +91,8 @@ func genesisChanges(ctx context.Context, repo *repo_model.Repository, revision s return nil, runErr } - objectFormat := git.ObjectFormatFromName(repo.ObjectFormatName) - var err error - changes.Updates, err = parseGitLsTreeOutput(objectFormat, stdout) + changes.Updates, err = parseGitLsTreeOutput(stdout) return &changes, err } @@ -172,8 +170,6 @@ func nonGenesisChanges(ctx context.Context, repo *repo_model.Repository, revisio return nil, err } - objectFormat := git.ObjectFormatFromName(repo.ObjectFormatName) - - changes.Updates, err = parseGitLsTreeOutput(objectFormat, lsTreeStdout) + changes.Updates, err = parseGitLsTreeOutput(lsTreeStdout) return &changes, err } diff --git a/modules/markup/markdown/goldmark.go b/modules/markup/markdown/goldmark.go index 8289e28677..0290e1312d 100644 --- a/modules/markup/markdown/goldmark.go +++ b/modules/markup/markdown/goldmark.go @@ -58,11 +58,11 @@ func (g *ASTTransformer) Transform(node *ast.Document, reader text.Reader, pc pa case *ast.Paragraph: g.applyElementDir(v) case *ast.Image: - g.transformImage(ctx, v, reader) + g.transformImage(ctx, v) case *ast.Link: - g.transformLink(ctx, v, reader) + g.transformLink(ctx, v) case *ast.List: - g.transformList(ctx, v, reader, rc) + g.transformList(ctx, v, rc) case *ast.Text: if v.SoftLineBreak() && !v.HardLineBreak() { if ctx.Metas["mode"] != "document" { diff --git a/modules/markup/markdown/transform_heading.go b/modules/markup/markdown/transform_heading.go index ce585a37de..6f38abfad9 100644 --- a/modules/markup/markdown/transform_heading.go +++ b/modules/markup/markdown/transform_heading.go @@ -13,7 +13,7 @@ import ( "github.com/yuin/goldmark/util" ) -func (g *ASTTransformer) transformHeading(ctx *markup.RenderContext, v *ast.Heading, reader text.Reader, tocList *[]markup.Header) { +func (g *ASTTransformer) transformHeading(_ *markup.RenderContext, v *ast.Heading, reader text.Reader, tocList *[]markup.Header) { for _, attr := range v.Attributes() { if _, ok := attr.Value.([]byte); !ok { v.SetAttribute(attr.Name, []byte(fmt.Sprintf("%v", attr.Value))) diff --git a/modules/markup/markdown/transform_image.go b/modules/markup/markdown/transform_image.go index db449e9dd9..b34a710fed 100644 --- a/modules/markup/markdown/transform_image.go +++ b/modules/markup/markdown/transform_image.go @@ -10,10 +10,9 @@ import ( giteautil "code.gitea.io/gitea/modules/util" "github.com/yuin/goldmark/ast" - "github.com/yuin/goldmark/text" ) -func (g *ASTTransformer) transformImage(ctx *markup.RenderContext, v *ast.Image, reader text.Reader) { +func (g *ASTTransformer) transformImage(ctx *markup.RenderContext, v *ast.Image) { // Images need two things: // // 1. Their src needs to munged to be a real value diff --git a/modules/markup/markdown/transform_link.go b/modules/markup/markdown/transform_link.go index aceae5b74f..e6f3836412 100644 --- a/modules/markup/markdown/transform_link.go +++ b/modules/markup/markdown/transform_link.go @@ -12,10 +12,9 @@ import ( giteautil "code.gitea.io/gitea/modules/util" "github.com/yuin/goldmark/ast" - "github.com/yuin/goldmark/text" ) -func (g *ASTTransformer) transformLink(ctx *markup.RenderContext, v *ast.Link, reader text.Reader) { +func (g *ASTTransformer) transformLink(ctx *markup.RenderContext, v *ast.Link) { // Links need their href to munged to be a real value link := v.Destination diff --git a/modules/markup/markdown/transform_list.go b/modules/markup/markdown/transform_list.go index 6563e2dd64..b982fd4a83 100644 --- a/modules/markup/markdown/transform_list.go +++ b/modules/markup/markdown/transform_list.go @@ -11,7 +11,6 @@ import ( "github.com/yuin/goldmark/ast" east "github.com/yuin/goldmark/extension/ast" "github.com/yuin/goldmark/renderer/html" - "github.com/yuin/goldmark/text" "github.com/yuin/goldmark/util" ) @@ -50,7 +49,7 @@ func (r *HTMLRenderer) renderTaskCheckBox(w util.BufWriter, source []byte, node return ast.WalkContinue, nil } -func (g *ASTTransformer) transformList(ctx *markup.RenderContext, v *ast.List, reader text.Reader, rc *RenderConfig) { +func (g *ASTTransformer) transformList(_ *markup.RenderContext, v *ast.List, rc *RenderConfig) { if v.HasChildren() { children := make([]ast.Node, 0, v.ChildCount()) child := v.FirstChild() diff --git a/modules/markup/mdstripper/mdstripper.go b/modules/markup/mdstripper/mdstripper.go index e19f8f6419..2a69d95224 100644 --- a/modules/markup/mdstripper/mdstripper.go +++ b/modules/markup/mdstripper/mdstripper.go @@ -54,7 +54,7 @@ func (r *stripRenderer) Render(w io.Writer, source []byte, doc ast.Node) error { } return ast.WalkContinue, nil case *ast.Link: - r.processLink(w, v.Destination) + r.processLink(v.Destination) return ast.WalkSkipChildren, nil case *ast.AutoLink: // This could be a reference to an issue or pull - if so convert it @@ -124,7 +124,7 @@ func (r *stripRenderer) processAutoLink(w io.Writer, link []byte) { _, _ = w.Write([]byte(parts[4])) } -func (r *stripRenderer) processLink(w io.Writer, link []byte) { +func (r *stripRenderer) processLink(link []byte) { // Links are processed out of band r.links = append(r.links, string(link)) } diff --git a/modules/optional/option_test.go b/modules/optional/option_test.go index 4f55608004..203e9221e3 100644 --- a/modules/optional/option_test.go +++ b/modules/optional/option_test.go @@ -22,7 +22,7 @@ func TestOption(t *testing.T) { assert.Equal(t, int(0), none.Value()) assert.Equal(t, int(1), none.ValueOrDefault(1)) - some := optional.Some[int](1) + some := optional.Some(1) assert.True(t, some.Has()) assert.Equal(t, int(1), some.Value()) assert.Equal(t, int(1), some.ValueOrDefault(2)) diff --git a/modules/packages/npm/creator.go b/modules/packages/npm/creator.go index 9e636757af..7d3d7cd6b5 100644 --- a/modules/packages/npm/creator.go +++ b/modules/packages/npm/creator.go @@ -78,6 +78,7 @@ type PackageMetadataVersion struct { Repository Repository `json:"repository,omitempty"` Keywords []string `json:"keywords,omitempty"` Dependencies map[string]string `json:"dependencies,omitempty"` + BundleDependencies []string `json:"bundleDependencies,omitempty"` DevDependencies map[string]string `json:"devDependencies,omitempty"` PeerDependencies map[string]string `json:"peerDependencies,omitempty"` Bin map[string]string `json:"bin,omitempty"` @@ -218,6 +219,7 @@ func ParsePackage(r io.Reader) (*Package, error) { ProjectURL: meta.Homepage, Keywords: meta.Keywords, Dependencies: meta.Dependencies, + BundleDependencies: meta.BundleDependencies, DevelopmentDependencies: meta.DevDependencies, PeerDependencies: meta.PeerDependencies, OptionalDependencies: meta.OptionalDependencies, diff --git a/modules/packages/npm/metadata.go b/modules/packages/npm/metadata.go index 77b77472a7..6bb77f302b 100644 --- a/modules/packages/npm/metadata.go +++ b/modules/packages/npm/metadata.go @@ -16,6 +16,7 @@ type Metadata struct { ProjectURL string `json:"project_url,omitempty"` Keywords []string `json:"keywords,omitempty"` Dependencies map[string]string `json:"dependencies,omitempty"` + BundleDependencies []string `json:"bundleDependencies,omitempty"` DevelopmentDependencies map[string]string `json:"development_dependencies,omitempty"` PeerDependencies map[string]string `json:"peer_dependencies,omitempty"` OptionalDependencies map[string]string `json:"optional_dependencies,omitempty"` diff --git a/modules/setting/incoming_email.go b/modules/setting/incoming_email.go index 314fdea137..287e72941c 100644 --- a/modules/setting/incoming_email.go +++ b/modules/setting/incoming_email.go @@ -56,12 +56,12 @@ func loadIncomingEmailFrom(rootCfg ConfigProvider) { } } - if err := checkReplyToAddress(IncomingEmail.ReplyToAddress); err != nil { + if err := checkReplyToAddress(); err != nil { log.Fatal("Invalid incoming_mail.REPLY_TO_ADDRESS (%s): %v", IncomingEmail.ReplyToAddress, err) } } -func checkReplyToAddress(address string) error { +func checkReplyToAddress() error { parsed, err := mail.ParseAddress(IncomingEmail.ReplyToAddress) if err != nil { return err diff --git a/modules/setting/storage.go b/modules/setting/storage.go index 1e2d28a88b..c082579d39 100644 --- a/modules/setting/storage.go +++ b/modules/setting/storage.go @@ -99,7 +99,7 @@ func getStorage(rootCfg ConfigProvider, name, typ string, sec ConfigSection) (*S return nil, err } - overrideSec := getStorageOverrideSection(rootCfg, targetSec, sec, tp, name) + overrideSec := getStorageOverrideSection(rootCfg, sec, tp, name) targetType := targetSec.Key("STORAGE_TYPE").String() switch targetType { @@ -191,7 +191,7 @@ func getStorageTargetSection(rootCfg ConfigProvider, name, typ string, sec Confi } // getStorageOverrideSection override section will be read SERVE_DIRECT, PATH, MINIO_BASE_PATH, MINIO_BUCKET to override the targetsec when possible -func getStorageOverrideSection(rootConfig ConfigProvider, targetSec, sec ConfigSection, targetSecType targetSecType, name string) ConfigSection { +func getStorageOverrideSection(rootConfig ConfigProvider, sec ConfigSection, targetSecType targetSecType, name string) ConfigSection { if targetSecType == targetSecIsSec { return nil } diff --git a/modules/structs/pull.go b/modules/structs/pull.go index 05a8d59633..b04def52b8 100644 --- a/modules/structs/pull.go +++ b/modules/structs/pull.go @@ -85,7 +85,7 @@ type CreatePullRequestOption struct { // EditPullRequestOption options when modify pull request type EditPullRequestOption struct { Title string `json:"title"` - Body string `json:"body"` + Body *string `json:"body"` Base string `json:"base"` Assignee string `json:"assignee"` Assignees []string `json:"assignees"` diff --git a/modules/structs/repo_actions.go b/modules/structs/repo_actions.go new file mode 100644 index 0000000000..b13f344738 --- /dev/null +++ b/modules/structs/repo_actions.go @@ -0,0 +1,34 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package structs + +import ( + "time" +) + +// ActionTask represents a ActionTask +type ActionTask struct { + ID int64 `json:"id"` + Name string `json:"name"` + HeadBranch string `json:"head_branch"` + HeadSHA string `json:"head_sha"` + RunNumber int64 `json:"run_number"` + Event string `json:"event"` + DisplayTitle string `json:"display_title"` + Status string `json:"status"` + WorkflowID string `json:"workflow_id"` + URL string `json:"url"` + // swagger:strfmt date-time + CreatedAt time.Time `json:"created_at"` + // swagger:strfmt date-time + UpdatedAt time.Time `json:"updated_at"` + // swagger:strfmt date-time + RunStartedAt time.Time `json:"run_started_at"` +} + +// ActionTaskResponse returns a ActionTask +type ActionTaskResponse struct { + Entries []*ActionTask `json:"workflow_runs"` + TotalCount int64 `json:"total_count"` +} diff --git a/options/license/Catharon b/options/license/Catharon new file mode 100644 index 0000000000..8d0ac128bc --- /dev/null +++ b/options/license/Catharon @@ -0,0 +1,121 @@ + The Catharon Open Source LICENSE + ---------------------------- + + 2000-Jul-04 + + Copyright (C) 2000 by Catharon Productions, Inc. + + + +Introduction +============ + + This license applies to source files distributed by Catharon + Productions, Inc. in several archive packages. This license + applies to all files found in such packages which do not fall + under their own explicit license. + + This license was inspired by the BSD, Artistic, and IJG + (Independent JPEG Group) licenses, which all encourage inclusion + and use of free software in commercial and freeware products + alike. As a consequence, its main points are that: + + o We don't promise that this software works. However, we are + interested in any kind of bug reports. (`as is' distribution) + + o You can use this software for whatever you want, in parts or + full form, without having to pay us. (`royalty-free' usage) + + o You may not pretend that you wrote this software. If you use + it, or only parts of it, in a program, you must acknowledge + somewhere in your documentation that you have used the + Catharon Code. (`credits') + + We specifically permit and encourage the inclusion of this + software, with or without modifications, in commercial products. + We disclaim all warranties covering the packages distributed by + Catharon Productions, Inc. and assume no liability related to + their use. + + +Legal Terms +=========== + +0. Definitions +-------------- + + Throughout this license, the terms `Catharon Package', `package', + and `Catharon Code' refer to the set of files originally + distributed by Catharon Productions, Inc. + + `You' refers to the licensee, or person using the project, where + `using' is a generic term including compiling the project's source + code as well as linking it to form a `program' or `executable'. + This program is referred to as `a program using one of the + Catharon Packages'. + + This license applies to all files distributed in the original + Catharon Package(s), including all source code, binaries and + documentation, unless otherwise stated in the file in its + original, unmodified form as distributed in the original archive. + If you are unsure whether or not a particular file is covered by + this license, you must contact us to verify this. + + The Catharon Packages are copyright (C) 2000 by Catharon + Productions, Inc. All rights reserved except as specified below. + +1. No Warranty +-------------- + + THE CATHARON PACKAGES ARE PROVIDED `AS IS' WITHOUT WARRANTY OF ANY + KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE. IN NO EVENT WILL ANY OF THE AUTHORS OR COPYRIGHT HOLDERS + BE LIABLE FOR ANY DAMAGES CAUSED BY THE USE OF OR THE INABILITY TO + USE THE CATHARON PACKAGE. + +2. Redistribution +----------------- + + This license grants a worldwide, royalty-free, perpetual and + irrevocable right and license to use, execute, perform, compile, + display, copy, create derivative works of, distribute and + sublicense the Catharon Packages (in both source and object code + forms) and derivative works thereof for any purpose; and to + authorize others to exercise some or all of the rights granted + herein, subject to the following conditions: + + o Redistribution of source code must retain this license file + (`license.txt') unaltered; any additions, deletions or changes + to the original files must be clearly indicated in + accompanying documentation. The copyright notices of the + unaltered, original files must be preserved in all copies of + source files. + + o Redistribution in binary form must provide a disclaimer that + states that the software is based in part on the work of + Catharon Productions, Inc. in the distribution documentation. + + These conditions apply to any software derived from or based on + the Catharon Packages, not just the unmodified files. If you use + our work, you must acknowledge us. However, no fee need be paid + to us. + +3. Advertising +-------------- + + Neither Catharon Productions, Inc. and contributors nor you shall + use the name of the other for commercial, advertising, or + promotional purposes without specific prior written permission. + + We suggest, but do not require, that you use the following phrase + to refer to this software in your documentation: 'this software is + based in part on the Catharon Typography Project'. + + As you have not signed this license, you are not required to + accept it. However, as the Catharon Packages are copyrighted + material, only this license, or another one contracted with the + authors, grants you the right to use, distribute, and modify it. + Therefore, by using, distributing, or modifying the Catharon + Packages, you indicate that you understand and accept all the + terms of this license. diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index af2eec070a..938154c546 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -3573,6 +3573,7 @@ npm.install = To install the package using npm, run the following command: npm.install2 = or add it to the package.json file: npm.dependencies = Dependencies npm.dependencies.development = Development Dependencies +npm.dependencies.bundle = Bundled Dependencies npm.dependencies.peer = Peer Dependencies npm.dependencies.optional = Optional Dependencies npm.details.tag = Tag diff --git a/routers/api/packages/maven/maven.go b/routers/api/packages/maven/maven.go index 7571797593..58271e1d43 100644 --- a/routers/api/packages/maven/maven.go +++ b/routers/api/packages/maven/maven.go @@ -143,9 +143,7 @@ func serveMavenMetadata(ctx *context.Context, params parameters) { ctx.Resp.Header().Set("Content-Length", strconv.Itoa(len(xmlMetadataWithHeader))) ctx.Resp.Header().Set("Content-Type", contentTypeXML) - if _, err := ctx.Resp.Write(xmlMetadataWithHeader); err != nil { - log.Error("write bytes failed: %v", err) - } + _, _ = ctx.Resp.Write(xmlMetadataWithHeader) } func servePackageFile(ctx *context.Context, params parameters, serveContent bool) { diff --git a/routers/api/packages/npm/api.go b/routers/api/packages/npm/api.go index f8e839c424..b4379f3f49 100644 --- a/routers/api/packages/npm/api.go +++ b/routers/api/packages/npm/api.go @@ -64,6 +64,7 @@ func createPackageMetadataVersion(registryURL string, pd *packages_model.Package Homepage: metadata.ProjectURL, License: metadata.License, Dependencies: metadata.Dependencies, + BundleDependencies: metadata.BundleDependencies, DevDependencies: metadata.DevelopmentDependencies, PeerDependencies: metadata.PeerDependencies, OptionalDependencies: metadata.OptionalDependencies, diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index 07b54406c0..e52cc4c366 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -1112,6 +1112,9 @@ func Routes() *web.Route { m.Post("", reqToken(), reqRepoWriter(unit.TypeCode), mustNotBeArchived, bind(api.CreateTagOption{}), repo.CreateTag) m.Delete("/*", reqToken(), reqRepoWriter(unit.TypeCode), mustNotBeArchived, repo.DeleteTag) }, reqRepoReader(unit.TypeCode), context.ReferencesGitRepo(true)) + m.Group("/actions", func() { + m.Get("/tasks", repo.ListActionTasks) + }, reqRepoReader(unit.TypeActions), context.ReferencesGitRepo(true)) m.Group("/keys", func() { m.Combo("").Get(repo.ListDeployKeys). Post(bind(api.CreateKeyOption{}), repo.CreateDeployKey) diff --git a/routers/api/v1/repo/action.go b/routers/api/v1/repo/action.go index 311cfca6e9..f6656d89c6 100644 --- a/routers/api/v1/repo/action.go +++ b/routers/api/v1/repo/action.go @@ -17,6 +17,7 @@ import ( "code.gitea.io/gitea/routers/api/v1/utils" actions_service "code.gitea.io/gitea/services/actions" "code.gitea.io/gitea/services/context" + "code.gitea.io/gitea/services/convert" secret_service "code.gitea.io/gitea/services/secrets" ) @@ -517,3 +518,68 @@ type Action struct{} func NewAction() actions_service.API { return Action{} } + +// ListActionTasks list all the actions of a repository +func ListActionTasks(ctx *context.APIContext) { + // swagger:operation GET /repos/{owner}/{repo}/actions/tasks repository ListActionTasks + // --- + // summary: List a repository's action tasks + // produces: + // - application/json + // parameters: + // - name: owner + // in: path + // description: owner of the repo + // type: string + // required: true + // - name: repo + // in: path + // description: name of the repo + // type: string + // required: true + // - name: page + // in: query + // description: page number of results to return (1-based) + // type: integer + // - name: limit + // in: query + // description: page size of results, default maximum page size is 50 + // type: integer + // responses: + // "200": + // "$ref": "#/responses/TasksList" + // "400": + // "$ref": "#/responses/error" + // "403": + // "$ref": "#/responses/forbidden" + // "404": + // "$ref": "#/responses/notFound" + // "409": + // "$ref": "#/responses/conflict" + // "422": + // "$ref": "#/responses/validationError" + + tasks, total, err := db.FindAndCount[actions_model.ActionTask](ctx, &actions_model.FindTaskOptions{ + ListOptions: utils.GetListOptions(ctx), + RepoID: ctx.Repo.Repository.ID, + }) + if err != nil { + ctx.Error(http.StatusInternalServerError, "ListActionTasks", err) + return + } + + res := new(api.ActionTaskResponse) + res.TotalCount = total + + res.Entries = make([]*api.ActionTask, len(tasks)) + for i := range tasks { + convertedTask, err := convert.ToActionTask(ctx, tasks[i]) + if err != nil { + ctx.Error(http.StatusInternalServerError, "ToActionTask", err) + return + } + res.Entries[i] = convertedTask + } + + ctx.JSON(http.StatusOK, &res) +} diff --git a/routers/api/v1/repo/issue.go b/routers/api/v1/repo/issue.go index 35c1fdcc0c..2b4342dbd1 100644 --- a/routers/api/v1/repo/issue.go +++ b/routers/api/v1/repo/issue.go @@ -29,7 +29,6 @@ import ( "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" issue_service "code.gitea.io/gitea/services/issue" - notify_service "code.gitea.io/gitea/services/notify" ) // SearchIssues searches for issues across the repositories that the user has access to @@ -809,12 +808,19 @@ func EditIssue(ctx *context.APIContext) { return } - oldTitle := issue.Title if len(form.Title) > 0 { - issue.Title = form.Title + err = issue_service.ChangeTitle(ctx, issue, ctx.Doer, form.Title) + if err != nil { + ctx.Error(http.StatusInternalServerError, "ChangeTitle", err) + return + } } if form.Body != nil { - issue.Content = *form.Body + err = issue_service.ChangeContent(ctx, issue, ctx.Doer, *form.Body) + if err != nil { + ctx.Error(http.StatusInternalServerError, "ChangeContent", err) + return + } } if form.Ref != nil { err = issue_service.ChangeIssueRef(ctx, issue, ctx.Doer, *form.Ref) @@ -882,24 +888,14 @@ func EditIssue(ctx *context.APIContext) { return } } - issue.IsClosed = api.StateClosed == api.StateType(*form.State) - } - statusChangeComment, titleChanged, err := issues_model.UpdateIssueByAPI(ctx, issue, ctx.Doer) - if err != nil { - if issues_model.IsErrDependenciesLeft(err) { - ctx.Error(http.StatusPreconditionFailed, "DependenciesLeft", "cannot close this issue because it still has open dependencies") + if err := issue_service.ChangeStatus(ctx, issue, ctx.Doer, "", api.StateClosed == api.StateType(*form.State)); err != nil { + if issues_model.IsErrDependenciesLeft(err) { + ctx.Error(http.StatusPreconditionFailed, "DependenciesLeft", "cannot close this issue because it still has open dependencies") + return + } + ctx.Error(http.StatusInternalServerError, "ChangeStatus", err) return } - ctx.Error(http.StatusInternalServerError, "UpdateIssueByAPI", err) - return - } - - if titleChanged { - notify_service.IssueChangeTitle(ctx, ctx.Doer, issue, oldTitle) - } - - if statusChangeComment != nil { - notify_service.IssueChangeStatus(ctx, ctx.Doer, "", issue, statusChangeComment, issue.IsClosed) } // Refetch from database to assign some automatic values diff --git a/routers/api/v1/repo/issue_attachment.go b/routers/api/v1/repo/issue_attachment.go index 6f3e4b5e77..658d18094a 100644 --- a/routers/api/v1/repo/issue_attachment.go +++ b/routers/api/v1/repo/issue_attachment.go @@ -15,6 +15,7 @@ import ( "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/services/attachment" "code.gitea.io/gitea/services/context" + "code.gitea.io/gitea/services/context/upload" "code.gitea.io/gitea/services/convert" issue_service "code.gitea.io/gitea/services/issue" ) @@ -159,6 +160,8 @@ func CreateIssueAttachment(ctx *context.APIContext) { // "$ref": "#/responses/error" // "404": // "$ref": "#/responses/error" + // "422": + // "$ref": "#/responses/validationError" // "423": // "$ref": "#/responses/repoArchivedError" @@ -207,7 +210,11 @@ func CreateIssueAttachment(ctx *context.APIContext) { CreatedUnix: issue.UpdatedUnix, }) if err != nil { - ctx.Error(http.StatusInternalServerError, "UploadAttachment", err) + if upload.IsErrFileTypeForbidden(err) { + ctx.Error(http.StatusUnprocessableEntity, "", err) + } else { + ctx.Error(http.StatusInternalServerError, "UploadAttachment", err) + } return } diff --git a/routers/api/v1/repo/issue_comment_attachment.go b/routers/api/v1/repo/issue_comment_attachment.go index 0f8fc96f08..ed8ea10293 100644 --- a/routers/api/v1/repo/issue_comment_attachment.go +++ b/routers/api/v1/repo/issue_comment_attachment.go @@ -15,6 +15,7 @@ import ( "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/services/attachment" "code.gitea.io/gitea/services/context" + "code.gitea.io/gitea/services/context/upload" "code.gitea.io/gitea/services/convert" issue_service "code.gitea.io/gitea/services/issue" ) @@ -156,6 +157,8 @@ func CreateIssueCommentAttachment(ctx *context.APIContext) { // "$ref": "#/responses/error" // "404": // "$ref": "#/responses/error" + // "422": + // "$ref": "#/responses/validationError" // "423": // "$ref": "#/responses/repoArchivedError" @@ -209,9 +212,14 @@ func CreateIssueCommentAttachment(ctx *context.APIContext) { CreatedUnix: comment.Issue.UpdatedUnix, }) if err != nil { - ctx.Error(http.StatusInternalServerError, "UploadAttachment", err) + if upload.IsErrFileTypeForbidden(err) { + ctx.Error(http.StatusUnprocessableEntity, "", err) + } else { + ctx.Error(http.StatusInternalServerError, "UploadAttachment", err) + } return } + if err := comment.LoadAttachments(ctx); err != nil { ctx.Error(http.StatusInternalServerError, "LoadAttachments", err) return diff --git a/routers/api/v1/repo/migrate.go b/routers/api/v1/repo/migrate.go index 2caaa130e8..f246b08c0a 100644 --- a/routers/api/v1/repo/migrate.go +++ b/routers/api/v1/repo/migrate.go @@ -180,7 +180,7 @@ func Migrate(ctx *context.APIContext) { Status: repo_model.RepositoryBeingMigrated, }) if err != nil { - handleMigrateError(ctx, repoOwner, remoteAddr, err) + handleMigrateError(ctx, repoOwner, err) return } @@ -207,7 +207,7 @@ func Migrate(ctx *context.APIContext) { }() if repo, err = migrations.MigrateRepository(graceful.GetManager().HammerContext(), ctx.Doer, repoOwner.Name, opts, nil); err != nil { - handleMigrateError(ctx, repoOwner, remoteAddr, err) + handleMigrateError(ctx, repoOwner, err) return } @@ -215,7 +215,7 @@ func Migrate(ctx *context.APIContext) { ctx.JSON(http.StatusCreated, convert.ToRepo(ctx, repo, access_model.Permission{AccessMode: perm.AccessModeAdmin})) } -func handleMigrateError(ctx *context.APIContext, repoOwner *user_model.User, remoteAddr string, err error) { +func handleMigrateError(ctx *context.APIContext, repoOwner *user_model.User, err error) { switch { case repo_model.IsErrRepoAlreadyExist(err): ctx.Error(http.StatusConflict, "", "The repository with the same name already exists.") diff --git a/routers/api/v1/repo/pull.go b/routers/api/v1/repo/pull.go index 852ec78ade..dbb7de6e66 100644 --- a/routers/api/v1/repo/pull.go +++ b/routers/api/v1/repo/pull.go @@ -601,12 +601,19 @@ func EditPullRequest(ctx *context.APIContext) { return } - oldTitle := issue.Title if len(form.Title) > 0 { - issue.Title = form.Title + err = issue_service.ChangeTitle(ctx, issue, ctx.Doer, form.Title) + if err != nil { + ctx.Error(http.StatusInternalServerError, "ChangeTitle", err) + return + } } - if len(form.Body) > 0 { - issue.Content = form.Body + if form.Body != nil { + err = issue_service.ChangeContent(ctx, issue, ctx.Doer, *form.Body) + if err != nil { + ctx.Error(http.StatusInternalServerError, "ChangeContent", err) + return + } } // Update or remove deadline if set @@ -683,24 +690,14 @@ func EditPullRequest(ctx *context.APIContext) { ctx.Error(http.StatusPreconditionFailed, "MergedPRState", "cannot change state of this pull request, it was already merged") return } - issue.IsClosed = api.StateClosed == api.StateType(*form.State) - } - statusChangeComment, titleChanged, err := issues_model.UpdateIssueByAPI(ctx, issue, ctx.Doer) - if err != nil { - if issues_model.IsErrDependenciesLeft(err) { - ctx.Error(http.StatusPreconditionFailed, "DependenciesLeft", "cannot close this pull request because it still has open dependencies") + if err := issue_service.ChangeStatus(ctx, issue, ctx.Doer, "", api.StateClosed == api.StateType(*form.State)); err != nil { + if issues_model.IsErrDependenciesLeft(err) { + ctx.Error(http.StatusPreconditionFailed, "DependenciesLeft", "cannot close this pull request because it still has open dependencies") + return + } + ctx.Error(http.StatusInternalServerError, "ChangeStatus", err) return } - ctx.Error(http.StatusInternalServerError, "UpdateIssueByAPI", err) - return - } - - if titleChanged { - notify_service.IssueChangeTitle(ctx, ctx.Doer, issue, oldTitle) - } - - if statusChangeComment != nil { - notify_service.IssueChangeStatus(ctx, ctx.Doer, "", issue, statusChangeComment, issue.IsClosed) } // change pull target branch diff --git a/routers/api/v1/swagger/repo.go b/routers/api/v1/swagger/repo.go index b55ea1d0a9..6d399ea185 100644 --- a/routers/api/v1/swagger/repo.go +++ b/routers/api/v1/swagger/repo.go @@ -422,6 +422,13 @@ type swaggerBlockedUserList struct { Body []api.BlockedUser `json:"body"` } +// TasksList +// swagger:response TasksList +type swaggerRepoTasksList struct { + // in:body + Body api.ActionTaskResponse `json:"body"` +} + // swagger:response Compare type swaggerCompare struct { // in:body diff --git a/routers/api/v1/user/repo.go b/routers/api/v1/user/repo.go index 81f8e0f3fe..9b6701b067 100644 --- a/routers/api/v1/user/repo.go +++ b/routers/api/v1/user/repo.go @@ -6,10 +6,8 @@ package user import ( "net/http" - "code.gitea.io/gitea/models/perm" access_model "code.gitea.io/gitea/models/perm/access" repo_model "code.gitea.io/gitea/models/repo" - unit_model "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/routers/api/v1/utils" @@ -44,7 +42,7 @@ func listUserRepos(ctx *context.APIContext, u *user_model.User, private bool) { ctx.Error(http.StatusInternalServerError, "GetUserRepoPermission", err) return } - if ctx.IsSigned && ctx.Doer.IsAdmin || permission.UnitAccessMode(unit_model.TypeCode) >= perm.AccessModeRead { + if ctx.IsSigned && ctx.Doer.IsAdmin || permission.HasAccess() { apiRepos = append(apiRepos, convert.ToRepo(ctx, repos[i], permission)) } } diff --git a/routers/private/hook_post_receive.go b/routers/private/hook_post_receive.go index 2558ffe1ab..381e3c6c77 100644 --- a/routers/private/hook_post_receive.go +++ b/routers/private/hook_post_receive.go @@ -114,16 +114,14 @@ func HookPostReceive(ctx *gitea_context.PrivateContext) { } } if len(branchesToSync) > 0 { - if gitRepo == nil { - var err error - gitRepo, err = gitrepo.OpenRepository(ctx, repo) - if err != nil { - log.Error("Failed to open repository: %s/%s Error: %v", ownerName, repoName, err) - ctx.JSON(http.StatusInternalServerError, private.HookPostReceiveResult{ - Err: fmt.Sprintf("Failed to open repository: %s/%s Error: %v", ownerName, repoName, err), - }) - return - } + var err error + gitRepo, err = gitrepo.OpenRepository(ctx, repo) + if err != nil { + log.Error("Failed to open repository: %s/%s Error: %v", ownerName, repoName, err) + ctx.JSON(http.StatusInternalServerError, private.HookPostReceiveResult{ + Err: fmt.Sprintf("Failed to open repository: %s/%s Error: %v", ownerName, repoName, err), + }) + return } var ( diff --git a/routers/web/admin/admin.go b/routers/web/admin/admin.go index fe98178ac3..6c778c686c 100644 --- a/routers/web/admin/admin.go +++ b/routers/web/admin/admin.go @@ -158,7 +158,7 @@ func DashboardPost(ctx *context.Context) { switch form.Op { case "sync_repo_branches": go func() { - if err := repo_service.AddAllRepoBranchesToSyncQueue(graceful.GetManager().ShutdownContext(), ctx.Doer.ID); err != nil { + if err := repo_service.AddAllRepoBranchesToSyncQueue(graceful.GetManager().ShutdownContext()); err != nil { log.Error("AddAllRepoBranchesToSyncQueue: %v: %v", ctx.Doer.ID, err) } }() diff --git a/routers/web/auth/oauth.go b/routers/web/auth/oauth.go index 3ad7a4738e..61c580cae3 100644 --- a/routers/web/auth/oauth.go +++ b/routers/web/auth/oauth.go @@ -471,8 +471,9 @@ func AuthorizeOAuth(ctx *context.Context) { return } - // Redirect if user already granted access - if grant != nil { + // Redirect if user already granted access and the application is confidential. + // I.e. always require authorization for public clients as recommended by RFC 6749 Section 10.2 + if app.ConfidentialClient && grant != nil { code, err := grant.GenerateNewAuthorizationCode(ctx, form.RedirectURI, form.CodeChallenge, form.CodeChallengeMethod) if err != nil { handleServerError(ctx, form.State, form.RedirectURI) diff --git a/routers/web/feed/convert.go b/routers/web/feed/convert.go index 743465082e..9ed57ec48c 100644 --- a/routers/web/feed/convert.go +++ b/routers/web/feed/convert.go @@ -287,7 +287,7 @@ func GetFeedType(name string, req *http.Request) (bool, string, string) { } // feedActionsToFeedItems convert gitea's Repo's Releases to feeds Item -func releasesToFeedItems(ctx *context.Context, releases []*repo_model.Release, isReleasesOnly bool) (items []*feeds.Item, err error) { +func releasesToFeedItems(ctx *context.Context, releases []*repo_model.Release) (items []*feeds.Item, err error) { for _, rel := range releases { err := rel.LoadAttributes(ctx) if err != nil { diff --git a/routers/web/feed/release.go b/routers/web/feed/release.go index 273f47e3b4..fb6e3add65 100644 --- a/routers/web/feed/release.go +++ b/routers/web/feed/release.go @@ -42,7 +42,7 @@ func ShowReleaseFeed(ctx *context.Context, repo *repo_model.Repository, isReleas Created: time.Now(), } - feed.Items, err = releasesToFeedItems(ctx, releases, isReleasesOnly) + feed.Items, err = releasesToFeedItems(ctx, releases) if err != nil { ctx.ServerError("releasesToFeedItems", err) return diff --git a/services/auth/source/oauth2/providers.go b/services/auth/source/oauth2/providers.go index 6ed6c184eb..f2c1bb4894 100644 --- a/services/auth/source/oauth2/providers.go +++ b/services/auth/source/oauth2/providers.go @@ -182,7 +182,7 @@ func createProvider(providerName string, source *Source) (goth.Provider, error) } // always set the name if provider is created so we can support multiple setups of 1 provider - if err == nil && provider != nil { + if provider != nil { provider.SetName(providerName) } diff --git a/services/context/base.go b/services/context/base.go index 25ff935055..0259e0d806 100644 --- a/services/context/base.go +++ b/services/context/base.go @@ -234,9 +234,7 @@ func (b *Base) plainTextInternal(skip, status int, bs []byte) { b.Resp.Header().Set("Content-Type", "text/plain;charset=utf-8") b.Resp.Header().Set("X-Content-Type-Options", "nosniff") b.Resp.WriteHeader(status) - if _, err := b.Resp.Write(bs); err != nil { - log.ErrorWithSkip(skip, "plainTextInternal (status=%d): write bytes failed: %v", status, err) - } + _, _ = b.Resp.Write(bs) } // PlainTextBytes renders bytes as plain text diff --git a/services/context/context_response.go b/services/context/context_response.go index 2f2d7b0e1b..f36b834a44 100644 --- a/services/context/context_response.go +++ b/services/context/context_response.go @@ -13,6 +13,7 @@ import ( "path" "strconv" "strings" + "syscall" "time" user_model "code.gitea.io/gitea/models/user" @@ -80,7 +81,7 @@ func (ctx *Context) HTML(status int, name base.TplName) { } err := ctx.Render.HTML(ctx.Resp, status, string(name), ctx.Data, ctx.TemplateContext) - if err == nil { + if err == nil || errors.Is(err, syscall.EPIPE) { return } diff --git a/services/context/repo.go b/services/context/repo.go index 3e30f2ba97..54453cc2d9 100644 --- a/services/context/repo.go +++ b/services/context/repo.go @@ -810,7 +810,7 @@ func (rt RepoRefType) RefTypeIncludesTags() bool { return false } -func getRefNameFromPath(ctx *Base, repo *Repository, path string, isExist func(string) bool) string { +func getRefNameFromPath(repo *Repository, path string, isExist func(string) bool) string { refName := "" parts := strings.Split(path, "/") for i, part := range parts { @@ -846,7 +846,7 @@ func getRefName(ctx *Base, repo *Repository, pathType RepoRefType) string { repo.TreePath = path return repo.Repository.DefaultBranch case RepoRefBranch: - ref := getRefNameFromPath(ctx, repo, path, repo.GitRepo.IsBranchExist) + ref := getRefNameFromPath(repo, path, repo.GitRepo.IsBranchExist) if len(ref) == 0 { // check if ref is HEAD parts := strings.Split(path, "/") @@ -856,7 +856,7 @@ func getRefName(ctx *Base, repo *Repository, pathType RepoRefType) string { } // maybe it's a renamed branch - return getRefNameFromPath(ctx, repo, path, func(s string) bool { + return getRefNameFromPath(repo, path, func(s string) bool { b, exist, err := git_model.FindRenamedBranch(ctx, repo.Repository.ID, s) if err != nil { log.Error("FindRenamedBranch: %v", err) @@ -876,7 +876,7 @@ func getRefName(ctx *Base, repo *Repository, pathType RepoRefType) string { return ref case RepoRefTag: - return getRefNameFromPath(ctx, repo, path, repo.GitRepo.IsTagExist) + return getRefNameFromPath(repo, path, repo.GitRepo.IsTagExist) case RepoRefCommit: parts := strings.Split(path, "/") diff --git a/services/convert/convert.go b/services/convert/convert.go index 55996d8fe3..abcdf917cd 100644 --- a/services/convert/convert.go +++ b/services/convert/convert.go @@ -11,6 +11,7 @@ import ( "strings" "time" + actions_model "code.gitea.io/gitea/models/actions" asymkey_model "code.gitea.io/gitea/models/asymkey" "code.gitea.io/gitea/models/auth" git_model "code.gitea.io/gitea/models/git" @@ -24,6 +25,7 @@ import ( "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/services/gitdiff" @@ -195,6 +197,31 @@ func ToTag(repo *repo_model.Repository, t *git.Tag) *api.Tag { } } +// ToActionTask convert a actions_model.ActionTask to an api.ActionTask +func ToActionTask(ctx context.Context, t *actions_model.ActionTask) (*api.ActionTask, error) { + if err := t.LoadAttributes(ctx); err != nil { + return nil, err + } + + url := strings.TrimSuffix(setting.AppURL, "/") + t.GetRunLink() + + return &api.ActionTask{ + ID: t.ID, + Name: t.Job.Name, + HeadBranch: t.Job.Run.PrettyRef(), + HeadSHA: t.Job.CommitSHA, + RunNumber: t.Job.Run.Index, + Event: t.Job.Run.TriggerEvent, + DisplayTitle: t.Job.Run.Title, + Status: t.Status.String(), + WorkflowID: t.Job.Run.WorkflowID, + URL: url, + CreatedAt: t.Created.AsLocalTime(), + UpdatedAt: t.Updated.AsLocalTime(), + RunStartedAt: t.Started.AsLocalTime(), + }, nil +} + // ToVerification convert a git.Commit.Signature to an api.PayloadCommitVerification func ToVerification(ctx context.Context, c *git.Commit) *api.PayloadCommitVerification { verif := asymkey_model.ParseCommitWithSignature(ctx, c) diff --git a/services/convert/issue.go b/services/convert/issue.go index 54b00cd88e..668affe09a 100644 --- a/services/convert/issue.go +++ b/services/convert/issue.go @@ -211,13 +211,11 @@ func ToLabel(label *issues_model.Label, repo *repo_model.Repository, org *user_m IsArchived: label.IsArchived(), } + labelBelongsToRepo := label.BelongsToRepo() + // calculate URL - if label.BelongsToRepo() && repo != nil { - if repo != nil { - result.URL = fmt.Sprintf("%s/labels/%d", repo.APIURL(), label.ID) - } else { - log.Error("ToLabel did not get repo to calculate url for label with id '%d'", label.ID) - } + if labelBelongsToRepo && repo != nil { + result.URL = fmt.Sprintf("%s/labels/%d", repo.APIURL(), label.ID) } else { // BelongsToOrg if org != nil { result.URL = fmt.Sprintf("%sapi/v1/orgs/%s/labels/%d", setting.AppURL, url.PathEscape(org.Name), label.ID) @@ -226,6 +224,10 @@ func ToLabel(label *issues_model.Label, repo *repo_model.Repository, org *user_m } } + if labelBelongsToRepo && repo == nil { + log.Error("ToLabel did not get repo to calculate url for label with id '%d'", label.ID) + } + return result } diff --git a/services/doctor/storage.go b/services/doctor/storage.go index 787df27549..3f3b562c37 100644 --- a/services/doctor/storage.go +++ b/services/doctor/storage.go @@ -27,7 +27,7 @@ type commonStorageCheckOptions struct { name string } -func commonCheckStorage(ctx context.Context, logger log.Logger, autofix bool, opts *commonStorageCheckOptions) error { +func commonCheckStorage(logger log.Logger, autofix bool, opts *commonStorageCheckOptions) error { totalCount, orphanedCount := 0, 0 totalSize, orphanedSize := int64(0), int64(0) @@ -98,7 +98,7 @@ func checkStorage(opts *checkStorageOptions) func(ctx context.Context, logger lo } if opts.Attachments || opts.All { - if err := commonCheckStorage(ctx, logger, autofix, + if err := commonCheckStorage(logger, autofix, &commonStorageCheckOptions{ storer: storage.Attachments, isOrphaned: func(path string, obj storage.Object, stat fs.FileInfo) (bool, error) { @@ -116,7 +116,7 @@ func checkStorage(opts *checkStorageOptions) func(ctx context.Context, logger lo logger.Info("LFS isn't enabled (skipped)") return nil } - if err := commonCheckStorage(ctx, logger, autofix, + if err := commonCheckStorage(logger, autofix, &commonStorageCheckOptions{ storer: storage.LFS, isOrphaned: func(path string, obj storage.Object, stat fs.FileInfo) (bool, error) { @@ -132,7 +132,7 @@ func checkStorage(opts *checkStorageOptions) func(ctx context.Context, logger lo } if opts.Avatars || opts.All { - if err := commonCheckStorage(ctx, logger, autofix, + if err := commonCheckStorage(logger, autofix, &commonStorageCheckOptions{ storer: storage.Avatars, isOrphaned: func(path string, obj storage.Object, stat fs.FileInfo) (bool, error) { @@ -146,7 +146,7 @@ func checkStorage(opts *checkStorageOptions) func(ctx context.Context, logger lo } if opts.RepoAvatars || opts.All { - if err := commonCheckStorage(ctx, logger, autofix, + if err := commonCheckStorage(logger, autofix, &commonStorageCheckOptions{ storer: storage.RepoAvatars, isOrphaned: func(path string, obj storage.Object, stat fs.FileInfo) (bool, error) { @@ -160,7 +160,7 @@ func checkStorage(opts *checkStorageOptions) func(ctx context.Context, logger lo } if opts.RepoArchives || opts.All { - if err := commonCheckStorage(ctx, logger, autofix, + if err := commonCheckStorage(logger, autofix, &commonStorageCheckOptions{ storer: storage.RepoArchives, isOrphaned: func(path string, obj storage.Object, stat fs.FileInfo) (bool, error) { @@ -182,7 +182,7 @@ func checkStorage(opts *checkStorageOptions) func(ctx context.Context, logger lo logger.Info("Packages isn't enabled (skipped)") return nil } - if err := commonCheckStorage(ctx, logger, autofix, + if err := commonCheckStorage(logger, autofix, &commonStorageCheckOptions{ storer: storage.Packages, isOrphaned: func(path string, obj storage.Object, stat fs.FileInfo) (bool, error) { diff --git a/services/migrations/gitea_uploader.go b/services/migrations/gitea_uploader.go index 9baae6d31d..21a9c53a5c 100644 --- a/services/migrations/gitea_uploader.go +++ b/services/migrations/gitea_uploader.go @@ -982,25 +982,24 @@ func (g *GiteaLocalUploader) Finish() error { } func (g *GiteaLocalUploader) remapUser(source user_model.ExternalUserMigrated, target user_model.ExternalUserRemappable) error { - var userid int64 + var userID int64 var err error if g.sameApp { - userid, err = g.remapLocalUser(source, target) + userID, err = g.remapLocalUser(source) } else { - userid, err = g.remapExternalUser(source, target) + userID, err = g.remapExternalUser(source) } - if err != nil { return err } - if userid > 0 { - return target.RemapExternalUser("", 0, userid) + if userID > 0 { + return target.RemapExternalUser("", 0, userID) } return target.RemapExternalUser(source.GetExternalName(), source.GetExternalID(), g.doer.ID) } -func (g *GiteaLocalUploader) remapLocalUser(source user_model.ExternalUserMigrated, target user_model.ExternalUserRemappable) (int64, error) { +func (g *GiteaLocalUploader) remapLocalUser(source user_model.ExternalUserMigrated) (int64, error) { userid, ok := g.userMap[source.GetExternalID()] if !ok { name, err := user_model.GetUserNameByID(g.ctx, source.GetExternalID()) @@ -1018,7 +1017,7 @@ func (g *GiteaLocalUploader) remapLocalUser(source user_model.ExternalUserMigrat return userid, nil } -func (g *GiteaLocalUploader) remapExternalUser(source user_model.ExternalUserMigrated, target user_model.ExternalUserRemappable) (userid int64, err error) { +func (g *GiteaLocalUploader) remapExternalUser(source user_model.ExternalUserMigrated) (userid int64, err error) { userid, ok := g.userMap[source.GetExternalID()] if !ok { userid, err = user_model.GetUserIDByExternalUserID(g.ctx, g.gitServiceType.Name(), fmt.Sprintf("%d", source.GetExternalID())) diff --git a/services/mirror/mirror.go b/services/mirror/mirror.go index 0270f87039..44218d6fb3 100644 --- a/services/mirror/mirror.go +++ b/services/mirror/mirror.go @@ -90,7 +90,7 @@ func Update(ctx context.Context, pullLimit, pushLimit int) error { pullMirrorsRequested := 0 if pullLimit != 0 { - if err := repo_model.MirrorsIterate(ctx, pullLimit, func(idx int, bean any) error { + if err := repo_model.MirrorsIterate(ctx, pullLimit, func(_ int, bean any) error { if err := handler(bean); err != nil { return err } diff --git a/services/pull/review.go b/services/pull/review.go index cff6f346ae..7a7f140602 100644 --- a/services/pull/review.go +++ b/services/pull/review.go @@ -49,7 +49,7 @@ var ErrSubmitReviewOnClosedPR = errors.New("can't submit review for a closed or // checkInvalidation checks if the line of code comment got changed by another commit. // If the line got changed the comment is going to be invalidated. -func checkInvalidation(ctx context.Context, c *issues_model.Comment, doer *user_model.User, repo *git.Repository, branch string) error { +func checkInvalidation(ctx context.Context, c *issues_model.Comment, repo *git.Repository, branch string) error { // FIXME differentiate between previous and proposed line commit, err := repo.LineBlame(branch, repo.Path, c.TreePath, uint(c.UnsignedLine())) if err != nil && (strings.Contains(err.Error(), "fatal: no such path") || notEnoughLines.MatchString(err.Error())) { @@ -83,7 +83,7 @@ func InvalidateCodeComments(ctx context.Context, prs issues_model.PullRequestLis return fmt.Errorf("find code comments: %v", err) } for _, comment := range codeComments { - if err := checkInvalidation(ctx, comment, doer, repo, branch); err != nil { + if err := checkInvalidation(ctx, comment, repo, branch); err != nil { return err } } diff --git a/services/pull/update.go b/services/pull/update.go index 1de125eb4d..1bba396880 100644 --- a/services/pull/update.go +++ b/services/pull/update.go @@ -39,7 +39,7 @@ func Update(ctx context.Context, pr *issues_model.PullRequest, doer *user_model. AddTestPullRequestTask(ctx, doer, pr.BaseRepo.ID, pr.BaseBranch, false, "", "", 0) }() - return updateHeadByRebaseOnToBase(ctx, pr, doer, message) + return updateHeadByRebaseOnToBase(ctx, pr, doer) } if err := pr.LoadBaseRepo(ctx); err != nil { diff --git a/services/pull/update_rebase.go b/services/pull/update_rebase.go index 8e7bfa0ffd..3e2a7be132 100644 --- a/services/pull/update_rebase.go +++ b/services/pull/update_rebase.go @@ -18,7 +18,7 @@ import ( ) // updateHeadByRebaseOnToBase handles updating a PR's head branch by rebasing it on the PR current base branch -func updateHeadByRebaseOnToBase(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.User, message string) error { +func updateHeadByRebaseOnToBase(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.User) error { // "Clone" base repo and add the cache headers for the head repo and branch mergeCtx, cancel, err := createTemporaryRepoForMerge(ctx, pr, doer, "") if err != nil { diff --git a/services/repository/adopt.go b/services/repository/adopt.go index 31e3e581b3..914cd9047b 100644 --- a/services/repository/adopt.go +++ b/services/repository/adopt.go @@ -80,7 +80,7 @@ func AdoptRepository(ctx context.Context, doer, u *user_model.User, opts CreateR return fmt.Errorf("getRepositoryByID: %w", err) } - if err := adoptRepository(ctx, repoPath, doer, repo, opts.DefaultBranch); err != nil { + if err := adoptRepository(ctx, repoPath, repo, opts.DefaultBranch); err != nil { return fmt.Errorf("createDelegateHooks: %w", err) } @@ -111,7 +111,7 @@ func AdoptRepository(ctx context.Context, doer, u *user_model.User, opts CreateR return repo, nil } -func adoptRepository(ctx context.Context, repoPath string, u *user_model.User, repo *repo_model.Repository, defaultBranch string) (err error) { +func adoptRepository(ctx context.Context, repoPath string, repo *repo_model.Repository, defaultBranch string) (err error) { isExist, err := util.IsExist(repoPath) if err != nil { log.Error("Unable to check if %s exists. Error: %v", repoPath, err) diff --git a/services/repository/branch.go b/services/repository/branch.go index a59ad69717..b34bfa5fd5 100644 --- a/services/repository/branch.go +++ b/services/repository/branch.go @@ -491,7 +491,7 @@ func handlerBranchSync(items ...*BranchSyncOptions) []*BranchSyncOptions { return nil } -func addRepoToBranchSyncQueue(repoID, doerID int64) error { +func addRepoToBranchSyncQueue(repoID int64) error { return branchSyncQueue.Push(&BranchSyncOptions{ RepoID: repoID, }) @@ -507,9 +507,9 @@ func initBranchSyncQueue(ctx context.Context) error { return nil } -func AddAllRepoBranchesToSyncQueue(ctx context.Context, doerID int64) error { +func AddAllRepoBranchesToSyncQueue(ctx context.Context) error { if err := db.Iterate(ctx, builder.Eq{"is_empty": false}, func(ctx context.Context, repo *repo_model.Repository) error { - return addRepoToBranchSyncQueue(repo.ID, doerID) + return addRepoToBranchSyncQueue(repo.ID) }); err != nil { return fmt.Errorf("run sync all branches failed: %v", err) } diff --git a/services/repository/files/update.go b/services/repository/files/update.go index 81a61da5ed..d6025b6ced 100644 --- a/services/repository/files/update.go +++ b/services/repository/files/update.go @@ -211,7 +211,7 @@ func ChangeRepoFiles(ctx context.Context, repo *repo_model.Repository, doer *use } for _, file := range opts.Files { - if err := handleCheckErrors(file, commit, opts, repo); err != nil { + if err := handleCheckErrors(file, commit, opts); err != nil { return nil, err } } @@ -277,7 +277,7 @@ func ChangeRepoFiles(ctx context.Context, repo *repo_model.Repository, doer *use } // handles the check for various issues for ChangeRepoFiles -func handleCheckErrors(file *ChangeRepoFile, commit *git.Commit, opts *ChangeRepoFilesOptions, repo *repo_model.Repository) error { +func handleCheckErrors(file *ChangeRepoFile, commit *git.Commit, opts *ChangeRepoFilesOptions) error { if file.Operation == "update" || file.Operation == "delete" { fromEntry, err := commit.GetTreeEntryByPath(file.Options.fromTreePath) if err != nil { diff --git a/services/user/update_test.go b/services/user/update_test.go index c2ff26a140..fc24a6c212 100644 --- a/services/user/update_test.go +++ b/services/user/update_test.go @@ -35,7 +35,7 @@ func TestUpdateUser(t *testing.T) { Description: optional.Some("description"), AllowGitHook: optional.Some(true), AllowImportLocal: optional.Some(true), - MaxRepoCreation: optional.Some[int](10), + MaxRepoCreation: optional.Some(10), IsRestricted: optional.Some(true), IsActive: optional.Some(false), IsAdmin: optional.Some(true), diff --git a/templates/package/content/npm.tmpl b/templates/package/content/npm.tmpl index c5d9b3f428..1ffbd199e3 100644 --- a/templates/package/content/npm.tmpl +++ b/templates/package/content/npm.tmpl @@ -45,6 +45,15 @@ {{end}} + {{if .PackageDescriptor.Metadata.BundleDependencies}} +

{{ctx.Locale.Tr "packages.npm.dependencies.bundle"}}

+
+ {{range .PackageDescriptor.Metadata.BundleDependencies}} + {{.}} + {{end}} +
+ {{end}} + {{if .PackageDescriptor.Metadata.Keywords}}

{{ctx.Locale.Tr "packages.keywords"}}

diff --git a/templates/repo/issue/card.tmpl b/templates/repo/issue/card.tmpl index 4a0ac050aa..526f6dd5db 100644 --- a/templates/repo/issue/card.tmpl +++ b/templates/repo/issue/card.tmpl @@ -62,13 +62,13 @@
{{if or .Labels .Assignees}} -
-
+
+
{{range .Labels}} {{RenderLabel ctx ctx.Locale .}} {{end}}
-
+
{{range .Assignees}} {{ctx.AvatarUtils.Avatar . 28}} {{end}} diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 0137e05434..e7b140d1d3 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -3924,6 +3924,66 @@ } } }, + "/repos/{owner}/{repo}/actions/tasks": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "repository" + ], + "summary": "List a repository's action tasks", + "operationId": "ListActionTasks", + "parameters": [ + { + "type": "string", + "description": "owner of the repo", + "name": "owner", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "name of the repo", + "name": "repo", + "in": "path", + "required": true + }, + { + "type": "integer", + "description": "page number of results to return (1-based)", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "page size of results, default maximum page size is 50", + "name": "limit", + "in": "query" + } + ], + "responses": { + "200": { + "$ref": "#/responses/TasksList" + }, + "400": { + "$ref": "#/responses/error" + }, + "403": { + "$ref": "#/responses/forbidden" + }, + "404": { + "$ref": "#/responses/notFound" + }, + "409": { + "$ref": "#/responses/conflict" + }, + "422": { + "$ref": "#/responses/validationError" + } + } + } + }, "/repos/{owner}/{repo}/actions/variables": { "get": { "produces": [ @@ -7605,6 +7665,9 @@ "404": { "$ref": "#/responses/error" }, + "422": { + "$ref": "#/responses/validationError" + }, "423": { "$ref": "#/responses/repoArchivedError" } @@ -8231,6 +8294,9 @@ "404": { "$ref": "#/responses/error" }, + "422": { + "$ref": "#/responses/validationError" + }, "423": { "$ref": "#/responses/repoArchivedError" } @@ -18312,6 +18378,89 @@ }, "x-go-package": "code.gitea.io/gitea/modules/structs" }, + "ActionTask": { + "description": "ActionTask represents a ActionTask", + "type": "object", + "properties": { + "created_at": { + "type": "string", + "format": "date-time", + "x-go-name": "CreatedAt" + }, + "display_title": { + "type": "string", + "x-go-name": "DisplayTitle" + }, + "event": { + "type": "string", + "x-go-name": "Event" + }, + "head_branch": { + "type": "string", + "x-go-name": "HeadBranch" + }, + "head_sha": { + "type": "string", + "x-go-name": "HeadSHA" + }, + "id": { + "type": "integer", + "format": "int64", + "x-go-name": "ID" + }, + "name": { + "type": "string", + "x-go-name": "Name" + }, + "run_number": { + "type": "integer", + "format": "int64", + "x-go-name": "RunNumber" + }, + "run_started_at": { + "type": "string", + "format": "date-time", + "x-go-name": "RunStartedAt" + }, + "status": { + "type": "string", + "x-go-name": "Status" + }, + "updated_at": { + "type": "string", + "format": "date-time", + "x-go-name": "UpdatedAt" + }, + "url": { + "type": "string", + "x-go-name": "URL" + }, + "workflow_id": { + "type": "string", + "x-go-name": "WorkflowID" + } + }, + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, + "ActionTaskResponse": { + "description": "ActionTaskResponse returns a ActionTask", + "type": "object", + "properties": { + "total_count": { + "type": "integer", + "format": "int64", + "x-go-name": "TotalCount" + }, + "workflow_runs": { + "type": "array", + "items": { + "$ref": "#/definitions/ActionTask" + }, + "x-go-name": "Entries" + } + }, + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, "ActionVariable": { "description": "ActionVariable return value of the query API", "type": "object", @@ -25895,6 +26044,12 @@ } } }, + "TasksList": { + "description": "TasksList", + "schema": { + "$ref": "#/definitions/ActionTaskResponse" + } + }, "Team": { "description": "Team", "schema": { diff --git a/tests/integration/api_comment_attachment_test.go b/tests/integration/api_comment_attachment_test.go index d4368d51fe..1b3cae91e2 100644 --- a/tests/integration/api_comment_attachment_test.go +++ b/tests/integration/api_comment_attachment_test.go @@ -240,3 +240,31 @@ func TestAPIDeleteCommentAttachment(t *testing.T) { unittest.AssertNotExistsBean(t, &repo_model.Attachment{ID: attachment.ID, CommentID: comment.ID}) } + +func TestAPICreateCommentAttachmentWithUnallowedFile(t *testing.T) { + defer tests.PrepareTestEnv(t)() + + comment := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: 2}) + issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: comment.IssueID}) + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID}) + repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) + + session := loginUser(t, repoOwner.Name) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteIssue) + + filename := "file.bad" + body := &bytes.Buffer{} + + // Setup multi-part. + writer := multipart.NewWriter(body) + _, err := writer.CreateFormFile("attachment", filename) + assert.NoError(t, err) + err = writer.Close() + assert.NoError(t, err) + + req := NewRequestWithBody(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/issues/comments/%d/assets", repoOwner.Name, repo.Name, comment.ID), body). + AddTokenAuth(token). + SetHeader("Content-Type", writer.FormDataContentType()) + + session.MakeRequest(t, req, http.StatusUnprocessableEntity) +} diff --git a/tests/integration/api_issue_attachment_test.go b/tests/integration/api_issue_attachment_test.go index b6a0cca6d5..919dea2e62 100644 --- a/tests/integration/api_issue_attachment_test.go +++ b/tests/integration/api_issue_attachment_test.go @@ -173,6 +173,33 @@ func TestAPICreateIssueAttachmentAutoDate(t *testing.T) { }) } +func TestAPICreateIssueAttachmentWithUnallowedFile(t *testing.T) { + defer tests.PrepareTestEnv(t)() + + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) + issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{RepoID: repo.ID}) + repoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) + + session := loginUser(t, repoOwner.Name) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteIssue) + + filename := "file.bad" + body := &bytes.Buffer{} + + // Setup multi-part. + writer := multipart.NewWriter(body) + _, err := writer.CreateFormFile("attachment", filename) + assert.NoError(t, err) + err = writer.Close() + assert.NoError(t, err) + + req := NewRequestWithBody(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/issues/%d/assets", repoOwner.Name, repo.Name, issue.Index), body). + AddTokenAuth(token) + req.Header.Add("Content-Type", writer.FormDataContentType()) + + session.MakeRequest(t, req, http.StatusUnprocessableEntity) +} + func TestAPIEditIssueAttachment(t *testing.T) { defer tests.PrepareTestEnv(t)() diff --git a/tests/integration/api_issue_test.go b/tests/integration/api_issue_test.go index 849535ff8e..e6fb62a3a9 100644 --- a/tests/integration/api_issue_test.go +++ b/tests/integration/api_issue_test.go @@ -188,6 +188,10 @@ func TestAPIEditIssue(t *testing.T) { issueAfter := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 10}) repoAfter := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issueBefore.RepoID}) + // check comment history + unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{IssueID: issueAfter.ID, OldTitle: issueBefore.Title, NewTitle: title}) + unittest.AssertExistsAndLoadBean(t, &issues_model.ContentHistory{IssueID: issueAfter.ID, ContentText: body, IsFirstCreated: false}) + // check deleted user assert.Equal(t, int64(500), issueAfter.PosterID) assert.NoError(t, issueAfter.LoadAttributes(db.DefaultContext)) diff --git a/tests/integration/api_packages_chef_test.go b/tests/integration/api_packages_chef_test.go index 05545f11a6..6efb2708af 100644 --- a/tests/integration/api_packages_chef_test.go +++ b/tests/integration/api_packages_chef_test.go @@ -169,7 +169,7 @@ nwIDAQAB assert.Nil(t, u) assert.Error(t, err) - signRequest := func(t *testing.T, rw *RequestWrapper, version string) { + signRequest := func(rw *RequestWrapper, version string) { req := rw.Request username := req.Header.Get("X-Ops-Userid") if version != "1.0" && version != "1.3" { @@ -255,7 +255,7 @@ nwIDAQAB t.Run(v, func(t *testing.T) { defer tests.PrintCurrentTest(t)() - signRequest(t, req, v) + signRequest(req, v) u, err = auth.Verify(req.Request, nil, nil, nil) assert.NotNil(t, u) assert.NoError(t, err) diff --git a/tests/integration/api_pull_test.go b/tests/integration/api_pull_test.go index 21417bb77e..4f068502f7 100644 --- a/tests/integration/api_pull_test.go +++ b/tests/integration/api_pull_test.go @@ -223,23 +223,33 @@ func TestAPIEditPull(t *testing.T) { session := loginUser(t, owner10.Name) token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) + title := "create a success pr" req := NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls", owner10.Name, repo10.Name), &api.CreatePullRequestOption{ Head: "develop", Base: "master", - Title: "create a success pr", + Title: title, }).AddTokenAuth(token) - pull := new(api.PullRequest) + apiPull := new(api.PullRequest) resp := MakeRequest(t, req, http.StatusCreated) - DecodeJSON(t, resp, pull) - assert.EqualValues(t, "master", pull.Base.Name) + DecodeJSON(t, resp, apiPull) + assert.EqualValues(t, "master", apiPull.Base.Name) - req = NewRequestWithJSON(t, http.MethodPatch, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d", owner10.Name, repo10.Name, pull.Index), &api.EditPullRequestOption{ + newTitle := "edit a this pr" + newBody := "edited body" + req = NewRequestWithJSON(t, http.MethodPatch, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d", owner10.Name, repo10.Name, apiPull.Index), &api.EditPullRequestOption{ Base: "feature/1", - Title: "edit a this pr", + Title: newTitle, + Body: &newBody, }).AddTokenAuth(token) resp = MakeRequest(t, req, http.StatusCreated) - DecodeJSON(t, resp, pull) - assert.EqualValues(t, "feature/1", pull.Base.Name) + DecodeJSON(t, resp, apiPull) + assert.EqualValues(t, "feature/1", apiPull.Base.Name) + // check comment history + pull := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: apiPull.ID}) + err := pull.LoadIssue(db.DefaultContext) + assert.NoError(t, err) + unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{IssueID: pull.Issue.ID, OldTitle: title, NewTitle: newTitle}) + unittest.AssertExistsAndLoadBean(t, &issues_model.ContentHistory{IssueID: pull.Issue.ID, ContentText: newBody, IsFirstCreated: false}) req = NewRequestWithJSON(t, http.MethodPatch, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d", owner10.Name, repo10.Name, pull.Index), &api.EditPullRequestOption{ Base: "not-exist", diff --git a/tests/integration/api_releases_test.go b/tests/integration/api_releases_test.go index 0a392d0a15..398ad15571 100644 --- a/tests/integration/api_releases_test.go +++ b/tests/integration/api_releases_test.go @@ -77,7 +77,7 @@ func TestAPIListReleases(t *testing.T) { testFilterByLen(true, url.Values{"draft": {"true"}, "pre-release": {"true"}}, 0, "there is no pre-release draft") } -func createNewReleaseUsingAPI(t *testing.T, session *TestSession, token string, owner *user_model.User, repo *repo_model.Repository, name, target, title, desc string) *api.Release { +func createNewReleaseUsingAPI(t *testing.T, token string, owner *user_model.User, repo *repo_model.Repository, name, target, title, desc string) *api.Release { urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/releases", owner.Name, repo.Name) req := NewRequestWithJSON(t, "POST", urlStr, &api.CreateReleaseOption{ TagName: name, @@ -120,7 +120,7 @@ func TestAPICreateAndUpdateRelease(t *testing.T) { target, err := gitRepo.GetTagCommitID("v0.0.1") assert.NoError(t, err) - newRelease := createNewReleaseUsingAPI(t, session, token, owner, repo, "v0.0.1", target, "v0.0.1", "test") + newRelease := createNewReleaseUsingAPI(t, token, owner, repo, "v0.0.1", target, "v0.0.1", "test") urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/releases/%d", owner.Name, repo.Name, newRelease.ID) req := NewRequest(t, "GET", urlStr). @@ -167,7 +167,7 @@ func TestAPICreateReleaseToDefaultBranch(t *testing.T) { session := loginUser(t, owner.LowerName) token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) - createNewReleaseUsingAPI(t, session, token, owner, repo, "v0.0.1", "", "v0.0.1", "test") + createNewReleaseUsingAPI(t, token, owner, repo, "v0.0.1", "", "v0.0.1", "test") } func TestAPICreateReleaseToDefaultBranchOnExistingTag(t *testing.T) { @@ -185,7 +185,7 @@ func TestAPICreateReleaseToDefaultBranchOnExistingTag(t *testing.T) { err = gitRepo.CreateTag("v0.0.1", "master") assert.NoError(t, err) - createNewReleaseUsingAPI(t, session, token, owner, repo, "v0.0.1", "", "v0.0.1", "test") + createNewReleaseUsingAPI(t, token, owner, repo, "v0.0.1", "", "v0.0.1", "test") } func TestAPIGetLatestRelease(t *testing.T) { @@ -237,7 +237,7 @@ func TestAPIDeleteReleaseByTagName(t *testing.T) { session := loginUser(t, owner.LowerName) token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) - createNewReleaseUsingAPI(t, session, token, owner, repo, "release-tag", "", "Release Tag", "test") + createNewReleaseUsingAPI(t, token, owner, repo, "release-tag", "", "Release Tag", "test") // delete release req := NewRequestf(t, http.MethodDelete, fmt.Sprintf("/api/v1/repos/%s/%s/releases/tags/release-tag", owner.Name, repo.Name)). @@ -263,7 +263,7 @@ func TestAPIUploadAssetRelease(t *testing.T) { session := loginUser(t, owner.LowerName) token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) - r := createNewReleaseUsingAPI(t, session, token, owner, repo, "release-tag", "", "Release Tag", "test") + r := createNewReleaseUsingAPI(t, token, owner, repo, "release-tag", "", "Release Tag", "test") filename := "image.png" buff := generateImg() @@ -335,7 +335,7 @@ func TestAPIGetReleaseArchiveDownloadCount(t *testing.T) { name := "ReleaseDownloadCount" - createNewReleaseUsingAPI(t, session, token, owner, repo, name, "", name, "test") + createNewReleaseUsingAPI(t, token, owner, repo, name, "", name, "test") urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/releases/tags/%s", owner.Name, repo.Name, name) diff --git a/tests/integration/api_repo_git_tags_test.go b/tests/integration/api_repo_git_tags_test.go index 937f6a829c..c5883a8058 100644 --- a/tests/integration/api_repo_git_tags_test.go +++ b/tests/integration/api_repo_git_tags_test.go @@ -80,7 +80,7 @@ func TestAPIDeleteTagByName(t *testing.T) { _ = MakeRequest(t, req, http.StatusNoContent) // Make sure that actual releases can't be deleted outright - createNewReleaseUsingAPI(t, session, token, owner, repo, "release-tag", "", "Release Tag", "test") + createNewReleaseUsingAPI(t, token, owner, repo, "release-tag", "", "Release Tag", "test") req = NewRequest(t, http.MethodDelete, fmt.Sprintf("/api/v1/repos/%s/%s/tags/release-tag", owner.Name, repo.Name)). AddTokenAuth(token) diff --git a/tests/integration/api_repo_test.go b/tests/integration/api_repo_test.go index 2fb89cfa6e..a503b201bc 100644 --- a/tests/integration/api_repo_test.go +++ b/tests/integration/api_repo_test.go @@ -13,6 +13,7 @@ import ( "code.gitea.io/gitea/models/db" access_model "code.gitea.io/gitea/models/perm/access" repo_model "code.gitea.io/gitea/models/repo" + unit_model "code.gitea.io/gitea/models/unit" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/setting" @@ -326,6 +327,39 @@ func TestAPIOrgRepos(t *testing.T) { } } +// See issue #28483. Tests to make sure we consider more than just code unit-enabled repositories. +func TestAPIOrgReposWithCodeUnitDisabled(t *testing.T) { + defer tests.PrepareTestEnv(t)() + repo21 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{Name: "repo21"}) + org3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo21.OwnerID}) + + // Disable code repository unit. + var units []unit_model.Type + units = append(units, unit_model.TypeCode) + + if err := repo_service.UpdateRepositoryUnits(db.DefaultContext, repo21, nil, units); err != nil { + assert.Fail(t, "should have been able to delete code repository unit; failed to %v", err) + } + assert.False(t, repo21.UnitEnabled(db.DefaultContext, unit_model.TypeCode)) + + session := loginUser(t, "user2") + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadOrganization) + + req := NewRequestf(t, "GET", "/api/v1/orgs/%s/repos", org3.Name). + AddTokenAuth(token) + + resp := MakeRequest(t, req, http.StatusOK) + var apiRepos []*api.Repository + DecodeJSON(t, resp, &apiRepos) + + var repoNames []string + for _, r := range apiRepos { + repoNames = append(repoNames, r.Name) + } + + assert.Contains(t, repoNames, repo21.Name) +} + func TestAPIGetRepoByIDUnauthorized(t *testing.T) { defer tests.PrepareTestEnv(t)() user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}) @@ -684,7 +718,9 @@ func TestAPIRepoGetReviewers(t *testing.T) { resp := MakeRequest(t, req, http.StatusOK) var reviewers []*api.User DecodeJSON(t, resp, &reviewers) - assert.Len(t, reviewers, 4) + if assert.Len(t, reviewers, 3) { + assert.ElementsMatch(t, []int64{1, 4, 11}, []int64{reviewers[0].ID, reviewers[1].ID, reviewers[2].ID}) + } } func TestAPIRepoGetAssignees(t *testing.T) { diff --git a/tests/integration/git_test.go b/tests/integration/git_test.go index 6ee3be2df2..d19b34a528 100644 --- a/tests/integration/git_test.go +++ b/tests/integration/git_test.go @@ -83,7 +83,7 @@ func testGit(t *testing.T, u *url.URL) { rawTest(t, &httpContext, little, big, littleLFS, bigLFS) mediaTest(t, &httpContext, little, big, littleLFS, bigLFS) - t.Run("CreateAgitFlowPull", doCreateAgitFlowPull(dstPath, &httpContext, "master", "test/head")) + t.Run("CreateAgitFlowPull", doCreateAgitFlowPull(dstPath, &httpContext, "test/head")) t.Run("InternalReferences", doInternalReferences(&httpContext, dstPath)) t.Run("BranchProtectMerge", doBranchProtectPRMerge(&httpContext, dstPath)) t.Run("AutoMerge", doAutoPRMerge(&httpContext, dstPath)) @@ -125,7 +125,7 @@ func testGit(t *testing.T, u *url.URL) { rawTest(t, &sshContext, little, big, littleLFS, bigLFS) mediaTest(t, &sshContext, little, big, littleLFS, bigLFS) - t.Run("CreateAgitFlowPull", doCreateAgitFlowPull(dstPath, &sshContext, "master", "test/head2")) + t.Run("CreateAgitFlowPull", doCreateAgitFlowPull(dstPath, &sshContext, "test/head2")) t.Run("InternalReferences", doInternalReferences(&sshContext, dstPath)) t.Run("BranchProtectMerge", doBranchProtectPRMerge(&sshContext, dstPath)) t.Run("MergeFork", func(t *testing.T) { @@ -333,9 +333,6 @@ func generateCommitWithNewData(size int, repoPath, email, fullName, prefix strin } written += n } - if err != nil { - return "", err - } // Commit // Now here we should explicitly allow lfs filters to run @@ -750,7 +747,7 @@ func doInternalReferences(ctx *APITestContext, dstPath string) func(t *testing.T } } -func doCreateAgitFlowPull(dstPath string, ctx *APITestContext, baseBranch, headBranch string) func(t *testing.T) { +func doCreateAgitFlowPull(dstPath string, ctx *APITestContext, headBranch string) func(t *testing.T) { return func(t *testing.T) { defer tests.PrintCurrentTest(t)() diff --git a/tests/integration/pull_create_test.go b/tests/integration/pull_create_test.go index 338305d950..a1252fa014 100644 --- a/tests/integration/pull_create_test.go +++ b/tests/integration/pull_create_test.go @@ -455,8 +455,6 @@ func TestRecentlyPushed(t *testing.T) { t.Run("unrelated branches are not shown", func(t *testing.T) { defer tests.PrintCurrentTest(t)() - adminUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{IsAdmin: true}) - // Create a new branch with no relation to the default branch. // 1. Create a new Tree object cmd := git.NewCommand(db.DefaultContext, "write-tree") @@ -473,7 +471,7 @@ func TestRecentlyPushed(t *testing.T) { _, _, gitErr = cmd.RunStdString(&git.RunOpts{Dir: repo.RepoPath()}) assert.NoError(t, gitErr) // 4. Sync the git repo to the database - syncErr := repo_service.AddAllRepoBranchesToSyncQueue(graceful.GetManager().ShutdownContext(), adminUser.ID) + syncErr := repo_service.AddAllRepoBranchesToSyncQueue(graceful.GetManager().ShutdownContext()) assert.NoError(t, syncErr) // 5. Add a fresh commit, so that FindRecentlyPushedBranches has // something to find. diff --git a/tests/integration/repo_archive_test.go b/tests/integration/repo_archive_test.go new file mode 100644 index 0000000000..ebc6467378 --- /dev/null +++ b/tests/integration/repo_archive_test.go @@ -0,0 +1,31 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package integration + +import ( + "io" + "net/http" + "testing" + + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/test" + "code.gitea.io/gitea/routers" + "code.gitea.io/gitea/tests" + + "github.com/stretchr/testify/assert" +) + +func TestRepoDownloadArchive(t *testing.T) { + defer tests.PrepareTestEnv(t)() + defer test.MockVariableValue(&setting.EnableGzip, true)() + defer test.MockVariableValue(&testWebRoutes, routers.NormalRoutes())() + + req := NewRequest(t, "GET", "/user2/repo1/archive/master.zip") + req.Header.Set("Accept-Encoding", "gzip") + resp := MakeRequest(t, req, http.StatusOK) + bs, err := io.ReadAll(resp.Body) + assert.NoError(t, err) + assert.Empty(t, resp.Header().Get("Content-Encoding")) + assert.Equal(t, 320, len(bs)) +} diff --git a/tests/integration/repo_badges_test.go b/tests/integration/repo_badges_test.go index dbf6acb098..fda991b8dd 100644 --- a/tests/integration/repo_badges_test.go +++ b/tests/integration/repo_badges_test.go @@ -215,7 +215,7 @@ func TestBadges(t *testing.T) { token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) err := release.CreateNewTag(git.DefaultContext, repo.Owner, repo, "main", "repo-name-2.0", "dash in the tag name") assert.NoError(t, err) - createNewReleaseUsingAPI(t, session, token, repo.Owner, repo, "repo-name-2.0", "main", "dashed release", "dashed release") + createNewReleaseUsingAPI(t, token, repo.Owner, repo, "repo-name-2.0", "main", "dashed release", "dashed release") req := NewRequestf(t, "GET", "/user2/%s/badges/release.svg", repo.Name) resp := MakeRequest(t, req, http.StatusSeeOther) diff --git a/tests/integration/repo_branch_test.go b/tests/integration/repo_branch_test.go index 99d4f16d56..7f18bc7ddf 100644 --- a/tests/integration/repo_branch_test.go +++ b/tests/integration/repo_branch_test.go @@ -16,7 +16,6 @@ import ( git_model "code.gitea.io/gitea/models/git" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" - user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/setting" @@ -170,7 +169,6 @@ func TestCreateBranchInvalidCSRF(t *testing.T) { func TestDatabaseMissingABranch(t *testing.T) { onGiteaRun(t, func(t *testing.T, URL *url.URL) { - adminUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{IsAdmin: true}) session := loginUser(t, "user2") // Create two branches @@ -178,7 +176,7 @@ func TestDatabaseMissingABranch(t *testing.T) { testCreateBranch(t, session, "user2", "repo1", "branch/master", "will-be-missing", http.StatusSeeOther) // Run the repo branch sync, to ensure the db and git agree. - err2 := repo_service.AddAllRepoBranchesToSyncQueue(graceful.GetManager().ShutdownContext(), adminUser.ID) + err2 := repo_service.AddAllRepoBranchesToSyncQueue(graceful.GetManager().ShutdownContext()) assert.NoError(t, err2) // Delete one branch from git only, leaving it in the database @@ -197,7 +195,7 @@ func TestDatabaseMissingABranch(t *testing.T) { assert.GreaterOrEqual(t, firstBranchCount, 3) // Run the repo branch sync again - err2 = repo_service.AddAllRepoBranchesToSyncQueue(graceful.GetManager().ShutdownContext(), adminUser.ID) + err2 = repo_service.AddAllRepoBranchesToSyncQueue(graceful.GetManager().ShutdownContext()) assert.NoError(t, err2) // Verify that loading the repo's branches page works still, and that it diff --git a/tests/integration/repofiles_change_test.go b/tests/integration/repofiles_change_test.go index 49abeb83fb..7633d6915f 100644 --- a/tests/integration/repofiles_change_test.go +++ b/tests/integration/repofiles_change_test.go @@ -78,7 +78,7 @@ func getDeleteRepoFilesOptions(repo *repo_model.Repository) *files_service.Chang } } -func getExpectedFileResponseForRepofilesDelete(u *url.URL) *api.FileResponse { +func getExpectedFileResponseForRepofilesDelete() *api.FileResponse { // Just returns fields that don't change, i.e. fields with commit SHAs and dates can't be determined return &api.FileResponse{ Content: nil, @@ -418,7 +418,7 @@ func testDeleteRepoFiles(t *testing.T, u *url.URL) { t.Run("Delete README.md file", func(t *testing.T) { filesResponse, err := files_service.ChangeRepoFiles(git.DefaultContext, repo, doer, opts) assert.NoError(t, err) - expectedFileResponse := getExpectedFileResponseForRepofilesDelete(u) + expectedFileResponse := getExpectedFileResponseForRepofilesDelete() assert.NotNil(t, filesResponse) assert.Nil(t, filesResponse.Files[0]) assert.EqualValues(t, expectedFileResponse.Commit.Message, filesResponse.Commit.Message) @@ -460,7 +460,7 @@ func testDeleteRepoFilesWithoutBranchNames(t *testing.T, u *url.URL) { t.Run("Delete README.md without Branch Name", func(t *testing.T) { filesResponse, err := files_service.ChangeRepoFiles(git.DefaultContext, repo, doer, opts) assert.NoError(t, err) - expectedFileResponse := getExpectedFileResponseForRepofilesDelete(u) + expectedFileResponse := getExpectedFileResponseForRepofilesDelete() assert.NotNil(t, filesResponse) assert.Nil(t, filesResponse.Files[0]) assert.EqualValues(t, expectedFileResponse.Commit.Message, filesResponse.Commit.Message) diff --git a/web_src/css/features/heatmap.css b/web_src/css/features/heatmap.css index 364754751a..c064590c46 100644 --- a/web_src/css/features/heatmap.css +++ b/web_src/css/features/heatmap.css @@ -31,6 +31,10 @@ padding: 0 5px; } +#user-heatmap .vch__day__square:hover { + outline: 1.5px solid var(--color-text); +} + /* move the "? contributions in the last ? months" text from top to bottom */ #user-heatmap .total-contributions { font-size: 11px; diff --git a/web_src/css/repo.css b/web_src/css/repo.css index bf6bfd464b..a29e9a0986 100644 --- a/web_src/css/repo.css +++ b/web_src/css/repo.css @@ -2368,18 +2368,12 @@ td .commit-summary { display: inline-flex; flex-wrap: wrap; gap: 2.5px; -} - -.labels-list a { - display: flex; - text-decoration: none; + align-items: center; } .labels-list .label { padding: 0 6px; - margin: 0 !important; min-height: 20px; - display: inline-flex !important; line-height: 1.3; /* there is a `font-size: 1.25em` for inside emoji, so here the line-height needs to be larger slightly */ } diff --git a/web_src/css/repo/issue-card.css b/web_src/css/repo/issue-card.css index 609b1b3dbd..390bfb6a01 100644 --- a/web_src/css/repo/issue-card.css +++ b/web_src/css/repo/issue-card.css @@ -23,3 +23,18 @@ .issue-card.sortable-chosen .issue-card-title { cursor: inherit; } + +.issue-card-bottom { + display: flex; + width: 100%; + justify-content: space-between; + gap: 0.25em; +} + +.issue-card-assignees { + display: flex; + align-items: center; + gap: 0.25em; + justify-content: end; + flex-wrap: wrap; +}