mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2024-12-21 12:44:49 -05:00
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>
This commit is contained in:
commit
380e266440
28 changed files with 526 additions and 32 deletions
|
@ -37,6 +37,7 @@ type ActionRun struct {
|
||||||
TriggerUser *user_model.User `xorm:"-"`
|
TriggerUser *user_model.User `xorm:"-"`
|
||||||
ScheduleID int64
|
ScheduleID int64
|
||||||
Ref string `xorm:"index"` // the commit/tag/… that caused the run
|
Ref string `xorm:"index"` // the commit/tag/… that caused the run
|
||||||
|
IsRefDeleted bool `xorm:"-"`
|
||||||
CommitSHA string
|
CommitSHA string
|
||||||
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.
|
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.
|
||||||
NeedApproval bool // may need approval if it's a fork pull request
|
NeedApproval bool // may need approval if it's a fork pull request
|
||||||
|
|
|
@ -137,7 +137,7 @@ func UpdateRunJob(ctx context.Context, job *ActionRunJob, cond builder.Cond, col
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
run.Status = aggregateJobStatus(jobs)
|
run.Status = AggregateJobStatus(jobs)
|
||||||
if run.Started.IsZero() && run.Status.IsRunning() {
|
if run.Started.IsZero() && run.Status.IsRunning() {
|
||||||
run.Started = timeutil.TimeStampNow()
|
run.Started = timeutil.TimeStampNow()
|
||||||
}
|
}
|
||||||
|
@ -152,7 +152,7 @@ func UpdateRunJob(ctx context.Context, job *ActionRunJob, cond builder.Cond, col
|
||||||
return affected, nil
|
return affected, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func aggregateJobStatus(jobs []*ActionRunJob) Status {
|
func AggregateJobStatus(jobs []*ActionRunJob) Status {
|
||||||
allDone := true
|
allDone := true
|
||||||
allWaiting := true
|
allWaiting := true
|
||||||
hasFailure := false
|
hasFailure := false
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
package db // it's not db_test, because this file is for testing the private type halfCommitter
|
package db // it's not db_test, because this file is for testing the private type halfCommitter
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"errors"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
@ -80,7 +80,7 @@ func Test_halfCommitter(t *testing.T) {
|
||||||
testWithCommitter(mockCommitter, func(committer Committer) error {
|
testWithCommitter(mockCommitter, func(committer Committer) error {
|
||||||
defer committer.Close()
|
defer committer.Close()
|
||||||
if true {
|
if true {
|
||||||
return fmt.Errorf("error")
|
return errors.New("error")
|
||||||
}
|
}
|
||||||
return committer.Commit()
|
return committer.Commit()
|
||||||
})
|
})
|
||||||
|
@ -94,7 +94,7 @@ func Test_halfCommitter(t *testing.T) {
|
||||||
testWithCommitter(mockCommitter, func(committer Committer) error {
|
testWithCommitter(mockCommitter, func(committer Committer) error {
|
||||||
committer.Close()
|
committer.Close()
|
||||||
committer.Commit()
|
committer.Commit()
|
||||||
return fmt.Errorf("error")
|
return errors.New("error")
|
||||||
})
|
})
|
||||||
|
|
||||||
mockCommitter.Assert(t)
|
mockCommitter.Assert(t)
|
||||||
|
|
|
@ -413,6 +413,25 @@
|
||||||
},
|
},
|
||||||
"total_commits": 0
|
"total_commits": 0
|
||||||
}
|
}
|
||||||
|
-
|
||||||
|
id: 793
|
||||||
|
title: "job output"
|
||||||
|
repo_id: 4
|
||||||
|
owner_id: 1
|
||||||
|
workflow_id: "test.yaml"
|
||||||
|
index: 189
|
||||||
|
trigger_user_id: 1
|
||||||
|
ref: "refs/heads/master"
|
||||||
|
commit_sha: "c2d72f548424103f01ee1dc02889c1e2bff816b0"
|
||||||
|
event: "push"
|
||||||
|
is_fork_pull_request: 0
|
||||||
|
status: 1
|
||||||
|
started: 1683636528
|
||||||
|
stopped: 1683636626
|
||||||
|
created: 1683636108
|
||||||
|
updated: 1683636626
|
||||||
|
need_approval: 0
|
||||||
|
approved_by: 0
|
||||||
-
|
-
|
||||||
id: 891
|
id: 891
|
||||||
title: "update actions"
|
title: "update actions"
|
||||||
|
|
|
@ -26,6 +26,49 @@
|
||||||
status: 1
|
status: 1
|
||||||
started: 1683636528
|
started: 1683636528
|
||||||
stopped: 1683636626
|
stopped: 1683636626
|
||||||
|
-
|
||||||
|
id: 194
|
||||||
|
run_id: 793
|
||||||
|
repo_id: 4
|
||||||
|
owner_id: 1
|
||||||
|
commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0
|
||||||
|
is_fork_pull_request: 0
|
||||||
|
name: job1 (1)
|
||||||
|
attempt: 1
|
||||||
|
job_id: job1
|
||||||
|
task_id: 49
|
||||||
|
status: 1
|
||||||
|
started: 1683636528
|
||||||
|
stopped: 1683636626
|
||||||
|
-
|
||||||
|
id: 195
|
||||||
|
run_id: 793
|
||||||
|
repo_id: 4
|
||||||
|
owner_id: 1
|
||||||
|
commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0
|
||||||
|
is_fork_pull_request: 0
|
||||||
|
name: job1 (2)
|
||||||
|
attempt: 1
|
||||||
|
job_id: job1
|
||||||
|
task_id: 50
|
||||||
|
status: 1
|
||||||
|
started: 1683636528
|
||||||
|
stopped: 1683636626
|
||||||
|
-
|
||||||
|
id: 196
|
||||||
|
run_id: 793
|
||||||
|
repo_id: 4
|
||||||
|
owner_id: 1
|
||||||
|
commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0
|
||||||
|
is_fork_pull_request: 0
|
||||||
|
name: job2
|
||||||
|
attempt: 1
|
||||||
|
job_id: job2
|
||||||
|
needs: [job1]
|
||||||
|
task_id: 51
|
||||||
|
status: 5
|
||||||
|
started: 1683636528
|
||||||
|
stopped: 1683636626
|
||||||
-
|
-
|
||||||
id: 292
|
id: 292
|
||||||
run_id: 891
|
run_id: 891
|
||||||
|
|
|
@ -57,3 +57,63 @@
|
||||||
log_length: 707
|
log_length: 707
|
||||||
log_size: 90179
|
log_size: 90179
|
||||||
log_expired: 0
|
log_expired: 0
|
||||||
|
-
|
||||||
|
id: 49
|
||||||
|
job_id: 194
|
||||||
|
attempt: 1
|
||||||
|
runner_id: 1
|
||||||
|
status: 1 # success
|
||||||
|
started: 1683636528
|
||||||
|
stopped: 1683636626
|
||||||
|
repo_id: 4
|
||||||
|
owner_id: 1
|
||||||
|
commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0
|
||||||
|
is_fork_pull_request: 0
|
||||||
|
token_hash: b8d3962425466b6709b9ac51446f93260c54afe8e7b6d3686e34f991fb8a8953822b0deed86fe41a103f34bc48dbc4784220
|
||||||
|
token_salt: ffffffffff
|
||||||
|
token_last_eight: ffffffff
|
||||||
|
log_filename: artifact-test2/2f/47.log
|
||||||
|
log_in_storage: 1
|
||||||
|
log_length: 707
|
||||||
|
log_size: 90179
|
||||||
|
log_expired: 0
|
||||||
|
-
|
||||||
|
id: 50
|
||||||
|
job_id: 195
|
||||||
|
attempt: 1
|
||||||
|
runner_id: 1
|
||||||
|
status: 1 # success
|
||||||
|
started: 1683636528
|
||||||
|
stopped: 1683636626
|
||||||
|
repo_id: 4
|
||||||
|
owner_id: 1
|
||||||
|
commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0
|
||||||
|
is_fork_pull_request: 0
|
||||||
|
token_hash: b8d3962425466b6709b9ac51446f93260c54afe8e7b6d3686e34f991fb8a8953822b0deed86fe41a103f34bc48dbc4784221
|
||||||
|
token_salt: ffffffffff
|
||||||
|
token_last_eight: ffffffff
|
||||||
|
log_filename: artifact-test2/2f/47.log
|
||||||
|
log_in_storage: 1
|
||||||
|
log_length: 707
|
||||||
|
log_size: 90179
|
||||||
|
log_expired: 0
|
||||||
|
-
|
||||||
|
id: 51
|
||||||
|
job_id: 196
|
||||||
|
attempt: 1
|
||||||
|
runner_id: 1
|
||||||
|
status: 6 # running
|
||||||
|
started: 1683636528
|
||||||
|
stopped: 1683636626
|
||||||
|
repo_id: 4
|
||||||
|
owner_id: 1
|
||||||
|
commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0
|
||||||
|
is_fork_pull_request: 0
|
||||||
|
token_hash: b8d3962425466b6709b9ac51446f93260c54afe8e7b6d3686e34f991fb8a8953822b0deed86fe41a103f34bc48dbc4784222
|
||||||
|
token_salt: ffffffffff
|
||||||
|
token_last_eight: ffffffff
|
||||||
|
log_filename: artifact-test2/2f/47.log
|
||||||
|
log_in_storage: 1
|
||||||
|
log_length: 707
|
||||||
|
log_size: 90179
|
||||||
|
log_expired: 0
|
||||||
|
|
20
models/fixtures/action_task_output.yml
Normal file
20
models/fixtures/action_task_output.yml
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
-
|
||||||
|
id: 1
|
||||||
|
task_id: 49
|
||||||
|
output_key: output_a
|
||||||
|
output_value: abc
|
||||||
|
-
|
||||||
|
id: 2
|
||||||
|
task_id: 49
|
||||||
|
output_key: output_b
|
||||||
|
output_value: ''
|
||||||
|
-
|
||||||
|
id: 3
|
||||||
|
task_id: 50
|
||||||
|
output_key: output_a
|
||||||
|
output_value: ''
|
||||||
|
-
|
||||||
|
id: 4
|
||||||
|
task_id: 50
|
||||||
|
output_key: output_b
|
||||||
|
output_value: bbb
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
repo_model "code.gitea.io/gitea/models/repo"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
user_model "code.gitea.io/gitea/models/user"
|
user_model "code.gitea.io/gitea/models/user"
|
||||||
|
"code.gitea.io/gitea/modules/container"
|
||||||
"code.gitea.io/gitea/modules/git"
|
"code.gitea.io/gitea/modules/git"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/timeutil"
|
"code.gitea.io/gitea/modules/timeutil"
|
||||||
|
@ -162,9 +163,22 @@ func GetBranch(ctx context.Context, repoID int64, branchName string) (*Branch, e
|
||||||
return &branch, nil
|
return &branch, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetBranches(ctx context.Context, repoID int64, branchNames []string) ([]*Branch, error) {
|
func GetBranches(ctx context.Context, repoID int64, branchNames []string, includeDeleted bool) ([]*Branch, error) {
|
||||||
branches := make([]*Branch, 0, len(branchNames))
|
branches := make([]*Branch, 0, len(branchNames))
|
||||||
return branches, db.GetEngine(ctx).Where("repo_id=?", repoID).In("name", branchNames).Find(&branches)
|
|
||||||
|
sess := db.GetEngine(ctx).Where("repo_id=?", repoID).In("name", branchNames)
|
||||||
|
if !includeDeleted {
|
||||||
|
sess.And("is_deleted=?", false)
|
||||||
|
}
|
||||||
|
return branches, sess.Find(&branches)
|
||||||
|
}
|
||||||
|
|
||||||
|
func BranchesToNamesSet(branches []*Branch) container.Set[string] {
|
||||||
|
names := make(container.Set[string], len(branches))
|
||||||
|
for _, branch := range branches {
|
||||||
|
names.Add(branch.Name)
|
||||||
|
}
|
||||||
|
return names
|
||||||
}
|
}
|
||||||
|
|
||||||
func AddBranches(ctx context.Context, branches []*Branch) error {
|
func AddBranches(ctx context.Context, branches []*Branch) error {
|
||||||
|
|
|
@ -72,7 +72,10 @@ func (c *HTTPClient) batch(ctx context.Context, operation string, objects []Poin
|
||||||
|
|
||||||
url := fmt.Sprintf("%s/objects/batch", c.endpoint)
|
url := fmt.Sprintf("%s/objects/batch", c.endpoint)
|
||||||
|
|
||||||
request := &BatchRequest{operation, c.transferNames(), nil, objects}
|
// `ref` is an "optional object describing the server ref that the objects belong to"
|
||||||
|
// but some (incorrect) lfs servers require it, so maybe adding an empty ref here doesn't break the correct ones.
|
||||||
|
// https://github.com/git-lfs/git-lfs/blob/a32a02b44bf8a511aa14f047627c49e1a7fd5021/docs/api/batch.md?plain=1#L37
|
||||||
|
request := &BatchRequest{operation, c.transferNames(), &Reference{}, objects}
|
||||||
payload := new(bytes.Buffer)
|
payload := new(bytes.Buffer)
|
||||||
err := json.NewEncoder(payload).Encode(request)
|
err := json.NewEncoder(payload).Encode(request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -236,6 +239,7 @@ func createRequest(ctx context.Context, method, url string, headers map[string]s
|
||||||
req.Header.Set(key, value)
|
req.Header.Set(key, value)
|
||||||
}
|
}
|
||||||
req.Header.Set("Accept", AcceptHeader)
|
req.Header.Set("Accept", AcceptHeader)
|
||||||
|
req.Header.Set("User-Agent", UserAgentHeader)
|
||||||
|
|
||||||
return req, nil
|
return req, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,8 +14,12 @@ import (
|
||||||
const (
|
const (
|
||||||
// MediaType contains the media type for LFS server requests
|
// MediaType contains the media type for LFS server requests
|
||||||
MediaType = "application/vnd.git-lfs+json"
|
MediaType = "application/vnd.git-lfs+json"
|
||||||
// Some LFS servers offer content with other types, so fallback to '*/*' if application/vnd.git-lfs+json cannot be served
|
// AcceptHeader Some LFS servers offer content with other types, so fallback to '*/*' if application/vnd.git-lfs+json cannot be served
|
||||||
AcceptHeader = "application/vnd.git-lfs+json;q=0.9, */*;q=0.8"
|
AcceptHeader = "application/vnd.git-lfs+json;q=0.9, */*;q=0.8"
|
||||||
|
// UserAgentHeader Add User-Agent for gitea's self-implemented lfs client,
|
||||||
|
// and the version is consistent with the latest version of git lfs can be avoided incompatibilities.
|
||||||
|
// Some lfs servers will check this
|
||||||
|
UserAgentHeader = "git-lfs/3.6.0 (Forgejo)"
|
||||||
)
|
)
|
||||||
|
|
||||||
// BatchRequest contains multiple requests processed in one batch operation.
|
// BatchRequest contains multiple requests processed in one batch operation.
|
||||||
|
|
|
@ -290,6 +290,16 @@ type CreateBranchRepoOption struct {
|
||||||
OldRefName string `json:"old_ref_name" binding:"GitRefName;MaxSize(100)"`
|
OldRefName string `json:"old_ref_name" binding:"GitRefName;MaxSize(100)"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UpdateBranchRepoOption options when updating a branch in a repository
|
||||||
|
// swagger:model
|
||||||
|
type UpdateBranchRepoOption struct {
|
||||||
|
// New branch name
|
||||||
|
//
|
||||||
|
// required: true
|
||||||
|
// unique: true
|
||||||
|
Name string `json:"name" binding:"Required;GitRefName;MaxSize(100)"`
|
||||||
|
}
|
||||||
|
|
||||||
// TransferRepoOption options when transfer a repository's ownership
|
// TransferRepoOption options when transfer a repository's ownership
|
||||||
// swagger:model
|
// swagger:model
|
||||||
type TransferRepoOption struct {
|
type TransferRepoOption struct {
|
||||||
|
|
4
release-notes/6271.md
Normal file
4
release-notes/6271.md
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
fix: [commit](https://codeberg.org/forgejo/forgejo/commit/96a7f0a3f065c5db8fdf352c93c8367e24d259de) Fix missing outputs for jobs with matrix
|
||||||
|
fix: [commit](https://codeberg.org/forgejo/forgejo/commit/2b5c69c451a684b20119e2521dc23734c7869241) Detect whether action view branch was deleted
|
||||||
|
feat: [commit](https://codeberg.org/forgejo/forgejo/commit/b0d6a7f07bff836190a8e87fe5645d5557893e32) Implement update branch API
|
||||||
|
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
Normal file
16
routers/api/actions/runner/main_test.go
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package runner
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models/unittest"
|
||||||
|
|
||||||
|
_ "code.gitea.io/gitea/models/forgefed"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMain(m *testing.M) {
|
||||||
|
unittest.MainTest(m)
|
||||||
|
}
|
|
@ -162,28 +162,56 @@ func findTaskNeeds(ctx context.Context, task *actions_model.ActionTask) (map[str
|
||||||
return nil, fmt.Errorf("FindRunJobs: %w", err)
|
return nil, fmt.Errorf("FindRunJobs: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
ret := make(map[string]*runnerv1.TaskNeed, len(needs))
|
jobIDJobs := make(map[string][]*actions_model.ActionRunJob)
|
||||||
for _, job := range jobs {
|
for _, job := range jobs {
|
||||||
if !needs.Contains(job.JobID) {
|
jobIDJobs[job.JobID] = append(jobIDJobs[job.JobID], job)
|
||||||
|
}
|
||||||
|
|
||||||
|
ret := make(map[string]*runnerv1.TaskNeed, len(needs))
|
||||||
|
for jobID, jobsWithSameID := range jobIDJobs {
|
||||||
|
if !needs.Contains(jobID) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if job.TaskID == 0 || !job.Status.IsDone() {
|
var jobOutputs map[string]string
|
||||||
// it shouldn't happen, or the job has been rerun
|
for _, job := range jobsWithSameID {
|
||||||
continue
|
if job.TaskID == 0 || !job.Status.IsDone() {
|
||||||
|
// it shouldn't happen, or the job has been rerun
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
got, err := actions_model.FindTaskOutputByTaskID(ctx, job.TaskID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("FindTaskOutputByTaskID: %w", err)
|
||||||
|
}
|
||||||
|
outputs := make(map[string]string, len(got))
|
||||||
|
for _, v := range got {
|
||||||
|
outputs[v.OutputKey] = v.OutputValue
|
||||||
|
}
|
||||||
|
if len(jobOutputs) == 0 {
|
||||||
|
jobOutputs = outputs
|
||||||
|
} else {
|
||||||
|
jobOutputs = mergeTwoOutputs(outputs, jobOutputs)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
outputs := make(map[string]string)
|
ret[jobID] = &runnerv1.TaskNeed{
|
||||||
got, err := actions_model.FindTaskOutputByTaskID(ctx, job.TaskID)
|
Outputs: jobOutputs,
|
||||||
if err != nil {
|
Result: runnerv1.Result(actions_model.AggregateJobStatus(jobsWithSameID)),
|
||||||
return nil, fmt.Errorf("FindTaskOutputByTaskID: %w", err)
|
|
||||||
}
|
|
||||||
for _, v := range got {
|
|
||||||
outputs[v.OutputKey] = v.OutputValue
|
|
||||||
}
|
|
||||||
ret[job.JobID] = &runnerv1.TaskNeed{
|
|
||||||
Outputs: outputs,
|
|
||||||
Result: runnerv1.Result(job.Status),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret, nil
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// mergeTwoOutputs merges two outputs from two different ActionRunJobs
|
||||||
|
// Values with the same output name may be overridden. The user should ensure the output names are unique.
|
||||||
|
// See https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#using-job-outputs-in-a-matrix-job
|
||||||
|
func mergeTwoOutputs(o1, o2 map[string]string) map[string]string {
|
||||||
|
ret := make(map[string]string, len(o1))
|
||||||
|
for k1, v1 := range o1 {
|
||||||
|
if len(v1) > 0 {
|
||||||
|
ret[k1] = v1
|
||||||
|
} else {
|
||||||
|
ret[k1] = o2[k1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
29
routers/api/actions/runner/utils_test.go
Normal file
29
routers/api/actions/runner/utils_test.go
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
package runner
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
actions_model "code.gitea.io/gitea/models/actions"
|
||||||
|
"code.gitea.io/gitea/models/unittest"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_findTaskNeeds(t *testing.T) {
|
||||||
|
require.NoError(t, unittest.PrepareTestDatabase())
|
||||||
|
|
||||||
|
task := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionTask{ID: 51})
|
||||||
|
|
||||||
|
ret, err := findTaskNeeds(context.Background(), task)
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Len(t, ret, 1)
|
||||||
|
assert.Contains(t, ret, "job1")
|
||||||
|
assert.Len(t, ret["job1"].Outputs, 2)
|
||||||
|
assert.Equal(t, "abc", ret["job1"].Outputs["output_a"])
|
||||||
|
assert.Equal(t, "bbb", ret["job1"].Outputs["output_b"])
|
||||||
|
}
|
|
@ -337,6 +337,7 @@ func CommonRoutes() *web.Route {
|
||||||
r.Get("/PACKAGES", cran.EnumerateSourcePackages)
|
r.Get("/PACKAGES", cran.EnumerateSourcePackages)
|
||||||
r.Get("/PACKAGES{format}", cran.EnumerateSourcePackages)
|
r.Get("/PACKAGES{format}", cran.EnumerateSourcePackages)
|
||||||
r.Get("/{filename}", cran.DownloadSourcePackageFile)
|
r.Get("/{filename}", cran.DownloadSourcePackageFile)
|
||||||
|
r.Get("/Archive/{packagename}/{filename}", cran.DownloadSourcePackageFile)
|
||||||
})
|
})
|
||||||
r.Put("", reqPackageAccess(perm.AccessModeWrite), enforcePackagesQuota(), cran.UploadSourcePackageFile)
|
r.Put("", reqPackageAccess(perm.AccessModeWrite), enforcePackagesQuota(), cran.UploadSourcePackageFile)
|
||||||
})
|
})
|
||||||
|
|
|
@ -1153,6 +1153,7 @@ func Routes() *web.Route {
|
||||||
m.Get("/*", repo.GetBranch)
|
m.Get("/*", repo.GetBranch)
|
||||||
m.Delete("/*", reqToken(), reqRepoWriter(unit.TypeCode), mustNotBeArchived, repo.DeleteBranch)
|
m.Delete("/*", reqToken(), reqRepoWriter(unit.TypeCode), mustNotBeArchived, repo.DeleteBranch)
|
||||||
m.Post("", reqToken(), reqRepoWriter(unit.TypeCode), mustNotBeArchived, bind(api.CreateBranchRepoOption{}), context.EnforceQuotaAPI(quota_model.LimitSubjectSizeGitAll, context.QuotaTargetRepo), repo.CreateBranch)
|
m.Post("", reqToken(), reqRepoWriter(unit.TypeCode), mustNotBeArchived, bind(api.CreateBranchRepoOption{}), context.EnforceQuotaAPI(quota_model.LimitSubjectSizeGitAll, context.QuotaTargetRepo), repo.CreateBranch)
|
||||||
|
m.Patch("/*", reqToken(), reqRepoWriter(unit.TypeCode), mustNotBeArchived, bind(api.UpdateBranchRepoOption{}), repo.UpdateBranch)
|
||||||
}, context.ReferencesGitRepo(), reqRepoReader(unit.TypeCode))
|
}, context.ReferencesGitRepo(), reqRepoReader(unit.TypeCode))
|
||||||
m.Group("/branch_protections", func() {
|
m.Group("/branch_protections", func() {
|
||||||
m.Get("", repo.ListBranchProtections)
|
m.Get("", repo.ListBranchProtections)
|
||||||
|
|
|
@ -393,6 +393,77 @@ func ListBranches(ctx *context.APIContext) {
|
||||||
ctx.JSON(http.StatusOK, apiBranches)
|
ctx.JSON(http.StatusOK, apiBranches)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UpdateBranch updates a repository's branch.
|
||||||
|
func UpdateBranch(ctx *context.APIContext) {
|
||||||
|
// swagger:operation PATCH /repos/{owner}/{repo}/branches/{branch} repository repoUpdateBranch
|
||||||
|
// ---
|
||||||
|
// summary: Update a branch
|
||||||
|
// consumes:
|
||||||
|
// - application/json
|
||||||
|
// 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: branch
|
||||||
|
// in: path
|
||||||
|
// description: name of the branch
|
||||||
|
// type: string
|
||||||
|
// required: true
|
||||||
|
// - name: body
|
||||||
|
// in: body
|
||||||
|
// schema:
|
||||||
|
// "$ref": "#/definitions/UpdateBranchRepoOption"
|
||||||
|
// responses:
|
||||||
|
// "204":
|
||||||
|
// "$ref": "#/responses/empty"
|
||||||
|
// "403":
|
||||||
|
// "$ref": "#/responses/forbidden"
|
||||||
|
// "404":
|
||||||
|
// "$ref": "#/responses/notFound"
|
||||||
|
// "422":
|
||||||
|
// "$ref": "#/responses/validationError"
|
||||||
|
|
||||||
|
opt := web.GetForm(ctx).(*api.UpdateBranchRepoOption)
|
||||||
|
|
||||||
|
oldName := ctx.Params("*")
|
||||||
|
repo := ctx.Repo.Repository
|
||||||
|
|
||||||
|
if repo.IsEmpty {
|
||||||
|
ctx.Error(http.StatusNotFound, "", "Git Repository is empty.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if repo.IsMirror {
|
||||||
|
ctx.Error(http.StatusForbidden, "", "Git Repository is a mirror.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
msg, err := repo_service.RenameBranch(ctx, repo, ctx.Doer, ctx.Repo.GitRepo, oldName, opt.Name)
|
||||||
|
if err != nil {
|
||||||
|
ctx.Error(http.StatusInternalServerError, "RenameBranch", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if msg == "target_exist" {
|
||||||
|
ctx.Error(http.StatusUnprocessableEntity, "", "Cannot rename a branch using the same name or rename to a branch that already exists.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if msg == "from_not_exist" {
|
||||||
|
ctx.Error(http.StatusNotFound, "", "Branch doesn't exist.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Status(http.StatusNoContent)
|
||||||
|
}
|
||||||
|
|
||||||
// GetBranchProtection gets a branch protection
|
// GetBranchProtection gets a branch protection
|
||||||
func GetBranchProtection(ctx *context.APIContext) {
|
func GetBranchProtection(ctx *context.APIContext) {
|
||||||
// swagger:operation GET /repos/{owner}/{repo}/branch_protections/{name} repository repoGetBranchProtection
|
// swagger:operation GET /repos/{owner}/{repo}/branch_protections/{name} repository repoGetBranchProtection
|
||||||
|
|
|
@ -101,6 +101,8 @@ type swaggerParameterBodies struct {
|
||||||
// in:body
|
// in:body
|
||||||
EditRepoOption api.EditRepoOption
|
EditRepoOption api.EditRepoOption
|
||||||
// in:body
|
// in:body
|
||||||
|
UpdateBranchRepoOption api.UpdateBranchRepoOption
|
||||||
|
// in:body
|
||||||
TransferRepoOption api.TransferRepoOption
|
TransferRepoOption api.TransferRepoOption
|
||||||
// in:body
|
// in:body
|
||||||
CreateForkOption api.CreateForkOption
|
CreateForkOption api.CreateForkOption
|
||||||
|
|
|
@ -12,11 +12,13 @@ import (
|
||||||
|
|
||||||
actions_model "code.gitea.io/gitea/models/actions"
|
actions_model "code.gitea.io/gitea/models/actions"
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
|
git_model "code.gitea.io/gitea/models/git"
|
||||||
"code.gitea.io/gitea/models/unit"
|
"code.gitea.io/gitea/models/unit"
|
||||||
"code.gitea.io/gitea/modules/actions"
|
"code.gitea.io/gitea/modules/actions"
|
||||||
"code.gitea.io/gitea/modules/base"
|
"code.gitea.io/gitea/modules/base"
|
||||||
"code.gitea.io/gitea/modules/container"
|
"code.gitea.io/gitea/modules/container"
|
||||||
"code.gitea.io/gitea/modules/git"
|
"code.gitea.io/gitea/modules/git"
|
||||||
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/optional"
|
"code.gitea.io/gitea/modules/optional"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
|
@ -222,6 +224,10 @@ func List(ctx *context.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := loadIsRefDeleted(ctx, runs); err != nil {
|
||||||
|
log.Error("LoadIsRefDeleted", err)
|
||||||
|
}
|
||||||
|
|
||||||
ctx.Data["Runs"] = runs
|
ctx.Data["Runs"] = runs
|
||||||
|
|
||||||
ctx.Data["Repo"] = ctx.Repo
|
ctx.Data["Repo"] = ctx.Repo
|
||||||
|
@ -245,3 +251,31 @@ func List(ctx *context.Context) {
|
||||||
|
|
||||||
ctx.HTML(http.StatusOK, tplListActions)
|
ctx.HTML(http.StatusOK, tplListActions)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// loadIsRefDeleted loads the IsRefDeleted field for each run in the list.
|
||||||
|
// TODO: move this function to models/actions/run_list.go but now it will result in a circular import.
|
||||||
|
func loadIsRefDeleted(ctx *context.Context, runs actions_model.RunList) error {
|
||||||
|
branches := make(container.Set[string], len(runs))
|
||||||
|
for _, run := range runs {
|
||||||
|
refName := git.RefName(run.Ref)
|
||||||
|
if refName.IsBranch() {
|
||||||
|
branches.Add(refName.ShortName())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(branches) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
branchInfos, err := git_model.GetBranches(ctx, ctx.Repo.Repository.ID, branches.Values(), false)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
branchSet := git_model.BranchesToNamesSet(branchInfos)
|
||||||
|
for _, run := range runs {
|
||||||
|
refName := git.RefName(run.Ref)
|
||||||
|
if refName.IsBranch() && !branchSet.Contains(run.Ref) {
|
||||||
|
run.IsRefDeleted = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -20,10 +20,13 @@ import (
|
||||||
|
|
||||||
actions_model "code.gitea.io/gitea/models/actions"
|
actions_model "code.gitea.io/gitea/models/actions"
|
||||||
"code.gitea.io/gitea/models/db"
|
"code.gitea.io/gitea/models/db"
|
||||||
|
git_model "code.gitea.io/gitea/models/git"
|
||||||
repo_model "code.gitea.io/gitea/models/repo"
|
repo_model "code.gitea.io/gitea/models/repo"
|
||||||
"code.gitea.io/gitea/models/unit"
|
"code.gitea.io/gitea/models/unit"
|
||||||
"code.gitea.io/gitea/modules/actions"
|
"code.gitea.io/gitea/modules/actions"
|
||||||
"code.gitea.io/gitea/modules/base"
|
"code.gitea.io/gitea/modules/base"
|
||||||
|
"code.gitea.io/gitea/modules/git"
|
||||||
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"code.gitea.io/gitea/modules/storage"
|
"code.gitea.io/gitea/modules/storage"
|
||||||
"code.gitea.io/gitea/modules/templates"
|
"code.gitea.io/gitea/modules/templates"
|
||||||
|
@ -157,8 +160,9 @@ type ViewUser struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type ViewBranch struct {
|
type ViewBranch struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Link string `json:"link"`
|
Link string `json:"link"`
|
||||||
|
IsDeleted bool `json:"isDeleted"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ViewJobStep struct {
|
type ViewJobStep struct {
|
||||||
|
@ -227,6 +231,16 @@ func ViewPost(ctx *context_module.Context) {
|
||||||
Name: run.PrettyRef(),
|
Name: run.PrettyRef(),
|
||||||
Link: run.RefLink(),
|
Link: run.RefLink(),
|
||||||
}
|
}
|
||||||
|
refName := git.RefName(run.Ref)
|
||||||
|
if refName.IsBranch() {
|
||||||
|
b, err := git_model.GetBranch(ctx, ctx.Repo.Repository.ID, refName.ShortName())
|
||||||
|
if err != nil && !git_model.IsErrBranchNotExist(err) {
|
||||||
|
log.Error("GetBranch: %v", err)
|
||||||
|
} else if git_model.IsErrBranchNotExist(err) || (b != nil && b.IsDeleted) {
|
||||||
|
branch.IsDeleted = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
resp.State.Run.Commit = ViewCommit{
|
resp.State.Run.Commit = ViewCommit{
|
||||||
LocaleCommit: ctx.Locale.TrString("actions.runs.commit"),
|
LocaleCommit: ctx.Locale.TrString("actions.runs.commit"),
|
||||||
LocalePushedBy: ctx.Locale.TrString("actions.runs.pushed_by"),
|
LocalePushedBy: ctx.Locale.TrString("actions.runs.pushed_by"),
|
||||||
|
|
|
@ -254,7 +254,7 @@ func SyncBranchesToDB(ctx context.Context, repoID, pusherID int64, branchNames,
|
||||||
}
|
}
|
||||||
|
|
||||||
return db.WithTx(ctx, func(ctx context.Context) error {
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||||
branches, err := git_model.GetBranches(ctx, repoID, branchNames)
|
branches, err := git_model.GetBranches(ctx, repoID, branchNames, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("git_model.GetBranches: %v", err)
|
return fmt.Errorf("git_model.GetBranches: %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,10 +27,10 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-item-trailing">
|
<div class="flex-item-trailing">
|
||||||
{{if .RefLink}}
|
{{if .IsRefDeleted}}
|
||||||
<a class="ui label run-list-ref gt-ellipsis" href="{{.RefLink}}">{{.PrettyRef}}</a>
|
<span class="ui label run-list-ref gt-ellipsis tw-line-through" data-tooltip-content="{{.PrettyRef}}">{{.PrettyRef}}</span>
|
||||||
{{else}}
|
{{else}}
|
||||||
<span class="ui label run-list-ref gt-ellipsis">{{.PrettyRef}}</span>
|
<a class="ui label run-list-ref gt-ellipsis" href="{{.RefLink}}" data-tooltip-content="{{.PrettyRef}}">{{.PrettyRef}}</a>
|
||||||
{{end}}
|
{{end}}
|
||||||
<div class="run-list-item-right">
|
<div class="run-list-item-right">
|
||||||
<div class="run-list-meta">{{svg "octicon-calendar" 16}}{{DateUtils.TimeSince .Updated}}</div>
|
<div class="run-list-meta">{{svg "octicon-calendar" 16}}{{DateUtils.TimeSince .Updated}}</div>
|
||||||
|
|
73
templates/swagger/v1_json.tmpl
generated
73
templates/swagger/v1_json.tmpl
generated
|
@ -5820,6 +5820,63 @@
|
||||||
"$ref": "#/responses/repoArchivedError"
|
"$ref": "#/responses/repoArchivedError"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"patch": {
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"repository"
|
||||||
|
],
|
||||||
|
"summary": "Update a branch",
|
||||||
|
"operationId": "repoUpdateBranch",
|
||||||
|
"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": "string",
|
||||||
|
"description": "name of the branch",
|
||||||
|
"name": "branch",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "body",
|
||||||
|
"in": "body",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/UpdateBranchRepoOption"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"204": {
|
||||||
|
"$ref": "#/responses/empty"
|
||||||
|
},
|
||||||
|
"403": {
|
||||||
|
"$ref": "#/responses/forbidden"
|
||||||
|
},
|
||||||
|
"404": {
|
||||||
|
"$ref": "#/responses/notFound"
|
||||||
|
},
|
||||||
|
"422": {
|
||||||
|
"$ref": "#/responses/validationError"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/repos/{owner}/{repo}/collaborators": {
|
"/repos/{owner}/{repo}/collaborators": {
|
||||||
|
@ -27302,6 +27359,22 @@
|
||||||
},
|
},
|
||||||
"x-go-package": "code.gitea.io/gitea/modules/structs"
|
"x-go-package": "code.gitea.io/gitea/modules/structs"
|
||||||
},
|
},
|
||||||
|
"UpdateBranchRepoOption": {
|
||||||
|
"description": "UpdateBranchRepoOption options when updating a branch in a repository",
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"name"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"description": "New branch name",
|
||||||
|
"type": "string",
|
||||||
|
"uniqueItems": true,
|
||||||
|
"x-go-name": "Name"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"x-go-package": "code.gitea.io/gitea/modules/structs"
|
||||||
|
},
|
||||||
"UpdateFileOptions": {
|
"UpdateFileOptions": {
|
||||||
"description": "UpdateFileOptions options for updating files\nNote: `author` and `committer` are optional (if only one is given, it will be used for the other, otherwise the authenticated user will be used)",
|
"description": "UpdateFileOptions options for updating files\nNote: `author` and `committer` are optional (if only one is given, it will be used for the other, otherwise the authenticated user will be used)",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
|
|
|
@ -5,6 +5,7 @@ package integration
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
"net/url"
|
"net/url"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
@ -187,6 +188,37 @@ func testAPICreateBranch(t testing.TB, session *TestSession, user, repo, oldBran
|
||||||
return resp.Result().StatusCode == status
|
return resp.Result().StatusCode == status
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAPIUpdateBranch(t *testing.T) {
|
||||||
|
onGiteaRun(t, func(t *testing.T, _ *url.URL) {
|
||||||
|
t.Run("UpdateBranchWithEmptyRepo", func(t *testing.T) {
|
||||||
|
testAPIUpdateBranch(t, "user10", "repo6", "master", "test", http.StatusNotFound)
|
||||||
|
})
|
||||||
|
t.Run("UpdateBranchWithSameBranchNames", func(t *testing.T) {
|
||||||
|
resp := testAPIUpdateBranch(t, "user2", "repo1", "master", "master", http.StatusUnprocessableEntity)
|
||||||
|
assert.Contains(t, resp.Body.String(), "Cannot rename a branch using the same name or rename to a branch that already exists.")
|
||||||
|
})
|
||||||
|
t.Run("UpdateBranchThatAlreadyExists", func(t *testing.T) {
|
||||||
|
resp := testAPIUpdateBranch(t, "user2", "repo1", "master", "branch2", http.StatusUnprocessableEntity)
|
||||||
|
assert.Contains(t, resp.Body.String(), "Cannot rename a branch using the same name or rename to a branch that already exists.")
|
||||||
|
})
|
||||||
|
t.Run("UpdateBranchWithNonExistentBranch", func(t *testing.T) {
|
||||||
|
resp := testAPIUpdateBranch(t, "user2", "repo1", "i-dont-exist", "new-branch-name", http.StatusNotFound)
|
||||||
|
assert.Contains(t, resp.Body.String(), "Branch doesn't exist.")
|
||||||
|
})
|
||||||
|
t.Run("RenameBranchNormalScenario", func(t *testing.T) {
|
||||||
|
testAPIUpdateBranch(t, "user2", "repo1", "branch2", "new-branch-name", http.StatusNoContent)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAPIUpdateBranch(t *testing.T, ownerName, repoName, from, to string, expectedHTTPStatus int) *httptest.ResponseRecorder {
|
||||||
|
token := getUserToken(t, ownerName, auth_model.AccessTokenScopeWriteRepository)
|
||||||
|
req := NewRequestWithJSON(t, "PATCH", "api/v1/repos/"+ownerName+"/"+repoName+"/branches/"+from, &api.UpdateBranchRepoOption{
|
||||||
|
Name: to,
|
||||||
|
}).AddTokenAuth(token)
|
||||||
|
return MakeRequest(t, req, expectedHTTPStatus)
|
||||||
|
}
|
||||||
|
|
||||||
func TestAPIBranchProtection(t *testing.T) {
|
func TestAPIBranchProtection(t *testing.T) {
|
||||||
defer tests.PrepareTestEnv(t)()
|
defer tests.PrepareTestEnv(t)()
|
||||||
|
|
||||||
|
|
|
@ -116,6 +116,14 @@ func TestPackageCran(t *testing.T) {
|
||||||
MakeRequest(t, req, http.StatusOK)
|
MakeRequest(t, req, http.StatusOK)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("DownloadArchived", func(t *testing.T) {
|
||||||
|
defer tests.PrintCurrentTest(t)()
|
||||||
|
|
||||||
|
req := NewRequest(t, "GET", fmt.Sprintf("%s/src/contrib/Archive/%s/%s_%s.tar.gz", url, packageName, packageName, packageVersion)).
|
||||||
|
AddBasicAuth(user.Name)
|
||||||
|
MakeRequest(t, req, http.StatusOK)
|
||||||
|
})
|
||||||
|
|
||||||
t.Run("Enumerate", func(t *testing.T) {
|
t.Run("Enumerate", func(t *testing.T) {
|
||||||
defer tests.PrintCurrentTest(t)()
|
defer tests.PrintCurrentTest(t)()
|
||||||
|
|
||||||
|
|
|
@ -166,6 +166,11 @@ func TestMain(m *testing.M) {
|
||||||
os.Unsetenv("GIT_COMMITTER_EMAIL")
|
os.Unsetenv("GIT_COMMITTER_EMAIL")
|
||||||
os.Unsetenv("GIT_COMMITTER_DATE")
|
os.Unsetenv("GIT_COMMITTER_DATE")
|
||||||
|
|
||||||
|
// Avoid loading the default system config. On MacOS, this config
|
||||||
|
// sets the osxkeychain credential helper, which will cause tests
|
||||||
|
// to freeze with a dialog.
|
||||||
|
os.Setenv("GIT_CONFIG_NOSYSTEM", "true")
|
||||||
|
|
||||||
err := unittest.InitFixtures(
|
err := unittest.InitFixtures(
|
||||||
unittest.FixturesOptions{
|
unittest.FixturesOptions{
|
||||||
Dir: filepath.Join(filepath.Dir(setting.AppPath), "models/fixtures/"),
|
Dir: filepath.Join(filepath.Dir(setting.AppPath), "models/fixtures/"),
|
||||||
|
|
|
@ -444,7 +444,8 @@ export function initRepositoryActionView() {
|
||||||
{{ run.commit.localePushedBy }}
|
{{ run.commit.localePushedBy }}
|
||||||
<a class="muted" :href="run.commit.pusher.link">{{ run.commit.pusher.displayName }}</a>
|
<a class="muted" :href="run.commit.pusher.link">{{ run.commit.pusher.displayName }}</a>
|
||||||
<span class="ui label tw-max-w-full" v-if="run.commit.shortSHA">
|
<span class="ui label tw-max-w-full" v-if="run.commit.shortSHA">
|
||||||
<a class="gt-ellipsis" :href="run.commit.branch.link">{{ run.commit.branch.name }}</a>
|
<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>
|
||||||
|
<a v-else class="gt-ellipsis" :href="run.commit.branch.link" :data-tooltip-content="run.commit.branch.name">{{ run.commit.branch.name }}</a>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="action-summary">
|
<div class="action-summary">
|
||||||
|
|
Loading…
Reference in a new issue