Compare commits

...

13 Commits

Author SHA1 Message Date
Giteabot 810de30345
Merge branch 'main' into fix-comment-edit-submit 2024-04-27 22:04:34 +08:00
wxiaoguang 6d2a307ad8
Rename migration package name for 1.22-rc1 (#30730)
Ref: Propose to restart 1.22 release #30501
2024-04-27 14:02:07 +00:00
silverwind b93c87b6fe
Issue card improvements (#30687)
Fixes https://github.com/go-gitea/gitea/issues/30682 and does a few
improvements:

- Use gap instead of margin/padding
- Don't render empty image div
- Remove `right floated` class that did nothing

<img width="406" alt="Screenshot 2024-04-24 at 20 21 20"
src="https://github.com/go-gitea/gitea/assets/115237/2fa88707-c2c4-40df-aee7-a684c3097ed0">

---------

Co-authored-by: KN4CK3R <admin@oldschoolhack.me>
2024-04-27 13:35:26 +00:00
Yarden Shoham 51c28d9683
Don't show loading indicators when refreshing the system status (#30712)
Signed-off-by: Yarden Shoham <git@yardenshoham.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
2024-04-27 13:05:06 +00:00
wxiaoguang d3cdef88ad
Add some tests to clarify the "must-change-password" behavior (#30693)
Follow  #30472:

When a user is created by command line `./gitea admin user create`:

Old behavior before #30472: the first user (admin or non-admin) doesn't
need to change password.

Revert to the old behavior before #30472
2024-04-27 12:23:37 +00:00
Kemal Zebari dd301cae1c
Prevent allow/reject reviews on merged/closed PRs (#30686)
Resolves #30675.
2024-04-27 11:55:03 +00:00
silverwind 238eb3ff9f
Update JS dependencies (#30713)
- Update all JS dependencies
- Remove
[now-unnecessary](https://github.com/microsoft/monaco-editor/issues/4325)
monaco workaround
- Update stylelint config for new rule
- Tested Monaco, Swagger UI, Mermaid
2024-04-27 11:28:28 +00:00
silverwind b2abac5e5f
Improve diff stats bar (#30669)
Minor tweaks:

- Remove unnecessary `item` class which was causing unwanted padding to
be added.
- Add some padding and prevent wrapping so it looks better on mobile.
- Increase width by 4px.

<img width="116" alt="Screenshot 2024-04-24 at 00 15 07"
src="https://github.com/go-gitea/gitea/assets/115237/1f1cf54c-8053-4297-b309-71d9c2ceb9ee">
<img width="441" alt="Screenshot 2024-04-24 at 00 14 57"
src="https://github.com/go-gitea/gitea/assets/115237/2f3a33dc-edad-4b97-b64c-6812aae513cb">
2024-04-27 11:22:55 +00:00
Chongyi Zheng 4ae6b1a553
Remove unused parameter for some functions in `services/mirror` (#30724)
Suggested by gopls `unusedparams`
2024-04-27 10:44:49 +00:00
silverwind 9b2536b78f
Update misspell to 0.5.1 and add `misspellings.csv` (#30573)
Misspell 0.5.0 supports passing a csv file to extend the list of
misspellings, so I added some common ones from the codebase. There is at
least one typo in a API response so we need to decided whether to revert
that and then likely remove the dict entry.
2024-04-27 08:03:49 +00:00
silverwind dcc3c17e5c
Suppress browserslist warning in webpack target (#30571)
1. Set
[`BROWSERSLIST_IGNORE_OLD_DATA`](c6ddf7b387/node.js (L400))
to avoid warning on outdated browserslist data which the end user can
likely not do anything about and which is currently visible in the v1.21
branch.
2. Suppress all command echoing and add a "Running webpack..." message
in place.

Warning in question was this:

```
Browserslist: caniuse-lite is outdated. Please run:
  npx update-browserslist-db@latest
  Why you should do it regularly: https://github.com/browserslist/update-db#readme
```
2024-04-27 07:21:07 +00:00
GiteaBot 27861d711b [skip ci] Updated translations via Crowdin 2024-04-27 00:24:31 +00:00
silverwind c93eefb42b
Diff color enhancements, add line number background (#30670)
1. Bring back the background on line numbers. This feature was lost a
long time ago.

<img width="457" alt="Screenshot 2024-04-24 at 01 36 09"
src="https://github.com/go-gitea/gitea/assets/115237/76a7f5a9-c22a-4c72-9f0a-ebf16a66513e">
<img width="473" alt="Screenshot 2024-04-24 at 01 22 47"
src="https://github.com/go-gitea/gitea/assets/115237/eef06cf2-f1b9-40e3-947d-dd5852ec12a3">
<img width="457" alt="Screenshot 2024-04-24 at 02 13 18"
src="https://github.com/go-gitea/gitea/assets/115237/59e317d4-76a7-468c-8a19-10d88c675cc3">
<img width="459" alt="Screenshot 2024-04-24 at 01 23 21"
src="https://github.com/go-gitea/gitea/assets/115237/f1a46f8d-8846-4d78-a9d7-8b7dc18ac6e4">

2. Expanded lines background is now full-line, including line numbers:

<img width="1303" alt="Screenshot 2024-04-24 at 01 37 12"
src="https://github.com/go-gitea/gitea/assets/115237/271eefe2-0869-424e-93fb-ccd8adc87806">

3. Sort affected colors alphabetically in the CSS

Fixes #14603
2024-04-26 19:37:21 +00:00
71 changed files with 752 additions and 543 deletions

View File

@ -30,7 +30,7 @@ EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-che
GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.6.0 GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.6.0
GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/cmd/golangci-lint@v1.57.2 GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/cmd/golangci-lint@v1.57.2
GXZ_PACKAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.11 GXZ_PACKAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.11
MISSPELL_PACKAGE ?= github.com/golangci/misspell/cmd/misspell@v0.4.1 MISSPELL_PACKAGE ?= github.com/golangci/misspell/cmd/misspell@v0.5.1
SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@db51e79a0e37c572d8b59ae0c58bf2bbbbe53285 SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@db51e79a0e37c572d8b59ae0c58bf2bbbbe53285
XGO_PACKAGE ?= src.techknowlogick.com/xgo@latest XGO_PACKAGE ?= src.techknowlogick.com/xgo@latest
GO_LICENSES_PACKAGE ?= github.com/google/go-licenses@v1 GO_LICENSES_PACKAGE ?= github.com/google/go-licenses@v1
@ -397,11 +397,11 @@ lint-md: node_modules
.PHONY: lint-spell .PHONY: lint-spell
lint-spell: lint-spell:
@go run $(MISSPELL_PACKAGE) -error $(SPELLCHECK_FILES) @go run $(MISSPELL_PACKAGE) -dict tools/misspellings.csv -error $(SPELLCHECK_FILES)
.PHONY: lint-spell-fix .PHONY: lint-spell-fix
lint-spell-fix: lint-spell-fix:
@go run $(MISSPELL_PACKAGE) -w $(SPELLCHECK_FILES) @go run $(MISSPELL_PACKAGE) -dict tools/misspellings.csv -w $(SPELLCHECK_FILES)
.PHONY: lint-go .PHONY: lint-go
lint-go: lint-go:
@ -908,8 +908,9 @@ webpack: $(WEBPACK_DEST)
$(WEBPACK_DEST): $(WEBPACK_SOURCES) $(WEBPACK_CONFIGS) package-lock.json $(WEBPACK_DEST): $(WEBPACK_SOURCES) $(WEBPACK_CONFIGS) package-lock.json
@$(MAKE) -s node-check node_modules @$(MAKE) -s node-check node_modules
rm -rf $(WEBPACK_DEST_ENTRIES) @rm -rf $(WEBPACK_DEST_ENTRIES)
npx webpack @echo "Running webpack..."
@BROWSERSLIST_IGNORE_OLD_DATA=true npx webpack
@touch $(WEBPACK_DEST) @touch $(WEBPACK_DEST)
.PHONY: svg .PHONY: svg

View File

@ -35,7 +35,7 @@ var microcmdUserChangePassword = &cli.Command{
}, },
&cli.BoolFlag{ &cli.BoolFlag{
Name: "must-change-password", Name: "must-change-password",
Usage: "User must change password", Usage: "User must change password (can be disabled by --must-change-password=false)",
Value: true, Value: true,
}, },
}, },

View File

@ -4,6 +4,7 @@
package cmd package cmd
import ( import (
"context"
"errors" "errors"
"fmt" "fmt"
@ -48,7 +49,7 @@ var microcmdUserCreate = &cli.Command{
}, },
&cli.BoolFlag{ &cli.BoolFlag{
Name: "must-change-password", Name: "must-change-password",
Usage: "Set to false to prevent forcing the user to change their password after initial login", Usage: "User must change password after initial login, defaults to true for all users except the first one (can be disabled by --must-change-password=false)",
DisableDefaultText: true, DisableDefaultText: true,
}, },
&cli.IntFlag{ &cli.IntFlag{
@ -91,11 +92,16 @@ func runCreateUser(c *cli.Context) error {
_, _ = fmt.Fprintf(c.App.ErrWriter, "--name flag is deprecated. Use --username instead.\n") _, _ = fmt.Fprintf(c.App.ErrWriter, "--name flag is deprecated. Use --username instead.\n")
} }
ctx, cancel := installSignals() ctx := c.Context
defer cancel() if !setting.IsInTesting {
// FIXME: need to refactor the "installSignals/initDB" related code later
if err := initDB(ctx); err != nil { // it doesn't make sense to call it in (almost) every command action function
return err var cancel context.CancelFunc
ctx, cancel = installSignals()
defer cancel()
if err := initDB(ctx); err != nil {
return err
}
} }
var password string var password string
@ -123,8 +129,8 @@ func runCreateUser(c *cli.Context) error {
if err != nil { if err != nil {
return fmt.Errorf("IsTableNotEmpty: %w", err) return fmt.Errorf("IsTableNotEmpty: %w", err)
} }
if !hasUserRecord && isAdmin { if !hasUserRecord {
// if this is the first admin being created, don't force to change password (keep the old behavior) // if this is the first one being created, don't force to change password (keep the old behavior)
mustChangePassword = false mustChangePassword = false
} }
} }

View File

@ -0,0 +1,44 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package cmd
import (
"fmt"
"strings"
"testing"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
"github.com/stretchr/testify/assert"
)
func TestAdminUserCreate(t *testing.T) {
app := NewMainApp(AppVersion{})
reset := func() {
assert.NoError(t, db.TruncateBeans(db.DefaultContext, &user_model.User{}))
assert.NoError(t, db.TruncateBeans(db.DefaultContext, &user_model.EmailAddress{}))
}
type createCheck struct{ IsAdmin, MustChangePassword bool }
createUser := func(name, args string) createCheck {
assert.NoError(t, app.Run(strings.Fields(fmt.Sprintf("./gitea admin user create --username %s --email %s@gitea.local %s --password foobar", name, name, args))))
u := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: name})
return createCheck{u.IsAdmin, u.MustChangePassword}
}
reset()
assert.Equal(t, createCheck{IsAdmin: false, MustChangePassword: false}, createUser("u", ""), "first non-admin user doesn't need to change password")
reset()
assert.Equal(t, createCheck{IsAdmin: true, MustChangePassword: false}, createUser("u", "--admin"), "first admin user doesn't need to change password")
reset()
assert.Equal(t, createCheck{IsAdmin: true, MustChangePassword: true}, createUser("u", "--admin --must-change-password"))
assert.Equal(t, createCheck{IsAdmin: true, MustChangePassword: true}, createUser("u2", "--admin"))
assert.Equal(t, createCheck{IsAdmin: true, MustChangePassword: false}, createUser("u3", "--admin --must-change-password=false"))
assert.Equal(t, createCheck{IsAdmin: false, MustChangePassword: true}, createUser("u4", ""))
assert.Equal(t, createCheck{IsAdmin: false, MustChangePassword: false}, createUser("u5", "--must-change-password=false"))
}

View File

@ -112,13 +112,18 @@ func prepareWorkPathAndCustomConf(action cli.ActionFunc) func(ctx *cli.Context)
} }
} }
func NewMainApp(version, versionExtra string) *cli.App { type AppVersion struct {
Version string
Extra string
}
func NewMainApp(appVer AppVersion) *cli.App {
app := cli.NewApp() app := cli.NewApp()
app.Name = "Gitea" app.Name = "Gitea"
app.HelpName = "gitea" app.HelpName = "gitea"
app.Usage = "A painless self-hosted Git service" app.Usage = "A painless self-hosted Git service"
app.Description = `Gitea program contains "web" and other subcommands. If no subcommand is given, it starts the web server by default. Use "web" subcommand for more web server arguments, use other subcommands for other purposes.` app.Description = `Gitea program contains "web" and other subcommands. If no subcommand is given, it starts the web server by default. Use "web" subcommand for more web server arguments, use other subcommands for other purposes.`
app.Version = version + versionExtra app.Version = appVer.Version + appVer.Extra
app.EnableBashCompletion = true app.EnableBashCompletion = true
// these sub-commands need to use config file // these sub-commands need to use config file

View File

@ -28,7 +28,7 @@ func makePathOutput(workPath, customPath, customConf string) string {
} }
func newTestApp(testCmdAction func(ctx *cli.Context) error) *cli.App { func newTestApp(testCmdAction func(ctx *cli.Context) error) *cli.App {
app := NewMainApp("version", "version-extra") app := NewMainApp(AppVersion{})
testCmd := &cli.Command{Name: "test-cmd", Action: testCmdAction} testCmd := &cli.Command{Name: "test-cmd", Action: testCmdAction}
prepareSubcommandWithConfig(testCmd, appGlobalFlags()) prepareSubcommandWithConfig(testCmd, appGlobalFlags())
app.Commands = append(app.Commands, testCmd) app.Commands = append(app.Commands, testCmd)

View File

@ -1322,7 +1322,7 @@ Defaultly every storage has their default base path like below
| actions_log | actions_log/ | | actions_log | actions_log/ |
| actions_artifacts | actions_artifacts/ | | actions_artifacts | actions_artifacts/ |
And bucket, basepath or `SERVE_DIRECT` could be special or overrided, if you want to use a different you can: And bucket, basepath or `SERVE_DIRECT` could be special or overridden, if you want to use a different you can:
```ini ```ini
[storage.actions_log] [storage.actions_log]

View File

@ -42,7 +42,7 @@ func main() {
log.GetManager().Close() log.GetManager().Close()
os.Exit(code) os.Exit(code)
} }
app := cmd.NewMainApp(Version, formatBuiltWith()) app := cmd.NewMainApp(cmd.AppVersion{Version: Version, Extra: formatBuiltWith()})
_ = cmd.RunMainApp(app, os.Args...) // all errors should have been handled by the RunMainApp _ = cmd.RunMainApp(app, os.Args...) // all errors should have been handled by the RunMainApp
log.GetManager().Close() log.GetManager().Close()
} }

View File

@ -262,11 +262,11 @@ func CancelPreviousJobs(ctx context.Context, repoID int64, ref, workflowID strin
// InsertRun inserts a run // InsertRun inserts a run
func InsertRun(ctx context.Context, run *ActionRun, jobs []*jobparser.SingleWorkflow) error { func InsertRun(ctx context.Context, run *ActionRun, jobs []*jobparser.SingleWorkflow) error {
ctx, commiter, err := db.TxContext(ctx) ctx, committer, err := db.TxContext(ctx)
if err != nil { if err != nil {
return err return err
} }
defer commiter.Close() defer committer.Close()
index, err := db.GetNextResourceIndex(ctx, "action_run_index", run.RepoID) index, err := db.GetNextResourceIndex(ctx, "action_run_index", run.RepoID)
if err != nil { if err != nil {
@ -331,7 +331,7 @@ func InsertRun(ctx context.Context, run *ActionRun, jobs []*jobparser.SingleWork
} }
} }
return commiter.Commit() return committer.Commit()
} }
func GetRunByID(ctx context.Context, id int64) (*ActionRun, error) { func GetRunByID(ctx context.Context, id int64) (*ActionRun, error) {

View File

@ -216,11 +216,11 @@ func GetRunningTaskByToken(ctx context.Context, token string) (*ActionTask, erro
} }
func CreateTaskForRunner(ctx context.Context, runner *ActionRunner) (*ActionTask, bool, error) { func CreateTaskForRunner(ctx context.Context, runner *ActionRunner) (*ActionTask, bool, error) {
ctx, commiter, err := db.TxContext(ctx) ctx, committer, err := db.TxContext(ctx)
if err != nil { if err != nil {
return nil, false, err return nil, false, err
} }
defer commiter.Close() defer committer.Close()
e := db.GetEngine(ctx) e := db.GetEngine(ctx)
@ -322,7 +322,7 @@ func CreateTaskForRunner(ctx context.Context, runner *ActionRunner) (*ActionTask
task.Job = job task.Job = job
if err := commiter.Commit(); err != nil { if err := committer.Commit(); err != nil {
return nil, false, err return nil, false, err
} }
@ -347,11 +347,11 @@ func UpdateTaskByState(ctx context.Context, state *runnerv1.TaskState) (*ActionT
stepStates[v.Id] = v stepStates[v.Id] = v
} }
ctx, commiter, err := db.TxContext(ctx) ctx, committer, err := db.TxContext(ctx)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer commiter.Close() defer committer.Close()
e := db.GetEngine(ctx) e := db.GetEngine(ctx)
@ -412,7 +412,7 @@ func UpdateTaskByState(ctx context.Context, state *runnerv1.TaskState) (*ActionT
} }
} }
if err := commiter.Commit(); err != nil { if err := committer.Commit(); err != nil {
return nil, err return nil, err
} }

View File

@ -13,7 +13,7 @@ import (
// ActionTasksVersion // ActionTasksVersion
// If both ownerID and repoID is zero, its scope is global. // If both ownerID and repoID is zero, its scope is global.
// If ownerID is not zero and repoID is zero, its scope is org (there is no user-level runner currrently). // If ownerID is not zero and repoID is zero, its scope is org (there is no user-level runner currently).
// If ownerID is zero and repoID is not zero, its scope is repo. // If ownerID is zero and repoID is not zero, its scope is repo.
type ActionTasksVersion struct { type ActionTasksVersion struct {
ID int64 `xorm:"pk autoincr"` ID int64 `xorm:"pk autoincr"`
@ -73,11 +73,11 @@ func increaseTasksVersionByScope(ctx context.Context, ownerID, repoID int64) err
} }
func IncreaseTaskVersion(ctx context.Context, ownerID, repoID int64) error { func IncreaseTaskVersion(ctx context.Context, ownerID, repoID int64) error {
ctx, commiter, err := db.TxContext(ctx) ctx, committer, err := db.TxContext(ctx)
if err != nil { if err != nil {
return err return err
} }
defer commiter.Close() defer committer.Close()
// 1. increase global // 1. increase global
if err := increaseTasksVersionByScope(ctx, 0, 0); err != nil { if err := increaseTasksVersionByScope(ctx, 0, 0); err != nil {
@ -101,5 +101,5 @@ func IncreaseTaskVersion(ctx context.Context, ownerID, repoID int64) error {
} }
} }
return commiter.Commit() return committer.Commit()
} }

View File

@ -1,7 +1,7 @@
- -
id: 1 id: 1
type: 0 # gitea pull request type: 0 # gitea pull request
status: 2 # mergable status: 2 # mergeable
issue_id: 2 issue_id: 2
index: 2 index: 2
head_repo_id: 1 head_repo_id: 1
@ -16,7 +16,7 @@
- -
id: 2 id: 2
type: 0 # gitea pull request type: 0 # gitea pull request
status: 2 # mergable status: 2 # mergeable
issue_id: 3 issue_id: 3
index: 3 index: 3
head_repo_id: 1 head_repo_id: 1
@ -29,7 +29,7 @@
- -
id: 3 id: 3
type: 0 # gitea pull request type: 0 # gitea pull request
status: 2 # mergable status: 2 # mergeable
issue_id: 8 issue_id: 8
index: 1 index: 1
head_repo_id: 11 head_repo_id: 11
@ -42,7 +42,7 @@
- -
id: 4 id: 4
type: 0 # gitea pull request type: 0 # gitea pull request
status: 2 # mergable status: 2 # mergeable
issue_id: 9 issue_id: 9
index: 1 index: 1
head_repo_id: 48 head_repo_id: 48
@ -55,7 +55,7 @@
- -
id: 5 # this PR is outdated (one commit behind branch1 ) id: 5 # this PR is outdated (one commit behind branch1 )
type: 0 # gitea pull request type: 0 # gitea pull request
status: 2 # mergable status: 2 # mergeable
issue_id: 11 issue_id: 11
index: 5 index: 5
head_repo_id: 1 head_repo_id: 1
@ -68,7 +68,7 @@
- -
id: 6 id: 6
type: 0 # gitea pull request type: 0 # gitea pull request
status: 2 # mergable status: 2 # mergeable
issue_id: 12 issue_id: 12
index: 2 index: 2
head_repo_id: 3 head_repo_id: 3
@ -81,7 +81,7 @@
- -
id: 7 id: 7
type: 0 # gitea pull request type: 0 # gitea pull request
status: 2 # mergable status: 2 # mergeable
issue_id: 19 issue_id: 19
index: 1 index: 1
head_repo_id: 58 head_repo_id: 58
@ -94,7 +94,7 @@
- -
id: 8 id: 8
type: 0 # gitea pull request type: 0 # gitea pull request
status: 2 # mergable status: 2 # mergeable
issue_id: 20 issue_id: 20
index: 1 index: 1
head_repo_id: 23 head_repo_id: 23
@ -103,7 +103,7 @@
- -
id: 9 id: 9
type: 0 # gitea pull request type: 0 # gitea pull request
status: 2 # mergable status: 2 # mergeable
issue_id: 21 issue_id: 21
index: 1 index: 1
head_repo_id: 60 head_repo_id: 60
@ -112,7 +112,7 @@
- -
id: 10 id: 10
type: 0 # gitea pull request type: 0 # gitea pull request
status: 2 # mergable status: 2 # mergeable
issue_id: 22 issue_id: 22
index: 1 index: 1
head_repo_id: 61 head_repo_id: 61

View File

@ -807,7 +807,7 @@ func UpdateAllowEdits(ctx context.Context, pr *PullRequest) error {
// Mergeable returns if the pullrequest is mergeable. // Mergeable returns if the pullrequest is mergeable.
func (pr *PullRequest) Mergeable(ctx context.Context) bool { func (pr *PullRequest) Mergeable(ctx context.Context) bool {
// If a pull request isn't mergable if it's: // If a pull request isn't mergeable if it's:
// - Being conflict checked. // - Being conflict checked.
// - Has a conflict. // - Has a conflict.
// - Received a error while being conflict checked. // - Received a error while being conflict checked.

View File

@ -187,8 +187,8 @@ func AddTime(ctx context.Context, user *user_model.User, issue *Issue, amount in
Issue: issue, Issue: issue,
Repo: issue.Repo, Repo: issue.Repo,
Doer: user, Doer: user,
// Content before v1.21 did store the formated string instead of seconds, // Content before v1.21 did store the formatted string instead of seconds,
// so use "|" as delimeter to mark the new format // so use "|" as delimiter to mark the new format
Content: fmt.Sprintf("|%d", amount), Content: fmt.Sprintf("|%d", amount),
Type: CommentTypeAddTimeManual, Type: CommentTypeAddTimeManual,
TimeID: t.ID, TimeID: t.ID,
@ -267,8 +267,8 @@ func DeleteIssueUserTimes(ctx context.Context, issue *Issue, user *user_model.Us
Issue: issue, Issue: issue,
Repo: issue.Repo, Repo: issue.Repo,
Doer: user, Doer: user,
// Content before v1.21 did store the formated string instead of seconds, // Content before v1.21 did store the formatted string instead of seconds,
// so use "|" as delimeter to mark the new format // so use "|" as delimiter to mark the new format
Content: fmt.Sprintf("|%d", removedTime), Content: fmt.Sprintf("|%d", removedTime),
Type: CommentTypeDeleteTimeManual, Type: CommentTypeDeleteTimeManual,
}); err != nil { }); err != nil {
@ -298,8 +298,8 @@ func DeleteTime(ctx context.Context, t *TrackedTime) error {
Issue: t.Issue, Issue: t.Issue,
Repo: t.Issue.Repo, Repo: t.Issue.Repo,
Doer: t.User, Doer: t.User,
// Content before v1.21 did store the formated string instead of seconds, // Content before v1.21 did store the formatted string instead of seconds,
// so use "|" as delimeter to mark the new format // so use "|" as delimiter to mark the new format
Content: fmt.Sprintf("|%d", t.Time), Content: fmt.Sprintf("|%d", t.Time),
Type: CommentTypeDeleteTimeManual, Type: CommentTypeDeleteTimeManual,
}); err != nil { }); err != nil {

View File

@ -21,7 +21,6 @@ import (
"code.gitea.io/gitea/models/migrations/v1_20" "code.gitea.io/gitea/models/migrations/v1_20"
"code.gitea.io/gitea/models/migrations/v1_21" "code.gitea.io/gitea/models/migrations/v1_21"
"code.gitea.io/gitea/models/migrations/v1_22" "code.gitea.io/gitea/models/migrations/v1_22"
"code.gitea.io/gitea/models/migrations/v1_23"
"code.gitea.io/gitea/models/migrations/v1_6" "code.gitea.io/gitea/models/migrations/v1_6"
"code.gitea.io/gitea/models/migrations/v1_7" "code.gitea.io/gitea/models/migrations/v1_7"
"code.gitea.io/gitea/models/migrations/v1_8" "code.gitea.io/gitea/models/migrations/v1_8"
@ -574,18 +573,20 @@ var migrations = []Migration{
// v293 -> v294 // v293 -> v294
NewMigration("Ensure every project has exactly one default column", v1_22.CheckProjectColumnsConsistency), NewMigration("Ensure every project has exactly one default column", v1_22.CheckProjectColumnsConsistency),
// Gitea 1.22.0 ends at 294 // Gitea 1.22.0-rc0 ends at 294
// v294 -> v295 // v294 -> v295
NewMigration("Add unique index for project issue table", v1_23.AddUniqueIndexForProjectIssue), NewMigration("Add unique index for project issue table", v1_22.AddUniqueIndexForProjectIssue),
// v295 -> v296 // v295 -> v296
NewMigration("Add commit status summary table", v1_23.AddCommitStatusSummary), NewMigration("Add commit status summary table", v1_22.AddCommitStatusSummary),
// v296 -> v297 // v296 -> v297
NewMigration("Add missing field of commit status summary table", v1_23.AddCommitStatusSummary2), NewMigration("Add missing field of commit status summary table", v1_22.AddCommitStatusSummary2),
// v297 -> v298 // v297 -> v298
NewMigration("Add everyone_access_mode for repo_unit", v1_23.AddRepoUnitEveryoneAccessMode), NewMigration("Add everyone_access_mode for repo_unit", v1_22.AddRepoUnitEveryoneAccessMode),
// v298 -> v299 // v298 -> v299
NewMigration("Drop wrongly created table o_auth2_application", v1_23.DropWronglyCreatedTable), NewMigration("Drop wrongly created table o_auth2_application", v1_22.DropWronglyCreatedTable),
// Gitea 1.22.0-rc1 ends at 299
} }
// GetCurrentDBVersion returns the current db version // GetCurrentDBVersion returns the current db version

View File

@ -4,4 +4,4 @@
package v1_17 //nolint package v1_17 //nolint
// This migration added non-ideal indices to the action table which on larger datasets slowed things down // This migration added non-ideal indices to the action table which on larger datasets slowed things down
// it has been superceded by v218.go // it has been superseded by v218.go

View File

@ -1,7 +1,7 @@
// Copyright 2024 The Gitea Authors. All rights reserved. // Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
package v1_23 //nolint package v1_22 //nolint
import ( import (
"fmt" "fmt"

View File

@ -1,7 +1,7 @@
// Copyright 2024 The Gitea Authors. All rights reserved. // Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
package v1_23 //nolint package v1_22 //nolint
import ( import (
"slices" "slices"

View File

@ -1,7 +1,7 @@
// Copyright 2024 The Gitea Authors. All rights reserved. // Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
package v1_23 //nolint package v1_22 //nolint
import "xorm.io/xorm" import "xorm.io/xorm"

View File

@ -1,7 +1,7 @@
// Copyright 2024 The Gitea Authors. All rights reserved. // Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
package v1_23 //nolint package v1_22 //nolint
import "xorm.io/xorm" import "xorm.io/xorm"

View File

@ -1,7 +1,7 @@
// Copyright 2024 The Gitea Authors. All rights reserved. // Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
package v1_23 //nolint package v1_22 //nolint
import ( import (
"code.gitea.io/gitea/models/perm" "code.gitea.io/gitea/models/perm"

View File

@ -1,7 +1,7 @@
// Copyright 2024 The Gitea Authors. All rights reserved. // Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
package v1_23 //nolint package v1_22 //nolint
import "xorm.io/xorm" import "xorm.io/xorm"

View File

@ -6,7 +6,6 @@ package unittest
import ( import (
"context" "context"
"fmt" "fmt"
"log"
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
@ -18,6 +17,7 @@ import (
"code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/cache" "code.gitea.io/gitea/modules/cache"
"code.gitea.io/gitea/modules/git" "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/setting/config" "code.gitea.io/gitea/modules/setting/config"
"code.gitea.io/gitea/modules/storage" "code.gitea.io/gitea/modules/storage"
@ -46,6 +46,14 @@ func fatalTestError(fmtStr string, args ...any) {
// InitSettings initializes config provider and load common settings for tests // InitSettings initializes config provider and load common settings for tests
func InitSettings() { func InitSettings() {
setting.IsInTesting = true
log.OsExiter = func(code int) {
if code != 0 {
// non-zero exit code (log.Fatal) shouldn't occur during testing, if it happens, show a full stacktrace for more details
panic(fmt.Errorf("non-zero exit code during testing: %d", code))
}
os.Exit(0)
}
if setting.CustomConf == "" { if setting.CustomConf == "" {
setting.CustomConf = filepath.Join(setting.CustomPath, "conf/app-unittest-tmp.ini") setting.CustomConf = filepath.Join(setting.CustomPath, "conf/app-unittest-tmp.ini")
_ = os.Remove(setting.CustomConf) _ = os.Remove(setting.CustomConf)
@ -54,7 +62,7 @@ func InitSettings() {
setting.LoadCommonSettings() setting.LoadCommonSettings()
if err := setting.PrepareAppDataPath(); err != nil { if err := setting.PrepareAppDataPath(); err != nil {
log.Fatalf("Can not prepare APP_DATA_PATH: %v", err) log.Fatal("Can not prepare APP_DATA_PATH: %v", err)
} }
// register the dummy hash algorithm function used in the test fixtures // register the dummy hash algorithm function used in the test fixtures
_ = hash.Register("dummy", hash.NewDummyHasher) _ = hash.Register("dummy", hash.NewDummyHasher)

View File

@ -184,7 +184,7 @@ func (ref RefName) RefGroup() string {
} }
// RefType returns the simple ref type of the reference, e.g. branch, tag // RefType returns the simple ref type of the reference, e.g. branch, tag
// It's differrent from RefGroup, which is using the name of the directory under .git/refs // It's different from RefGroup, which is using the name of the directory under .git/refs
// Here we using branch but not heads, using tag but not tags // Here we using branch but not heads, using tag but not tags
func (ref RefName) RefType() string { func (ref RefName) RefType() string {
var refType string var refType string

View File

@ -57,11 +57,13 @@ func Critical(format string, v ...any) {
Log(1, ERROR, format, v...) Log(1, ERROR, format, v...)
} }
var OsExiter = os.Exit
// Fatal records fatal log and exit process // Fatal records fatal log and exit process
func Fatal(format string, v ...any) { func Fatal(format string, v ...any) {
Log(1, FATAL, format, v...) Log(1, FATAL, format, v...)
GetManager().Close() GetManager().Close()
os.Exit(1) OsExiter(1)
} }
func GetLogger(name string) Logger { func GetLogger(name string) Logger {

View File

@ -134,7 +134,7 @@ func (pm *Manager) AddTypedContext(parent context.Context, description, processT
// //
// Most processes will not need to use the cancel function but there will be cases whereby you want to cancel the process but not immediately remove it from the // Most processes will not need to use the cancel function but there will be cases whereby you want to cancel the process but not immediately remove it from the
// process table. // process table.
func (pm *Manager) AddContextTimeout(parent context.Context, timeout time.Duration, description string) (ctx context.Context, cancel context.CancelFunc, finshed FinishedFunc) { func (pm *Manager) AddContextTimeout(parent context.Context, timeout time.Duration, description string) (ctx context.Context, cancel context.CancelFunc, finished FinishedFunc) {
if timeout <= 0 { if timeout <= 0 {
// it's meaningless to use timeout <= 0, and it must be a bug! so we must panic here to tell developers to make the timeout correct // it's meaningless to use timeout <= 0, and it must be a bug! so we must panic here to tell developers to make the timeout correct
panic("the timeout must be greater than zero, otherwise the context will be cancelled immediately") panic("the timeout must be greater than zero, otherwise the context will be cancelled immediately")
@ -142,9 +142,9 @@ func (pm *Manager) AddContextTimeout(parent context.Context, timeout time.Durati
ctx, cancel = context.WithTimeout(parent, timeout) ctx, cancel = context.WithTimeout(parent, timeout)
ctx, _, finshed = pm.Add(ctx, description, cancel, NormalProcessType, true) ctx, _, finished = pm.Add(ctx, description, cancel, NormalProcessType, true)
return ctx, cancel, finshed return ctx, cancel, finished
} }
// Add create a new process // Add create a new process

View File

@ -49,9 +49,9 @@ func TestSubjectBodySeparator(t *testing.T) {
test("Multiple\n---\n-------\n---\nSeparators", test("Multiple\n---\n-------\n---\nSeparators",
"Multiple\n", "Multiple\n",
"\n-------\n---\nSeparators") "\n-------\n---\nSeparators")
test("Insuficient\n--\nSeparators", test("Insufficient\n--\nSeparators",
"", "",
"Insuficient\n--\nSeparators") "Insufficient\n--\nSeparators")
} }
func TestJSEscapeSafe(t *testing.T) { func TestJSEscapeSafe(t *testing.T) {

View File

@ -436,6 +436,7 @@ oauth_signin_submit=Vincular conta
oauth.signin.error=Ocorreu um erro durante o processamento do pedido de autorização. Se este erro persistir, contacte o administrador. oauth.signin.error=Ocorreu um erro durante o processamento do pedido de autorização. Se este erro persistir, contacte o administrador.
oauth.signin.error.access_denied=O pedido de autorização foi negado. oauth.signin.error.access_denied=O pedido de autorização foi negado.
oauth.signin.error.temporarily_unavailable=A autorização falhou porque o servidor de autenticação está temporariamente indisponível. Tente mais tarde. oauth.signin.error.temporarily_unavailable=A autorização falhou porque o servidor de autenticação está temporariamente indisponível. Tente mais tarde.
oauth_callback_unable_auto_reg=O registo automático está habilitado, mas o fornecedor OAuth2 %[1]s sinalizou campos em falta: %[2]s, por isso não foi possível criar uma conta automaticamente. Crie ou vincule uma conta ou contacte o administrador do sítio.
openid_connect_submit=Estabelecer ligação openid_connect_submit=Estabelecer ligação
openid_connect_title=Estabelecer ligação a uma conta existente openid_connect_title=Estabelecer ligação a uma conta existente
openid_connect_desc=O URI do OpenID escolhido é desconhecido. Associe-o a uma nova conta aqui. openid_connect_desc=O URI do OpenID escolhido é desconhecido. Associe-o a uma nova conta aqui.

665
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -4,9 +4,9 @@
"node": ">= 18.0.0" "node": ">= 18.0.0"
}, },
"dependencies": { "dependencies": {
"@citation-js/core": "0.7.9", "@citation-js/core": "0.7.11",
"@citation-js/plugin-bibtex": "0.7.9", "@citation-js/plugin-bibtex": "0.7.11",
"@citation-js/plugin-csl": "0.7.9", "@citation-js/plugin-csl": "0.7.11",
"@citation-js/plugin-software-formats": "0.6.1", "@citation-js/plugin-software-formats": "0.6.1",
"@github/markdown-toolbar-element": "2.2.3", "@github/markdown-toolbar-element": "2.2.3",
"@github/relative-time-element": "4.4.0", "@github/relative-time-element": "4.4.0",
@ -33,17 +33,17 @@
"katex": "0.16.10", "katex": "0.16.10",
"license-checker-webpack-plugin": "0.2.1", "license-checker-webpack-plugin": "0.2.1",
"mermaid": "10.9.0", "mermaid": "10.9.0",
"mini-css-extract-plugin": "2.8.1", "mini-css-extract-plugin": "2.9.0",
"minimatch": "9.0.4", "minimatch": "9.0.4",
"monaco-editor": "0.47.0", "monaco-editor": "0.48.0",
"monaco-editor-webpack-plugin": "7.1.0", "monaco-editor-webpack-plugin": "7.1.0",
"pdfobject": "2.3.0", "pdfobject": "2.3.0",
"postcss": "8.4.38", "postcss": "8.4.38",
"postcss-loader": "8.1.1", "postcss-loader": "8.1.1",
"postcss-nesting": "12.1.1", "postcss-nesting": "12.1.2",
"pretty-ms": "9.0.0", "pretty-ms": "9.0.0",
"sortablejs": "1.15.2", "sortablejs": "1.15.2",
"swagger-ui-dist": "5.15.1", "swagger-ui-dist": "5.17.2",
"tailwindcss": "3.4.3", "tailwindcss": "3.4.3",
"temporal-polyfill": "0.2.4", "temporal-polyfill": "0.2.4",
"throttle-debounce": "5.0.0", "throttle-debounce": "5.0.0",
@ -53,7 +53,7 @@
"tributejs": "5.1.3", "tributejs": "5.1.3",
"uint8-to-base64": "0.2.0", "uint8-to-base64": "0.2.0",
"vanilla-colorful": "0.7.2", "vanilla-colorful": "0.7.2",
"vue": "3.4.21", "vue": "3.4.25",
"vue-bar-graph": "2.0.0", "vue-bar-graph": "2.0.0",
"vue-chartjs": "5.3.1", "vue-chartjs": "5.3.1",
"vue-loader": "17.4.2", "vue-loader": "17.4.2",
@ -66,7 +66,7 @@
"@eslint-community/eslint-plugin-eslint-comments": "4.3.0", "@eslint-community/eslint-plugin-eslint-comments": "4.3.0",
"@playwright/test": "1.43.1", "@playwright/test": "1.43.1",
"@stoplight/spectral-cli": "6.11.1", "@stoplight/spectral-cli": "6.11.1",
"@stylistic/eslint-plugin-js": "1.7.0", "@stylistic/eslint-plugin-js": "1.7.2",
"@stylistic/stylelint-plugin": "2.1.1", "@stylistic/stylelint-plugin": "2.1.1",
"@vitejs/plugin-vue": "5.0.4", "@vitejs/plugin-vue": "5.0.4",
"eslint": "8.57.0", "eslint": "8.57.0",
@ -81,20 +81,20 @@
"eslint-plugin-unicorn": "52.0.0", "eslint-plugin-unicorn": "52.0.0",
"eslint-plugin-vitest": "0.4.1", "eslint-plugin-vitest": "0.4.1",
"eslint-plugin-vitest-globals": "1.5.0", "eslint-plugin-vitest-globals": "1.5.0",
"eslint-plugin-vue": "9.24.1", "eslint-plugin-vue": "9.25.0",
"eslint-plugin-vue-scoped-css": "2.8.0", "eslint-plugin-vue-scoped-css": "2.8.0",
"eslint-plugin-wc": "2.1.0", "eslint-plugin-wc": "2.1.0",
"happy-dom": "14.7.1", "happy-dom": "14.7.1",
"markdownlint-cli": "0.39.0", "markdownlint-cli": "0.39.0",
"postcss-html": "1.6.0", "postcss-html": "1.6.0",
"stylelint": "16.3.1", "stylelint": "16.4.0",
"stylelint-declaration-block-no-ignored-properties": "2.8.0", "stylelint-declaration-block-no-ignored-properties": "2.8.0",
"stylelint-declaration-strict-value": "1.10.4", "stylelint-declaration-strict-value": "1.10.4",
"stylelint-value-no-unknown-custom-properties": "6.0.1", "stylelint-value-no-unknown-custom-properties": "6.0.1",
"svgo": "3.2.0", "svgo": "3.2.0",
"updates": "16.0.1", "updates": "16.0.1",
"vite-string-plugin": "1.1.5", "vite-string-plugin": "1.2.0",
"vitest": "1.5.0" "vitest": "1.5.2"
}, },
"browserslist": [ "browserslist": [
"defaults" "defaults"

View File

@ -301,7 +301,7 @@ func (ar artifactRoutes) uploadArtifact(ctx *ArtifactContext) {
}) })
} }
// comfirmUploadArtifact comfirm upload artifact. // comfirmUploadArtifact confirm upload artifact.
// if all chunks are uploaded, merge them to one file. // if all chunks are uploaded, merge them to one file.
func (ar artifactRoutes) comfirmUploadArtifact(ctx *ArtifactContext) { func (ar artifactRoutes) comfirmUploadArtifact(ctx *ArtifactContext) {
_, runID, ok := validateRunID(ctx) _, runID, ok := validateRunID(ctx)

View File

@ -36,7 +36,7 @@ var withRunner = connect.WithInterceptors(connect.UnaryInterceptorFunc(func(unar
uuid := request.Header().Get(uuidHeaderKey) uuid := request.Header().Get(uuidHeaderKey)
token := request.Header().Get(tokenHeaderKey) token := request.Header().Get(tokenHeaderKey)
// TODO: version will be removed from request header after Gitea 1.20 released. // TODO: version will be removed from request header after Gitea 1.20 released.
// And Gitea will not try to read version from reuqest header // And Gitea will not try to read version from request header
version := request.Header().Get(versionHeaderKey) version := request.Header().Get(versionHeaderKey)
runner, err := actions_model.GetRunnerByUUID(ctx, uuid) runner, err := actions_model.GetRunnerByUUID(ctx, uuid)
@ -53,7 +53,7 @@ var withRunner = connect.WithInterceptors(connect.UnaryInterceptorFunc(func(unar
cols := []string{"last_online"} cols := []string{"last_online"}
// TODO: version will be removed from request header after Gitea 1.20 released. // TODO: version will be removed from request header after Gitea 1.20 released.
// And Gitea will not try to read version from reuqest header // And Gitea will not try to read version from request header
version, _ = util.SplitStringAtByteN(version, 64) version, _ = util.SplitStringAtByteN(version, 64)
if !util.IsEmptyString(version) && runner.Version != version { if !util.IsEmptyString(version) && runner.Version != version {
runner.Version = version runner.Version = version

View File

@ -19,7 +19,7 @@ The package registry code is divided into multiple modules to split the function
## Models ## Models
Every package registry implementation uses the same underlaying models: Every package registry implementation uses the same underlying models:
| Model | Description | | Model | Description |
| - | - | | - | - |

View File

@ -4,6 +4,7 @@
package repo package repo
import ( import (
"errors"
"fmt" "fmt"
"net/http" "net/http"
"strings" "strings"
@ -372,7 +373,11 @@ func CreatePullReview(ctx *context.APIContext) {
// create review and associate all pending review comments // create review and associate all pending review comments
review, _, err := pull_service.SubmitReview(ctx, ctx.Doer, ctx.Repo.GitRepo, pr.Issue, reviewType, opts.Body, opts.CommitID, nil) review, _, err := pull_service.SubmitReview(ctx, ctx.Doer, ctx.Repo.GitRepo, pr.Issue, reviewType, opts.Body, opts.CommitID, nil)
if err != nil { if err != nil {
ctx.Error(http.StatusInternalServerError, "SubmitReview", err) if errors.Is(err, pull_service.ErrSubmitReviewOnClosedPR) {
ctx.Error(http.StatusUnprocessableEntity, "", err)
} else {
ctx.Error(http.StatusInternalServerError, "SubmitReview", err)
}
return return
} }
@ -460,7 +465,11 @@ func SubmitPullReview(ctx *context.APIContext) {
// create review and associate all pending review comments // create review and associate all pending review comments
review, _, err = pull_service.SubmitReview(ctx, ctx.Doer, ctx.Repo.GitRepo, pr.Issue, reviewType, opts.Body, headCommitID, nil) review, _, err = pull_service.SubmitReview(ctx, ctx.Doer, ctx.Repo.GitRepo, pr.Issue, reviewType, opts.Body, headCommitID, nil)
if err != nil { if err != nil {
ctx.Error(http.StatusInternalServerError, "SubmitReview", err) if errors.Is(err, pull_service.ErrSubmitReviewOnClosedPR) {
ctx.Error(http.StatusUnprocessableEntity, "", err)
} else {
ctx.Error(http.StatusInternalServerError, "SubmitReview", err)
}
return return
} }

View File

@ -12,7 +12,7 @@ import (
"code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/context"
) )
// RegistrationToken is response related to registeration token // RegistrationToken is response related to registration token
// swagger:response RegistrationToken // swagger:response RegistrationToken
type RegistrationToken struct { type RegistrationToken struct {
Token string `json:"token"` Token string `json:"token"`

View File

@ -359,7 +359,7 @@ func preReceiveBranch(ctx *preReceiveContext, oldCommitID, newCommitID string, r
}) })
return return
} }
log.Error("Unable to check if mergable: protected branch %s in %-v and pr #%d. Error: %v", ctx.opts.UserID, branchName, repo, pr.Index, err) log.Error("Unable to check if mergeable: protected branch %s in %-v and pr #%d. Error: %v", ctx.opts.UserID, branchName, repo, pr.Index, err)
ctx.JSON(http.StatusInternalServerError, private.Response{ ctx.JSON(http.StatusInternalServerError, private.Response{
Err: fmt.Sprintf("Unable to get status of pull request %d. Error: %v", ctx.opts.PullRequestID, err), Err: fmt.Sprintf("Unable to get status of pull request %d. Error: %v", ctx.opts.PullRequestID, err),
}) })

View File

@ -30,7 +30,7 @@ func Organizations(ctx *context.Context) {
explore.RenderUserSearch(ctx, &user_model.SearchUserOptions{ explore.RenderUserSearch(ctx, &user_model.SearchUserOptions{
Actor: ctx.Doer, Actor: ctx.Doer,
Type: user_model.UserTypeOrganization, Type: user_model.UserTypeOrganization,
IncludeReserved: true, // administrator needs to list all acounts include reserved IncludeReserved: true, // administrator needs to list all accounts include reserved
ListOptions: db.ListOptions{ ListOptions: db.ListOptions{
PageSize: setting.UI.Admin.OrgPagingNum, PageSize: setting.UI.Admin.OrgPagingNum,
}, },

View File

@ -81,7 +81,7 @@ func Users(ctx *context.Context) {
IsRestricted: util.OptionalBoolParse(statusFilterMap["is_restricted"]), IsRestricted: util.OptionalBoolParse(statusFilterMap["is_restricted"]),
IsTwoFactorEnabled: util.OptionalBoolParse(statusFilterMap["is_2fa_enabled"]), IsTwoFactorEnabled: util.OptionalBoolParse(statusFilterMap["is_2fa_enabled"]),
IsProhibitLogin: util.OptionalBoolParse(statusFilterMap["is_prohibit_login"]), IsProhibitLogin: util.OptionalBoolParse(statusFilterMap["is_prohibit_login"]),
IncludeReserved: true, // administrator needs to list all acounts include reserved, bot, remote ones IncludeReserved: true, // administrator needs to list all accounts include reserved, bot, remote ones
ExtraParamStrings: extraParamStrings, ExtraParamStrings: extraParamStrings,
}, tplUsers) }, tplUsers)
} }

View File

@ -812,7 +812,7 @@ func CompareDiff(ctx *context.Context) {
// applicable if you have one commit to compare and that commit has a message. // applicable if you have one commit to compare and that commit has a message.
// In that case the commit message will be prepend to the template body. // In that case the commit message will be prepend to the template body.
if templateContent, ok := ctx.Data[pullRequestTemplateKey].(string); ok && templateContent != "" { if templateContent, ok := ctx.Data[pullRequestTemplateKey].(string); ok && templateContent != "" {
// Re-use the same key as that's priortized over the "content" key. // Re-use the same key as that's prioritized over the "content" key.
// Add two new lines between the content to ensure there's always at least // Add two new lines between the content to ensure there's always at least
// one empty line between them. // one empty line between them.
ctx.Data[pullRequestTemplateKey] = content + "\n\n" + templateContent ctx.Data[pullRequestTemplateKey] = content + "\n\n" + templateContent

View File

@ -1760,8 +1760,8 @@ func ViewIssue(ctx *context.Context) {
// drop error since times could be pruned from DB.. // drop error since times could be pruned from DB..
_ = comment.LoadTime(ctx) _ = comment.LoadTime(ctx)
if comment.Content != "" { if comment.Content != "" {
// Content before v1.21 did store the formated string instead of seconds, // Content before v1.21 did store the formatted string instead of seconds,
// so "|" is used as delimeter to mark the new format // so "|" is used as delimiter to mark the new format
if comment.Content[0] != '|' { if comment.Content[0] != '|' {
// handle old time comments that have formatted text stored // handle old time comments that have formatted text stored
comment.RenderedContent = templates.SanitizeHTML(comment.Content) comment.RenderedContent = templates.SanitizeHTML(comment.Content)

View File

@ -264,6 +264,8 @@ func SubmitReview(ctx *context.Context) {
if issues_model.IsContentEmptyErr(err) { if issues_model.IsContentEmptyErr(err) {
ctx.Flash.Error(ctx.Tr("repo.issues.review.content.empty")) ctx.Flash.Error(ctx.Tr("repo.issues.review.content.empty"))
ctx.JSONRedirect(fmt.Sprintf("%s/pulls/%d/files", ctx.Repo.RepoLink, issue.Index)) ctx.JSONRedirect(fmt.Sprintf("%s/pulls/%d/files", ctx.Repo.RepoLink, issue.Index))
} else if errors.Is(err, pull_service.ErrSubmitReviewOnClosedPR) {
ctx.Status(http.StatusUnprocessableEntity)
} else { } else {
ctx.ServerError("SubmitReview", err) ctx.ServerError("SubmitReview", err)
} }

View File

@ -72,8 +72,8 @@ func ToTimelineComment(ctx context.Context, repo *repo_model.Repository, c *issu
c.Type == issues_model.CommentTypeStopTracking || c.Type == issues_model.CommentTypeStopTracking ||
c.Type == issues_model.CommentTypeDeleteTimeManual) && c.Type == issues_model.CommentTypeDeleteTimeManual) &&
c.Content[0] == '|' { c.Content[0] == '|' {
// TimeTracking Comments from v1.21 on store the seconds instead of an formated string // TimeTracking Comments from v1.21 on store the seconds instead of an formatted string
// so we check for the "|" delimeter and convert new to legacy format on demand // so we check for the "|" delimiter and convert new to legacy format on demand
c.Content = util.SecToTime(c.Content[1:]) c.Content = util.SecToTime(c.Content[1:])
} }
} }

View File

@ -229,12 +229,12 @@ func TeamReviewRequest(ctx context.Context, issue *issues_model.Issue, doer *use
return comment, teamReviewRequestNotify(ctx, issue, doer, reviewer, isAdd, comment) return comment, teamReviewRequestNotify(ctx, issue, doer, reviewer, isAdd, comment)
} }
func ReviewRequestNotify(ctx context.Context, issue *issues_model.Issue, doer *user_model.User, reviewNotifers []*ReviewRequestNotifier) { func ReviewRequestNotify(ctx context.Context, issue *issues_model.Issue, doer *user_model.User, reviewNotifiers []*ReviewRequestNotifier) {
for _, reviewNotifer := range reviewNotifers { for _, reviewNotifier := range reviewNotifiers {
if reviewNotifer.Reviwer != nil { if reviewNotifier.Reviewer != nil {
notify_service.PullRequestReviewRequest(ctx, issue.Poster, issue, reviewNotifer.Reviwer, reviewNotifer.IsAdd, reviewNotifer.Comment) notify_service.PullRequestReviewRequest(ctx, issue.Poster, issue, reviewNotifier.Reviewer, reviewNotifier.IsAdd, reviewNotifier.Comment)
} else if reviewNotifer.ReviewTeam != nil { } else if reviewNotifier.ReviewTeam != nil {
if err := teamReviewRequestNotify(ctx, issue, issue.Poster, reviewNotifer.ReviewTeam, reviewNotifer.IsAdd, reviewNotifer.Comment); err != nil { if err := teamReviewRequestNotify(ctx, issue, issue.Poster, reviewNotifier.ReviewTeam, reviewNotifier.IsAdd, reviewNotifier.Comment); err != nil {
log.Error("teamReviewRequestNotify: %v", err) log.Error("teamReviewRequestNotify: %v", err)
} }
} }

View File

@ -90,17 +90,17 @@ func ChangeTitle(ctx context.Context, issue *issues_model.Issue, doer *user_mode
return err return err
} }
var reviewNotifers []*ReviewRequestNotifier var reviewNotifiers []*ReviewRequestNotifier
if issue.IsPull && issues_model.HasWorkInProgressPrefix(oldTitle) && !issues_model.HasWorkInProgressPrefix(title) { if issue.IsPull && issues_model.HasWorkInProgressPrefix(oldTitle) && !issues_model.HasWorkInProgressPrefix(title) {
var err error var err error
reviewNotifers, err = PullRequestCodeOwnersReview(ctx, issue, issue.PullRequest) reviewNotifiers, err = PullRequestCodeOwnersReview(ctx, issue, issue.PullRequest)
if err != nil { if err != nil {
log.Error("PullRequestCodeOwnersReview: %v", err) log.Error("PullRequestCodeOwnersReview: %v", err)
} }
} }
notify_service.IssueChangeTitle(ctx, doer, issue, oldTitle) notify_service.IssueChangeTitle(ctx, doer, issue, oldTitle)
ReviewRequestNotify(ctx, issue, issue.Poster, reviewNotifers) ReviewRequestNotify(ctx, issue, issue.Poster, reviewNotifiers)
return nil return nil
} }

View File

@ -36,7 +36,7 @@ func getMergeBase(repo *git.Repository, pr *issues_model.PullRequest, baseBranch
type ReviewRequestNotifier struct { type ReviewRequestNotifier struct {
Comment *issues_model.Comment Comment *issues_model.Comment
IsAdd bool IsAdd bool
Reviwer *user_model.User Reviewer *user_model.User
ReviewTeam *org_model.Team ReviewTeam *org_model.Team
} }
@ -124,9 +124,9 @@ func PullRequestCodeOwnersReview(ctx context.Context, issue *issues_model.Issue,
return nil, err return nil, err
} }
notifiers = append(notifiers, &ReviewRequestNotifier{ notifiers = append(notifiers, &ReviewRequestNotifier{
Comment: comment, Comment: comment,
IsAdd: true, IsAdd: true,
Reviwer: u, Reviewer: u,
}) })
} }
} }

View File

@ -40,7 +40,7 @@ func Update(ctx context.Context, pullLimit, pushLimit int) error {
} }
log.Trace("Doing: Update") log.Trace("Doing: Update")
handler := func(idx int, bean any) error { handler := func(bean any) error {
var repo *repo_model.Repository var repo *repo_model.Repository
var mirrorType SyncType var mirrorType SyncType
var referenceID int64 var referenceID int64
@ -91,7 +91,7 @@ func Update(ctx context.Context, pullLimit, pushLimit int) error {
pullMirrorsRequested := 0 pullMirrorsRequested := 0
if pullLimit != 0 { if pullLimit != 0 {
if err := repo_model.MirrorsIterate(ctx, pullLimit, func(idx int, bean any) error { if err := repo_model.MirrorsIterate(ctx, pullLimit, func(idx int, bean any) error {
if err := handler(idx, bean); err != nil { if err := handler(bean); err != nil {
return err return err
} }
pullMirrorsRequested++ pullMirrorsRequested++
@ -105,7 +105,7 @@ func Update(ctx context.Context, pullLimit, pushLimit int) error {
pushMirrorsRequested := 0 pushMirrorsRequested := 0
if pushLimit != 0 { if pushLimit != 0 {
if err := repo_model.PushMirrorsIterate(ctx, pushLimit, func(idx int, bean any) error { if err := repo_model.PushMirrorsIterate(ctx, pushLimit, func(idx int, bean any) error {
if err := handler(idx, bean); err != nil { if err := handler(bean); err != nil {
return err return err
} }
pushMirrorsRequested++ pushMirrorsRequested++

View File

@ -466,7 +466,7 @@ func SyncPullMirror(ctx context.Context, repoID int64) bool {
log.Trace("SyncMirrors [repo: %-v]: %d branches updated", m.Repo, len(results)) log.Trace("SyncMirrors [repo: %-v]: %d branches updated", m.Repo, len(results))
if len(results) > 0 { if len(results) > 0 {
if ok := checkAndUpdateEmptyRepository(ctx, m, gitRepo, results); !ok { if ok := checkAndUpdateEmptyRepository(ctx, m, results); !ok {
log.Error("SyncMirrors [repo: %-v]: checkAndUpdateEmptyRepository: %v", m.Repo, err) log.Error("SyncMirrors [repo: %-v]: checkAndUpdateEmptyRepository: %v", m.Repo, err)
return false return false
} }
@ -564,7 +564,7 @@ func SyncPullMirror(ctx context.Context, repoID int64) bool {
return true return true
} }
func checkAndUpdateEmptyRepository(ctx context.Context, m *repo_model.Mirror, gitRepo *git.Repository, results []*mirrorSyncResult) bool { func checkAndUpdateEmptyRepository(ctx context.Context, m *repo_model.Mirror, results []*mirrorSyncResult) bool {
if !m.Repo.IsEmpty { if !m.Repo.IsEmpty {
return true return true
} }

View File

@ -20,11 +20,11 @@ import (
// DeleteOrganization completely and permanently deletes everything of organization. // DeleteOrganization completely and permanently deletes everything of organization.
func DeleteOrganization(ctx context.Context, org *org_model.Organization, purge bool) error { func DeleteOrganization(ctx context.Context, org *org_model.Organization, purge bool) error {
ctx, commiter, err := db.TxContext(ctx) ctx, committer, err := db.TxContext(ctx)
if err != nil { if err != nil {
return err return err
} }
defer commiter.Close() defer committer.Close()
if purge { if purge {
err := repo_service.DeleteOwnerRepositoriesDirectly(ctx, org.AsUser()) err := repo_service.DeleteOwnerRepositoriesDirectly(ctx, org.AsUser())
@ -52,7 +52,7 @@ func DeleteOrganization(ctx context.Context, org *org_model.Organization, purge
return fmt.Errorf("DeleteOrganization: %w", err) return fmt.Errorf("DeleteOrganization: %w", err)
} }
if err := commiter.Commit(); err != nil { if err := committer.Commit(); err != nil {
return err return err
} }

View File

@ -66,7 +66,7 @@ const (
MergeCheckTypeAuto // Auto Merge (Scheduled Merge) After Checks Succeed MergeCheckTypeAuto // Auto Merge (Scheduled Merge) After Checks Succeed
) )
// CheckPullMergable check if the pull mergable based on all conditions (branch protection, merge options, ...) // CheckPullMergable check if the pull mergeable based on all conditions (branch protection, merge options, ...)
func CheckPullMergable(stdCtx context.Context, doer *user_model.User, perm *access_model.Permission, pr *issues_model.PullRequest, mergeCheckType MergeCheckType, adminSkipProtectionCheck bool) error { func CheckPullMergable(stdCtx context.Context, doer *user_model.User, perm *access_model.Permission, pr *issues_model.PullRequest, mergeCheckType MergeCheckType, adminSkipProtectionCheck bool) error {
return db.WithTx(stdCtx, func(ctx context.Context) error { return db.WithTx(stdCtx, func(ctx context.Context) error {
if pr.HasMerged { if pr.HasMerged {

View File

@ -46,7 +46,7 @@ func getCommitIDsFromRepo(ctx context.Context, repo *repo_model.Repository, oldC
return commitIDs, isForcePush, err return commitIDs, isForcePush, err
} }
// Find commits between new and old commit exclusing base branch commits // Find commits between new and old commit excluding base branch commits
commits, err := gitRepo.CommitsBetweenNotBase(newCommit, oldCommit, baseBranch) commits, err := gitRepo.CommitsBetweenNotBase(newCommit, oldCommit, baseBranch)
if err != nil { if err != nil {
return nil, false, err return nil, false, err

View File

@ -77,7 +77,7 @@ func NewPullRequest(ctx context.Context, repo *repo_model.Repository, issue *iss
} }
defer baseGitRepo.Close() defer baseGitRepo.Close()
var reviewNotifers []*issue_service.ReviewRequestNotifier var reviewNotifiers []*issue_service.ReviewRequestNotifier
if err := db.WithTx(ctx, func(ctx context.Context) error { if err := db.WithTx(ctx, func(ctx context.Context) error {
if err := issues_model.NewPullRequest(ctx, repo, issue, labelIDs, uuids, pr); err != nil { if err := issues_model.NewPullRequest(ctx, repo, issue, labelIDs, uuids, pr); err != nil {
return err return err
@ -137,7 +137,7 @@ func NewPullRequest(ctx context.Context, repo *repo_model.Repository, issue *iss
} }
if !pr.IsWorkInProgress(ctx) { if !pr.IsWorkInProgress(ctx) {
reviewNotifers, err = issue_service.PullRequestCodeOwnersReview(ctx, issue, pr) reviewNotifiers, err = issue_service.PullRequestCodeOwnersReview(ctx, issue, pr)
if err != nil { if err != nil {
return err return err
} }
@ -152,7 +152,7 @@ func NewPullRequest(ctx context.Context, repo *repo_model.Repository, issue *iss
} }
baseGitRepo.Close() // close immediately to avoid notifications will open the repository again baseGitRepo.Close() // close immediately to avoid notifications will open the repository again
issue_service.ReviewRequestNotify(ctx, issue, issue.Poster, reviewNotifers) issue_service.ReviewRequestNotify(ctx, issue, issue.Poster, reviewNotifiers)
mentions, err := issues_model.FindAndUpdateIssueMentions(ctx, issue, issue.Poster, issue.Content) mentions, err := issues_model.FindAndUpdateIssueMentions(ctx, issue, issue.Poster, issue.Content)
if err != nil { if err != nil {

View File

@ -6,6 +6,7 @@ package pull
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"io" "io"
"regexp" "regexp"
@ -43,6 +44,9 @@ func (err ErrDismissRequestOnClosedPR) Unwrap() error {
return util.ErrPermissionDenied return util.ErrPermissionDenied
} }
// ErrSubmitReviewOnClosedPR represents an error when an user tries to submit an approve or reject review associated to a closed or merged PR.
var ErrSubmitReviewOnClosedPR = errors.New("can't submit review for a closed or merged PR")
// checkInvalidation checks if the line of code comment got changed by another commit. // checkInvalidation checks if the line of code comment got changed by another commit.
// If the line got changed the comment is going to be invalidated. // If the line got changed the comment is going to be invalidated.
func checkInvalidation(ctx context.Context, c *issues_model.Comment, doer *user_model.User, repo *git.Repository, branch string) error { func checkInvalidation(ctx context.Context, c *issues_model.Comment, doer *user_model.User, repo *git.Repository, branch string) error {
@ -293,6 +297,10 @@ func SubmitReview(ctx context.Context, doer *user_model.User, gitRepo *git.Repos
if reviewType != issues_model.ReviewTypeApprove && reviewType != issues_model.ReviewTypeReject { if reviewType != issues_model.ReviewTypeApprove && reviewType != issues_model.ReviewTypeReject {
stale = false stale = false
} else { } else {
if issue.IsClosed {
return nil, nil, ErrSubmitReviewOnClosedPR
}
headCommitID, err := gitRepo.GetRefCommitID(pr.GetGitRefName()) headCommitID, err := gitRepo.GetRefCommitID(pr.GetGitRefName())
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err

View File

@ -191,8 +191,9 @@ export default {
'no-invalid-double-slash-comments': true, 'no-invalid-double-slash-comments': true,
'no-invalid-position-at-import-rule': [true, {ignoreAtRules: ['tailwind']}], 'no-invalid-position-at-import-rule': [true, {ignoreAtRules: ['tailwind']}],
'no-irregular-whitespace': true, 'no-irregular-whitespace': true,
'no-unknown-animations': null, 'no-unknown-animations': null, // disabled until stylelint supports multi-file linting
'no-unknown-custom-properties': null, 'no-unknown-custom-media': null, // disabled until stylelint supports multi-file linting
'no-unknown-custom-properties': null, // disabled until stylelint supports multi-file linting
'number-max-precision': null, 'number-max-precision': null,
'plugin/declaration-block-no-ignored-properties': true, 'plugin/declaration-block-no-ignored-properties': true,
'property-allowed-list': null, 'property-allowed-list': null,

View File

@ -76,7 +76,8 @@
{{ctx.Locale.Tr "admin.dashboard.system_status"}} {{ctx.Locale.Tr "admin.dashboard.system_status"}}
</h4> </h4>
{{/* TODO: make these stats work in multi-server deployments, likely needs per-server stats in DB */}} {{/* TODO: make these stats work in multi-server deployments, likely needs per-server stats in DB */}}
<div hx-get="{{$.Link}}/system_status" hx-swap="morph:innerHTML" hx-trigger="every 5s" hx-indicator=".divider" class="ui attached table segment"> <div class="no-loading-indicator tw-hidden"></div>
<div hx-get="{{$.Link}}/system_status" hx-swap="morph:innerHTML" hx-trigger="every 5s" hx-indicator=".no-loading-indicator" class="ui attached table segment">
{{template "admin/system_status" .}} {{template "admin/system_status" .}}
</div> </div>
</div> </div>

View File

@ -1,6 +1,6 @@
{{if $.IsSplitStyle}} {{if $.IsSplitStyle}}
{{range $k, $line := $.section.Lines}} {{range $k, $line := $.section.Lines}}
<tr class="{{.GetHTMLDiffLineType}}-code nl-{{$k}} ol-{{$k}}"> <tr class="{{.GetHTMLDiffLineType}}-code nl-{{$k}} ol-{{$k}} line-expanded">
{{if eq .GetType 4}} {{if eq .GetType 4}}
<td class="lines-num lines-num-old" data-line-num="{{if $line.LeftIdx}}{{$line.LeftIdx}}{{end}}"> <td class="lines-num lines-num-old" data-line-num="{{if $line.LeftIdx}}{{$line.LeftIdx}}{{end}}">
<div class="tw-flex"> <div class="tw-flex">
@ -26,17 +26,17 @@
{{else}} {{else}}
{{$inlineDiff := $.section.GetComputedInlineDiffFor $line ctx.Locale}} {{$inlineDiff := $.section.GetComputedInlineDiffFor $line ctx.Locale}}
<td class="lines-num lines-num-old" data-line-num="{{if $line.LeftIdx}}{{$line.LeftIdx}}{{end}}"><span rel="{{if $line.LeftIdx}}diff-{{$.FileNameHash}}L{{$line.LeftIdx}}{{end}}"></span></td> <td class="lines-num lines-num-old" data-line-num="{{if $line.LeftIdx}}{{$line.LeftIdx}}{{end}}"><span rel="{{if $line.LeftIdx}}diff-{{$.FileNameHash}}L{{$line.LeftIdx}}{{end}}"></span></td>
<td class="blob-excerpt lines-escape lines-escape-old">{{if and $line.LeftIdx $inlineDiff.EscapeStatus.Escaped}}<button class="toggle-escape-button btn interact-bg" title="{{template "repo/diff/escape_title" dict "diff" $inlineDiff}}"></button>{{end}}</td> <td class="lines-escape lines-escape-old">{{if and $line.LeftIdx $inlineDiff.EscapeStatus.Escaped}}<button class="toggle-escape-button btn interact-bg" title="{{template "repo/diff/escape_title" dict "diff" $inlineDiff}}"></button>{{end}}</td>
<td class="blob-excerpt lines-type-marker lines-type-marker-old">{{if $line.LeftIdx}}<span class="tw-font-mono" data-type-marker=""></span>{{end}}</td> <td class="lines-type-marker lines-type-marker-old">{{if $line.LeftIdx}}<span class="tw-font-mono" data-type-marker=""></span>{{end}}</td>
<td class="blob-excerpt lines-code lines-code-old">{{/* <td class="lines-code lines-code-old">{{/*
*/}}{{if $line.LeftIdx}}{{template "repo/diff/section_code" dict "diff" $inlineDiff}}{{else}}{{/* */}}{{if $line.LeftIdx}}{{template "repo/diff/section_code" dict "diff" $inlineDiff}}{{else}}{{/*
*/}}<code class="code-inner"></code>{{/* */}}<code class="code-inner"></code>{{/*
*/}}{{end}}{{/* */}}{{end}}{{/*
*/}}</td> */}}</td>
<td class="lines-num lines-num-new" data-line-num="{{if $line.RightIdx}}{{$line.RightIdx}}{{end}}"><span rel="{{if $line.RightIdx}}diff-{{$.FileNameHash}}R{{$line.RightIdx}}{{end}}"></span></td> <td class="lines-num lines-num-new" data-line-num="{{if $line.RightIdx}}{{$line.RightIdx}}{{end}}"><span rel="{{if $line.RightIdx}}diff-{{$.FileNameHash}}R{{$line.RightIdx}}{{end}}"></span></td>
<td class="blob-excerpt lines-escape lines-escape-new">{{if and $line.RightIdx $inlineDiff.EscapeStatus.Escaped}}<button class="toggle-escape-button btn interact-bg" title="{{template "repo/diff/escape_title" dict "diff" $inlineDiff}}"></button>{{end}}</td> <td class="lines-escape lines-escape-new">{{if and $line.RightIdx $inlineDiff.EscapeStatus.Escaped}}<button class="toggle-escape-button btn interact-bg" title="{{template "repo/diff/escape_title" dict "diff" $inlineDiff}}"></button>{{end}}</td>
<td class="blob-excerpt lines-type-marker lines-type-marker-new">{{if $line.RightIdx}}<span class="tw-font-mono" data-type-marker=""></span>{{end}}</td> <td class="lines-type-marker lines-type-marker-new">{{if $line.RightIdx}}<span class="tw-font-mono" data-type-marker=""></span>{{end}}</td>
<td class="blob-excerpt lines-code lines-code-new">{{/* <td class="lines-code lines-code-new">{{/*
*/}}{{if $line.RightIdx}}{{template "repo/diff/section_code" dict "diff" $inlineDiff}}{{else}}{{/* */}}{{if $line.RightIdx}}{{template "repo/diff/section_code" dict "diff" $inlineDiff}}{{else}}{{/*
*/}}<code class="code-inner"></code>{{/* */}}<code class="code-inner"></code>{{/*
*/}}{{end}}{{/* */}}{{end}}{{/*
@ -46,7 +46,7 @@
{{end}} {{end}}
{{else}} {{else}}
{{range $k, $line := $.section.Lines}} {{range $k, $line := $.section.Lines}}
<tr class="{{.GetHTMLDiffLineType}}-code nl-{{$k}} ol-{{$k}}"> <tr class="{{.GetHTMLDiffLineType}}-code nl-{{$k}} ol-{{$k}} line-expanded">
{{if eq .GetType 4}} {{if eq .GetType 4}}
<td colspan="2" class="lines-num"> <td colspan="2" class="lines-num">
<div class="tw-flex"> <div class="tw-flex">
@ -72,9 +72,9 @@
<td class="lines-num lines-num-new" data-line-num="{{if $line.RightIdx}}{{$line.RightIdx}}{{end}}"><span rel="{{if $line.RightIdx}}diff-{{$.FileNameHash}}R{{$line.RightIdx}}{{end}}"></span></td> <td class="lines-num lines-num-new" data-line-num="{{if $line.RightIdx}}{{$line.RightIdx}}{{end}}"><span rel="{{if $line.RightIdx}}diff-{{$.FileNameHash}}R{{$line.RightIdx}}{{end}}"></span></td>
{{end}} {{end}}
{{$inlineDiff := $.section.GetComputedInlineDiffFor $line ctx.Locale}} {{$inlineDiff := $.section.GetComputedInlineDiffFor $line ctx.Locale}}
<td class="blob-excerpt lines-escape">{{if $inlineDiff.EscapeStatus.Escaped}}<button class="toggle-escape-button btn interact-bg" title="{{template "repo/diff/escape_title" dict "diff" $inlineDiff}}"></button>{{end}}</td> <td class="lines-escape">{{if $inlineDiff.EscapeStatus.Escaped}}<button class="toggle-escape-button btn interact-bg" title="{{template "repo/diff/escape_title" dict "diff" $inlineDiff}}"></button>{{end}}</td>
<td class="blob-excerpt lines-type-marker"><span class="tw-font-mono" data-type-marker="{{$line.GetLineTypeMarker}}"></span></td> <td class="lines-type-marker"><span class="tw-font-mono" data-type-marker="{{$line.GetLineTypeMarker}}"></span></td>
<td class="blob-excerpt lines-code{{if (not $line.RightIdx)}} lines-code-old{{end}}"><code {{if $inlineDiff.EscapeStatus.Escaped}}class="code-inner has-escaped" title="{{template "repo/diff/escape_title" dict "diff" $inlineDiff}}"{{else}}class="code-inner"{{end}}>{{$inlineDiff.Content}}</code></td> <td class="lines-code{{if (not $line.RightIdx)}} lines-code-old{{end}}"><code {{if $inlineDiff.EscapeStatus.Escaped}}class="code-inner has-escaped" title="{{template "repo/diff/escape_title" dict "diff" $inlineDiff}}"{{else}}class="code-inner"{{end}}>{{$inlineDiff.Content}}</code></td>
</tr> </tr>
{{end}} {{end}}
{{end}} {{end}}

View File

@ -30,20 +30,24 @@
{{end}} {{end}}
<div class="divider"></div> <div class="divider"></div>
{{$showSelfTooltip := (and $.IsSigned ($.Issue.IsPoster $.SignedUser.ID))}} {{$showSelfTooltip := (and $.IsSigned ($.Issue.IsPoster $.SignedUser.ID))}}
{{if $showSelfTooltip}} {{if not $.Issue.IsClosed}}
<span class="tw-inline-block" data-tooltip-content="{{ctx.Locale.Tr "repo.diff.review.self_approve"}}"> {{if $showSelfTooltip}}
<button type="submit" name="type" value="approve" disabled class="ui submit primary tiny button btn-submit">{{ctx.Locale.Tr "repo.diff.review.approve"}}</button> <span class="tw-inline-block" data-tooltip-content="{{ctx.Locale.Tr "repo.diff.review.self_approve"}}">
</span> <button type="submit" name="type" value="approve" disabled class="ui submit primary tiny button btn-submit">{{ctx.Locale.Tr "repo.diff.review.approve"}}</button>
{{else}} </span>
<button type="submit" name="type" value="approve" class="ui submit primary tiny button btn-submit">{{ctx.Locale.Tr "repo.diff.review.approve"}}</button> {{else}}
<button type="submit" name="type" value="approve" class="ui submit primary tiny button btn-submit">{{ctx.Locale.Tr "repo.diff.review.approve"}}</button>
{{end}}
{{end}} {{end}}
<button type="submit" name="type" value="comment" class="ui submit tiny basic button btn-submit">{{ctx.Locale.Tr "repo.diff.review.comment"}}</button> <button type="submit" name="type" value="comment" class="ui submit tiny basic button btn-submit">{{ctx.Locale.Tr "repo.diff.review.comment"}}</button>
{{if $showSelfTooltip}} {{if not $.Issue.IsClosed}}
<span class="tw-inline-block" data-tooltip-content="{{ctx.Locale.Tr "repo.diff.review.self_reject"}}"> {{if $showSelfTooltip}}
<button type="submit" name="type" value="reject" disabled class="ui submit red tiny button btn-submit">{{ctx.Locale.Tr "repo.diff.review.reject"}}</button> <span class="tw-inline-block" data-tooltip-content="{{ctx.Locale.Tr "repo.diff.review.self_reject"}}">
</span> <button type="submit" name="type" value="reject" disabled class="ui submit red tiny button btn-submit">{{ctx.Locale.Tr "repo.diff.review.reject"}}</button>
{{else}} </span>
<button type="submit" name="type" value="reject" class="ui submit red tiny button btn-submit">{{ctx.Locale.Tr "repo.diff.review.reject"}}</button> {{else}}
<button type="submit" name="type" value="reject" class="ui submit red tiny button btn-submit">{{ctx.Locale.Tr "repo.diff.review.reject"}}</button>
{{end}}
{{end}} {{end}}
</form> </form>
</div> </div>

View File

@ -1,13 +1,16 @@
{{with .Issue}} {{with .Issue}}
{{if eq $.Page.Project.CardType 1}}{{/* Images and Text*/}} {{if eq $.Page.Project.CardType 1}}{{/* Images and Text*/}}
{{$attachments := index $.Page.issuesAttachmentMap .ID}}
{{if $attachments}}
<div class="card-attachment-images"> <div class="card-attachment-images">
{{range (index $.Page.issuesAttachmentMap .ID)}} {{range $attachments}}
<img src="{{.DownloadURL}}" alt="{{.Name}}" /> <img src="{{.DownloadURL}}" alt="{{.Name}}" />
{{end}} {{end}}
</div> </div>
{{end}}
{{end}} {{end}}
<div class="content tw-p-0 tw-w-full"> <div class="content tw-w-full">
<div class="tw-flex tw-items-start"> <div class="tw-flex tw-items-start tw-gap-[5px]">
<div class="issue-card-icon"> <div class="issue-card-icon">
{{template "shared/issueicon" .}} {{template "shared/issueicon" .}}
</div> </div>
@ -18,7 +21,7 @@
</a> </a>
{{end}} {{end}}
</div> </div>
<div class="meta tw-my-1"> <div class="meta">
<span class="text light grey muted-links"> <span class="text light grey muted-links">
{{if not $.Page.Repository}}{{.Repo.FullName}}{{end}}#{{.Index}} {{if not $.Page.Repository}}{{.Repo.FullName}}{{end}}#{{.Index}}
{{$timeStr := TimeSinceUnix .GetLastEventTimestamp ctx.Locale}} {{$timeStr := TimeSinceUnix .GetLastEventTimestamp ctx.Locale}}
@ -59,13 +62,15 @@
</div> </div>
{{if or .Labels .Assignees}} {{if or .Labels .Assignees}}
<div class="extra content labels-list tw-p-0 tw-pt-1"> <div class="tw-flex tw-justify-between">
{{range .Labels}} <div class="labels-list tw-flex-1">
<a target="_blank" href="{{$.Issue.Repo.Link}}/issues?labels={{.ID}}">{{RenderLabel ctx ctx.Locale .}}</a> {{range .Labels}}
{{end}} <a target="_blank" href="{{$.Issue.Repo.Link}}/issues?labels={{.ID}}">{{RenderLabel ctx ctx.Locale .}}</a>
<div class="right floated"> {{end}}
</div>
<div class="tw-flex tw-flex-wrap tw-content-start tw-gap-1">
{{range .Assignees}} {{range .Assignees}}
<a target="_blank" href="{{.HomeLink}}" data-tooltip-content="{{ctx.Locale.Tr "repo.projects.column.assigned_to"}} {{.Name}}">{{ctx.AvatarUtils.Avatar . 28 "mini tw-mr-2"}}</a> <a target="_blank" href="{{.HomeLink}}" data-tooltip-content="{{ctx.Locale.Tr "repo.projects.column.assigned_to"}} {{.Name}}">{{ctx.AvatarUtils.Avatar . 28}}</a>
{{end}} {{end}}
</div> </div>
</div> </div>

View File

@ -16,7 +16,7 @@
<span class="ui small label">{{if .NumFiles}}{{.NumFiles}}{{else}}-{{end}}</span> <span class="ui small label">{{if .NumFiles}}{{.NumFiles}}{{else}}-{{end}}</span>
</a> </a>
{{if or .Diff.TotalAddition .Diff.TotalDeletion}} {{if or .Diff.TotalAddition .Diff.TotalDeletion}}
<span class="item tw-ml-auto tw-pr-0 tw-font-bold tw-flex tw-items-center tw-gap-2"> <span class="tw-ml-auto tw-pl-3 tw-whitespace-nowrap tw-pr-0 tw-font-bold tw-flex tw-items-center tw-gap-2">
<span><span class="text green">{{if .Diff.TotalAddition}}+{{.Diff.TotalAddition}}{{end}}</span> <span class="text red">{{if .Diff.TotalDeletion}}-{{.Diff.TotalDeletion}}{{end}}</span></span> <span><span class="text green">{{if .Diff.TotalAddition}}+{{.Diff.TotalAddition}}{{end}}</span> <span class="text red">{{if .Diff.TotalDeletion}}-{{.Diff.TotalDeletion}}{{end}}</span></span>
<span class="diff-stats-bar"> <span class="diff-stats-bar">
<div class="diff-stats-add-bar" style="width: {{Eval 100 "*" .Diff.TotalAddition "/" "(" .Diff.TotalAddition "+" .Diff.TotalDeletion "+" 0.0 ")"}}%"></div> <div class="diff-stats-add-bar" style="width: {{Eval 100 "*" .Diff.TotalAddition "/" "(" .Diff.TotalAddition "+" .Diff.TotalDeletion "+" 0.0 ")"}}%"></div>

View File

@ -25282,7 +25282,7 @@
} }
}, },
"RegistrationToken": { "RegistrationToken": {
"description": "RegistrationToken is response related to registeration token", "description": "RegistrationToken is response related to registration token",
"headers": { "headers": {
"token": { "token": {
"type": "string" "type": "string"

View File

@ -119,9 +119,9 @@ func TestAPIRepoIssueConfigPaths(t *testing.T) {
".github/issue_template/config", ".github/issue_template/config",
} }
for _, canidate := range templateConfigCandidates { for _, candidate := range templateConfigCandidates {
for _, extension := range []string{".yaml", ".yml"} { for _, extension := range []string{".yaml", ".yml"} {
fullPath := canidate + extension fullPath := candidate + extension
t.Run(fullPath, func(t *testing.T) { t.Run(fullPath, func(t *testing.T) {
configMap := make(map[string]any) configMap := make(map[string]any)
configMap["blank_issues_enabled"] = false configMap["blank_issues_enabled"] = false

View File

@ -67,7 +67,7 @@ func TestCompareBranches(t *testing.T) {
session := loginUser(t, "user2") session := loginUser(t, "user2")
// Inderect compare remove-files-b (head) with add-csv (base) branch // Indirect compare remove-files-b (head) with add-csv (base) branch
// //
// 'link_hi' and 'test.csv' are deleted, 'test.txt' is added // 'link_hi' and 'test.csv' are deleted, 'test.txt' is added
req := NewRequest(t, "GET", "/user2/repo20/compare/add-csv...remove-files-b") req := NewRequest(t, "GET", "/user2/repo20/compare/add-csv...remove-files-b")
@ -79,7 +79,7 @@ func TestCompareBranches(t *testing.T) {
inspectCompare(t, htmlDoc, diffCount, diffChanges) inspectCompare(t, htmlDoc, diffCount, diffChanges)
// Inderect compare remove-files-b (head) with remove-files-a (base) branch // Indirect compare remove-files-b (head) with remove-files-a (base) branch
// //
// 'link_hi' and 'test.csv' are deleted, 'test.txt' is added // 'link_hi' and 'test.csv' are deleted, 'test.txt' is added
@ -92,7 +92,7 @@ func TestCompareBranches(t *testing.T) {
inspectCompare(t, htmlDoc, diffCount, diffChanges) inspectCompare(t, htmlDoc, diffCount, diffChanges)
// Inderect compare remove-files-a (head) with remove-files-b (base) branch // Indirect compare remove-files-a (head) with remove-files-b (base) branch
// //
// 'link_hi' and 'test.csv' are deleted // 'link_hi' and 'test.csv' are deleted

View File

@ -5,12 +5,15 @@ package integration
import ( import (
"net/http" "net/http"
"net/http/httptest"
"net/url" "net/url"
"path"
"strings" "strings"
"testing" "testing"
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues" issues_model "code.gitea.io/gitea/models/issues"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/git"
@ -176,3 +179,82 @@ func TestPullView_CodeOwner(t *testing.T) {
}) })
}) })
} }
func TestPullView_GivenApproveOrRejectReviewOnClosedPR(t *testing.T) {
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
user1Session := loginUser(t, "user1")
user2Session := loginUser(t, "user2")
// Have user1 create a fork of repo1.
testRepoFork(t, user1Session, "user2", "repo1", "user1", "repo1")
t.Run("Submit approve/reject review on merged PR", func(t *testing.T) {
// Create a merged PR (made by user1) in the upstream repo1.
testEditFile(t, user1Session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
resp := testPullCreate(t, user1Session, "user1", "repo1", false, "master", "master", "This is a pull title")
elem := strings.Split(test.RedirectURL(resp), "/")
assert.EqualValues(t, "pulls", elem[3])
testPullMerge(t, user1Session, elem[1], elem[2], elem[4], repo_model.MergeStyleMerge, false)
// Grab the CSRF token.
req := NewRequest(t, "GET", path.Join(elem[1], elem[2], "pulls", elem[4]))
resp = user2Session.MakeRequest(t, req, http.StatusOK)
htmlDoc := NewHTMLParser(t, resp.Body)
// Submit an approve review on the PR.
testSubmitReview(t, user2Session, htmlDoc.GetCSRF(), "user2", "repo1", elem[4], "approve", http.StatusUnprocessableEntity)
// Submit a reject review on the PR.
testSubmitReview(t, user2Session, htmlDoc.GetCSRF(), "user2", "repo1", elem[4], "reject", http.StatusUnprocessableEntity)
})
t.Run("Submit approve/reject review on closed PR", func(t *testing.T) {
// Created a closed PR (made by user1) in the upstream repo1.
testEditFileToNewBranch(t, user1Session, "user1", "repo1", "master", "a-test-branch", "README.md", "Hello, World (Editied...again)\n")
resp := testPullCreate(t, user1Session, "user1", "repo1", false, "master", "a-test-branch", "This is a pull title")
elem := strings.Split(test.RedirectURL(resp), "/")
assert.EqualValues(t, "pulls", elem[3])
testIssueClose(t, user1Session, elem[1], elem[2], elem[4])
// Grab the CSRF token.
req := NewRequest(t, "GET", path.Join(elem[1], elem[2], "pulls", elem[4]))
resp = user2Session.MakeRequest(t, req, http.StatusOK)
htmlDoc := NewHTMLParser(t, resp.Body)
// Submit an approve review on the PR.
testSubmitReview(t, user2Session, htmlDoc.GetCSRF(), "user2", "repo1", elem[4], "approve", http.StatusUnprocessableEntity)
// Submit a reject review on the PR.
testSubmitReview(t, user2Session, htmlDoc.GetCSRF(), "user2", "repo1", elem[4], "reject", http.StatusUnprocessableEntity)
})
})
}
func testSubmitReview(t *testing.T, session *TestSession, csrf, owner, repo, pullNumber, reviewType string, expectedSubmitStatus int) *httptest.ResponseRecorder {
options := map[string]string{
"_csrf": csrf,
"commit_id": "",
"content": "test",
"type": reviewType,
}
submitURL := path.Join(owner, repo, "pulls", pullNumber, "files", "reviews", "submit")
req := NewRequestWithValues(t, "POST", submitURL, options)
return session.MakeRequest(t, req, expectedSubmitStatus)
}
func testIssueClose(t *testing.T, session *TestSession, owner, repo, issueNumber string) *httptest.ResponseRecorder {
req := NewRequest(t, "GET", path.Join(owner, repo, "pulls", issueNumber))
resp := session.MakeRequest(t, req, http.StatusOK)
htmlDoc := NewHTMLParser(t, resp.Body)
closeURL := path.Join(owner, repo, "issues", issueNumber, "comments")
options := map[string]string{
"_csrf": htmlDoc.GetCSRF(),
"status": "close",
}
req = NewRequestWithValues(t, "POST", closeURL, options)
return session.MakeRequest(t, req, http.StatusOK)
}

View File

@ -46,7 +46,6 @@ func InitTest(requireGitea bool) {
// TODO: Speedup tests that rely on the event source ticker, confirm whether there is any bug or failure. // TODO: Speedup tests that rely on the event source ticker, confirm whether there is any bug or failure.
// setting.UI.Notification.EventSourceUpdateTime = time.Second // setting.UI.Notification.EventSourceUpdateTime = time.Second
setting.IsInTesting = true
setting.AppWorkPath = giteaRoot setting.AppWorkPath = giteaRoot
setting.CustomPath = filepath.Join(setting.AppWorkPath, "custom") setting.CustomPath = filepath.Join(setting.AppWorkPath, "custom")
if requireGitea { if requireGitea {

21
tools/misspellings.csv Normal file
View File

@ -0,0 +1,21 @@
acounts,accounts
canidate,candidate
comfirm,confirm
converage,coverage
currrently,currently
delimeter,delimiter
differrent,different
exclusing,excluding
finshed,finished
formated,formatted
inderect,indirect
insuficient,insufficient
likly,likely
mergable,mergeable
overrided,overridden
priortized,prioritized
registeration,registration
reuqest,request
reviwer,reviewer
superceded,superseded
underlaying,underlying
1 acounts accounts
2 canidate candidate
3 comfirm confirm
4 converage coverage
5 currrently currently
6 delimeter delimiter
7 differrent different
8 exclusing excluding
9 finshed finished
10 formated formatted
11 inderect indirect
12 insuficient insufficient
13 likly likely
14 mergable mergeable
15 overrided overridden
16 priortized prioritized
17 registeration registration
18 reuqest request
19 reviwer reviewer
20 superceded superseded
21 underlaying underlying

View File

@ -2377,7 +2377,7 @@ tbody.commit-list {
.tag-code, .tag-code,
.tag-code td, .tag-code td,
.tag-code .blob-excerpt { .tag-code.line-expanded {
background-color: var(--color-box-body-highlight); background-color: var(--color-box-body-highlight);
vertical-align: middle; vertical-align: middle;
} }
@ -2393,8 +2393,8 @@ tbody.commit-list {
padding-top: 0 !important; padding-top: 0 !important;
} }
.blob-excerpt { .line-expanded {
background-color: var(--color-secondary-alpha-30); background-color: var(--color-secondary-alpha-20);
} }
.issue-keyword { .issue-keyword {
@ -2520,7 +2520,7 @@ tbody.commit-list {
display: inline-block; display: inline-block;
background-color: var(--color-red); background-color: var(--color-red);
height: 12px; height: 12px;
width: 40px; width: 44px;
} }
.diff-stats-bar .diff-stats-add-bar { .diff-stats-bar .diff-stats-add-bar {
@ -2553,11 +2553,9 @@ tbody.commit-list {
.code-diff-unified .add-code, .code-diff-unified .add-code,
.code-diff-unified .add-code td, .code-diff-unified .add-code td,
.code-diff-split .add-code .lines-num-new,
.code-diff-split .add-code .lines-type-marker-new, .code-diff-split .add-code .lines-type-marker-new,
.code-diff-split .add-code .lines-escape-new, .code-diff-split .add-code .lines-escape-new,
.code-diff-split .add-code .lines-code-new, .code-diff-split .add-code .lines-code-new,
.code-diff-split .del-code .add-code.lines-num-new,
.code-diff-split .del-code .add-code.lines-type-marker-new, .code-diff-split .del-code .add-code.lines-type-marker-new,
.code-diff-split .del-code .add-code.lines-escape-new, .code-diff-split .del-code .add-code.lines-escape-new,
.code-diff-split .del-code .add-code.lines-code-new { .code-diff-split .del-code .add-code.lines-code-new {
@ -2565,17 +2563,33 @@ tbody.commit-list {
border-color: var(--color-diff-added-row-border); border-color: var(--color-diff-added-row-border);
} }
.code-diff-split .del-code .lines-num-new,
.code-diff-split .del-code .lines-type-marker-new, .code-diff-split .del-code .lines-type-marker-new,
.code-diff-split .del-code .lines-code-new, .code-diff-split .del-code .lines-code-new,
.code-diff-split .del-code .lines-escape-new, .code-diff-split .del-code .lines-escape-new,
.code-diff-split .add-code .lines-num-old,
.code-diff-split .add-code .lines-escape-old, .code-diff-split .add-code .lines-escape-old,
.code-diff-split .add-code .lines-type-marker-old, .code-diff-split .add-code .lines-type-marker-old,
.code-diff-split .add-code .lines-code-old { .code-diff-split .add-code .lines-code-old {
background: var(--color-diff-inactive); background: var(--color-diff-inactive);
} }
.code-diff-split .add-code .lines-num.lines-num-old,
.code-diff-split .del-code .lines-num.lines-num-new {
background: var(--color-diff-inactive);
}
.code-diff-unified .del-code .lines-num,
.code-diff-split .del-code .lines-num {
background: var(--color-diff-removed-linenum-bg);
color: var(--color-text);
}
.code-diff-unified .add-code .lines-num,
.code-diff-split .add-code .lines-num,
.code-diff-split .del-code .add-code.lines-num {
background: var(--color-diff-added-linenum-bg);
color: var(--color-text);
}
.code-diff-split tbody tr td:nth-child(5), .code-diff-split tbody tr td:nth-child(5),
.code-diff-split tbody tr td.add-comment-right { .code-diff-split tbody tr td.add-comment-right {
border-left: 1px solid var(--color-secondary); border-left: 1px solid var(--color-secondary);

View File

@ -1,6 +1,7 @@
.issue-card { .issue-card {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 4px;
align-items: start; align-items: start;
border-radius: var(--border-radius); border-radius: var(--border-radius);
padding: 8px 10px; padding: 8px 10px;
@ -17,7 +18,6 @@
.issue-card-title { .issue-card-title {
flex: 1; flex: 1;
font-size: 14px; font-size: 14px;
margin-left: 4px;
} }
.issue-card.sortable-chosen .issue-card-title { .issue-card.sortable-chosen .issue-card-title {

View File

@ -3,9 +3,10 @@
/* red/green colorblind-friendly colors */ /* red/green colorblind-friendly colors */
/* from GitHub: --diffBlob-addition-*, --diffBlob-deletion-*, etc */ /* from GitHub: --diffBlob-addition-*, --diffBlob-deletion-*, etc */
:root { :root {
--color-diff-added-word-bg: #388bfd66; --color-diff-added-linenum-bg: #1979fd46;
--color-diff-added-row-bg: #388bfd26; --color-diff-added-row-bg: #1979fd20;
--color-diff-added-word-bg: #1979fd66;
--color-diff-removed-word-bg: #db6d2866; --color-diff-removed-linenum-bg: #c8622146;
--color-diff-removed-row-bg: #db6d2826; --color-diff-removed-row-bg: #c8622120;
--color-diff-removed-word-bg: #c8622166;
} }

View File

@ -143,14 +143,16 @@
--color-grey-light: #818f9e; --color-grey-light: #818f9e;
--color-gold: #b1983b; --color-gold: #b1983b;
--color-white: #ffffff; --color-white: #ffffff;
--color-diff-removed-word-bg: #6f3333; --color-diff-added-linenum-bg: #274227;
--color-diff-added-word-bg: #3c653c; --color-diff-added-row-bg: #203224;
--color-diff-removed-row-bg: #3c2626;
--color-diff-moved-row-bg: #818044;
--color-diff-added-row-bg: #283e2d;
--color-diff-removed-row-border: #634343;
--color-diff-moved-row-border: #bcca6f;
--color-diff-added-row-border: #314a37; --color-diff-added-row-border: #314a37;
--color-diff-added-word-bg: #3c653c;
--color-diff-moved-row-bg: #818044;
--color-diff-moved-row-border: #bcca6f;
--color-diff-removed-linenum-bg: #482121;
--color-diff-removed-row-bg: #301e1e;
--color-diff-removed-row-border: #634343;
--color-diff-removed-word-bg: #6f3333;
--color-diff-inactive: #22282d; --color-diff-inactive: #22282d;
--color-error-border: #a04141; --color-error-border: #a04141;
--color-error-bg: #522; --color-error-bg: #522;

View File

@ -3,9 +3,10 @@
/* red/green colorblind-friendly colors */ /* red/green colorblind-friendly colors */
/* from GitHub: --diffBlob-addition-*, --diffBlob-deletion-*, etc */ /* from GitHub: --diffBlob-addition-*, --diffBlob-deletion-*, etc */
:root { :root {
--color-diff-added-word-bg: #54aeff66; --color-diff-added-linenum-bg: #54aeff4d;
--color-diff-added-row-bg: #ddf4ff80; --color-diff-added-row-bg: #ddf4ff80;
--color-diff-added-word-bg: #54aeff66;
--color-diff-removed-word-bg: #ffb77c80; --color-diff-removed-linenum-bg: #ffb77c4d;
--color-diff-removed-row-bg: #fff1e580; --color-diff-removed-row-bg: #fff1e580;
--color-diff-removed-word-bg: #ffb77c80;
} }

View File

@ -143,14 +143,16 @@
--color-grey-light: #7c838a; --color-grey-light: #7c838a;
--color-gold: #a1882b; --color-gold: #a1882b;
--color-white: #ffffff; --color-white: #ffffff;
--color-diff-removed-word-bg: #fdb8c0; --color-diff-added-linenum-bg: #d1f8d9;
--color-diff-added-word-bg: #acf2bd;
--color-diff-removed-row-bg: #ffeef0;
--color-diff-moved-row-bg: #f1f8d1;
--color-diff-added-row-bg: #e6ffed; --color-diff-added-row-bg: #e6ffed;
--color-diff-removed-row-border: #f1c0c0;
--color-diff-moved-row-border: #d0e27f;
--color-diff-added-row-border: #e6ffed; --color-diff-added-row-border: #e6ffed;
--color-diff-added-word-bg: #acf2bd;
--color-diff-moved-row-bg: #f1f8d1;
--color-diff-moved-row-border: #d0e27f;
--color-diff-removed-linenum-bg: #ffcecb;
--color-diff-removed-row-bg: #ffeef0;
--color-diff-removed-row-border: #f1c0c0;
--color-diff-removed-word-bg: #fdb8c0;
--color-diff-inactive: #f0f2f4; --color-diff-inactive: #f0f2f4;
--color-error-border: #e0b4b4; --color-error-border: #e0b4b4;
--color-error-bg: #fff6f6; --color-error-bg: #fff6f6;

View File

@ -6,18 +6,10 @@
// This file must be imported before any lazy-loading is being attempted. // This file must be imported before any lazy-loading is being attempted.
__webpack_public_path__ = `${window.config?.assetUrlPrefix ?? '/assets'}/`; __webpack_public_path__ = `${window.config?.assetUrlPrefix ?? '/assets'}/`;
const filteredErrors = new Set([
'getModifierState is not a function', // https://github.com/microsoft/monaco-editor/issues/4325
]);
export function showGlobalErrorMessage(msg) { export function showGlobalErrorMessage(msg) {
const pageContent = document.querySelector('.page-content'); const pageContent = document.querySelector('.page-content');
if (!pageContent) return; if (!pageContent) return;
for (const filteredError of filteredErrors) {
if (msg.includes(filteredError)) return;
}
// compact the message to a data attribute to avoid too many duplicated messages // compact the message to a data attribute to avoid too many duplicated messages
const msgCompact = msg.replace(/\W/g, '').trim(); const msgCompact = msg.replace(/\W/g, '').trim();
let msgDiv = pageContent.querySelector(`.js-global-error[data-global-error-msg-compact="${msgCompact}"]`); let msgDiv = pageContent.querySelector(`.js-global-error[data-global-error-msg-compact="${msgCompact}"]`);
@ -50,7 +42,7 @@ function processWindowErrorEvent({error, reason, message, type, filename, lineno
const assetBaseUrl = String(new URL(__webpack_public_path__, window.location.origin)); const assetBaseUrl = String(new URL(__webpack_public_path__, window.location.origin));
const {runModeIsProd} = window.config ?? {}; const {runModeIsProd} = window.config ?? {};
// `error` and `reason` are not guaranteed to be errors. If the value is falsy, it is likly a // `error` and `reason` are not guaranteed to be errors. If the value is falsy, it is likely a
// non-critical event from the browser. We log them but don't show them to users. Examples: // non-critical event from the browser. We log them but don't show them to users. Examples:
// - https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver#observation_errors // - https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver#observation_errors
// - https://github.com/mozilla-mobile/firefox-ios/issues/10817 // - https://github.com/mozilla-mobile/firefox-ios/issues/10817