Skip to content

Commit 7696519

Browse files
authored
Merge branch 'release/v1.24' into backport34348
2 parents 97e425c + d2cbe2f commit 7696519

File tree

9 files changed

+201
-54
lines changed

9 files changed

+201
-54
lines changed

models/migrations/v1_23/v302.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,8 @@ func AddIndexToActionTaskStoppedLogExpired(x *xorm.Engine) error {
1414
Stopped timeutil.TimeStamp `xorm:"index(stopped_log_expired)"`
1515
LogExpired bool `xorm:"index(stopped_log_expired)"`
1616
}
17-
return x.Sync(new(ActionTask))
17+
_, err := x.SyncWithOptions(xorm.SyncOptions{
18+
IgnoreDropIndices: true,
19+
}, new(ActionTask))
20+
return err
1821
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// Copyright 2025 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package v1_23 //nolint
5+
6+
import (
7+
"testing"
8+
9+
"code.gitea.io/gitea/models/migrations/base"
10+
"code.gitea.io/gitea/modules/timeutil"
11+
12+
"github.com/stretchr/testify/assert"
13+
)
14+
15+
func Test_AddIndexToActionTaskStoppedLogExpired(t *testing.T) {
16+
type ActionTask struct {
17+
ID int64
18+
JobID int64
19+
Attempt int64
20+
RunnerID int64 `xorm:"index"`
21+
Status int `xorm:"index"`
22+
Started timeutil.TimeStamp `xorm:"index"`
23+
Stopped timeutil.TimeStamp `xorm:"index(stopped_log_expired)"`
24+
25+
RepoID int64 `xorm:"index"`
26+
OwnerID int64 `xorm:"index"`
27+
CommitSHA string `xorm:"index"`
28+
IsForkPullRequest bool
29+
30+
Token string `xorm:"-"`
31+
TokenHash string `xorm:"UNIQUE"` // sha256 of token
32+
TokenSalt string
33+
TokenLastEight string `xorm:"index token_last_eight"`
34+
35+
LogFilename string // file name of log
36+
LogInStorage bool // read log from database or from storage
37+
LogLength int64 // lines count
38+
LogSize int64 // blob size
39+
LogIndexes []int64 `xorm:"LONGBLOB"` // line number to offset
40+
LogExpired bool `xorm:"index(stopped_log_expired)"` // files that are too old will be deleted
41+
42+
Created timeutil.TimeStamp `xorm:"created"`
43+
Updated timeutil.TimeStamp `xorm:"updated index"`
44+
}
45+
46+
// Prepare and load the testing database
47+
x, deferable := base.PrepareTestEnv(t, 0, new(ActionTask))
48+
defer deferable()
49+
50+
assert.NoError(t, AddIndexToActionTaskStoppedLogExpired(x))
51+
}

models/migrations/v1_23/v304.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,8 @@ func AddIndexForReleaseSha1(x *xorm.Engine) error {
99
type Release struct {
1010
Sha1 string `xorm:"INDEX VARCHAR(64)"`
1111
}
12-
return x.Sync(new(Release))
12+
_, err := x.SyncWithOptions(xorm.SyncOptions{
13+
IgnoreDropIndices: true,
14+
}, new(Release))
15+
return err
1316
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// Copyright 2025 The Gitea Authors. All rights reserved.
2+
// SPDX-License-Identifier: MIT
3+
4+
package v1_23 //nolint
5+
6+
import (
7+
"testing"
8+
9+
"code.gitea.io/gitea/models/migrations/base"
10+
"code.gitea.io/gitea/modules/timeutil"
11+
12+
"github.com/stretchr/testify/assert"
13+
)
14+
15+
func Test_AddIndexForReleaseSha1(t *testing.T) {
16+
type Release struct {
17+
ID int64 `xorm:"pk autoincr"`
18+
RepoID int64 `xorm:"INDEX UNIQUE(n)"`
19+
PublisherID int64 `xorm:"INDEX"`
20+
TagName string `xorm:"INDEX UNIQUE(n)"`
21+
OriginalAuthor string
22+
OriginalAuthorID int64 `xorm:"index"`
23+
LowerTagName string
24+
Target string
25+
Title string
26+
Sha1 string `xorm:"VARCHAR(64)"`
27+
NumCommits int64
28+
Note string `xorm:"TEXT"`
29+
IsDraft bool `xorm:"NOT NULL DEFAULT false"`
30+
IsPrerelease bool `xorm:"NOT NULL DEFAULT false"`
31+
IsTag bool `xorm:"NOT NULL DEFAULT false"` // will be true only if the record is a tag and has no related releases
32+
CreatedUnix timeutil.TimeStamp `xorm:"INDEX"`
33+
}
34+
35+
// Prepare and load the testing database
36+
x, deferable := base.PrepareTestEnv(t, 0, new(Release))
37+
defer deferable()
38+
39+
assert.NoError(t, AddIndexForReleaseSha1(x))
40+
}

routers/api/v1/repo/issue_comment.go

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -609,15 +609,17 @@ func editIssueComment(ctx *context.APIContext, form api.EditIssueCommentOption)
609609
return
610610
}
611611

612-
oldContent := comment.Content
613-
comment.Content = form.Body
614-
if err := issue_service.UpdateComment(ctx, comment, comment.ContentVersion, ctx.Doer, oldContent); err != nil {
615-
if errors.Is(err, user_model.ErrBlockedUser) {
616-
ctx.APIError(http.StatusForbidden, err)
617-
} else {
618-
ctx.APIErrorInternal(err)
612+
if form.Body != comment.Content {
613+
oldContent := comment.Content
614+
comment.Content = form.Body
615+
if err := issue_service.UpdateComment(ctx, comment, comment.ContentVersion, ctx.Doer, oldContent); err != nil {
616+
if errors.Is(err, user_model.ErrBlockedUser) {
617+
ctx.APIError(http.StatusForbidden, err)
618+
} else {
619+
ctx.APIErrorInternal(err)
620+
}
621+
return
619622
}
620-
return
621623
}
622624

623625
ctx.JSON(http.StatusOK, convert.ToAPIComment(ctx, ctx.Repo.Repository, comment))

routers/web/repo/actions/view.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -584,20 +584,20 @@ func getRunJobs(ctx *context_module.Context, runIndex, jobIndex int64) (*actions
584584
run, err := actions_model.GetRunByIndex(ctx, ctx.Repo.Repository.ID, runIndex)
585585
if err != nil {
586586
if errors.Is(err, util.ErrNotExist) {
587-
ctx.HTTPError(http.StatusNotFound, err.Error())
587+
ctx.NotFound(nil)
588588
return nil, nil
589589
}
590-
ctx.HTTPError(http.StatusInternalServerError, err.Error())
590+
ctx.ServerError("GetRunByIndex", err)
591591
return nil, nil
592592
}
593593
run.Repo = ctx.Repo.Repository
594594
jobs, err := actions_model.GetRunJobsByRunID(ctx, run.ID)
595595
if err != nil {
596-
ctx.HTTPError(http.StatusInternalServerError, err.Error())
596+
ctx.ServerError("GetRunJobsByRunID", err)
597597
return nil, nil
598598
}
599599
if len(jobs) == 0 {
600-
ctx.HTTPError(http.StatusNotFound)
600+
ctx.NotFound(nil)
601601
return nil, nil
602602
}
603603

routers/web/repo/issue_comment.go

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -239,21 +239,28 @@ func UpdateCommentContent(ctx *context.Context) {
239239
return
240240
}
241241

242-
oldContent := comment.Content
243242
newContent := ctx.FormString("content")
244243
contentVersion := ctx.FormInt("content_version")
244+
if contentVersion != comment.ContentVersion {
245+
ctx.JSONError(ctx.Tr("repo.comments.edit.already_changed"))
246+
return
247+
}
245248

246-
// allow to save empty content
247-
comment.Content = newContent
248-
if err = issue_service.UpdateComment(ctx, comment, contentVersion, ctx.Doer, oldContent); err != nil {
249-
if errors.Is(err, user_model.ErrBlockedUser) {
250-
ctx.JSONError(ctx.Tr("repo.issues.comment.blocked_user"))
251-
} else if errors.Is(err, issues_model.ErrCommentAlreadyChanged) {
252-
ctx.JSONError(ctx.Tr("repo.comments.edit.already_changed"))
253-
} else {
254-
ctx.ServerError("UpdateComment", err)
249+
if newContent != comment.Content {
250+
// allow to save empty content
251+
oldContent := comment.Content
252+
comment.Content = newContent
253+
254+
if err = issue_service.UpdateComment(ctx, comment, contentVersion, ctx.Doer, oldContent); err != nil {
255+
if errors.Is(err, user_model.ErrBlockedUser) {
256+
ctx.JSONError(ctx.Tr("repo.issues.comment.blocked_user"))
257+
} else if errors.Is(err, issues_model.ErrCommentAlreadyChanged) {
258+
ctx.JSONError(ctx.Tr("repo.comments.edit.already_changed"))
259+
} else {
260+
ctx.ServerError("UpdateComment", err)
261+
}
262+
return
255263
}
256-
return
257264
}
258265

259266
if err := comment.LoadAttachments(ctx); err != nil {

tests/integration/repo_webhook_test.go

Lines changed: 61 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -241,19 +241,68 @@ func Test_WebhookIssueComment(t *testing.T) {
241241

242242
testAPICreateWebhookForRepo(t, session, "user2", "repo1", provider.URL(), "issue_comment")
243243

244-
// 2. trigger the webhook
245-
issueURL := testNewIssue(t, session, "user2", "repo1", "Title2", "Description2")
246-
testIssueAddComment(t, session, issueURL, "issue title2 comment1", "")
244+
t.Run("create comment", func(t *testing.T) {
245+
// 2. trigger the webhook
246+
issueURL := testNewIssue(t, session, "user2", "repo1", "Title2", "Description2")
247+
testIssueAddComment(t, session, issueURL, "issue title2 comment1", "")
247248

248-
// 3. validate the webhook is triggered
249-
assert.Equal(t, "issue_comment", triggeredEvent)
250-
assert.Len(t, payloads, 1)
251-
assert.EqualValues(t, "created", payloads[0].Action)
252-
assert.Equal(t, "repo1", payloads[0].Issue.Repo.Name)
253-
assert.Equal(t, "user2/repo1", payloads[0].Issue.Repo.FullName)
254-
assert.Equal(t, "Title2", payloads[0].Issue.Title)
255-
assert.Equal(t, "Description2", payloads[0].Issue.Body)
256-
assert.Equal(t, "issue title2 comment1", payloads[0].Comment.Body)
249+
// 3. validate the webhook is triggered
250+
assert.Equal(t, "issue_comment", triggeredEvent)
251+
assert.Len(t, payloads, 1)
252+
assert.EqualValues(t, "created", payloads[0].Action)
253+
assert.Equal(t, "repo1", payloads[0].Issue.Repo.Name)
254+
assert.Equal(t, "user2/repo1", payloads[0].Issue.Repo.FullName)
255+
assert.Equal(t, "Title2", payloads[0].Issue.Title)
256+
assert.Equal(t, "Description2", payloads[0].Issue.Body)
257+
assert.Equal(t, "issue title2 comment1", payloads[0].Comment.Body)
258+
})
259+
260+
t.Run("update comment", func(t *testing.T) {
261+
payloads = make([]api.IssueCommentPayload, 0, 2)
262+
triggeredEvent = ""
263+
264+
// 2. trigger the webhook
265+
issueURL := testNewIssue(t, session, "user2", "repo1", "Title3", "Description3")
266+
commentID := testIssueAddComment(t, session, issueURL, "issue title3 comment1", "")
267+
modifiedContent := "issue title2 comment1 - modified"
268+
req := NewRequestWithValues(t, "POST", fmt.Sprintf("/%s/%s/comments/%d", "user2", "repo1", commentID), map[string]string{
269+
"_csrf": GetUserCSRFToken(t, session),
270+
"content": modifiedContent,
271+
})
272+
session.MakeRequest(t, req, http.StatusOK)
273+
274+
// 3. validate the webhook is triggered
275+
assert.Equal(t, "issue_comment", triggeredEvent)
276+
assert.Len(t, payloads, 2)
277+
assert.EqualValues(t, "edited", payloads[1].Action)
278+
assert.Equal(t, "repo1", payloads[1].Issue.Repo.Name)
279+
assert.Equal(t, "user2/repo1", payloads[1].Issue.Repo.FullName)
280+
assert.Equal(t, "Title3", payloads[1].Issue.Title)
281+
assert.Equal(t, "Description3", payloads[1].Issue.Body)
282+
assert.Equal(t, modifiedContent, payloads[1].Comment.Body)
283+
})
284+
285+
t.Run("Update comment with no content change", func(t *testing.T) {
286+
payloads = make([]api.IssueCommentPayload, 0, 2)
287+
triggeredEvent = ""
288+
commentContent := "issue title3 comment1"
289+
290+
// 2. trigger the webhook
291+
issueURL := testNewIssue(t, session, "user2", "repo1", "Title3", "Description3")
292+
commentID := testIssueAddComment(t, session, issueURL, commentContent, "")
293+
294+
payloads = make([]api.IssueCommentPayload, 0, 2)
295+
triggeredEvent = ""
296+
req := NewRequestWithValues(t, "POST", fmt.Sprintf("/%s/%s/comments/%d", "user2", "repo1", commentID), map[string]string{
297+
"_csrf": GetUserCSRFToken(t, session),
298+
"content": commentContent,
299+
})
300+
session.MakeRequest(t, req, http.StatusOK)
301+
302+
// 3. validate the webhook is not triggered because no content change
303+
assert.Empty(t, triggeredEvent)
304+
assert.Empty(t, payloads)
305+
})
257306
})
258307
}
259308

web_src/css/features/projects.css

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
.project-header {
1212
padding: 0.5em 0;
13-
overflow-x: auto; /* in fullscreen mode, the position is fixed, so we can't use "flex wrap" which would change the height */
13+
flex-wrap: wrap;
1414
}
1515

1616
.project-header h2 {
@@ -101,17 +101,11 @@
101101
opacity: 0;
102102
}
103103

104-
.fullscreen.projects-view .project-header {
105-
position: fixed;
106-
z-index: 1000;
107-
top: 0;
108-
left: 0;
109-
right: 0;
110-
padding: 0.5em;
111-
width: 100%;
112-
max-width: 100%;
113-
background-color: var(--color-body);
114-
border-bottom: 1px solid var(--color-secondary);
104+
.fullscreen.projects-view {
105+
position: absolute;
106+
inset: 0;
107+
display: flex;
108+
flex-direction: column;
115109
}
116110

117111
/* Hide project-description in full-screen due to its variable height. No need to show it for better space use. */
@@ -120,9 +114,7 @@
120114
}
121115

122116
.fullscreen.projects-view #project-board {
123-
position: absolute;
124-
top: 60px;
125-
left: 0;
126-
right: 0;
127-
max-height: calc(100vh - 70px);
117+
flex: 1;
118+
max-height: unset;
119+
padding-bottom: 0.5em;
128120
}

0 commit comments

Comments
 (0)