mirror of
https://github.com/go-gitea/gitea.git
synced 2025-04-08 17:05:45 +02:00
Merge branch 'main' into workflow_job_webhook_bp
This commit is contained in:
commit
ab8bab918a
4
.github/workflows/release-tag-version.yml
vendored
4
.github/workflows/release-tag-version.yml
vendored
@ -88,9 +88,9 @@ jobs:
|
||||
# 1.2
|
||||
# 1.2.3
|
||||
tags: |
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=semver,pattern={{version}}
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
@ -126,9 +126,9 @@ jobs:
|
||||
# 1.2
|
||||
# 1.2.3
|
||||
tags: |
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=semver,pattern={{version}}
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
|
4
go.mod
4
go.mod
@ -117,10 +117,10 @@ require (
|
||||
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc
|
||||
github.com/yuin/goldmark-meta v1.1.0
|
||||
gitlab.com/gitlab-org/api/client-go v0.123.0
|
||||
golang.org/x/crypto v0.33.0
|
||||
golang.org/x/crypto v0.35.0
|
||||
golang.org/x/image v0.24.0
|
||||
golang.org/x/net v0.35.0
|
||||
golang.org/x/oauth2 v0.26.0
|
||||
golang.org/x/oauth2 v0.27.0
|
||||
golang.org/x/sync v0.11.0
|
||||
golang.org/x/sys v0.30.0
|
||||
golang.org/x/text v0.22.0
|
||||
|
7
go.sum
7
go.sum
@ -831,8 +831,9 @@ golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliY
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
|
||||
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
|
||||
golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs=
|
||||
golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ=
|
||||
golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa h1:t2QcU6V556bFjYgu4L6C+6VrCPyJZ+eyRsABUPs1mz4=
|
||||
golang.org/x/exp v0.0.0-20250218142911-aa4b98e5adaa/go.mod h1:BHOTPb3L19zxehTsLoJXVaTktb06DFgmdW6Wb9s8jqk=
|
||||
golang.org/x/image v0.24.0 h1:AN7zRgVsbvmTfNyqIbbOraYL8mSwcKncEj8ofjgzcMQ=
|
||||
@ -868,8 +869,8 @@ golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
||||
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
|
||||
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
|
||||
golang.org/x/oauth2 v0.26.0 h1:afQXWNNaeC4nvZ0Ed9XvCCzXM6UHJG7iCg0W4fPqSBE=
|
||||
golang.org/x/oauth2 v0.26.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||
golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M=
|
||||
golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
|
@ -454,6 +454,24 @@ func ActivityReadable(user, doer *user_model.User) bool {
|
||||
doer != nil && (doer.IsAdmin || user.ID == doer.ID)
|
||||
}
|
||||
|
||||
func FeedDateCond(opts GetFeedsOptions) builder.Cond {
|
||||
cond := builder.NewCond()
|
||||
if opts.Date == "" {
|
||||
return cond
|
||||
}
|
||||
|
||||
dateLow, err := time.ParseInLocation("2006-01-02", opts.Date, setting.DefaultUILocation)
|
||||
if err != nil {
|
||||
log.Warn("Unable to parse %s, filter not applied: %v", opts.Date, err)
|
||||
} else {
|
||||
dateHigh := dateLow.Add(86399000000000) // 23h59m59s
|
||||
|
||||
cond = cond.And(builder.Gte{"`action`.created_unix": dateLow.Unix()})
|
||||
cond = cond.And(builder.Lte{"`action`.created_unix": dateHigh.Unix()})
|
||||
}
|
||||
return cond
|
||||
}
|
||||
|
||||
func ActivityQueryCondition(ctx context.Context, opts GetFeedsOptions) (builder.Cond, error) {
|
||||
cond := builder.NewCond()
|
||||
|
||||
@ -534,17 +552,7 @@ func ActivityQueryCondition(ctx context.Context, opts GetFeedsOptions) (builder.
|
||||
cond = cond.And(builder.Eq{"is_deleted": false})
|
||||
}
|
||||
|
||||
if opts.Date != "" {
|
||||
dateLow, err := time.ParseInLocation("2006-01-02", opts.Date, setting.DefaultUILocation)
|
||||
if err != nil {
|
||||
log.Warn("Unable to parse %s, filter not applied: %v", opts.Date, err)
|
||||
} else {
|
||||
dateHigh := dateLow.Add(86399000000000) // 23h59m59s
|
||||
|
||||
cond = cond.And(builder.Gte{"`action`.created_unix": dateLow.Unix()})
|
||||
cond = cond.And(builder.Lte{"`action`.created_unix": dateHigh.Unix()})
|
||||
}
|
||||
}
|
||||
cond = cond.And(FeedDateCond(opts))
|
||||
|
||||
return cond, nil
|
||||
}
|
||||
|
@ -208,9 +208,31 @@ func GetFeeds(ctx context.Context, opts GetFeedsOptions) (ActionList, int64, err
|
||||
return nil, 0, fmt.Errorf("need at least one of these filters: RequestedUser, RequestedTeam, RequestedRepo")
|
||||
}
|
||||
|
||||
cond, err := ActivityQueryCondition(ctx, opts)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
var err error
|
||||
var cond builder.Cond
|
||||
// if the actor is the requested user or is an administrator, we can skip the ActivityQueryCondition
|
||||
if opts.Actor != nil && opts.RequestedUser != nil && (opts.Actor.IsAdmin || opts.Actor.ID == opts.RequestedUser.ID) {
|
||||
cond = builder.Eq{
|
||||
"user_id": opts.RequestedUser.ID,
|
||||
}.And(
|
||||
FeedDateCond(opts),
|
||||
)
|
||||
|
||||
if !opts.IncludeDeleted {
|
||||
cond = cond.And(builder.Eq{"is_deleted": false})
|
||||
}
|
||||
|
||||
if !opts.IncludePrivate {
|
||||
cond = cond.And(builder.Eq{"is_private": false})
|
||||
}
|
||||
if opts.OnlyPerformedBy {
|
||||
cond = cond.And(builder.Eq{"act_user_id": opts.RequestedUser.ID})
|
||||
}
|
||||
} else {
|
||||
cond, err = ActivityQueryCondition(ctx, opts)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
}
|
||||
|
||||
actions := make([]*Action, 0, opts.PageSize)
|
||||
|
@ -1465,6 +1465,8 @@ issues.filter_milestones = Filter Milestone
|
||||
issues.filter_projects = Filter Project
|
||||
issues.filter_labels = Filter Label
|
||||
issues.filter_reviewers = Filter Reviewer
|
||||
issues.filter_no_results = No results
|
||||
issues.filter_no_results_placeholder = Try adjusting your search filters.
|
||||
issues.new = New Issue
|
||||
issues.new.title_empty = Title cannot be empty
|
||||
issues.new.labels = Labels
|
||||
|
@ -52,13 +52,22 @@ func generateMockStepsLog(logCur actions.LogCursor) (stepsLog []*actions.ViewSte
|
||||
return stepsLog
|
||||
}
|
||||
|
||||
func MockActionsRunsJobs(ctx *context.Context) {
|
||||
req := web.GetForm(ctx).(*actions.ViewRequest)
|
||||
func MockActionsView(ctx *context.Context) {
|
||||
ctx.Data["RunID"] = ctx.PathParam("run")
|
||||
ctx.Data["JobID"] = ctx.PathParam("job")
|
||||
ctx.HTML(http.StatusOK, "devtest/repo-action-view")
|
||||
}
|
||||
|
||||
func MockActionsRunsJobs(ctx *context.Context) {
|
||||
runID := ctx.PathParamInt64("run")
|
||||
|
||||
req := web.GetForm(ctx).(*actions.ViewRequest)
|
||||
resp := &actions.ViewResponse{}
|
||||
resp.State.Run.TitleHTML = `mock run title <a href="/">link</a>`
|
||||
resp.State.Run.Status = actions_model.StatusRunning.String()
|
||||
resp.State.Run.CanCancel = true
|
||||
resp.State.Run.CanCancel = runID == 10
|
||||
resp.State.Run.CanApprove = runID == 20
|
||||
resp.State.Run.CanRerun = runID == 30
|
||||
resp.State.Run.CanDeleteArtifact = true
|
||||
resp.State.Run.WorkflowID = "workflow-id"
|
||||
resp.State.Run.WorkflowLink = "./workflow-link"
|
||||
@ -85,6 +94,29 @@ func MockActionsRunsJobs(ctx *context.Context) {
|
||||
Size: 1024 * 1024,
|
||||
Status: "completed",
|
||||
})
|
||||
|
||||
resp.State.Run.Jobs = append(resp.State.Run.Jobs, &actions.ViewJob{
|
||||
ID: runID * 10,
|
||||
Name: "job 100",
|
||||
Status: actions_model.StatusRunning.String(),
|
||||
CanRerun: true,
|
||||
Duration: "1h",
|
||||
})
|
||||
resp.State.Run.Jobs = append(resp.State.Run.Jobs, &actions.ViewJob{
|
||||
ID: runID*10 + 1,
|
||||
Name: "job 101",
|
||||
Status: actions_model.StatusWaiting.String(),
|
||||
CanRerun: false,
|
||||
Duration: "2h",
|
||||
})
|
||||
resp.State.Run.Jobs = append(resp.State.Run.Jobs, &actions.ViewJob{
|
||||
ID: runID*10 + 2,
|
||||
Name: "job 102",
|
||||
Status: actions_model.StatusFailure.String(),
|
||||
CanRerun: false,
|
||||
Duration: "3h",
|
||||
})
|
||||
|
||||
resp.State.CurrentJob.Steps = append(resp.State.CurrentJob.Steps, &actions.ViewJobStep{
|
||||
Summary: "step 0 (mock slow)",
|
||||
Duration: time.Hour.String(),
|
||||
|
@ -78,7 +78,7 @@ func httpBase(ctx *context.Context) *serviceHandler {
|
||||
strings.HasSuffix(ctx.Req.URL.Path, "git-upload-archive") {
|
||||
isPull = true
|
||||
} else {
|
||||
isPull = ctx.Req.Method == "GET"
|
||||
isPull = ctx.Req.Method == "HEAD" || ctx.Req.Method == "GET"
|
||||
}
|
||||
|
||||
var accessMode perm.AccessMode
|
||||
|
@ -1634,6 +1634,7 @@ func registerRoutes(m *web.Router) {
|
||||
m.Any("", devtest.List)
|
||||
m.Any("/fetch-action-test", devtest.FetchActionTest)
|
||||
m.Any("/{sub}", devtest.Tmpl)
|
||||
m.Get("/repo-action-view/{run}/{job}", devtest.MockActionsView)
|
||||
m.Post("/actions-mock/runs/{run}/jobs/{job}", web.Bind(actions.ViewRequest{}), devtest.MockActionsRunsJobs)
|
||||
})
|
||||
}
|
||||
|
@ -291,6 +291,11 @@ func RepoRefForAPI(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
ctx := GetAPIContext(req)
|
||||
|
||||
if ctx.Repo.Repository.IsEmpty {
|
||||
ctx.APIErrorNotFound("repository is empty")
|
||||
return
|
||||
}
|
||||
|
||||
if ctx.Repo.GitRepo == nil {
|
||||
ctx.APIErrorInternal(fmt.Errorf("no open git repo"))
|
||||
return
|
||||
|
@ -1,8 +1,13 @@
|
||||
{{template "base/head" .}}
|
||||
<div class="page-content">
|
||||
<div class="tw-flex tw-justify-center tw-items-center tw-gap-5">
|
||||
<a href="/devtest/repo-action-view/10/100">Run:CanCancel</a>
|
||||
<a href="/devtest/repo-action-view/20/200">Run:CanApprove</a>
|
||||
<a href="/devtest/repo-action-view/30/300">Run:CanRerun</a>
|
||||
</div>
|
||||
{{template "repo/actions/view_component" (dict
|
||||
"RunIndex" 1
|
||||
"JobIndex" 2
|
||||
"RunIndex" (or .RunID 10)
|
||||
"JobIndex" (or .JobID 100)
|
||||
"ActionsURL" (print AppSubUrl "/devtest/actions-mock")
|
||||
)}}
|
||||
</div>
|
||||
|
@ -153,6 +153,11 @@
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="tw-text-center tw-p-8">
|
||||
<h3 class="tw-my-4">{{ctx.Locale.Tr "repo.issues.filter_no_results"}}</h3>
|
||||
<p class="tw-text-placeholder-text">{{ctx.Locale.Tr "repo.issues.filter_no_results_placeholder"}}</p>
|
||||
</div>
|
||||
{{end}}
|
||||
{{if .IssueIndexerUnavailable}}
|
||||
<div class="ui error message">
|
||||
|
@ -60,12 +60,20 @@ func TestEmptyRepoAddFile(t *testing.T) {
|
||||
defer tests.PrepareTestEnv(t)()
|
||||
|
||||
session := loginUser(t, "user30")
|
||||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadRepository)
|
||||
|
||||
// test web page
|
||||
req := NewRequest(t, "GET", "/user30/empty")
|
||||
resp := session.MakeRequest(t, req, http.StatusOK)
|
||||
bodyString := resp.Body.String()
|
||||
assert.Contains(t, bodyString, "empty-repo-guide")
|
||||
assert.True(t, test.IsNormalPageCompleted(bodyString))
|
||||
|
||||
// test api
|
||||
req = NewRequest(t, "GET", "/api/v1/repos/user30/empty/raw/main/README.md").AddTokenAuth(token)
|
||||
session.MakeRequest(t, req, http.StatusNotFound)
|
||||
|
||||
// create a new file
|
||||
req = NewRequest(t, "GET", "/user30/empty/_new/"+setting.Repository.DefaultBranch)
|
||||
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||
doc := NewHTMLParser(t, resp.Body).Find(`input[name="commit_choice"]`)
|
||||
|
@ -9,7 +9,10 @@ import (
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestGitSmartHTTP(t *testing.T) {
|
||||
@ -18,51 +21,55 @@ func TestGitSmartHTTP(t *testing.T) {
|
||||
|
||||
func testGitSmartHTTP(t *testing.T, u *url.URL) {
|
||||
kases := []struct {
|
||||
p string
|
||||
code int
|
||||
method, path string
|
||||
code int
|
||||
}{
|
||||
{
|
||||
p: "user2/repo1/info/refs",
|
||||
path: "user2/repo1/info/refs",
|
||||
code: http.StatusOK,
|
||||
},
|
||||
{
|
||||
p: "user2/repo1/HEAD",
|
||||
method: "HEAD",
|
||||
path: "user2/repo1/info/refs",
|
||||
code: http.StatusOK,
|
||||
},
|
||||
{
|
||||
path: "user2/repo1/HEAD",
|
||||
code: http.StatusOK,
|
||||
},
|
||||
{
|
||||
p: "user2/repo1/objects/info/alternates",
|
||||
path: "user2/repo1/objects/info/alternates",
|
||||
code: http.StatusNotFound,
|
||||
},
|
||||
{
|
||||
p: "user2/repo1/objects/info/http-alternates",
|
||||
path: "user2/repo1/objects/info/http-alternates",
|
||||
code: http.StatusNotFound,
|
||||
},
|
||||
{
|
||||
p: "user2/repo1/../../custom/conf/app.ini",
|
||||
path: "user2/repo1/../../custom/conf/app.ini",
|
||||
code: http.StatusNotFound,
|
||||
},
|
||||
{
|
||||
p: "user2/repo1/objects/info/../../../../custom/conf/app.ini",
|
||||
path: "user2/repo1/objects/info/../../../../custom/conf/app.ini",
|
||||
code: http.StatusNotFound,
|
||||
},
|
||||
{
|
||||
p: `user2/repo1/objects/info/..\..\..\..\custom\conf\app.ini`,
|
||||
path: `user2/repo1/objects/info/..\..\..\..\custom\conf\app.ini`,
|
||||
code: http.StatusBadRequest,
|
||||
},
|
||||
}
|
||||
|
||||
for _, kase := range kases {
|
||||
t.Run(kase.p, func(t *testing.T) {
|
||||
p := u.String() + kase.p
|
||||
req, err := http.NewRequest("GET", p, nil)
|
||||
assert.NoError(t, err)
|
||||
t.Run(kase.path, func(t *testing.T) {
|
||||
req, err := http.NewRequest(util.IfZero(kase.method, "GET"), u.String()+kase.path, nil)
|
||||
require.NoError(t, err)
|
||||
req.SetBasicAuth("user2", userPassword)
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
defer resp.Body.Close()
|
||||
assert.EqualValues(t, kase.code, resp.StatusCode)
|
||||
_, err = io.ReadAll(resp.Body)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -105,7 +105,6 @@ export default defineComponent({
|
||||
intervalID: null as IntervalId | null,
|
||||
currentJobStepsStates: [] as Array<Record<string, any>>,
|
||||
artifacts: [] as Array<Record<string, any>>,
|
||||
onHoverRerunIndex: -1,
|
||||
menuVisible: false,
|
||||
isFullScreen: false,
|
||||
timeVisible: {
|
||||
@ -120,7 +119,7 @@ export default defineComponent({
|
||||
link: '',
|
||||
title: '',
|
||||
titleHTML: '',
|
||||
status: 'unknown' as RunStatus,
|
||||
status: '' as RunStatus, // do not show the status before initialized, otherwise it would show an incorrect "error" icon
|
||||
canCancel: false,
|
||||
canApprove: false,
|
||||
canRerun: false,
|
||||
@ -492,13 +491,13 @@ export default defineComponent({
|
||||
<div class="action-view-left">
|
||||
<div class="job-group-section">
|
||||
<div class="job-brief-list">
|
||||
<a class="job-brief-item" :href="run.link+'/jobs/'+index" :class="parseInt(jobIndex) === index ? 'selected' : ''" v-for="(job, index) in run.jobs" :key="job.id" @mouseenter="onHoverRerunIndex = job.id" @mouseleave="onHoverRerunIndex = -1">
|
||||
<a class="job-brief-item" :href="run.link+'/jobs/'+index" :class="parseInt(jobIndex) === index ? 'selected' : ''" v-for="(job, index) in run.jobs" :key="job.id">
|
||||
<div class="job-brief-item-left">
|
||||
<ActionRunStatus :locale-status="locale.status[job.status]" :status="job.status"/>
|
||||
<span class="job-brief-name tw-mx-2 gt-ellipsis">{{ job.name }}</span>
|
||||
</div>
|
||||
<span class="job-brief-item-right">
|
||||
<SvgIcon name="octicon-sync" role="button" :data-tooltip-content="locale.rerun" class="job-brief-rerun tw-mx-2 link-action" :data-url="`${run.link}/jobs/${index}/rerun`" v-if="job.canRerun && onHoverRerunIndex === job.id"/>
|
||||
<SvgIcon name="octicon-sync" role="button" :data-tooltip-content="locale.rerun" class="job-brief-rerun tw-mx-2 link-action" :data-url="`${run.link}/jobs/${index}/rerun`" v-if="job.canRerun"/>
|
||||
<span class="step-summary-duration">{{ job.duration }}</span>
|
||||
</span>
|
||||
</a>
|
||||
@ -721,11 +720,6 @@ export default defineComponent({
|
||||
|
||||
.job-brief-item .job-brief-rerun {
|
||||
cursor: pointer;
|
||||
transition: transform 0.2s;
|
||||
}
|
||||
|
||||
.job-brief-item .job-brief-rerun:hover {
|
||||
transform: scale(130%);
|
||||
}
|
||||
|
||||
.job-brief-item .job-brief-item-left {
|
||||
|
Loading…
x
Reference in New Issue
Block a user