loading up the forgejo repo on tangled to test page performance
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge pull request '[gitea] week 2024-51 cherry pick (gitea/main -> forgejo)' (#6271) from earl-warren/wcp/2024-51 into forgejo

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/6271
Reviewed-by: Gusted <gusted@noreply.codeberg.org>

+526 -32
+1
models/actions/run.go
··· 37 37 TriggerUser *user_model.User `xorm:"-"` 38 38 ScheduleID int64 39 39 Ref string `xorm:"index"` // the commit/tag/… that caused the run 40 + IsRefDeleted bool `xorm:"-"` 40 41 CommitSHA string 41 42 IsForkPullRequest bool // If this is triggered by a PR from a forked repository or an untrusted user, we need to check if it is approved and limit permissions when running the workflow. 42 43 NeedApproval bool // may need approval if it's a fork pull request
+2 -2
models/actions/run_job.go
··· 137 137 if err != nil { 138 138 return 0, err 139 139 } 140 - run.Status = aggregateJobStatus(jobs) 140 + run.Status = AggregateJobStatus(jobs) 141 141 if run.Started.IsZero() && run.Status.IsRunning() { 142 142 run.Started = timeutil.TimeStampNow() 143 143 } ··· 152 152 return affected, nil 153 153 } 154 154 155 - func aggregateJobStatus(jobs []*ActionRunJob) Status { 155 + func AggregateJobStatus(jobs []*ActionRunJob) Status { 156 156 allDone := true 157 157 allWaiting := true 158 158 hasFailure := false
+3 -3
models/db/context_committer_test.go
··· 4 4 package db // it's not db_test, because this file is for testing the private type halfCommitter 5 5 6 6 import ( 7 - "fmt" 7 + "errors" 8 8 "testing" 9 9 10 10 "github.com/stretchr/testify/assert" ··· 80 80 testWithCommitter(mockCommitter, func(committer Committer) error { 81 81 defer committer.Close() 82 82 if true { 83 - return fmt.Errorf("error") 83 + return errors.New("error") 84 84 } 85 85 return committer.Commit() 86 86 }) ··· 94 94 testWithCommitter(mockCommitter, func(committer Committer) error { 95 95 committer.Close() 96 96 committer.Commit() 97 - return fmt.Errorf("error") 97 + return errors.New("error") 98 98 }) 99 99 100 100 mockCommitter.Assert(t)
+19
models/fixtures/action_run.yml
··· 414 414 "total_commits": 0 415 415 } 416 416 - 417 + id: 793 418 + title: "job output" 419 + repo_id: 4 420 + owner_id: 1 421 + workflow_id: "test.yaml" 422 + index: 189 423 + trigger_user_id: 1 424 + ref: "refs/heads/master" 425 + commit_sha: "c2d72f548424103f01ee1dc02889c1e2bff816b0" 426 + event: "push" 427 + is_fork_pull_request: 0 428 + status: 1 429 + started: 1683636528 430 + stopped: 1683636626 431 + created: 1683636108 432 + updated: 1683636626 433 + need_approval: 0 434 + approved_by: 0 435 + - 417 436 id: 891 418 437 title: "update actions" 419 438 repo_id: 1
+43
models/fixtures/action_run_job.yml
··· 27 27 started: 1683636528 28 28 stopped: 1683636626 29 29 - 30 + id: 194 31 + run_id: 793 32 + repo_id: 4 33 + owner_id: 1 34 + commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0 35 + is_fork_pull_request: 0 36 + name: job1 (1) 37 + attempt: 1 38 + job_id: job1 39 + task_id: 49 40 + status: 1 41 + started: 1683636528 42 + stopped: 1683636626 43 + - 44 + id: 195 45 + run_id: 793 46 + repo_id: 4 47 + owner_id: 1 48 + commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0 49 + is_fork_pull_request: 0 50 + name: job1 (2) 51 + attempt: 1 52 + job_id: job1 53 + task_id: 50 54 + status: 1 55 + started: 1683636528 56 + stopped: 1683636626 57 + - 58 + id: 196 59 + run_id: 793 60 + repo_id: 4 61 + owner_id: 1 62 + commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0 63 + is_fork_pull_request: 0 64 + name: job2 65 + attempt: 1 66 + job_id: job2 67 + needs: [job1] 68 + task_id: 51 69 + status: 5 70 + started: 1683636528 71 + stopped: 1683636626 72 + - 30 73 id: 292 31 74 run_id: 891 32 75 repo_id: 1
+60
models/fixtures/action_task.yml
··· 57 57 log_length: 707 58 58 log_size: 90179 59 59 log_expired: 0 60 + - 61 + id: 49 62 + job_id: 194 63 + attempt: 1 64 + runner_id: 1 65 + status: 1 # success 66 + started: 1683636528 67 + stopped: 1683636626 68 + repo_id: 4 69 + owner_id: 1 70 + commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0 71 + is_fork_pull_request: 0 72 + token_hash: b8d3962425466b6709b9ac51446f93260c54afe8e7b6d3686e34f991fb8a8953822b0deed86fe41a103f34bc48dbc4784220 73 + token_salt: ffffffffff 74 + token_last_eight: ffffffff 75 + log_filename: artifact-test2/2f/47.log 76 + log_in_storage: 1 77 + log_length: 707 78 + log_size: 90179 79 + log_expired: 0 80 + - 81 + id: 50 82 + job_id: 195 83 + attempt: 1 84 + runner_id: 1 85 + status: 1 # success 86 + started: 1683636528 87 + stopped: 1683636626 88 + repo_id: 4 89 + owner_id: 1 90 + commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0 91 + is_fork_pull_request: 0 92 + token_hash: b8d3962425466b6709b9ac51446f93260c54afe8e7b6d3686e34f991fb8a8953822b0deed86fe41a103f34bc48dbc4784221 93 + token_salt: ffffffffff 94 + token_last_eight: ffffffff 95 + log_filename: artifact-test2/2f/47.log 96 + log_in_storage: 1 97 + log_length: 707 98 + log_size: 90179 99 + log_expired: 0 100 + - 101 + id: 51 102 + job_id: 196 103 + attempt: 1 104 + runner_id: 1 105 + status: 6 # running 106 + started: 1683636528 107 + stopped: 1683636626 108 + repo_id: 4 109 + owner_id: 1 110 + commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0 111 + is_fork_pull_request: 0 112 + token_hash: b8d3962425466b6709b9ac51446f93260c54afe8e7b6d3686e34f991fb8a8953822b0deed86fe41a103f34bc48dbc4784222 113 + token_salt: ffffffffff 114 + token_last_eight: ffffffff 115 + log_filename: artifact-test2/2f/47.log 116 + log_in_storage: 1 117 + log_length: 707 118 + log_size: 90179 119 + log_expired: 0
+20
models/fixtures/action_task_output.yml
··· 1 + - 2 + id: 1 3 + task_id: 49 4 + output_key: output_a 5 + output_value: abc 6 + - 7 + id: 2 8 + task_id: 49 9 + output_key: output_b 10 + output_value: '' 11 + - 12 + id: 3 13 + task_id: 50 14 + output_key: output_a 15 + output_value: '' 16 + - 17 + id: 4 18 + task_id: 50 19 + output_key: output_b 20 + output_value: bbb
+16 -2
models/git/branch.go
··· 11 11 "code.gitea.io/gitea/models/db" 12 12 repo_model "code.gitea.io/gitea/models/repo" 13 13 user_model "code.gitea.io/gitea/models/user" 14 + "code.gitea.io/gitea/modules/container" 14 15 "code.gitea.io/gitea/modules/git" 15 16 "code.gitea.io/gitea/modules/log" 16 17 "code.gitea.io/gitea/modules/timeutil" ··· 162 163 return &branch, nil 163 164 } 164 165 165 - func GetBranches(ctx context.Context, repoID int64, branchNames []string) ([]*Branch, error) { 166 + func GetBranches(ctx context.Context, repoID int64, branchNames []string, includeDeleted bool) ([]*Branch, error) { 166 167 branches := make([]*Branch, 0, len(branchNames)) 167 - return branches, db.GetEngine(ctx).Where("repo_id=?", repoID).In("name", branchNames).Find(&branches) 168 + 169 + sess := db.GetEngine(ctx).Where("repo_id=?", repoID).In("name", branchNames) 170 + if !includeDeleted { 171 + sess.And("is_deleted=?", false) 172 + } 173 + return branches, sess.Find(&branches) 174 + } 175 + 176 + func BranchesToNamesSet(branches []*Branch) container.Set[string] { 177 + names := make(container.Set[string], len(branches)) 178 + for _, branch := range branches { 179 + names.Add(branch.Name) 180 + } 181 + return names 168 182 } 169 183 170 184 func AddBranches(ctx context.Context, branches []*Branch) error {
+5 -1
modules/lfs/http_client.go
··· 72 72 73 73 url := fmt.Sprintf("%s/objects/batch", c.endpoint) 74 74 75 - request := &BatchRequest{operation, c.transferNames(), nil, objects} 75 + // `ref` is an "optional object describing the server ref that the objects belong to" 76 + // but some (incorrect) lfs servers require it, so maybe adding an empty ref here doesn't break the correct ones. 77 + // https://github.com/git-lfs/git-lfs/blob/a32a02b44bf8a511aa14f047627c49e1a7fd5021/docs/api/batch.md?plain=1#L37 78 + request := &BatchRequest{operation, c.transferNames(), &Reference{}, objects} 76 79 payload := new(bytes.Buffer) 77 80 err := json.NewEncoder(payload).Encode(request) 78 81 if err != nil { ··· 236 239 req.Header.Set(key, value) 237 240 } 238 241 req.Header.Set("Accept", AcceptHeader) 242 + req.Header.Set("User-Agent", UserAgentHeader) 239 243 240 244 return req, nil 241 245 }
+5 -1
modules/lfs/shared.go
··· 14 14 const ( 15 15 // MediaType contains the media type for LFS server requests 16 16 MediaType = "application/vnd.git-lfs+json" 17 - // Some LFS servers offer content with other types, so fallback to '*/*' if application/vnd.git-lfs+json cannot be served 17 + // AcceptHeader Some LFS servers offer content with other types, so fallback to '*/*' if application/vnd.git-lfs+json cannot be served 18 18 AcceptHeader = "application/vnd.git-lfs+json;q=0.9, */*;q=0.8" 19 + // UserAgentHeader Add User-Agent for gitea's self-implemented lfs client, 20 + // and the version is consistent with the latest version of git lfs can be avoided incompatibilities. 21 + // Some lfs servers will check this 22 + UserAgentHeader = "git-lfs/3.6.0 (Forgejo)" 19 23 ) 20 24 21 25 // BatchRequest contains multiple requests processed in one batch operation.
+10
modules/structs/repo.go
··· 290 290 OldRefName string `json:"old_ref_name" binding:"GitRefName;MaxSize(100)"` 291 291 } 292 292 293 + // UpdateBranchRepoOption options when updating a branch in a repository 294 + // swagger:model 295 + type UpdateBranchRepoOption struct { 296 + // New branch name 297 + // 298 + // required: true 299 + // unique: true 300 + Name string `json:"name" binding:"Required;GitRefName;MaxSize(100)"` 301 + } 302 + 293 303 // TransferRepoOption options when transfer a repository's ownership 294 304 // swagger:model 295 305 type TransferRepoOption struct {
+4
release-notes/6271.md
··· 1 + fix: [commit](https://codeberg.org/forgejo/forgejo/commit/96a7f0a3f065c5db8fdf352c93c8367e24d259de) Fix missing outputs for jobs with matrix 2 + fix: [commit](https://codeberg.org/forgejo/forgejo/commit/2b5c69c451a684b20119e2521dc23734c7869241) Detect whether action view branch was deleted 3 + feat: [commit](https://codeberg.org/forgejo/forgejo/commit/b0d6a7f07bff836190a8e87fe5645d5557893e32) Implement update branch API 4 + fix: [commit](https://codeberg.org/forgejo/forgejo/commit/bf934c96c92d643678ac7a18697b6563bc9d20a5) Add standard-compliant route to serve outdated R packages
+16
routers/api/actions/runner/main_test.go
··· 1 + // Copyright 2024 The Gitea Authors. All rights reserved. 2 + // SPDX-License-Identifier: MIT 3 + 4 + package runner 5 + 6 + import ( 7 + "testing" 8 + 9 + "code.gitea.io/gitea/models/unittest" 10 + 11 + _ "code.gitea.io/gitea/models/forgefed" 12 + ) 13 + 14 + func TestMain(m *testing.M) { 15 + unittest.MainTest(m) 16 + }
+44 -16
routers/api/actions/runner/utils.go
··· 162 162 return nil, fmt.Errorf("FindRunJobs: %w", err) 163 163 } 164 164 165 - ret := make(map[string]*runnerv1.TaskNeed, len(needs)) 165 + jobIDJobs := make(map[string][]*actions_model.ActionRunJob) 166 166 for _, job := range jobs { 167 - if !needs.Contains(job.JobID) { 167 + jobIDJobs[job.JobID] = append(jobIDJobs[job.JobID], job) 168 + } 169 + 170 + ret := make(map[string]*runnerv1.TaskNeed, len(needs)) 171 + for jobID, jobsWithSameID := range jobIDJobs { 172 + if !needs.Contains(jobID) { 168 173 continue 169 174 } 170 - if job.TaskID == 0 || !job.Status.IsDone() { 171 - // it shouldn't happen, or the job has been rerun 172 - continue 173 - } 174 - outputs := make(map[string]string) 175 - got, err := actions_model.FindTaskOutputByTaskID(ctx, job.TaskID) 176 - if err != nil { 177 - return nil, fmt.Errorf("FindTaskOutputByTaskID: %w", err) 178 - } 179 - for _, v := range got { 180 - outputs[v.OutputKey] = v.OutputValue 175 + var jobOutputs map[string]string 176 + for _, job := range jobsWithSameID { 177 + if job.TaskID == 0 || !job.Status.IsDone() { 178 + // it shouldn't happen, or the job has been rerun 179 + continue 180 + } 181 + got, err := actions_model.FindTaskOutputByTaskID(ctx, job.TaskID) 182 + if err != nil { 183 + return nil, fmt.Errorf("FindTaskOutputByTaskID: %w", err) 184 + } 185 + outputs := make(map[string]string, len(got)) 186 + for _, v := range got { 187 + outputs[v.OutputKey] = v.OutputValue 188 + } 189 + if len(jobOutputs) == 0 { 190 + jobOutputs = outputs 191 + } else { 192 + jobOutputs = mergeTwoOutputs(outputs, jobOutputs) 193 + } 181 194 } 182 - ret[job.JobID] = &runnerv1.TaskNeed{ 183 - Outputs: outputs, 184 - Result: runnerv1.Result(job.Status), 195 + ret[jobID] = &runnerv1.TaskNeed{ 196 + Outputs: jobOutputs, 197 + Result: runnerv1.Result(actions_model.AggregateJobStatus(jobsWithSameID)), 185 198 } 186 199 } 187 200 188 201 return ret, nil 189 202 } 203 + 204 + // mergeTwoOutputs merges two outputs from two different ActionRunJobs 205 + // Values with the same output name may be overridden. The user should ensure the output names are unique. 206 + // See https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#using-job-outputs-in-a-matrix-job 207 + func mergeTwoOutputs(o1, o2 map[string]string) map[string]string { 208 + ret := make(map[string]string, len(o1)) 209 + for k1, v1 := range o1 { 210 + if len(v1) > 0 { 211 + ret[k1] = v1 212 + } else { 213 + ret[k1] = o2[k1] 214 + } 215 + } 216 + return ret 217 + }
+29
routers/api/actions/runner/utils_test.go
··· 1 + // Copyright 2024 The Gitea Authors. All rights reserved. 2 + // SPDX-License-Identifier: MIT 3 + 4 + package runner 5 + 6 + import ( 7 + "context" 8 + "testing" 9 + 10 + actions_model "code.gitea.io/gitea/models/actions" 11 + "code.gitea.io/gitea/models/unittest" 12 + 13 + "github.com/stretchr/testify/assert" 14 + "github.com/stretchr/testify/require" 15 + ) 16 + 17 + func Test_findTaskNeeds(t *testing.T) { 18 + require.NoError(t, unittest.PrepareTestDatabase()) 19 + 20 + task := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionTask{ID: 51}) 21 + 22 + ret, err := findTaskNeeds(context.Background(), task) 23 + require.NoError(t, err) 24 + assert.Len(t, ret, 1) 25 + assert.Contains(t, ret, "job1") 26 + assert.Len(t, ret["job1"].Outputs, 2) 27 + assert.Equal(t, "abc", ret["job1"].Outputs["output_a"]) 28 + assert.Equal(t, "bbb", ret["job1"].Outputs["output_b"]) 29 + }
+1
routers/api/packages/api.go
··· 337 337 r.Get("/PACKAGES", cran.EnumerateSourcePackages) 338 338 r.Get("/PACKAGES{format}", cran.EnumerateSourcePackages) 339 339 r.Get("/{filename}", cran.DownloadSourcePackageFile) 340 + r.Get("/Archive/{packagename}/{filename}", cran.DownloadSourcePackageFile) 340 341 }) 341 342 r.Put("", reqPackageAccess(perm.AccessModeWrite), enforcePackagesQuota(), cran.UploadSourcePackageFile) 342 343 })
+1
routers/api/v1/api.go
··· 1153 1153 m.Get("/*", repo.GetBranch) 1154 1154 m.Delete("/*", reqToken(), reqRepoWriter(unit.TypeCode), mustNotBeArchived, repo.DeleteBranch) 1155 1155 m.Post("", reqToken(), reqRepoWriter(unit.TypeCode), mustNotBeArchived, bind(api.CreateBranchRepoOption{}), context.EnforceQuotaAPI(quota_model.LimitSubjectSizeGitAll, context.QuotaTargetRepo), repo.CreateBranch) 1156 + m.Patch("/*", reqToken(), reqRepoWriter(unit.TypeCode), mustNotBeArchived, bind(api.UpdateBranchRepoOption{}), repo.UpdateBranch) 1156 1157 }, context.ReferencesGitRepo(), reqRepoReader(unit.TypeCode)) 1157 1158 m.Group("/branch_protections", func() { 1158 1159 m.Get("", repo.ListBranchProtections)
+71
routers/api/v1/repo/branch.go
··· 393 393 ctx.JSON(http.StatusOK, apiBranches) 394 394 } 395 395 396 + // UpdateBranch updates a repository's branch. 397 + func UpdateBranch(ctx *context.APIContext) { 398 + // swagger:operation PATCH /repos/{owner}/{repo}/branches/{branch} repository repoUpdateBranch 399 + // --- 400 + // summary: Update a branch 401 + // consumes: 402 + // - application/json 403 + // produces: 404 + // - application/json 405 + // parameters: 406 + // - name: owner 407 + // in: path 408 + // description: owner of the repo 409 + // type: string 410 + // required: true 411 + // - name: repo 412 + // in: path 413 + // description: name of the repo 414 + // type: string 415 + // required: true 416 + // - name: branch 417 + // in: path 418 + // description: name of the branch 419 + // type: string 420 + // required: true 421 + // - name: body 422 + // in: body 423 + // schema: 424 + // "$ref": "#/definitions/UpdateBranchRepoOption" 425 + // responses: 426 + // "204": 427 + // "$ref": "#/responses/empty" 428 + // "403": 429 + // "$ref": "#/responses/forbidden" 430 + // "404": 431 + // "$ref": "#/responses/notFound" 432 + // "422": 433 + // "$ref": "#/responses/validationError" 434 + 435 + opt := web.GetForm(ctx).(*api.UpdateBranchRepoOption) 436 + 437 + oldName := ctx.Params("*") 438 + repo := ctx.Repo.Repository 439 + 440 + if repo.IsEmpty { 441 + ctx.Error(http.StatusNotFound, "", "Git Repository is empty.") 442 + return 443 + } 444 + 445 + if repo.IsMirror { 446 + ctx.Error(http.StatusForbidden, "", "Git Repository is a mirror.") 447 + return 448 + } 449 + 450 + msg, err := repo_service.RenameBranch(ctx, repo, ctx.Doer, ctx.Repo.GitRepo, oldName, opt.Name) 451 + if err != nil { 452 + ctx.Error(http.StatusInternalServerError, "RenameBranch", err) 453 + return 454 + } 455 + if msg == "target_exist" { 456 + ctx.Error(http.StatusUnprocessableEntity, "", "Cannot rename a branch using the same name or rename to a branch that already exists.") 457 + return 458 + } 459 + if msg == "from_not_exist" { 460 + ctx.Error(http.StatusNotFound, "", "Branch doesn't exist.") 461 + return 462 + } 463 + 464 + ctx.Status(http.StatusNoContent) 465 + } 466 + 396 467 // GetBranchProtection gets a branch protection 397 468 func GetBranchProtection(ctx *context.APIContext) { 398 469 // swagger:operation GET /repos/{owner}/{repo}/branch_protections/{name} repository repoGetBranchProtection
+2
routers/api/v1/swagger/options.go
··· 101 101 // in:body 102 102 EditRepoOption api.EditRepoOption 103 103 // in:body 104 + UpdateBranchRepoOption api.UpdateBranchRepoOption 105 + // in:body 104 106 TransferRepoOption api.TransferRepoOption 105 107 // in:body 106 108 CreateForkOption api.CreateForkOption
+34
routers/web/repo/actions/actions.go
··· 12 12 13 13 actions_model "code.gitea.io/gitea/models/actions" 14 14 "code.gitea.io/gitea/models/db" 15 + git_model "code.gitea.io/gitea/models/git" 15 16 "code.gitea.io/gitea/models/unit" 16 17 "code.gitea.io/gitea/modules/actions" 17 18 "code.gitea.io/gitea/modules/base" 18 19 "code.gitea.io/gitea/modules/container" 19 20 "code.gitea.io/gitea/modules/git" 21 + "code.gitea.io/gitea/modules/log" 20 22 "code.gitea.io/gitea/modules/optional" 21 23 "code.gitea.io/gitea/modules/setting" 22 24 "code.gitea.io/gitea/modules/util" ··· 222 224 return 223 225 } 224 226 227 + if err := loadIsRefDeleted(ctx, runs); err != nil { 228 + log.Error("LoadIsRefDeleted", err) 229 + } 230 + 225 231 ctx.Data["Runs"] = runs 226 232 227 233 ctx.Data["Repo"] = ctx.Repo ··· 245 251 246 252 ctx.HTML(http.StatusOK, tplListActions) 247 253 } 254 + 255 + // loadIsRefDeleted loads the IsRefDeleted field for each run in the list. 256 + // TODO: move this function to models/actions/run_list.go but now it will result in a circular import. 257 + func loadIsRefDeleted(ctx *context.Context, runs actions_model.RunList) error { 258 + branches := make(container.Set[string], len(runs)) 259 + for _, run := range runs { 260 + refName := git.RefName(run.Ref) 261 + if refName.IsBranch() { 262 + branches.Add(refName.ShortName()) 263 + } 264 + } 265 + if len(branches) == 0 { 266 + return nil 267 + } 268 + 269 + branchInfos, err := git_model.GetBranches(ctx, ctx.Repo.Repository.ID, branches.Values(), false) 270 + if err != nil { 271 + return err 272 + } 273 + branchSet := git_model.BranchesToNamesSet(branchInfos) 274 + for _, run := range runs { 275 + refName := git.RefName(run.Ref) 276 + if refName.IsBranch() && !branchSet.Contains(run.Ref) { 277 + run.IsRefDeleted = true 278 + } 279 + } 280 + return nil 281 + }
+16 -2
routers/web/repo/actions/view.go
··· 20 20 21 21 actions_model "code.gitea.io/gitea/models/actions" 22 22 "code.gitea.io/gitea/models/db" 23 + git_model "code.gitea.io/gitea/models/git" 23 24 repo_model "code.gitea.io/gitea/models/repo" 24 25 "code.gitea.io/gitea/models/unit" 25 26 "code.gitea.io/gitea/modules/actions" 26 27 "code.gitea.io/gitea/modules/base" 28 + "code.gitea.io/gitea/modules/git" 29 + "code.gitea.io/gitea/modules/log" 27 30 "code.gitea.io/gitea/modules/setting" 28 31 "code.gitea.io/gitea/modules/storage" 29 32 "code.gitea.io/gitea/modules/templates" ··· 157 160 } 158 161 159 162 type ViewBranch struct { 160 - Name string `json:"name"` 161 - Link string `json:"link"` 163 + Name string `json:"name"` 164 + Link string `json:"link"` 165 + IsDeleted bool `json:"isDeleted"` 162 166 } 163 167 164 168 type ViewJobStep struct { ··· 227 231 Name: run.PrettyRef(), 228 232 Link: run.RefLink(), 229 233 } 234 + refName := git.RefName(run.Ref) 235 + if refName.IsBranch() { 236 + b, err := git_model.GetBranch(ctx, ctx.Repo.Repository.ID, refName.ShortName()) 237 + if err != nil && !git_model.IsErrBranchNotExist(err) { 238 + log.Error("GetBranch: %v", err) 239 + } else if git_model.IsErrBranchNotExist(err) || (b != nil && b.IsDeleted) { 240 + branch.IsDeleted = true 241 + } 242 + } 243 + 230 244 resp.State.Run.Commit = ViewCommit{ 231 245 LocaleCommit: ctx.Locale.TrString("actions.runs.commit"), 232 246 LocalePushedBy: ctx.Locale.TrString("actions.runs.pushed_by"),
+1 -1
services/repository/branch.go
··· 254 254 } 255 255 256 256 return db.WithTx(ctx, func(ctx context.Context) error { 257 - branches, err := git_model.GetBranches(ctx, repoID, branchNames) 257 + branches, err := git_model.GetBranches(ctx, repoID, branchNames, true) 258 258 if err != nil { 259 259 return fmt.Errorf("git_model.GetBranches: %v", err) 260 260 }
+3 -3
templates/repo/actions/runs_list.tmpl
··· 27 27 </div> 28 28 </div> 29 29 <div class="flex-item-trailing"> 30 - {{if .RefLink}} 31 - <a class="ui label run-list-ref gt-ellipsis" href="{{.RefLink}}">{{.PrettyRef}}</a> 30 + {{if .IsRefDeleted}} 31 + <span class="ui label run-list-ref gt-ellipsis tw-line-through" data-tooltip-content="{{.PrettyRef}}">{{.PrettyRef}}</span> 32 32 {{else}} 33 - <span class="ui label run-list-ref gt-ellipsis">{{.PrettyRef}}</span> 33 + <a class="ui label run-list-ref gt-ellipsis" href="{{.RefLink}}" data-tooltip-content="{{.PrettyRef}}">{{.PrettyRef}}</a> 34 34 {{end}} 35 35 <div class="run-list-item-right"> 36 36 <div class="run-list-meta">{{svg "octicon-calendar" 16}}{{DateUtils.TimeSince .Updated}}</div>
+73
templates/swagger/v1_json.tmpl
··· 5820 5820 "$ref": "#/responses/repoArchivedError" 5821 5821 } 5822 5822 } 5823 + }, 5824 + "patch": { 5825 + "consumes": [ 5826 + "application/json" 5827 + ], 5828 + "produces": [ 5829 + "application/json" 5830 + ], 5831 + "tags": [ 5832 + "repository" 5833 + ], 5834 + "summary": "Update a branch", 5835 + "operationId": "repoUpdateBranch", 5836 + "parameters": [ 5837 + { 5838 + "type": "string", 5839 + "description": "owner of the repo", 5840 + "name": "owner", 5841 + "in": "path", 5842 + "required": true 5843 + }, 5844 + { 5845 + "type": "string", 5846 + "description": "name of the repo", 5847 + "name": "repo", 5848 + "in": "path", 5849 + "required": true 5850 + }, 5851 + { 5852 + "type": "string", 5853 + "description": "name of the branch", 5854 + "name": "branch", 5855 + "in": "path", 5856 + "required": true 5857 + }, 5858 + { 5859 + "name": "body", 5860 + "in": "body", 5861 + "schema": { 5862 + "$ref": "#/definitions/UpdateBranchRepoOption" 5863 + } 5864 + } 5865 + ], 5866 + "responses": { 5867 + "204": { 5868 + "$ref": "#/responses/empty" 5869 + }, 5870 + "403": { 5871 + "$ref": "#/responses/forbidden" 5872 + }, 5873 + "404": { 5874 + "$ref": "#/responses/notFound" 5875 + }, 5876 + "422": { 5877 + "$ref": "#/responses/validationError" 5878 + } 5879 + } 5823 5880 } 5824 5881 }, 5825 5882 "/repos/{owner}/{repo}/collaborators": { ··· 27298 27355 "format": "int64" 27299 27356 }, 27300 27357 "x-go-name": "TeamIDs" 27358 + } 27359 + }, 27360 + "x-go-package": "code.gitea.io/gitea/modules/structs" 27361 + }, 27362 + "UpdateBranchRepoOption": { 27363 + "description": "UpdateBranchRepoOption options when updating a branch in a repository", 27364 + "type": "object", 27365 + "required": [ 27366 + "name" 27367 + ], 27368 + "properties": { 27369 + "name": { 27370 + "description": "New branch name", 27371 + "type": "string", 27372 + "uniqueItems": true, 27373 + "x-go-name": "Name" 27301 27374 } 27302 27375 }, 27303 27376 "x-go-package": "code.gitea.io/gitea/modules/structs"
+32
tests/integration/api_branch_test.go
··· 5 5 6 6 import ( 7 7 "net/http" 8 + "net/http/httptest" 8 9 "net/url" 9 10 "testing" 10 11 ··· 185 186 } 186 187 187 188 return resp.Result().StatusCode == status 189 + } 190 + 191 + func TestAPIUpdateBranch(t *testing.T) { 192 + onGiteaRun(t, func(t *testing.T, _ *url.URL) { 193 + t.Run("UpdateBranchWithEmptyRepo", func(t *testing.T) { 194 + testAPIUpdateBranch(t, "user10", "repo6", "master", "test", http.StatusNotFound) 195 + }) 196 + t.Run("UpdateBranchWithSameBranchNames", func(t *testing.T) { 197 + resp := testAPIUpdateBranch(t, "user2", "repo1", "master", "master", http.StatusUnprocessableEntity) 198 + assert.Contains(t, resp.Body.String(), "Cannot rename a branch using the same name or rename to a branch that already exists.") 199 + }) 200 + t.Run("UpdateBranchThatAlreadyExists", func(t *testing.T) { 201 + resp := testAPIUpdateBranch(t, "user2", "repo1", "master", "branch2", http.StatusUnprocessableEntity) 202 + assert.Contains(t, resp.Body.String(), "Cannot rename a branch using the same name or rename to a branch that already exists.") 203 + }) 204 + t.Run("UpdateBranchWithNonExistentBranch", func(t *testing.T) { 205 + resp := testAPIUpdateBranch(t, "user2", "repo1", "i-dont-exist", "new-branch-name", http.StatusNotFound) 206 + assert.Contains(t, resp.Body.String(), "Branch doesn't exist.") 207 + }) 208 + t.Run("RenameBranchNormalScenario", func(t *testing.T) { 209 + testAPIUpdateBranch(t, "user2", "repo1", "branch2", "new-branch-name", http.StatusNoContent) 210 + }) 211 + }) 212 + } 213 + 214 + func testAPIUpdateBranch(t *testing.T, ownerName, repoName, from, to string, expectedHTTPStatus int) *httptest.ResponseRecorder { 215 + token := getUserToken(t, ownerName, auth_model.AccessTokenScopeWriteRepository) 216 + req := NewRequestWithJSON(t, "PATCH", "api/v1/repos/"+ownerName+"/"+repoName+"/branches/"+from, &api.UpdateBranchRepoOption{ 217 + Name: to, 218 + }).AddTokenAuth(token) 219 + return MakeRequest(t, req, expectedHTTPStatus) 188 220 } 189 221 190 222 func TestAPIBranchProtection(t *testing.T) {
+8
tests/integration/api_packages_cran_test.go
··· 116 116 MakeRequest(t, req, http.StatusOK) 117 117 }) 118 118 119 + t.Run("DownloadArchived", func(t *testing.T) { 120 + defer tests.PrintCurrentTest(t)() 121 + 122 + req := NewRequest(t, "GET", fmt.Sprintf("%s/src/contrib/Archive/%s/%s_%s.tar.gz", url, packageName, packageName, packageVersion)). 123 + AddBasicAuth(user.Name) 124 + MakeRequest(t, req, http.StatusOK) 125 + }) 126 + 119 127 t.Run("Enumerate", func(t *testing.T) { 120 128 defer tests.PrintCurrentTest(t)() 121 129
+5
tests/integration/integration_test.go
··· 166 166 os.Unsetenv("GIT_COMMITTER_EMAIL") 167 167 os.Unsetenv("GIT_COMMITTER_DATE") 168 168 169 + // Avoid loading the default system config. On MacOS, this config 170 + // sets the osxkeychain credential helper, which will cause tests 171 + // to freeze with a dialog. 172 + os.Setenv("GIT_CONFIG_NOSYSTEM", "true") 173 + 169 174 err := unittest.InitFixtures( 170 175 unittest.FixturesOptions{ 171 176 Dir: filepath.Join(filepath.Dir(setting.AppPath), "models/fixtures/"),
+2 -1
web_src/js/components/RepoActionView.vue
··· 444 444 {{ run.commit.localePushedBy }} 445 445 <a class="muted" :href="run.commit.pusher.link">{{ run.commit.pusher.displayName }}</a> 446 446 <span class="ui label tw-max-w-full" v-if="run.commit.shortSHA"> 447 - <a class="gt-ellipsis" :href="run.commit.branch.link">{{ run.commit.branch.name }}</a> 447 + <span v-if="run.commit.branch.isDeleted" class="gt-ellipsis tw-line-through" :data-tooltip-content="run.commit.branch.name">{{ run.commit.branch.name }}</span> 448 + <a v-else class="gt-ellipsis" :href="run.commit.branch.link" :data-tooltip-content="run.commit.branch.name">{{ run.commit.branch.name }}</a> 448 449 </span> 449 450 </div> 450 451 <div class="action-summary">