Переглянути джерело

Merge branch 'main' into misspell050

pull/30573/head
silverwind 3 тижднів тому
джерело
коміт
e35bd1f694
Аккаунт користувача з таким Email не знайдено
100 змінених файлів з 424 додано та 328 видалено
  1. 4
    4
      .eslintrc.yaml
  2. 32
    23
      .golangci.yml
  3. 34
    24
      cmd/dump.go
  4. 1
    1
      cmd/hook.go
  5. 4
    3
      custom/conf/app.example.ini
  6. 3
    4
      docs/content/administration/config-cheat-sheet.en-us.md
  7. 2
    3
      docs/content/administration/config-cheat-sheet.zh-cn.md
  8. 1
    1
      docs/content/administration/customizing-gitea.en-us.md
  9. 0
    11
      docs/content/help/faq.en-us.md
  10. 0
    11
      docs/content/help/faq.zh-cn.md
  11. 27
    6
      models/actions/run.go
  12. 24
    2
      models/actions/runner.go
  13. 5
    0
      models/actions/variable.go
  14. 0
    1
      models/asymkey/gpg_key_commit_verification.go
  15. 0
    2
      models/auth/oauth2_test.go
  16. 0
    1
      models/db/engine.go
  17. 0
    2
      models/issues/review.go
  18. 0
    5
      models/migrations/base/db.go
  19. 0
    0
      models/migrations/fixtures/Test_AddConfidentialClientColumnToOAuth2ApplicationTable/oauth2_application.yml
  20. 2
    0
      models/migrations/migrations.go
  21. 0
    2
      models/migrations/v1_11/v111.go
  22. 0
    5
      models/migrations/v1_16/v210.go
  23. 3
    3
      models/migrations/v1_18/v230.go
  24. 3
    3
      models/migrations/v1_18/v230_test.go
  25. 2
    2
      models/migrations/v1_20/v250.go
  26. 1
    1
      models/migrations/v1_22/v286.go
  27. 10
    0
      models/migrations/v1_23/v298.go
  28. 0
    1
      models/migrations/v1_6/v71.go
  29. 0
    1
      models/migrations/v1_9/v85.go
  30. 1
    2
      models/organization/team.go
  31. 0
    2
      models/project/board.go
  32. 0
    1
      models/repo/user_repo.go
  33. 5
    0
      models/unittest/testdb.go
  34. 10
    11
      models/user/user.go
  35. 4
    3
      models/user/user_test.go
  36. 6
    6
      modules/auth/password/password.go
  37. 3
    3
      modules/git/batch_reader.go
  38. 1
    2
      modules/git/commit_reader.go
  39. 32
    0
      modules/git/pipeline/lfs_common.go
  40. 3
    22
      modules/git/pipeline/lfs_gogit.go
  41. 7
    27
      modules/git/pipeline/lfs_nogogit.go
  42. 4
    4
      modules/git/repo_commit.go
  43. 0
    1
      modules/git/submodule.go
  44. 0
    2
      modules/indexer/code/bleve/bleve.go
  45. 2
    2
      modules/indexer/issues/dboptions.go
  46. 0
    1
      modules/indexer/issues/elasticsearch/elasticsearch.go
  47. 0
    1
      modules/log/event_format.go
  48. 0
    2
      modules/markup/markdown/markdown_test.go
  49. 16
    16
      modules/packages/rubygems/marshal.go
  50. 0
    1
      modules/process/manager_stacktraces.go
  51. 11
    7
      modules/queue/workergroup.go
  52. 0
    2
      modules/queue/workerqueue.go
  53. 23
    10
      modules/queue/workerqueue_test.go
  54. 0
    1
      modules/repository/temp.go
  55. 26
    0
      modules/session/mock.go
  56. 21
    2
      modules/session/store.go
  57. 3
    3
      modules/setting/config_provider.go
  58. 6
    10
      modules/setting/oauth2.go
  59. 1
    1
      modules/setting/setting.go
  60. 1
    2
      modules/setting/time.go
  61. 0
    1
      modules/setting/ui.go
  62. 12
    6
      modules/templates/helper.go
  63. 2
    3
      modules/templates/htmlrenderer.go
  64. 1
    2
      modules/templates/mailer.go
  65. 3
    3
      modules/util/util_test.go
  66. 10
    0
      options/license/HPND-UC-export-US
  67. 32
    0
      options/license/NCL
  68. 0
    1
      options/locale/locale_cs-CZ.ini
  69. 0
    1
      options/locale/locale_de-DE.ini
  70. 0
    1
      options/locale/locale_el-GR.ini
  71. 3
    1
      options/locale/locale_en-US.ini
  72. 0
    1
      options/locale/locale_es-ES.ini
  73. 0
    1
      options/locale/locale_fa-IR.ini
  74. 0
    1
      options/locale/locale_fi-FI.ini
  75. 0
    1
      options/locale/locale_fr-FR.ini
  76. 0
    1
      options/locale/locale_hu-HU.ini
  77. 0
    1
      options/locale/locale_id-ID.ini
  78. 0
    1
      options/locale/locale_is-IS.ini
  79. 0
    1
      options/locale/locale_it-IT.ini
  80. 0
    1
      options/locale/locale_ja-JP.ini
  81. 0
    1
      options/locale/locale_ko-KR.ini
  82. 0
    1
      options/locale/locale_lv-LV.ini
  83. 0
    1
      options/locale/locale_nl-NL.ini
  84. 0
    1
      options/locale/locale_pl-PL.ini
  85. 0
    1
      options/locale/locale_pt-BR.ini
  86. 10
    9
      options/locale/locale_pt-PT.ini
  87. 0
    1
      options/locale/locale_ru-RU.ini
  88. 0
    1
      options/locale/locale_si-LK.ini
  89. 0
    1
      options/locale/locale_sk-SK.ini
  90. 0
    1
      options/locale/locale_sv-SE.ini
  91. 0
    1
      options/locale/locale_tr-TR.ini
  92. 0
    1
      options/locale/locale_uk-UA.ini
  93. 0
    1
      options/locale/locale_zh-CN.ini
  94. 0
    1
      options/locale/locale_zh-HK.ini
  95. 0
    1
      options/locale/locale_zh-TW.ini
  96. 4
    4
      package-lock.json
  97. 1
    1
      package.json
  98. 0
    1
      routers/api/actions/artifacts.go
  99. 2
    2
      routers/api/packages/alpine/alpine.go
  100. 0
    0
      routers/api/packages/conan/conan.go

+ 4
- 4
.eslintrc.yaml Переглянути файл

jquery/no-merge: [2] jquery/no-merge: [2]
jquery/no-param: [2] jquery/no-param: [2]
jquery/no-parent: [0] jquery/no-parent: [0]
jquery/no-parents: [0]
jquery/no-parents: [2]
jquery/no-parse-html: [2] jquery/no-parse-html: [2]
jquery/no-prop: [2] jquery/no-prop: [2]
jquery/no-proxy: [2] jquery/no-proxy: [2]
jquery/no-show: [2] jquery/no-show: [2]
jquery/no-size: [2] jquery/no-size: [2]
jquery/no-sizzle: [2] jquery/no-sizzle: [2]
jquery/no-slide: [0]
jquery/no-submit: [0]
jquery/no-slide: [2]
jquery/no-submit: [2]
jquery/no-text: [0] jquery/no-text: [0]
jquery/no-toggle: [2] jquery/no-toggle: [2]
jquery/no-trigger: [0] jquery/no-trigger: [0]
no-jquery/no-other-utils: [2] no-jquery/no-other-utils: [2]
no-jquery/no-param: [2] no-jquery/no-param: [2]
no-jquery/no-parent: [0] no-jquery/no-parent: [0]
no-jquery/no-parents: [0]
no-jquery/no-parents: [2]
no-jquery/no-parse-html-literal: [0] no-jquery/no-parse-html-literal: [0]
no-jquery/no-parse-html: [2] no-jquery/no-parse-html: [2]
no-jquery/no-parse-json: [2] no-jquery/no-parse-json: [2]

+ 32
- 23
.golangci.yml Переглянути файл

linters: linters:
enable-all: false
disable-all: true
fast: false
enable: enable:
- bidichk - bidichk
# - deadcode # deprecated - https://github.com/golangci/golangci-lint/issues/1841
- depguard - depguard
- dupl - dupl
- errcheck - errcheck
- forbidigo - forbidigo
- gocritic - gocritic
# - gocyclo # The cyclomatic complexety of a lot of functions is too high, we should refactor those another time.
- gofmt - gofmt
- gofumpt - gofumpt
- gosimple - gosimple
- nolintlint - nolintlint
- revive - revive
- staticcheck - staticcheck
# - structcheck # deprecated - https://github.com/golangci/golangci-lint/issues/1841
- stylecheck - stylecheck
- typecheck - typecheck
- unconvert - unconvert
- unused - unused
# - varcheck # deprecated - https://github.com/golangci/golangci-lint/issues/1841
- wastedassign - wastedassign
enable-all: false
disable-all: true
fast: false


run: run:
timeout: 10m timeout: 10m


output:
sort-results: true

linters-settings: linters-settings:
stylecheck: stylecheck:
checks: ["all", "-ST1005", "-ST1003"] checks: ["all", "-ST1005", "-ST1003"]
errorCode: 1 errorCode: 1
warningCode: 1 warningCode: 1
rules: rules:
- name: atomic
- name: bare-return
- name: blank-imports - name: blank-imports
- name: constant-logical-expr
- name: context-as-argument - name: context-as-argument
- name: context-keys-type - name: context-keys-type
- name: dot-imports - name: dot-imports
- name: duplicated-imports
- name: empty-lines
- name: error-naming
- name: error-return - name: error-return
- name: error-strings - name: error-strings
- name: error-naming
- name: errorf
- name: exported - name: exported
- name: identical-branches
- name: if-return - name: if-return
- name: increment-decrement - name: increment-decrement
- name: var-naming
- name: var-declaration
- name: indent-error-flow
- name: modifies-value-receiver
- name: package-comments - name: package-comments
- name: range - name: range
- name: receiver-naming - name: receiver-naming
- name: redefines-builtin-id
- name: string-of-int
- name: superfluous-else
- name: time-naming - name: time-naming
- name: unconditional-recursion
- name: unexported-return - name: unexported-return
- name: indent-error-flow
- name: errorf
- name: duplicated-imports
- name: modifies-value-receiver
- name: unreachable-code
- name: var-declaration
- name: var-naming
gofumpt: gofumpt:
extra-rules: true extra-rules: true
depguard: depguard:
max-issues-per-linter: 0 max-issues-per-linter: 0
max-same-issues: 0 max-same-issues: 0
exclude-dirs: [node_modules, public, web_src] exclude-dirs: [node_modules, public, web_src]
exclude-case-sensitive: true
exclude-rules: exclude-rules:
# Exclude some linters from running on tests files.
- path: _test\.go - path: _test\.go
linters: linters:
- gocyclo - gocyclo
- path: cmd - path: cmd
linters: linters:
- forbidigo - forbidigo
- linters:
- text: "webhook"
linters:
- dupl - dupl
text: "webhook"
- linters:
- text: "`ID' should not be capitalized"
linters:
- gocritic - gocritic
text: "`ID' should not be capitalized"
- linters:
- text: "swagger"
linters:
- unused - unused
- deadcode - deadcode
text: "swagger"
- linters:
- text: "argument x is overwritten before first use"
linters:
- staticcheck - staticcheck
text: "argument x is overwritten before first use"
- text: "commentFormatting: put a space between `//` and comment text" - text: "commentFormatting: put a space between `//` and comment text"
linters: linters:
- gocritic - gocritic

+ 34
- 24
cmd/dump.go Переглянути файл

Name: "skip-index", Name: "skip-index",
Usage: "Skip bleve index data", Usage: "Skip bleve index data",
}, },
&cli.BoolFlag{
Name: "skip-db",
Usage: "Skip database",
},
&cli.StringFlag{ &cli.StringFlag{
Name: "type", Name: "type",
Usage: fmt.Sprintf(`Dump output format, default to "zip", supported types: %s`, strings.Join(dump.SupportedOutputTypes, ", ")), Usage: fmt.Sprintf(`Dump output format, default to "zip", supported types: %s`, strings.Join(dump.SupportedOutputTypes, ", ")),
} }
} }


tmpDir := ctx.String("tempdir")
if _, err := os.Stat(tmpDir); os.IsNotExist(err) {
fatal("Path does not exist: %s", tmpDir)
}
if ctx.Bool("skip-db") {
// Ensure that we don't dump the database file that may reside in setting.AppDataPath or elsewhere.
dumper.GlobalExcludeAbsPath(setting.Database.Path)
log.Info("Skipping database")
} else {
tmpDir := ctx.String("tempdir")
if _, err := os.Stat(tmpDir); os.IsNotExist(err) {
fatal("Path does not exist: %s", tmpDir)
}


dbDump, err := os.CreateTemp(tmpDir, "gitea-db.sql")
if err != nil {
fatal("Failed to create tmp file: %v", err)
}
defer func() {
_ = dbDump.Close()
if err := util.Remove(dbDump.Name()); err != nil {
log.Warn("Unable to remove temporary file: %s: Error: %v", dbDump.Name(), err)
dbDump, err := os.CreateTemp(tmpDir, "gitea-db.sql")
if err != nil {
fatal("Failed to create tmp file: %v", err)
} }
}()
defer func() {
_ = dbDump.Close()
if err := util.Remove(dbDump.Name()); err != nil {
log.Warn("Unable to remove temporary file: %s: Error: %v", dbDump.Name(), err)
}
}()


targetDBType := ctx.String("database")
if len(targetDBType) > 0 && targetDBType != setting.Database.Type.String() {
log.Info("Dumping database %s => %s...", setting.Database.Type, targetDBType)
} else {
log.Info("Dumping database...")
}
targetDBType := ctx.String("database")
if len(targetDBType) > 0 && targetDBType != setting.Database.Type.String() {
log.Info("Dumping database %s => %s...", setting.Database.Type, targetDBType)
} else {
log.Info("Dumping database...")
}


if err := db.DumpDatabase(dbDump.Name(), targetDBType); err != nil {
fatal("Failed to dump database: %v", err)
}
if err := db.DumpDatabase(dbDump.Name(), targetDBType); err != nil {
fatal("Failed to dump database: %v", err)
}


if err = dumper.AddFile("gitea-db.sql", dbDump.Name()); err != nil {
fatal("Failed to include gitea-db.sql: %v", err)
if err = dumper.AddFile("gitea-db.sql", dbDump.Name()); err != nil {
fatal("Failed to include gitea-db.sql: %v", err)
}
} }


log.Info("Adding custom configuration file from %s", setting.CustomConf) log.Info("Adding custom configuration file from %s", setting.CustomConf)

+ 1
- 1
cmd/hook.go Переглянути файл

fmt.Fprintf(os.Stderr, " %s\n", url) fmt.Fprintf(os.Stderr, " %s\n", url)
} }
fmt.Fprintln(os.Stderr, "") fmt.Fprintln(os.Stderr, "")
os.Stderr.Sync()
_ = os.Stderr.Sync()
} }


func pushOptions() map[string]string { func pushOptions() map[string]string {

+ 4
- 3
custom/conf/app.example.ini Переглянути файл

;DEFAULT_THEME = gitea-auto ;DEFAULT_THEME = gitea-auto
;; ;;
;; All available themes. Allow users select personalized themes regardless of the value of `DEFAULT_THEME`. ;; All available themes. Allow users select personalized themes regardless of the value of `DEFAULT_THEME`.
;THEMES = gitea-auto,gitea-light,gitea-dark
;; Leave it empty to allow users to select any theme from "{CustomPath}/public/assets/css/theme-*.css"
;THEMES =
;; ;;
;; All available reactions users can choose on issues/prs and comments. ;; All available reactions users can choose on issues/prs and comments.
;; Values can be emoji alias (:smile:) or a unicode emoji. ;; Values can be emoji alias (:smile:) or a unicode emoji.
;; email = use the username part of the email attribute ;; email = use the username part of the email attribute
;; Note: `nickname`, `preferred_username` and `email` options will normalize input strings using the following criteria: ;; Note: `nickname`, `preferred_username` and `email` options will normalize input strings using the following criteria:
;; - diacritics are removed ;; - diacritics are removed
;; - the characters in the set `['´\x60]` are removed
;; - the characters in the set `[\s~+]` are replaced with `-`
;; - the characters in the set ['´`] are removed
;; - the characters in the set [\s~+] are replaced with "-"
;USERNAME = nickname ;USERNAME = nickname
;; ;;
;; Update avatar if available from oauth2 provider. ;; Update avatar if available from oauth2 provider.

+ 3
- 4
docs/content/administration/config-cheat-sheet.en-us.md Переглянути файл

- `SITEMAP_PAGING_NUM`: **20**: Number of items that are displayed in a single subsitemap. - `SITEMAP_PAGING_NUM`: **20**: Number of items that are displayed in a single subsitemap.
- `GRAPH_MAX_COMMIT_NUM`: **100**: Number of maximum commits shown in the commit graph. - `GRAPH_MAX_COMMIT_NUM`: **100**: Number of maximum commits shown in the commit graph.
- `CODE_COMMENT_LINES`: **4**: Number of line of codes shown for a code comment. - `CODE_COMMENT_LINES`: **4**: Number of line of codes shown for a code comment.
- `DEFAULT_THEME`: **gitea-auto**: \[gitea-auto, gitea-light, gitea-dark\]: Set the default theme for the Gitea installation.
- `DEFAULT_THEME`: **gitea-auto**: Set the default theme for the Gitea installation, custom themes could be provided by "{CustomPath}/public/assets/css/theme-*.css".
- `SHOW_USER_EMAIL`: **true**: Whether the email of the user should be shown in the Explore Users page. - `SHOW_USER_EMAIL`: **true**: Whether the email of the user should be shown in the Explore Users page.
- `THEMES`: **gitea-auto,gitea-light,gitea-dark**: All available themes. Allow users select personalized themes.
regardless of the value of `DEFAULT_THEME`.
- `THEMES`: **_empty_**: All available themes by "{CustomPath}/public/assets/css/theme-*.css". Allow users select personalized themes.
- `MAX_DISPLAY_FILE_SIZE`: **8388608**: Max size of files to be displayed (default is 8MiB) - `MAX_DISPLAY_FILE_SIZE`: **8388608**: Max size of files to be displayed (default is 8MiB)
- `AMBIGUOUS_UNICODE_DETECTION`: **true**: Detect ambiguous unicode characters in file contents and show warnings on the UI - `AMBIGUOUS_UNICODE_DETECTION`: **true**: Detect ambiguous unicode characters in file contents and show warnings on the UI
- `REACTIONS`: All available reactions users can choose on issues/prs and comments - `REACTIONS`: All available reactions users can choose on issues/prs and comments
- `email` - use the username part of the email attribute - `email` - use the username part of the email attribute
- Note: `nickname`, `preferred_username` and `email` options will normalize input strings using the following criteria: - Note: `nickname`, `preferred_username` and `email` options will normalize input strings using the following criteria:
- diacritics are removed - diacritics are removed
- the characters in the set `['´\x60]` are removed
- the characters in the set ```['´`]``` are removed
- the characters in the set `[\s~+]` are replaced with `-` - the characters in the set `[\s~+]` are replaced with `-`
- `UPDATE_AVATAR`: **false**: Update avatar if available from oauth2 provider. Update will be performed on each login. - `UPDATE_AVATAR`: **false**: Update avatar if available from oauth2 provider. Update will be performed on each login.
- `ACCOUNT_LINKING`: **login**: How to handle if an account / email already exists: - `ACCOUNT_LINKING`: **login**: How to handle if an account / email already exists:

+ 2
- 3
docs/content/administration/config-cheat-sheet.zh-cn.md Переглянути файл

- `SITEMAP_PAGING_NUM`: **20**: 在单个子SiteMap中显示的项数。 - `SITEMAP_PAGING_NUM`: **20**: 在单个子SiteMap中显示的项数。
- `GRAPH_MAX_COMMIT_NUM`: **100**: 提交图中显示的最大commit数量。 - `GRAPH_MAX_COMMIT_NUM`: **100**: 提交图中显示的最大commit数量。
- `CODE_COMMENT_LINES`: **4**: 在代码评论中能够显示的最大代码行数。 - `CODE_COMMENT_LINES`: **4**: 在代码评论中能够显示的最大代码行数。
- `DEFAULT_THEME`: **gitea-auto**: \[gitea-auto, gitea-light, gitea-dark\]: 在Gitea安装时候设置的默认主题。
- `DEFAULT_THEME`: **gitea-auto**: 在Gitea安装时候设置的默认主题,自定义的主题可以通过 "{CustomPath}/public/assets/css/theme-*.css" 提供
- `SHOW_USER_EMAIL`: **true**: 用户的电子邮件是否应该显示在`Explore Users`页面中。 - `SHOW_USER_EMAIL`: **true**: 用户的电子邮件是否应该显示在`Explore Users`页面中。
- `THEMES`: **gitea-auto,gitea-light,gitea-dark**: 所有可用的主题。允许用户选择个性化的主题,
而不受DEFAULT_THEME 值的影响。
- `THEMES`: **_empty_**: 所有可用的主题(由 "{CustomPath}/public/assets/css/theme-*.css" 提供)。允许用户选择个性化的主题,
- `MAX_DISPLAY_FILE_SIZE`: **8388608**: 能够显示文件的最大大小(默认为8MiB)。 - `MAX_DISPLAY_FILE_SIZE`: **8388608**: 能够显示文件的最大大小(默认为8MiB)。
- `REACTIONS`: 用户可以在问题(Issue)、Pull Request(PR)以及评论中选择的所有可选的反应。 - `REACTIONS`: 用户可以在问题(Issue)、Pull Request(PR)以及评论中选择的所有可选的反应。
这些值可以是表情符号别名(例如::smile:)或Unicode表情符号。 这些值可以是表情符号别名(例如::smile:)或Unicode表情符号。

+ 1
- 1
docs/content/administration/customizing-gitea.en-us.md Переглянути файл



1. Add a CSS file to `$GITEA_CUSTOM/public/assets/css/theme-<theme-name>.css`. 1. Add a CSS file to `$GITEA_CUSTOM/public/assets/css/theme-<theme-name>.css`.
The value of `$GITEA_CUSTOM` of your instance can be queried by calling `gitea help` and looking up the value of "CustomPath". The value of `$GITEA_CUSTOM` of your instance can be queried by calling `gitea help` and looking up the value of "CustomPath".
2. Add `<theme-name>` to the comma-separated list of setting `THEMES` in `app.ini`
2. Add `<theme-name>` to the comma-separated list of setting `THEMES` in `app.ini`, or leave `THEMES` empty to allow all themes.


Community themes are listed in [gitea/awesome-gitea#themes](https://gitea.com/gitea/awesome-gitea#themes). Community themes are listed in [gitea/awesome-gitea#themes](https://gitea.com/gitea/awesome-gitea#themes).



+ 0
- 11
docs/content/help/faq.en-us.md Переглянути файл



Use [Fail2Ban](administration/fail2ban-setup.md) to monitor and stop automated login attempts or other malicious behavior based on log patterns Use [Fail2Ban](administration/fail2ban-setup.md) to monitor and stop automated login attempts or other malicious behavior based on log patterns


## How to add/use custom themes

Gitea supports three official themes right now, `gitea-light`, `gitea-dark`, and `gitea-auto` (automatically switches between the previous two depending on operating system settings).
To add your own theme, currently the only way is to provide a complete theme (not just color overrides)

As an example, let's say our theme is `arc-blue` (this is a real theme, and can be found [in this issue](https://github.com/go-gitea/gitea/issues/6011))

Name the `.css` file `theme-arc-blue.css` and add it to your custom folder in `custom/public/assets/css`

Allow users to use it by adding `arc-blue` to the list of `THEMES` in your `app.ini`

## SSHD vs built-in SSH ## SSHD vs built-in SSH


SSHD is the built-in SSH server on most Unix systems. SSHD is the built-in SSH server on most Unix systems.

+ 0
- 11
docs/content/help/faq.zh-cn.md Переглянути файл



使用 [Fail2Ban](administration/fail2ban-setup.md) 监视并阻止基于日志模式的自动登录尝试或其他恶意行为。 使用 [Fail2Ban](administration/fail2ban-setup.md) 监视并阻止基于日志模式的自动登录尝试或其他恶意行为。


## 如何添加/使用自定义主题

Gitea 目前支持三个官方主题,分别是 `gitea-light`、`gitea-dark` 和 `gitea-auto`(根据操作系统设置自动切换前两个主题)。
要添加自己的主题,目前唯一的方法是提供一个完整的主题(不仅仅是颜色覆盖)。

假设我们的主题是 `arc-blue`(这是一个真实的主题,可以在[此问题](https://github.com/go-gitea/gitea/issues/6011)中找到)

将`.css`文件命名为`theme-arc-blue.css`并将其添加到`custom/public/assets/css`文件夹中

通过将`arc-blue`添加到`app.ini`中的`THEMES`列表中,允许用户使用该主题

## SSHD vs 内建SSH ## SSHD vs 内建SSH


SSHD是大多数Unix系统上内建的SSH服务器。 SSHD是大多数Unix系统上内建的SSH服务器。

+ 27
- 6
models/actions/run.go Переглянути файл

return fmt.Sprintf("%s/actions/runs/%d", run.Repo.Link(), run.Index) return fmt.Sprintf("%s/actions/runs/%d", run.Repo.Link(), run.Index)
} }


func (run *ActionRun) WorkflowLink() string {
if run.Repo == nil {
return ""
}
return fmt.Sprintf("%s/actions/?workflow=%s", run.Repo.Link(), run.WorkflowID)
}

// RefLink return the url of run's ref // RefLink return the url of run's ref
func (run *ActionRun) RefLink() string { func (run *ActionRun) RefLink() string {
refName := git.RefName(run.Ref) refName := git.RefName(run.Ref)
return nil return nil
} }


if run.Repo == nil {
repo, err := repo_model.GetRepositoryByID(ctx, run.RepoID)
if err != nil {
return err
}
run.Repo = repo
if err := run.LoadRepo(ctx); err != nil {
return err
} }

if err := run.Repo.LoadAttributes(ctx); err != nil { if err := run.Repo.LoadAttributes(ctx); err != nil {
return err return err
} }
return nil return nil
} }


func (run *ActionRun) LoadRepo(ctx context.Context) error {
if run == nil || run.Repo != nil {
return nil
}

repo, err := repo_model.GetRepositoryByID(ctx, run.RepoID)
if err != nil {
return err
}
run.Repo = repo
return nil
}

func (run *ActionRun) Duration() time.Duration { func (run *ActionRun) Duration() time.Duration {
return calculateDuration(run.Started, run.Stopped, run.Status) + run.PreviousDuration return calculateDuration(run.Started, run.Stopped, run.Status) + run.PreviousDuration
} }
return nil, fmt.Errorf("event %s is not a pull request event", run.Event) return nil, fmt.Errorf("event %s is not a pull request event", run.Event)
} }


func (run *ActionRun) IsSchedule() bool {
return run.ScheduleID > 0
}

func updateRepoRunsNumbers(ctx context.Context, repo *repo_model.Repository) error { func updateRepoRunsNumbers(ctx context.Context, repo *repo_model.Repository) error {
_, err := db.GetEngine(ctx).ID(repo.ID). _, err := db.GetEngine(ctx).ID(repo.ID).
SetExpr("num_action_runs", SetExpr("num_action_runs",

+ 24
- 2
models/actions/runner.go Переглянути файл

// Only affect action runners were a owner ID is set, as actions runners // Only affect action runners were a owner ID is set, as actions runners
// could also be created on a repository. // could also be created on a repository.
return db.GetEngine(ctx).Table("action_runner"). return db.GetEngine(ctx).Table("action_runner").
Join("LEFT", "user", "`action_runner`.owner_id = `user`.id").
Join("LEFT", "`user`", "`action_runner`.owner_id = `user`.id").
Where("`action_runner`.owner_id != ?", 0). Where("`action_runner`.owner_id != ?", 0).
And(builder.IsNull{"`user`.id"}). And(builder.IsNull{"`user`.id"}).
Count(new(ActionRunner)) Count(new(ActionRunner))
func FixRunnersWithoutBelongingOwner(ctx context.Context) (int64, error) { func FixRunnersWithoutBelongingOwner(ctx context.Context) (int64, error) {
subQuery := builder.Select("`action_runner`.id"). subQuery := builder.Select("`action_runner`.id").
From("`action_runner`"). From("`action_runner`").
Join("LEFT", "user", "`action_runner`.owner_id = `user`.id").
Join("LEFT", "`user`", "`action_runner`.owner_id = `user`.id").
Where(builder.Neq{"`action_runner`.owner_id": 0}). Where(builder.Neq{"`action_runner`.owner_id": 0}).
And(builder.IsNull{"`user`.id"}) And(builder.IsNull{"`user`.id"})
b := builder.Delete(builder.In("id", subQuery)).From("`action_runner`") b := builder.Delete(builder.In("id", subQuery)).From("`action_runner`")
} }
return res.RowsAffected() return res.RowsAffected()
} }

func CountRunnersWithoutBelongingRepo(ctx context.Context) (int64, error) {
return db.GetEngine(ctx).Table("action_runner").
Join("LEFT", "`repository`", "`action_runner`.repo_id = `repository`.id").
Where("`action_runner`.repo_id != ?", 0).
And(builder.IsNull{"`repository`.id"}).
Count(new(ActionRunner))
}

func FixRunnersWithoutBelongingRepo(ctx context.Context) (int64, error) {
subQuery := builder.Select("`action_runner`.id").
From("`action_runner`").
Join("LEFT", "`repository`", "`action_runner`.repo_id = `repository`.id").
Where(builder.Neq{"`action_runner`.repo_id": 0}).
And(builder.IsNull{"`repository`.id"})
b := builder.Delete(builder.In("id", subQuery)).From("`action_runner`")
res, err := db.GetEngine(ctx).Exec(b)
if err != nil {
return 0, err
}
return res.RowsAffected()
}

+ 5
- 0
models/actions/variable.go Переглянути файл

func GetVariablesOfRun(ctx context.Context, run *ActionRun) (map[string]string, error) { func GetVariablesOfRun(ctx context.Context, run *ActionRun) (map[string]string, error) {
variables := map[string]string{} variables := map[string]string{}


if err := run.LoadRepo(ctx); err != nil {
log.Error("LoadRepo: %v", err)
return nil, err
}

// Global // Global
globalVariables, err := db.Find[ActionVariable](ctx, FindVariablesOpts{}) globalVariables, err := db.Find[ActionVariable](ctx, FindVariablesOpts{})
if err != nil { if err != nil {

+ 0
- 1
models/asymkey/gpg_key_commit_verification.go Переглянути файл

Reason: "gpg.error.no_committer_account", Reason: "gpg.error.no_committer_account",
} }
} }

} }
} }



+ 0
- 2
models/auth/oauth2_test.go Переглянути файл

"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )


//////////////////// Application

func TestOAuth2Application_GenerateClientSecret(t *testing.T) { func TestOAuth2Application_GenerateClientSecret(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase()) assert.NoError(t, unittest.PrepareTestDatabase())
app := unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Application{ID: 1}) app := unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Application{ID: 1})

+ 0
- 1
models/db/engine.go Переглянути файл

// Need to map provided names to beans... // Need to map provided names to beans...
beanMap := make(map[string]any) beanMap := make(map[string]any)
for _, bean := range tables { for _, bean := range tables {

beanMap[strings.ToLower(reflect.Indirect(reflect.ValueOf(bean)).Type().Name())] = bean beanMap[strings.ToLower(reflect.Indirect(reflect.ValueOf(bean)).Type().Name())] = bean
beanMap[strings.ToLower(x.TableName(bean))] = bean beanMap[strings.ToLower(x.TableName(bean))] = bean
beanMap[strings.ToLower(x.TableName(bean, true))] = bean beanMap[strings.ToLower(x.TableName(bean, true))] = bean

+ 0
- 2
models/issues/review.go Переглянути файл

return nil, err return nil, err
} }
} }

} else if opts.ReviewerTeam != nil { } else if opts.ReviewerTeam != nil {
review.Type = ReviewTypeRequest review.Type = ReviewTypeRequest
review.ReviewerTeamID = opts.ReviewerTeam.ID review.ReviewerTeamID = opts.ReviewerTeam.ID

} else { } else {
return nil, fmt.Errorf("provide either reviewer or reviewer team") return nil, fmt.Errorf("provide either reviewer or reviewer team")
} }

+ 0
- 5
models/migrations/base/db.go Переглянути файл

log.Error("Unable to recreate uniques on table %s. Error: %v", tableName, err) log.Error("Unable to recreate uniques on table %s. Error: %v", tableName, err)
return err return err
} }

case setting.Database.Type.IsMySQL(): case setting.Database.Type.IsMySQL():
// MySQL will drop all the constraints on the old table // MySQL will drop all the constraints on the old table
if _, err := sess.Exec(fmt.Sprintf("DROP TABLE `%s`", tableName)); err != nil { if _, err := sess.Exec(fmt.Sprintf("DROP TABLE `%s`", tableName)); err != nil {
return err return err
} }
sequenceMap[sequence] = sequenceData sequenceMap[sequence] = sequenceData

} }


// CASCADE causes postgres to drop all the constraints on the old table // CASCADE causes postgres to drop all the constraints on the old table
return err return err
} }
} }

} }

case setting.Database.Type.IsMSSQL(): case setting.Database.Type.IsMSSQL():
// MSSQL will drop all the constraints on the old table // MSSQL will drop all the constraints on the old table
if _, err := sess.Exec(fmt.Sprintf("DROP TABLE `%s`", tableName)); err != nil { if _, err := sess.Exec(fmt.Sprintf("DROP TABLE `%s`", tableName)); err != nil {
log.Error("Unable to rename %s to %s. Error: %v", tempTableName, tableName, err) log.Error("Unable to rename %s to %s. Error: %v", tempTableName, tableName, err)
return err return err
} }

default: default:
log.Fatal("Unrecognized DB") log.Fatal("Unrecognized DB")
} }

models/migrations/fixtures/Test_AddConfidentialClientColumnToOAuth2ApplicationTable/o_auth2_application.yml → models/migrations/fixtures/Test_AddConfidentialClientColumnToOAuth2ApplicationTable/oauth2_application.yml Переглянути файл


+ 2
- 0
models/migrations/migrations.go Переглянути файл

NewMigration("Add missing field of commit status summary table", v1_23.AddCommitStatusSummary2), NewMigration("Add missing field of commit status summary table", v1_23.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_23.AddRepoUnitEveryoneAccessMode),
// v298 -> v299
NewMigration("Drop wrongly created table o_auth2_application", v1_23.DropWronglyCreatedTable),
} }


// GetCurrentDBVersion returns the current db version // GetCurrentDBVersion returns the current db version

+ 0
- 2
models/migrations/v1_11/v111.go Переглянути файл

for _, u := range units { for _, u := range units {
var found bool var found bool
for _, team := range teams { for _, team := range teams {

var teamU []*TeamUnit var teamU []*TeamUnit
var unitEnabled bool var unitEnabled bool
err = sess.Where("team_id = ?", team.ID).Find(&teamU) err = sess.Where("team_id = ?", team.ID).Find(&teamU)
} }


if !protectedBranch.EnableApprovalsWhitelist { if !protectedBranch.EnableApprovalsWhitelist {

perm, err := getUserRepoPermission(sess, baseRepo, reviewer) perm, err := getUserRepoPermission(sess, baseRepo, reviewer)
if err != nil { if err != nil {
return false, err return false, err

+ 0
- 5
models/migrations/v1_16/v210.go Переглянути файл

if err != nil { if err != nil {
return err return err
} }
case schemas.ORACLE:
_, err := x.Exec("ALTER TABLE webauthn_credential MODIFY credential_id VARCHAR(410)")
if err != nil {
return err
}
case schemas.MSSQL: case schemas.MSSQL:
// This column has an index on it. I could write all of the code to attempt to change the index OR // This column has an index on it. I could write all of the code to attempt to change the index OR
// I could just use recreate table. // I could just use recreate table.

+ 3
- 3
models/migrations/v1_18/v230.go Переглянути файл



// AddConfidentialColumnToOAuth2ApplicationTable: add ConfidentialClient column, setting existing rows to true // AddConfidentialColumnToOAuth2ApplicationTable: add ConfidentialClient column, setting existing rows to true
func AddConfidentialClientColumnToOAuth2ApplicationTable(x *xorm.Engine) error { func AddConfidentialClientColumnToOAuth2ApplicationTable(x *xorm.Engine) error {
type OAuth2Application struct {
type oauth2Application struct {
ID int64
ConfidentialClient bool `xorm:"NOT NULL DEFAULT TRUE"` ConfidentialClient bool `xorm:"NOT NULL DEFAULT TRUE"`
} }

return x.Sync(new(OAuth2Application))
return x.Sync(new(oauth2Application))
} }

+ 3
- 3
models/migrations/v1_18/v230_test.go Переглянути файл



func Test_AddConfidentialClientColumnToOAuth2ApplicationTable(t *testing.T) { func Test_AddConfidentialClientColumnToOAuth2ApplicationTable(t *testing.T) {
// premigration // premigration
type OAuth2Application struct {
type oauth2Application struct {
ID int64 ID int64
} }


// Prepare and load the testing database // Prepare and load the testing database
x, deferable := base.PrepareTestEnv(t, 0, new(OAuth2Application))
x, deferable := base.PrepareTestEnv(t, 0, new(oauth2Application))
defer deferable() defer deferable()
if x == nil || t.Failed() { if x == nil || t.Failed() {
return return
} }


got := []ExpectedOAuth2Application{} got := []ExpectedOAuth2Application{}
if err := x.Table("o_auth2_application").Select("id, confidential_client").Find(&got); !assert.NoError(t, err) {
if err := x.Table("oauth2_application").Select("id, confidential_client").Find(&got); !assert.NoError(t, err) {
return return
} }



+ 2
- 2
models/migrations/v1_20/v250.go Переглянути файл



// Convert to new metadata format // Convert to new metadata format


new := &MetadataNew{
newMetadata := &MetadataNew{
Type: old.Type, Type: old.Type,
IsTagged: old.IsTagged, IsTagged: old.IsTagged,
Platform: old.Platform, Platform: old.Platform,
Manifests: manifests, Manifests: manifests,
} }


metadataJSON, err := json.Marshal(new)
metadataJSON, err := json.Marshal(newMetadata)
if err != nil { if err != nil {
return err return err
} }

+ 1
- 1
models/migrations/v1_22/v286.go Переглянути файл

if setting.Database.Type.IsMySQL() { if setting.Database.Type.IsMySQL() {
_, err = db.Exec(fmt.Sprintf("ALTER TABLE `%s` MODIFY COLUMN `%s` VARCHAR(64)", alts[0], alts[1])) _, err = db.Exec(fmt.Sprintf("ALTER TABLE `%s` MODIFY COLUMN `%s` VARCHAR(64)", alts[0], alts[1]))
} else if setting.Database.Type.IsMSSQL() { } else if setting.Database.Type.IsMSSQL() {
_, err = db.Exec(fmt.Sprintf("ALTER TABLE [%s] ALTER COLUMN [%s] VARCHAR(64)", alts[0], alts[1]))
_, err = db.Exec(fmt.Sprintf("ALTER TABLE [%s] ALTER COLUMN [%s] NVARCHAR(64)", alts[0], alts[1]))
} else { } else {
_, err = db.Exec(fmt.Sprintf("ALTER TABLE `%s` ALTER COLUMN `%s` TYPE VARCHAR(64)", alts[0], alts[1])) _, err = db.Exec(fmt.Sprintf("ALTER TABLE `%s` ALTER COLUMN `%s` TYPE VARCHAR(64)", alts[0], alts[1]))
} }

+ 10
- 0
models/migrations/v1_23/v298.go Переглянути файл

// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package v1_23 //nolint

import "xorm.io/xorm"

func DropWronglyCreatedTable(x *xorm.Engine) error {
return x.DropTables("o_auth2_application")
}

+ 0
- 1
models/migrations/v1_6/v71.go Переглянути файл

if _, err := sess.ID(tfa.ID).Cols("scratch_salt, scratch_hash").Update(tfa); err != nil { if _, err := sess.ID(tfa.ID).Cols("scratch_salt, scratch_hash").Update(tfa); err != nil {
return fmt.Errorf("couldn't add in scratch_hash and scratch_salt: %w", err) return fmt.Errorf("couldn't add in scratch_hash and scratch_salt: %w", err)
} }

} }
} }



+ 0
- 1
models/migrations/v1_9/v85.go Переглянути файл

if _, err := sess.ID(token.ID).Cols("token_hash, token_salt, token_last_eight, sha1").Update(token); err != nil { if _, err := sess.ID(token.ID).Cols("token_hash, token_salt, token_last_eight, sha1").Update(token); err != nil {
return fmt.Errorf("couldn't add in sha1, token_hash, token_salt and token_last_eight: %w", err) return fmt.Errorf("couldn't add in sha1, token_hash, token_salt and token_last_eight: %w", err)
} }

} }
} }



+ 1
- 2
models/organization/team.go Переглянути файл

if err != nil { if err != nil {
if ignoreNonExistent { if ignoreNonExistent {
continue continue
} else {
return nil, err
} }
return nil, err
} }
ids = append(ids, u.ID) ids = append(ids, u.ID)
} }

+ 0
- 2
models/project/board.go Переглянути файл

var items []string var items []string


switch project.BoardType { switch project.BoardType {

case BoardTypeBugTriage: case BoardTypeBugTriage:
items = setting.Project.ProjectBoardBugTriageType items = setting.Project.ProjectBoardBugTriageType


case BoardTypeBasicKanban: case BoardTypeBasicKanban:
items = setting.Project.ProjectBoardBasicKanbanType items = setting.Project.ProjectBoardBasicKanbanType

case BoardTypeNone: case BoardTypeNone:
fallthrough fallthrough
default: default:

+ 0
- 1
models/repo/user_repo.go Переглянути файл

// the owner of a private repo needs to be explicitly added. // the owner of a private repo needs to be explicitly added.
cond = cond.Or(builder.Eq{"`user`.id": repo.Owner.ID}) cond = cond.Or(builder.Eq{"`user`.id": repo.Owner.ID})
} }

} else { } else {
// This is a "public" repository: // This is a "public" repository:
// Any user that has read access, is a watcher or organization member can be requested to review // Any user that has read access, is a watcher or organization member can be requested to review

+ 5
- 0
models/unittest/testdb.go Переглянути файл

"code.gitea.io/gitea/models/system" "code.gitea.io/gitea/models/system"
"code.gitea.io/gitea/modules/auth/password/hash" "code.gitea.io/gitea/modules/auth/password/hash"
"code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/cache"
"code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/git"
"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"
fatalTestError("Error creating test engine: %v\n", err) fatalTestError("Error creating test engine: %v\n", err)
} }


setting.IsInTesting = true
setting.AppURL = "https://try.gitea.io/" setting.AppURL = "https://try.gitea.io/"
setting.RunUser = "runuser" setting.RunUser = "runuser"
setting.SSH.User = "sshuser" setting.SSH.User = "sshuser"


config.SetDynGetter(system.NewDatabaseDynKeyGetter()) config.SetDynGetter(system.NewDatabaseDynKeyGetter())


if err = cache.Init(); err != nil {
fatalTestError("cache.Init: %v\n", err)
}
if err = storage.Init(); err != nil { if err = storage.Init(); err != nil {
fatalTestError("storage.Init: %v\n", err) fatalTestError("storage.Init: %v\n", err)
} }

+ 10
- 11
models/user/user.go Переглянути файл

// Note: The set of characters here can safely expand without a breaking change, // Note: The set of characters here can safely expand without a breaking change,
// but characters removed from this set can cause user account linking to break // but characters removed from this set can cause user account linking to break
var ( var (
customCharsReplacement = strings.NewReplacer("Æ", "AE")
removeCharsRE = regexp.MustCompile(`['´\x60]`)
removeDiacriticsTransform = transform.Chain(norm.NFD, runes.Remove(runes.In(unicode.Mn)), norm.NFC)
replaceCharsHyphenRE = regexp.MustCompile(`[\s~+]`)
customCharsReplacement = strings.NewReplacer("Æ", "AE")
removeCharsRE = regexp.MustCompile("['`´]")
transformDiacritics = transform.Chain(norm.NFD, runes.Remove(runes.In(unicode.Mn)), norm.NFC)
replaceCharsHyphenRE = regexp.MustCompile(`[\s~+]`)
) )


// normalizeUserName returns a string with single-quotes and diacritics
// removed, and any other non-supported username characters replaced with
// a `-` character
// NormalizeUserName only takes the name part if it is an email address, transforms it diacritics to ASCII characters.
// It returns a string with the single-quotes removed, and any other non-supported username characters are replaced with a `-` character
func NormalizeUserName(s string) (string, error) { func NormalizeUserName(s string) (string, error) {
strDiacriticsRemoved, n, err := transform.String(removeDiacriticsTransform, customCharsReplacement.Replace(s))
s, _, _ = strings.Cut(s, "@")
strDiacriticsRemoved, n, err := transform.String(transformDiacritics, customCharsReplacement.Replace(s))
if err != nil { if err != nil {
return "", fmt.Errorf("Failed to normalize character `%v` in provided username `%v`", s[n], s)
return "", fmt.Errorf("failed to normalize the string of provided username %q at position %d", s, n)
} }
return replaceCharsHyphenRE.ReplaceAllLiteralString(removeCharsRE.ReplaceAllLiteralString(strDiacriticsRemoved, ""), "-"), nil return replaceCharsHyphenRE.ReplaceAllLiteralString(removeCharsRE.ReplaceAllLiteralString(strDiacriticsRemoved, ""), "-"), nil
} }
if err != nil { if err != nil {
if ignoreNonExistent { if ignoreNonExistent {
continue continue
} else {
return nil, err
} }
return nil, err
} }
ids = append(ids, u.ID) ids = append(ids, u.ID)
} }

+ 4
- 3
models/user/user_test.go Переглянути файл

Expected string Expected string
IsNormalizedValid bool IsNormalizedValid bool
}{ }{
{"test", "test", true},
{"name@example.com", "name", true},
{"test'`´name", "testname", true},
{"Sinéad.O'Connor", "Sinead.OConnor", true}, {"Sinéad.O'Connor", "Sinead.OConnor", true},
{"Æsir", "AEsir", true}, {"Æsir", "AEsir", true},
// \u00e9\u0065\u0301
{"éé", "ee", true},
{"éé", "ee", true}, // \u00e9\u0065\u0301
{"Awareness Hub", "Awareness-Hub", true}, {"Awareness Hub", "Awareness-Hub", true},
{"double__underscore", "double__underscore", false}, // We should consider squashing double non-alpha characters {"double__underscore", "double__underscore", false}, // We should consider squashing double non-alpha characters
{".bad.", ".bad.", false}, {".bad.", ".bad.", false},
{"new😀user", "new😀user", false}, // No plans to support {"new😀user", "new😀user", false}, // No plans to support
{`"quoted"`, `"quoted"`, false}, // No plans to support
} }
for _, testCase := range testCases { for _, testCase := range testCases {
normalizedName, err := user_model.NormalizeUserName(testCase.Input) normalizedName, err := user_model.NormalizeUserName(testCase.Input)

+ 6
- 6
modules/auth/password/password.go Переглянути файл

func setupComplexity(values []string) { func setupComplexity(values []string) {
if len(values) != 1 || values[0] != "off" { if len(values) != 1 || values[0] != "off" {
for _, val := range values { for _, val := range values {
if complex, ok := charComplexities[val]; ok {
validChars += complex.ValidChars
requiredList = append(requiredList, complex)
if complexity, ok := charComplexities[val]; ok {
validChars += complexity.ValidChars
requiredList = append(requiredList, complexity)
} }
} }
if len(requiredList) == 0 { if len(requiredList) == 0 {
// No valid character classes found; use all classes as default // No valid character classes found; use all classes as default
for _, complex := range charComplexities {
validChars += complex.ValidChars
requiredList = append(requiredList, complex)
for _, complexity := range charComplexities {
validChars += complexity.ValidChars
requiredList = append(requiredList, complexity)
} }
} }
} }

+ 3
- 3
modules/git/batch_reader.go Переглянути файл



// Deal with the binary hash // Deal with the binary hash
idx = 0 idx = 0
len := objectFormat.FullLength() / 2
for idx < len {
length := objectFormat.FullLength() / 2
for idx < length {
var read int var read int
read, err = rd.Read(shaBuf[idx:len])
read, err = rd.Read(shaBuf[idx:length])
n += read n += read
if err != nil { if err != nil {
return mode, fname, sha, n, err return mode, fname, sha, n, err

+ 1
- 2
modules/git/commit_reader.go Переглянути файл

if len(line) > 0 && line[0] == ' ' { if len(line) > 0 && line[0] == ' ' {
_, _ = signatureSB.Write(line[1:]) _, _ = signatureSB.Write(line[1:])
continue continue
} else {
pgpsig = false
} }
pgpsig = false
} }


if !message { if !message {

+ 32
- 0
modules/git/pipeline/lfs_common.go Переглянути файл

// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package pipeline

import (
"fmt"
"time"

"code.gitea.io/gitea/modules/git"
)

// LFSResult represents commits found using a provided pointer file hash
type LFSResult struct {
Name string
SHA string
Summary string
When time.Time
ParentHashes []git.ObjectID
BranchName string
FullCommitName string
}

type lfsResultSlice []*LFSResult

func (a lfsResultSlice) Len() int { return len(a) }
func (a lfsResultSlice) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a lfsResultSlice) Less(i, j int) bool { return a[j].When.After(a[i].When) }

func lfsError(msg string, err error) error {
return fmt.Errorf("LFS error occurred, %s: err: %w", msg, err)
}

modules/git/pipeline/lfs.go → modules/git/pipeline/lfs_gogit.go Переглянути файл



import ( import (
"bufio" "bufio"
"fmt"
"io" "io"
"sort" "sort"
"strings" "strings"
"sync" "sync"
"time"


"code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/git"


"github.com/go-git/go-git/v5/plumbing/object" "github.com/go-git/go-git/v5/plumbing/object"
) )


// LFSResult represents commits found using a provided pointer file hash
type LFSResult struct {
Name string
SHA string
Summary string
When time.Time
ParentHashes []git.ObjectID
BranchName string
FullCommitName string
}

type lfsResultSlice []*LFSResult

func (a lfsResultSlice) Len() int { return len(a) }
func (a lfsResultSlice) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a lfsResultSlice) Less(i, j int) bool { return a[j].When.After(a[i].When) }

// FindLFSFile finds commits that contain a provided pointer file hash // FindLFSFile finds commits that contain a provided pointer file hash
func FindLFSFile(repo *git.Repository, objectID git.ObjectID) ([]*LFSResult, error) { func FindLFSFile(repo *git.Repository, objectID git.ObjectID) ([]*LFSResult, error) {
resultsMap := map[string]*LFSResult{} resultsMap := map[string]*LFSResult{}
All: true, All: true,
}) })
if err != nil { if err != nil {
return nil, fmt.Errorf("Failed to get GoGit CommitsIter. Error: %w", err)
return nil, lfsError("failed to get GoGit CommitsIter", err)
} }


err = commitsIter.ForEach(func(gitCommit *object.Commit) error { err = commitsIter.ForEach(func(gitCommit *object.Commit) error {
return nil return nil
}) })
if err != nil && err != io.EOF { if err != nil && err != io.EOF {
return nil, fmt.Errorf("Failure in CommitIter.ForEach: %w", err)
return nil, lfsError("failure in CommitIter.ForEach", err)
} }


for _, result := range resultsMap { for _, result := range resultsMap {
select { select {
case err, has := <-errChan: case err, has := <-errChan:
if has { if has {
return nil, fmt.Errorf("Unable to obtain name for LFS files. Error: %w", err)
return nil, lfsError("unable to obtain name for LFS files", err)
} }
default: default:
} }

+ 7
- 27
modules/git/pipeline/lfs_nogogit.go Переглянути файл

import ( import (
"bufio" "bufio"
"bytes" "bytes"
"fmt"
"io" "io"
"sort" "sort"
"strings" "strings"
"sync" "sync"
"time"


"code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/git"
) )


// LFSResult represents commits found using a provided pointer file hash
type LFSResult struct {
Name string
SHA string
Summary string
When time.Time
ParentIDs []git.ObjectID
BranchName string
FullCommitName string
}

type lfsResultSlice []*LFSResult

func (a lfsResultSlice) Len() int { return len(a) }
func (a lfsResultSlice) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a lfsResultSlice) Less(i, j int) bool { return a[j].When.After(a[i].When) }

// FindLFSFile finds commits that contain a provided pointer file hash // FindLFSFile finds commits that contain a provided pointer file hash
func FindLFSFile(repo *git.Repository, objectID git.ObjectID) ([]*LFSResult, error) { func FindLFSFile(repo *git.Repository, objectID git.ObjectID) ([]*LFSResult, error) {
resultsMap := map[string]*LFSResult{} resultsMap := map[string]*LFSResult{}
n += int64(count) n += int64(count)
if bytes.Equal(binObjectID, objectID.RawValue()) { if bytes.Equal(binObjectID, objectID.RawValue()) {
result := LFSResult{ result := LFSResult{
Name: curPath + string(fname),
SHA: curCommit.ID.String(),
Summary: strings.Split(strings.TrimSpace(curCommit.CommitMessage), "\n")[0],
When: curCommit.Author.When,
ParentIDs: curCommit.Parents,
Name: curPath + string(fname),
SHA: curCommit.ID.String(),
Summary: strings.Split(strings.TrimSpace(curCommit.CommitMessage), "\n")[0],
When: curCommit.Author.When,
ParentHashes: curCommit.Parents,
} }
resultsMap[curCommit.ID.String()+":"+curPath+string(fname)] = &result resultsMap[curCommit.ID.String()+":"+curPath+string(fname)] = &result
} else if string(mode) == git.EntryModeTree.String() { } else if string(mode) == git.EntryModeTree.String() {


for _, result := range resultsMap { for _, result := range resultsMap {
hasParent := false hasParent := false
for _, parentID := range result.ParentIDs {
for _, parentID := range result.ParentHashes {
if _, hasParent = resultsMap[parentID.String()+":"+result.Name]; hasParent { if _, hasParent = resultsMap[parentID.String()+":"+result.Name]; hasParent {
break break
} }
errChan <- err errChan <- err
break break
} }

} }
}() }()


select { select {
case err, has := <-errChan: case err, has := <-errChan:
if has { if has {
return nil, fmt.Errorf("Unable to obtain name for LFS files. Error: %w", err)
return nil, lfsError("unable to obtain name for LFS files", err)
} }
default: default:
} }

+ 4
- 4
modules/git/repo_commit.go Переглянути файл

return nil, err return nil, err
} }


len := objectFormat.FullLength()
length := objectFormat.FullLength()
commits := []*Commit{} commits := []*Commit{}
shaline := make([]byte, len+1)
shaline := make([]byte, length+1)
for { for {
n, err := io.ReadFull(stdoutReader, shaline) n, err := io.ReadFull(stdoutReader, shaline)
if err != nil || n < len {
if err != nil || n < length {
if err == io.EOF { if err == io.EOF {
err = nil err = nil
} }
return commits, err return commits, err
} }
objectID, err := NewIDFromString(string(shaline[0:len]))
objectID, err := NewIDFromString(string(shaline[0:length]))
if err != nil { if err != nil {
return nil, err return nil, err
} }

+ 0
- 1
modules/git/submodule.go Переглянути файл

// ex: git@try.gitea.io:go-gitea/gitea // ex: git@try.gitea.io:go-gitea/gitea
match := scpSyntax.FindAllStringSubmatch(refURI, -1) match := scpSyntax.FindAllStringSubmatch(refURI, -1)
if len(match) > 0 { if len(match) > 0 {

m := match[0] m := match[0]
refHostname := m[2] refHostname := m[2]
pth := m[3] pth := m[3]

+ 0
- 2
modules/indexer/code/bleve/bleve.go Переглянути файл

func (b *Indexer) Index(ctx context.Context, repo *repo_model.Repository, sha string, changes *internal.RepoChanges) error { func (b *Indexer) Index(ctx context.Context, repo *repo_model.Repository, sha string, changes *internal.RepoChanges) error {
batch := inner_bleve.NewFlushingBatch(b.inner.Indexer, maxBatchSize) batch := inner_bleve.NewFlushingBatch(b.inner.Indexer, maxBatchSize)
if len(changes.Updates) > 0 { if len(changes.Updates) > 0 {

// Now because of some insanity with git cat-file not immediately failing if not run in a valid git directory we need to run git rev-parse first! // Now because of some insanity with git cat-file not immediately failing if not run in a valid git directory we need to run git rev-parse first!
if err := git.EnsureValidGitRepository(ctx, repo.RepoPath()); err != nil { if err := git.EnsureValidGitRepository(ctx, repo.RepoPath()); err != nil {
log.Error("Unable to open git repo: %s for %-v: %v", repo.RepoPath(), repo, err) log.Error("Unable to open git repo: %s for %-v: %v", repo.RepoPath(), repo, err)
if result, err = b.inner.Indexer.Search(facetRequest); err != nil { if result, err = b.inner.Indexer.Search(facetRequest); err != nil {
return 0, nil, nil, err return 0, nil, nil, err
} }

} }
languagesFacet := result.Facets["languages"] languagesFacet := result.Facets["languages"]
for _, term := range languagesFacet.Terms.Terms() { for _, term := range languagesFacet.Terms.Terms() {

+ 2
- 2
modules/indexer/issues/dboptions.go Переглянути файл

searchOpt.Paginator = opts.Paginator searchOpt.Paginator = opts.Paginator


switch opts.SortType { switch opts.SortType {
case "":
case "", "latest":
searchOpt.SortBy = SortByCreatedDesc searchOpt.SortBy = SortByCreatedDesc
case "oldest": case "oldest":
searchOpt.SortBy = SortByCreatedAsc searchOpt.SortBy = SortByCreatedAsc
searchOpt.SortBy = SortByDeadlineDesc searchOpt.SortBy = SortByDeadlineDesc
case "priority", "priorityrepo", "project-column-sorting": case "priority", "priorityrepo", "project-column-sorting":
// Unsupported sort type for search // Unsupported sort type for search
searchOpt.SortBy = SortByUpdatedDesc
fallthrough
default: default:
searchOpt.SortBy = SortByUpdatedDesc searchOpt.SortBy = SortByUpdatedDesc
} }

+ 0
- 1
modules/indexer/issues/elasticsearch/elasticsearch.go Переглянути файл

query := elastic.NewBoolQuery() query := elastic.NewBoolQuery()


if options.Keyword != "" { if options.Keyword != "" {

searchType := esMultiMatchTypePhrasePrefix searchType := esMultiMatchTypePhrasePrefix
if options.IsFuzzyKeyword { if options.IsFuzzyKeyword {
searchType = esMultiMatchTypeBestFields searchType = esMultiMatchTypeBestFields

+ 0
- 1
modules/log/event_format.go Переглянути файл

if mode.Colorize { if mode.Colorize {
buf = append(buf, resetBytes...) buf = append(buf, resetBytes...)
} }

} }
if flags&(Lshortfile|Llongfile) != 0 { if flags&(Lshortfile|Llongfile) != 0 {
if mode.Colorize { if mode.Colorize {

+ 0
- 2
modules/markup/markdown/markdown_test.go Переглянути файл

res, err := markdown.RenderString(&markup.RenderContext{Ctx: git.DefaultContext}, test.testcase) res, err := markdown.RenderString(&markup.RenderContext{Ctx: git.DefaultContext}, test.testcase)
assert.NoError(t, err, "Unexpected error in testcase: %q", test.testcase) assert.NoError(t, err, "Unexpected error in testcase: %q", test.testcase)
assert.Equal(t, template.HTML(test.expected), res, "Unexpected result in testcase %q", test.testcase) assert.Equal(t, template.HTML(test.expected), res, "Unexpected result in testcase %q", test.testcase)

} }


negativeTests := []string{ negativeTests := []string{
res, err := markdown.RenderString(&markup.RenderContext{Ctx: git.DefaultContext}, test.testcase) res, err := markdown.RenderString(&markup.RenderContext{Ctx: git.DefaultContext}, test.testcase)
assert.NoError(t, err, "Unexpected error in testcase: %q", test.testcase) assert.NoError(t, err, "Unexpected error in testcase: %q", test.testcase)
assert.Equal(t, template.HTML(test.expected), res, "Unexpected result in testcase %q", test.testcase) assert.Equal(t, template.HTML(test.expected), res, "Unexpected result in testcase %q", test.testcase)

} }
} }



+ 16
- 16
modules/packages/rubygems/marshal.go Переглянути файл

return e.w.WriteByte(byte(i - 5)) return e.w.WriteByte(byte(i - 5))
} }


var len int
var length int
if 122 < i && i <= 0xff { if 122 < i && i <= 0xff {
len = 1
length = 1
} else if 0xff < i && i <= 0xffff { } else if 0xff < i && i <= 0xffff {
len = 2
length = 2
} else if 0xffff < i && i <= 0xffffff { } else if 0xffff < i && i <= 0xffffff {
len = 3
length = 3
} else if 0xffffff < i && i <= 0x3fffffff { } else if 0xffffff < i && i <= 0x3fffffff {
len = 4
length = 4
} else if -0x100 <= i && i < -123 { } else if -0x100 <= i && i < -123 {
len = -1
length = -1
} else if -0x10000 <= i && i < -0x100 { } else if -0x10000 <= i && i < -0x100 {
len = -2
length = -2
} else if -0x1000000 <= i && i < -0x100000 { } else if -0x1000000 <= i && i < -0x100000 {
len = -3
length = -3
} else if -0x40000000 <= i && i < -0x1000000 { } else if -0x40000000 <= i && i < -0x1000000 {
len = -4
length = -4
} else { } else {
return ErrInvalidIntRange return ErrInvalidIntRange
} }


if err := e.w.WriteByte(byte(len)); err != nil {
if err := e.w.WriteByte(byte(length)); err != nil {
return err return err
} }
if len < 0 {
len = -len
if length < 0 {
length = -length
} }


for c := 0; c < len; c++ {
for c := 0; c < length; c++ {
if err := e.w.WriteByte(byte(i >> uint(8*c) & 0xff)); err != nil { if err := e.w.WriteByte(byte(i >> uint(8*c) & 0xff)); err != nil {
return err return err
} }
return err return err
} }


len := arr.Len()
length := arr.Len()


if err := e.marshalIntInternal(int64(len)); err != nil {
if err := e.marshalIntInternal(int64(length)); err != nil {
return err return err
} }


for i := 0; i < len; i++ {
for i := 0; i < length; i++ {
if err := e.marshal(arr.Index(i).Interface()); err != nil { if err := e.marshal(arr.Index(i).Interface()); err != nil {
return err return err
} }

+ 0
- 1
modules/process/manager_stacktraces.go Переглянути файл

} }
sort.Slice(processes, after(processes)) sort.Slice(processes, after(processes))
if !flat { if !flat {

var sortChildren func(process *Process) var sortChildren func(process *Process)


sortChildren = func(process *Process) { sortChildren = func(process *Process) {

+ 11
- 7
modules/queue/workergroup.go Переглянути файл

// TODO: the logic could be improved in the future, to avoid a data-race between "doStartNewWorker" and "workerNum" // TODO: the logic could be improved in the future, to avoid a data-race between "doStartNewWorker" and "workerNum"
// The root problem is that if we skip "doStartNewWorker" here, the "workerNum" might be decreased by other workers later // The root problem is that if we skip "doStartNewWorker" here, the "workerNum" might be decreased by other workers later
// So ideally, it should check whether there are enough workers by some approaches, and start new workers if necessary. // So ideally, it should check whether there are enough workers by some approaches, and start new workers if necessary.
// This data-race is not serious, as long as a new worker will be started soon to make sure there are enough workers,
// so no need to hugely refactor at the moment.
q.workerNumMu.Lock() q.workerNumMu.Lock()
noWorker := q.workerNum == 0 noWorker := q.workerNum == 0
if full || noWorker { if full || noWorker {
return true return true
} }


func resetIdleTicker(t *time.Ticker, dur time.Duration) {
t.Reset(dur)
select {
case <-t.C:
default:
}
}

// doStartNewWorker starts a new worker for the queue, the worker reads from worker's channel and handles the items. // doStartNewWorker starts a new worker for the queue, the worker reads from worker's channel and handles the items.
func (q *WorkerPoolQueue[T]) doStartNewWorker(wp *workerGroup[T]) { func (q *WorkerPoolQueue[T]) doStartNewWorker(wp *workerGroup[T]) {
wp.wg.Add(1) wp.wg.Add(1)
log.Debug("Queue %q starts new worker", q.GetName()) log.Debug("Queue %q starts new worker", q.GetName())
defer log.Debug("Queue %q stops idle worker", q.GetName()) defer log.Debug("Queue %q stops idle worker", q.GetName())


atomic.AddInt32(&q.workerStartedCounter, 1) // Only increase counter, used for debugging

t := time.NewTicker(workerIdleDuration) t := time.NewTicker(workerIdleDuration)
defer t.Stop() defer t.Stop()


} }
q.doWorkerHandle(batch) q.doWorkerHandle(batch)
// reset the idle ticker, and drain the tick after reset in case a tick is already triggered // reset the idle ticker, and drain the tick after reset in case a tick is already triggered
t.Reset(workerIdleDuration)
select {
case <-t.C:
default:
}
resetIdleTicker(t, workerIdleDuration) // key code for TestWorkerPoolQueueWorkerIdleReset
case <-t.C: case <-t.C:
q.workerNumMu.Lock() q.workerNumMu.Lock()
keepWorking = q.workerNum <= 1 // keep the last worker running keepWorking = q.workerNum <= 1 // keep the last worker running

+ 0
- 2
modules/queue/workerqueue.go Переглянути файл

workerMaxNum int workerMaxNum int
workerActiveNum int workerActiveNum int
workerNumMu sync.Mutex workerNumMu sync.Mutex

workerStartedCounter int32
} }


type flushType chan struct{} type flushType chan struct{}

+ 23
- 10
modules/queue/workerqueue_test.go Переглянути файл



import ( import (
"context" "context"
"slices"
"strconv" "strconv"
"sync" "sync"
"sync/atomic"
"testing" "testing"
"time" "time"




func TestWorkerPoolQueueWorkerIdleReset(t *testing.T) { func TestWorkerPoolQueueWorkerIdleReset(t *testing.T) {
defer test.MockVariableValue(&workerIdleDuration, 10*time.Millisecond)() defer test.MockVariableValue(&workerIdleDuration, 10*time.Millisecond)()
defer mockBackoffDuration(10 * time.Millisecond)()
defer mockBackoffDuration(5 * time.Millisecond)()


var q *WorkerPoolQueue[int]
var handledCount atomic.Int32
var hasOnlyOneWorkerRunning atomic.Bool
handler := func(items ...int) (unhandled []int) { handler := func(items ...int) (unhandled []int) {
time.Sleep(50 * time.Millisecond)
handledCount.Add(int32(len(items)))
// make each work have different duration, and check the active worker number periodically
var activeNums []int
for i := 0; i < 5-items[0]%2; i++ {
time.Sleep(workerIdleDuration * 2)
activeNums = append(activeNums, q.GetWorkerActiveNumber())
}
// When the queue never becomes empty, the existing workers should keep working
// It is not 100% true at the moment because the data-race in workergroup.go is not resolved, see that TODO */
// If the "active worker numbers" is like [2 2 ... 1 1], it means that an existing worker exited and the no new worker is started.
if slices.Equal([]int{1, 1}, activeNums[len(activeNums)-2:]) {
hasOnlyOneWorkerRunning.Store(true)
}
return nil return nil
} }

q, _ := newWorkerPoolQueueForTest("test-workpoolqueue", setting.QueueSettings{Type: "channel", BatchLength: 1, MaxWorkers: 2, Length: 100}, handler, false)
q, _ = newWorkerPoolQueueForTest("test-workpoolqueue", setting.QueueSettings{Type: "channel", BatchLength: 1, MaxWorkers: 2, Length: 100}, handler, false)
stop := runWorkerPoolQueue(q) stop := runWorkerPoolQueue(q)
for i := 0; i < 20; i++ {
for i := 0; i < 100; i++ {
assert.NoError(t, q.Push(i)) assert.NoError(t, q.Push(i))
} }

time.Sleep(500 * time.Millisecond) time.Sleep(500 * time.Millisecond)
assert.EqualValues(t, 2, q.GetWorkerNumber())
assert.EqualValues(t, 2, q.GetWorkerActiveNumber())
// when the queue never becomes empty, the existing workers should keep working
assert.EqualValues(t, 2, q.workerStartedCounter)
assert.Greater(t, int(handledCount.Load()), 4) // make sure there are enough items handled during the test
assert.False(t, hasOnlyOneWorkerRunning.Load(), "a slow handler should not block other workers from starting")
stop() stop()
} }

+ 0
- 1
modules/repository/temp.go Переглянути файл

if err != nil { if err != nil {
log.Error("Unable to create temporary directory: %s-*.git (%v)", prefix, err) log.Error("Unable to create temporary directory: %s-*.git (%v)", prefix, err)
return "", fmt.Errorf("Failed to create dir %s-*.git: %w", prefix, err) return "", fmt.Errorf("Failed to create dir %s-*.git: %w", prefix, err)

} }
return basePath, nil return basePath, nil
} }

+ 26
- 0
modules/session/mock.go Переглянути файл

// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package session

import (
"net/http"

"gitea.com/go-chi/session"
)

type MockStore struct {
*session.MemStore
}

func (m *MockStore) Destroy(writer http.ResponseWriter, request *http.Request) error {
return nil
}

type mockStoreContextKeyStruct struct{}

var MockStoreContextKey = mockStoreContextKeyStruct{}

func NewMockStore(sid string) *MockStore {
return &MockStore{session.NewMemStore(sid)}
}

+ 21
- 2
modules/session/store.go Переглянути файл

import ( import (
"net/http" "net/http"


"code.gitea.io/gitea/modules/setting"

"gitea.com/go-chi/session" "gitea.com/go-chi/session"
) )


Get(any) any Get(any) any
Set(any, any) error Set(any, any) error
Delete(any) error Delete(any) error
ID() string
Release() error
Flush() error
Destroy(http.ResponseWriter, *http.Request) error
} }


// RegenerateSession regenerates the underlying session and returns the new store // RegenerateSession regenerates the underlying session and returns the new store
for _, f := range BeforeRegenerateSession { for _, f := range BeforeRegenerateSession {
f(resp, req) f(resp, req)
} }
s, err := session.RegenerateSession(resp, req)
return s, err
if setting.IsInTesting {
if store, ok := req.Context().Value(MockStoreContextKey).(*MockStore); ok {
return store, nil
}
}
return session.RegenerateSession(resp, req)
}

func GetContextSession(req *http.Request) Store {
if setting.IsInTesting {
if store, ok := req.Context().Value(MockStoreContextKey).(*MockStore); ok {
return store
}
}
return session.GetSession(req)
} }


// BeforeRegenerateSession is a list of functions that are called before a session is regenerated. // BeforeRegenerateSession is a list of functions that are called before a session is regenerated.

+ 3
- 3
modules/setting/config_provider.go Переглянути файл

// StartupProblems contains the messages for various startup problems, including: setting option, file/folder, etc // StartupProblems contains the messages for various startup problems, including: setting option, file/folder, etc
var StartupProblems []string var StartupProblems []string


func logStartupProblem(skip int, level log.Level, format string, args ...any) {
func LogStartupProblem(skip int, level log.Level, format string, args ...any) {
msg := fmt.Sprintf(format, args...) msg := fmt.Sprintf(format, args...)
log.Log(skip+1, level, "%s", msg) log.Log(skip+1, level, "%s", msg)
StartupProblems = append(StartupProblems, msg) StartupProblems = append(StartupProblems, msg)


func deprecatedSetting(rootCfg ConfigProvider, oldSection, oldKey, newSection, newKey, version string) { func deprecatedSetting(rootCfg ConfigProvider, oldSection, oldKey, newSection, newKey, version string) {
if rootCfg.Section(oldSection).HasKey(oldKey) { if rootCfg.Section(oldSection).HasKey(oldKey) {
logStartupProblem(1, log.ERROR, "Deprecation: config option `[%s].%s` presents, please use `[%s].%s` instead because this fallback will be/has been removed in %s", oldSection, oldKey, newSection, newKey, version)
LogStartupProblem(1, log.ERROR, "Deprecation: config option `[%s].%s` presents, please use `[%s].%s` instead because this fallback will be/has been removed in %s", oldSection, oldKey, newSection, newKey, version)
} }
} }


// deprecatedSettingDB add a hint that the configuration has been moved to database but still kept in app.ini // deprecatedSettingDB add a hint that the configuration has been moved to database but still kept in app.ini
func deprecatedSettingDB(rootCfg ConfigProvider, oldSection, oldKey string) { func deprecatedSettingDB(rootCfg ConfigProvider, oldSection, oldKey string) {
if rootCfg.Section(oldSection).HasKey(oldKey) { if rootCfg.Section(oldSection).HasKey(oldKey) {
logStartupProblem(1, log.ERROR, "Deprecation: config option `[%s].%s` presents but it won't take effect because it has been moved to admin panel -> config setting", oldSection, oldKey)
LogStartupProblem(1, log.ERROR, "Deprecation: config option `[%s].%s` presents but it won't take effect because it has been moved to admin panel -> config setting", oldSection, oldKey)
} }
} }



+ 6
- 10
modules/setting/oauth2.go Переглянути файл

type OAuth2UsernameType string type OAuth2UsernameType string


const ( const (
// OAuth2UsernameUserid oauth2 userid field will be used as gitea name
OAuth2UsernameUserid OAuth2UsernameType = "userid"
// OAuth2UsernameNickname oauth2 nickname field will be used as gitea name
OAuth2UsernameNickname OAuth2UsernameType = "nickname"
// OAuth2UsernameEmail username of oauth2 email field will be used as gitea name
OAuth2UsernameEmail OAuth2UsernameType = "email"
// OAuth2UsernameEmail username of oauth2 preferred_username field will be used as gitea name
OAuth2UsernamePreferredUsername OAuth2UsernameType = "preferred_username"
OAuth2UsernameUserid OAuth2UsernameType = "userid" // use user id (sub) field as gitea's username
OAuth2UsernameNickname OAuth2UsernameType = "nickname" // use nickname field
OAuth2UsernameEmail OAuth2UsernameType = "email" // use email field
OAuth2UsernamePreferredUsername OAuth2UsernameType = "preferred_username" // use preferred_username field
) )


func (username OAuth2UsernameType) isValid() bool { func (username OAuth2UsernameType) isValid() bool {
OAuth2Client.EnableAutoRegistration = sec.Key("ENABLE_AUTO_REGISTRATION").MustBool() OAuth2Client.EnableAutoRegistration = sec.Key("ENABLE_AUTO_REGISTRATION").MustBool()
OAuth2Client.Username = OAuth2UsernameType(sec.Key("USERNAME").MustString(string(OAuth2UsernameNickname))) OAuth2Client.Username = OAuth2UsernameType(sec.Key("USERNAME").MustString(string(OAuth2UsernameNickname)))
if !OAuth2Client.Username.isValid() { if !OAuth2Client.Username.isValid() {
log.Warn("Username setting is not valid: '%s', will fallback to '%s'", OAuth2Client.Username, OAuth2UsernameNickname)
OAuth2Client.Username = OAuth2UsernameNickname OAuth2Client.Username = OAuth2UsernameNickname
log.Warn("[oauth2_client].USERNAME setting is invalid, falls back to %q", OAuth2Client.Username)
} }
OAuth2Client.UpdateAvatar = sec.Key("UPDATE_AVATAR").MustBool() OAuth2Client.UpdateAvatar = sec.Key("UPDATE_AVATAR").MustBool()
OAuth2Client.AccountLinking = OAuth2AccountLinkingType(sec.Key("ACCOUNT_LINKING").MustString(string(OAuth2AccountLinkingLogin))) OAuth2Client.AccountLinking = OAuth2AccountLinkingType(sec.Key("ACCOUNT_LINKING").MustString(string(OAuth2AccountLinkingLogin)))
} }
if generalSigningSecret.CompareAndSwap(old, &jwtSecret) { if generalSigningSecret.CompareAndSwap(old, &jwtSecret) {
// FIXME: in main branch, the signing token should be refactored (eg: one unique for LFS/OAuth2/etc ...) // FIXME: in main branch, the signing token should be refactored (eg: one unique for LFS/OAuth2/etc ...)
logStartupProblem(1, log.WARN, "OAuth2 is not enabled, unable to use a persistent signing secret, a new one is generated, which is not persistent between restarts and cluster nodes")
LogStartupProblem(1, log.WARN, "OAuth2 is not enabled, unable to use a persistent signing secret, a new one is generated, which is not persistent between restarts and cluster nodes")
return jwtSecret return jwtSecret
} }
return *generalSigningSecret.Load() return *generalSigningSecret.Load()

+ 1
- 1
modules/setting/setting.go Переглянути файл

func checkOverlappedPath(name, path string) { func checkOverlappedPath(name, path string) {
// TODO: some paths shouldn't overlap (storage.xxx.path), while some could (data path is the base path for storage path) // TODO: some paths shouldn't overlap (storage.xxx.path), while some could (data path is the base path for storage path)
if targetName, ok := configuredPaths[path]; ok && targetName != name { if targetName, ok := configuredPaths[path]; ok && targetName != name {
logStartupProblem(1, log.ERROR, "Configured path %q is used by %q and %q at the same time. The paths must be unique to prevent data loss.", path, targetName, name)
LogStartupProblem(1, log.ERROR, "Configured path %q is used by %q and %q at the same time. The paths must be unique to prevent data loss.", path, targetName, name)
} }
configuredPaths[path] = name configuredPaths[path] = name
} }

+ 1
- 2
modules/setting/time.go Переглянути файл

DefaultUILocation, err = time.LoadLocation(zone) DefaultUILocation, err = time.LoadLocation(zone)
if err != nil { if err != nil {
log.Fatal("Load time zone failed: %v", err) log.Fatal("Load time zone failed: %v", err)
} else {
log.Info("Default UI Location is %v", zone)
} }
log.Info("Default UI Location is %v", zone)
} }
if DefaultUILocation == nil { if DefaultUILocation == nil {
DefaultUILocation = time.Local DefaultUILocation = time.Local

+ 0
- 1
modules/setting/ui.go Переглянути файл

ReactionMaxUserNum: 10, ReactionMaxUserNum: 10,
MaxDisplayFileSize: 8388608, MaxDisplayFileSize: 8388608,
DefaultTheme: `gitea-auto`, DefaultTheme: `gitea-auto`,
Themes: []string{`gitea-auto`, `gitea-light`, `gitea-dark`},
Reactions: []string{`+1`, `-1`, `laugh`, `hooray`, `confused`, `heart`, `rocket`, `eyes`}, Reactions: []string{`+1`, `-1`, `laugh`, `hooray`, `confused`, `heart`, `rocket`, `eyes`},
CustomEmojis: []string{`git`, `gitea`, `codeberg`, `gitlab`, `github`, `gogs`}, CustomEmojis: []string{`git`, `gitea`, `codeberg`, `gitlab`, `github`, `gogs`},
CustomEmojisMap: map[string]string{"git": ":git:", "gitea": ":gitea:", "codeberg": ":codeberg:", "gitlab": ":gitlab:", "github": ":github:", "gogs": ":gogs:"}, CustomEmojisMap: map[string]string{"git": ":git:", "gitea": ":gitea:", "codeberg": ":codeberg:", "gitlab": ":gitlab:", "github": ":github:", "gogs": ":gogs:"},

+ 12
- 6
modules/templates/helper.go Переглянути файл

"code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/services/gitdiff" "code.gitea.io/gitea/services/gitdiff"
"code.gitea.io/gitea/services/webtheme"
) )


// NewFuncMap returns functions for injecting to templates // NewFuncMap returns functions for injecting to templates
"DisableImportLocal": func() bool { "DisableImportLocal": func() bool {
return !setting.ImportLocalPaths return !setting.ImportLocalPaths
}, },
"ThemeName": func(user *user_model.User) string {
if user == nil || user.Theme == "" {
return setting.UI.DefaultTheme
}
return user.Theme
},
"UserThemeName": UserThemeName,
"NotificationSettings": func() map[string]any { "NotificationSettings": func() map[string]any {
return map[string]any{ return map[string]any{
"MinTimeout": int(setting.UI.Notification.MinTimeout / time.Millisecond), "MinTimeout": int(setting.UI.Notification.MinTimeout / time.Millisecond),
n, err := eval.Expr(tokens...) n, err := eval.Expr(tokens...)
return n.Value, err return n.Value, err
} }

func UserThemeName(user *user_model.User) string {
if user == nil || user.Theme == "" {
return setting.UI.DefaultTheme
}
if webtheme.IsThemeAvailable(user.Theme) {
return user.Theme
}
return setting.UI.DefaultTheme
}

+ 2
- 3
modules/templates/htmlrenderer.go Переглянути файл

if setting.IsProd { if setting.IsProd {
// in prod mode, Gitea must have correct templates to run // in prod mode, Gitea must have correct templates to run
log.Fatal("Gitea can't run with template errors: %s", msg) log.Fatal("Gitea can't run with template errors: %s", msg)
} else {
// in dev mode, do not need to really exit, because the template errors could be fixed by developer soon and the templates get reloaded
log.Error("There are template errors but Gitea continues to run in dev mode: %s", msg)
} }
// in dev mode, do not need to really exit, because the template errors could be fixed by developer soon and the templates get reloaded
log.Error("There are template errors but Gitea continues to run in dev mode: %s", msg)
} }


type templateErrorPrettier struct { type templateErrorPrettier struct {

+ 1
- 2
modules/templates/mailer.go Переглянути файл

if err = buildSubjectBodyTemplate(subjectTemplates, bodyTemplates, tmplName, content); err != nil { if err = buildSubjectBodyTemplate(subjectTemplates, bodyTemplates, tmplName, content); err != nil {
if firstRun { if firstRun {
log.Fatal("Failed to parse mail template, err: %v", err) log.Fatal("Failed to parse mail template, err: %v", err)
} else {
log.Error("Failed to parse mail template, err: %v", err)
} }
log.Error("Failed to parse mail template, err: %v", err)
} }
} }
} }

+ 3
- 3
modules/util/util_test.go Переглянути файл

} }


func Test_RandomInt(t *testing.T) { func Test_RandomInt(t *testing.T) {
int, err := CryptoRandomInt(255)
assert.True(t, int >= 0)
assert.True(t, int <= 255)
randInt, err := CryptoRandomInt(255)
assert.True(t, randInt >= 0)
assert.True(t, randInt <= 255)
assert.NoError(t, err) assert.NoError(t, err)
} }



+ 10
- 0
options/license/HPND-UC-export-US Переглянути файл

Copyright (C) 1985, 1990 Regents of the University of California.

Permission to use, copy, modify, and distribute this
software and its documentation for any purpose and without
fee is hereby granted, provided that the above copyright
notice appear in all copies. The University of California
makes no representations about the suitability of this
software for any purpose. It is provided "as is" without
express or implied warranty. Export of this software outside
of the United States of America may require an export license.

+ 32
- 0
options/license/NCL Переглянути файл

Copyright (c) 2004 the University Corporation for Atmospheric
Research ("UCAR"). All rights reserved. Developed by NCAR's
Computational and Information Systems Laboratory, UCAR,
www.cisl.ucar.edu.

Redistribution and use of the Software in source and binary forms,
with or without modification, is permitted provided that the
following conditions are met:

- Neither the names of NCAR's Computational and Information Systems
Laboratory, the University Corporation for Atmospheric Research,
nor the names of its sponsors or contributors may be used to
endorse or promote products derived from this Software without
specific prior written permission.

- Redistributions of source code must retain the above copyright
notices, this list of conditions, and the disclaimer below.

- Redistributions in binary form must reproduce the above copyright
notice, this list of conditions, and the disclaimer below in the
documentation and/or other materials provided with the
distribution.

THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
SOFTWARE.

+ 0
- 1
options/locale/locale_cs-CZ.ini Переглянути файл

download_archive=Stáhnout repozitář download_archive=Stáhnout repozitář
more_operations=Další operace more_operations=Další operace


no_desc=Bez popisu
quick_guide=Krátká příručka quick_guide=Krátká příručka
clone_this_repo=Naklonovat tento repozitář clone_this_repo=Naklonovat tento repozitář
cite_this_repo=Citovat tento repozitář cite_this_repo=Citovat tento repozitář

+ 0
- 1
options/locale/locale_de-DE.ini Переглянути файл

download_archive=Repository herunterladen download_archive=Repository herunterladen
more_operations=Weitere Operationen more_operations=Weitere Operationen


no_desc=Keine Beschreibung
quick_guide=Kurzanleitung quick_guide=Kurzanleitung
clone_this_repo=Dieses Repository klonen clone_this_repo=Dieses Repository klonen
cite_this_repo=Dieses Repository zitieren cite_this_repo=Dieses Repository zitieren

+ 0
- 1
options/locale/locale_el-GR.ini Переглянути файл

download_archive=Λήψη Αποθετηρίου download_archive=Λήψη Αποθετηρίου
more_operations=Περισσότερες Λειτουργίες more_operations=Περισσότερες Λειτουργίες


no_desc=Χωρίς Περιγραφή
quick_guide=Γρήγορος Οδηγός quick_guide=Γρήγορος Οδηγός
clone_this_repo=Κλωνοποίηση αυτού του αποθετηρίου clone_this_repo=Κλωνοποίηση αυτού του αποθετηρίου
cite_this_repo=Αναφορά σε αυτό το αποθετήριο cite_this_repo=Αναφορά σε αυτό το αποθετήριο

+ 3
- 1
options/locale/locale_en-US.ini Переглянути файл

oauth.signin.error = There was an error processing the authorization request. If this error persists, please contact the site administrator. oauth.signin.error = There was an error processing the authorization request. If this error persists, please contact the site administrator.
oauth.signin.error.access_denied = The authorization request was denied. oauth.signin.error.access_denied = The authorization request was denied.
oauth.signin.error.temporarily_unavailable = Authorization failed because the authentication server is temporarily unavailable. Please try again later. oauth.signin.error.temporarily_unavailable = Authorization failed because the authentication server is temporarily unavailable. Please try again later.
oauth_callback_unable_auto_reg = Auto Registration is enabled, but OAuth2 Provider %[1]s returned missing fields: %[2]s, unable to create an account automatically, please create or link to an account, or contact the site administrator.
openid_connect_submit = Connect openid_connect_submit = Connect
openid_connect_title = Connect to an existing account openid_connect_title = Connect to an existing account
openid_connect_desc = The chosen OpenID URI is unknown. Associate it with a new account here. openid_connect_desc = The chosen OpenID URI is unknown. Associate it with a new account here.
manage_openid = Manage OpenID Addresses manage_openid = Manage OpenID Addresses
email_desc = Your primary email address will be used for notifications, password recovery and, provided that it is not hidden, web-based Git operations. email_desc = Your primary email address will be used for notifications, password recovery and, provided that it is not hidden, web-based Git operations.
theme_desc = This will be your default theme across the site. theme_desc = This will be your default theme across the site.
theme_colorblindness_help = Colorblindness Theme Support
theme_colorblindness_prompt = Gitea just gets some themes with basic colorblindness support, which only have a few colors defined. The work is still in progress. More improvements could be done by defining more colors in the theme CSS files.
primary = Primary primary = Primary
activated = Activated activated = Activated
requires_activation = Requires activation requires_activation = Requires activation
download_archive = Download Repository download_archive = Download Repository
more_operations = More Operations more_operations = More Operations


no_desc = No Description
quick_guide = Quick Guide quick_guide = Quick Guide
clone_this_repo = Clone this repository clone_this_repo = Clone this repository
cite_this_repo = Cite this repository cite_this_repo = Cite this repository

+ 0
- 1
options/locale/locale_es-ES.ini Переглянути файл

download_archive=Descargar repositorio download_archive=Descargar repositorio
more_operations=Más operaciones more_operations=Más operaciones


no_desc=Sin descripción
quick_guide=Guía rápida quick_guide=Guía rápida
clone_this_repo=Clonar este repositorio clone_this_repo=Clonar este repositorio
cite_this_repo=Citar este repositorio cite_this_repo=Citar este repositorio

+ 0
- 1
options/locale/locale_fa-IR.ini Переглянути файл

fork=انشعاب fork=انشعاب
download_archive=دانلود مخزن download_archive=دانلود مخزن


no_desc=بدون توضیح
quick_guide=راهنمای سریع quick_guide=راهنمای سریع
clone_this_repo=همسان‌سازی این مخزن clone_this_repo=همسان‌سازی این مخزن
create_new_repo_command=ایجاد یک مخزن جدید در خط فرمان create_new_repo_command=ایجاد یک مخزن جدید در خط فرمان

+ 0
- 1
options/locale/locale_fi-FI.ini Переглянути файл

star=Tähti star=Tähti
download_archive=Lataa repo download_archive=Lataa repo


no_desc=Ei kuvausta
quick_guide=Pikaopas quick_guide=Pikaopas
clone_this_repo=Kloonaa tämä repo clone_this_repo=Kloonaa tämä repo



+ 0
- 1
options/locale/locale_fr-FR.ini Переглянути файл

download_archive=Télécharger ce dépôt download_archive=Télécharger ce dépôt
more_operations=Plus d'opérations more_operations=Plus d'opérations


no_desc=Aucune description
quick_guide=Introduction rapide quick_guide=Introduction rapide
clone_this_repo=Cloner ce dépôt clone_this_repo=Cloner ce dépôt
cite_this_repo=Citer ce dépôt cite_this_repo=Citer ce dépôt

+ 0
- 1
options/locale/locale_hu-HU.ini Переглянути файл

fork=Tükrözés fork=Tükrözés
download_archive=Tároló letöltése download_archive=Tároló letöltése


no_desc=Nincs leírás
quick_guide=Gyors útmutató quick_guide=Gyors útmutató
clone_this_repo=Tároló klónozása clone_this_repo=Tároló klónozása
create_new_repo_command=Egy új tároló létrehozása a parancssorból create_new_repo_command=Egy új tároló létrehozása a parancssorból

+ 0
- 1
options/locale/locale_id-ID.ini Переглянути файл

fork=Garpu fork=Garpu
download_archive=Unduh Repositori download_archive=Unduh Repositori


no_desc=Tidak ada Deskripsi
quick_guide=Panduan Cepat quick_guide=Panduan Cepat
clone_this_repo=Klon repositori ini clone_this_repo=Klon repositori ini
create_new_repo_command=Membuat repositori baru pada baris perintah create_new_repo_command=Membuat repositori baru pada baris perintah

+ 0
- 1
options/locale/locale_is-IS.ini Переглянути файл

fork=Tvískipta fork=Tvískipta
download_archive=Hlaða Miður Geymslu download_archive=Hlaða Miður Geymslu


no_desc=Engin Lýsing
quick_guide=Stuttar Leiðbeiningar quick_guide=Stuttar Leiðbeiningar
clone_this_repo=Afrita þetta hugbúnaðarsafn clone_this_repo=Afrita þetta hugbúnaðarsafn
create_new_repo_command=Að búa til nýja geymslu með skipanalínu create_new_repo_command=Að búa til nýja geymslu með skipanalínu

+ 0
- 1
options/locale/locale_it-IT.ini Переглянути файл

fork=Forka fork=Forka
download_archive=Scarica Repository download_archive=Scarica Repository


no_desc=Nessuna descrizione
quick_guide=Guida rapida quick_guide=Guida rapida
clone_this_repo=Clona questo repository clone_this_repo=Clona questo repository
create_new_repo_command=Creazione di un nuovo repository da riga di comando create_new_repo_command=Creazione di un nuovo repository da riga di comando

+ 0
- 1
options/locale/locale_ja-JP.ini Переглянути файл

download_archive=リポジトリをダウンロード download_archive=リポジトリをダウンロード
more_operations=その他の操作 more_operations=その他の操作


no_desc=説明なし
quick_guide=クイック ガイド quick_guide=クイック ガイド
clone_this_repo=このリポジトリのクローンを作成 clone_this_repo=このリポジトリのクローンを作成
cite_this_repo=このリポジトリを引用 cite_this_repo=このリポジトリを引用

+ 0
- 1
options/locale/locale_ko-KR.ini Переглянути файл

fork=포크 fork=포크
download_archive=저장소 다운로드 download_archive=저장소 다운로드


no_desc=설명 없음
quick_guide=퀵 가이드 quick_guide=퀵 가이드
clone_this_repo=이 저장소 복제 clone_this_repo=이 저장소 복제
create_new_repo_command=커맨드 라인에서 새 레포리지터리 생성 create_new_repo_command=커맨드 라인에서 새 레포리지터리 생성

+ 0
- 1
options/locale/locale_lv-LV.ini Переглянути файл

download_archive=Lejupielādēt repozitoriju download_archive=Lejupielādēt repozitoriju
more_operations=Vairāk darbību more_operations=Vairāk darbību


no_desc=Nav apraksta
quick_guide=Īsa pamācība quick_guide=Īsa pamācība
clone_this_repo=Klonēt šo repozitoriju clone_this_repo=Klonēt šo repozitoriju
cite_this_repo=Citēt šo repozitoriju cite_this_repo=Citēt šo repozitoriju

+ 0
- 1
options/locale/locale_nl-NL.ini Переглянути файл

fork=Vork fork=Vork
download_archive=Download repository download_archive=Download repository


no_desc=Geen omschrijving
quick_guide=Snelstart gids quick_guide=Snelstart gids
clone_this_repo=Kloon deze repository clone_this_repo=Kloon deze repository
create_new_repo_command=Maak een nieuwe repository aan vanaf de console create_new_repo_command=Maak een nieuwe repository aan vanaf de console

+ 0
- 1
options/locale/locale_pl-PL.ini Переглянути файл

fork=Forkuj fork=Forkuj
download_archive=Pobierz repozytorium download_archive=Pobierz repozytorium


no_desc=Brak opisu
quick_guide=Skrócona instrukcja quick_guide=Skrócona instrukcja
clone_this_repo=Klonuj repozytorium clone_this_repo=Klonuj repozytorium
create_new_repo_command=Tworzenie nowego repozytorium z linii poleceń create_new_repo_command=Tworzenie nowego repozytorium z linii poleceń

+ 0
- 1
options/locale/locale_pt-BR.ini Переглянути файл

download_archive=Baixar repositório download_archive=Baixar repositório
more_operations=Mais Operações more_operations=Mais Operações


no_desc=Nenhuma descrição
quick_guide=Guia Rápido quick_guide=Guia Rápido
clone_this_repo=Clonar este repositório clone_this_repo=Clonar este repositório
cite_this_repo=Citar este repositório cite_this_repo=Citar este repositório

+ 10
- 9
options/locale/locale_pt-PT.ini Переглянути файл

manage_openid=Gerir endereços OpenID manage_openid=Gerir endereços OpenID
email_desc=O seu endereço de email principal irá ser usado para notificações, recuperação de senha e, desde que não esteja oculto, operações Git baseados na web. email_desc=O seu endereço de email principal irá ser usado para notificações, recuperação de senha e, desde que não esteja oculto, operações Git baseados na web.
theme_desc=Este será o seu tema padrão em todo o sítio. theme_desc=Este será o seu tema padrão em todo o sítio.
theme_colorblindness_help=Suporte a temas para daltónicos
theme_colorblindness_prompt=O Gitea acabou de obter alguns temas com suporte básico para daltónicos que têm apenas algumas cores definidas. O trabalho ainda está em andamento. Poderiam ser feitos mais melhoramentos se fossem definidas mais cores nos ficheiros CSS do tema.
primary=Principal primary=Principal
activated=Em uso activated=Em uso
requires_activation=Tem que ser habilitado requires_activation=Tem que ser habilitado
download_archive=Descarregar repositório download_archive=Descarregar repositório
more_operations=Mais operações more_operations=Mais operações


no_desc=Sem descrição
quick_guide=Guia rápido quick_guide=Guia rápido
clone_this_repo=Clonar este repositório clone_this_repo=Clonar este repositório
cite_this_repo=Citar este repositório cite_this_repo=Citar este repositório
settings.protected_branch_can_push=Permitir envios? settings.protected_branch_can_push=Permitir envios?
settings.protected_branch_can_push_yes=Pode enviar settings.protected_branch_can_push_yes=Pode enviar
settings.protected_branch_can_push_no=Não pode enviar settings.protected_branch_can_push_no=Não pode enviar
settings.branch_protection=Salvaguarda do ramo '<b>%s</b>'
settings.branch_protection=Regras de salvaguarda do ramo '<b>%s</b>'
settings.protect_this_branch=Habilitar salvaguarda do ramo settings.protect_this_branch=Habilitar salvaguarda do ramo
settings.protect_this_branch_desc=Impede a eliminação e restringe envios e integrações do Git no ramo. settings.protect_this_branch_desc=Impede a eliminação e restringe envios e integrações do Git no ramo.
settings.protect_disable_push=Desabilitar envios settings.protect_disable_push=Desabilitar envios
settings.protect_protected_file_patterns=Padrões de ficheiros protegidos (separados com ponto e vírgula ';'): settings.protect_protected_file_patterns=Padrões de ficheiros protegidos (separados com ponto e vírgula ';'):
settings.protect_protected_file_patterns_desc=Ficheiros protegidos não podem ser modificados imediatamente, mesmo que o utilizador tenha direitos para adicionar, editar ou eliminar ficheiros neste ramo. Múltiplos padrões podem ser separados com ponto e vírgula (';'). Veja a documentação em <a href='https://pkg.go.dev/github.com/gobwas/glob#Compile'>github.com/gobwas/glob</a> para ver a sintaxe. Exemplos: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>. settings.protect_protected_file_patterns_desc=Ficheiros protegidos não podem ser modificados imediatamente, mesmo que o utilizador tenha direitos para adicionar, editar ou eliminar ficheiros neste ramo. Múltiplos padrões podem ser separados com ponto e vírgula (';'). Veja a documentação em <a href='https://pkg.go.dev/github.com/gobwas/glob#Compile'>github.com/gobwas/glob</a> para ver a sintaxe. Exemplos: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>.
settings.protect_unprotected_file_patterns=Padrões de ficheiros desprotegidos (separados com ponto e vírgula ';'): settings.protect_unprotected_file_patterns=Padrões de ficheiros desprotegidos (separados com ponto e vírgula ';'):
settings.protect_unprotected_file_patterns_desc=Ficheiros desprotegidos que podem ser modificados imediatamente se o utilizador tiver direitos de escrita, contornando a restrição no envio. Múltiplos padrões podem ser separados com ponto e vírgula (';'). Veja a documentação em <a href='https://pkg.go.dev/github.com/gobwas/glob#Compile'>github.com/gobwas/glob</a> para ver a sintaxe. Exemplos: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>.
settings.protect_unprotected_file_patterns_desc=Ficheiros desprotegidos que podem ser modificados imediatamente se o utilizador tiver direitos de escrita, contornando a restrição no envio. Padrões múltiplos podem ser separados com ponto e vírgula (';'). Veja a documentação em <a href='https://pkg.go.dev/github.com/gobwas/glob#Compile'>github.com/gobwas/glob</a> para ver a sintaxe. Exemplos: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>.
settings.add_protected_branch=Habilitar salvaguarda settings.add_protected_branch=Habilitar salvaguarda
settings.delete_protected_branch=Desabilitar salvaguarda settings.delete_protected_branch=Desabilitar salvaguarda
settings.update_protect_branch_success=A salvaguarda do ramo "%s" foi modificada. settings.update_protect_branch_success=A salvaguarda do ramo "%s" foi modificada.
settings.block_outdated_branch_desc=A integração não será possível quando o ramo de topo estiver abaixo do ramo base. settings.block_outdated_branch_desc=A integração não será possível quando o ramo de topo estiver abaixo do ramo base.
settings.default_branch_desc=Escolha um ramo do repositório como sendo o predefinido para pedidos de integração e cometimentos: settings.default_branch_desc=Escolha um ramo do repositório como sendo o predefinido para pedidos de integração e cometimentos:
settings.merge_style_desc=Estilos de integração settings.merge_style_desc=Estilos de integração
settings.default_merge_style_desc=Tipo de integração predefinido para pedidos de integração:
settings.default_merge_style_desc=Tipo de integração predefinido
settings.choose_branch=Escolha um ramo… settings.choose_branch=Escolha um ramo…
settings.no_protected_branch=Não existem ramos protegidos. settings.no_protected_branch=Não existem ramos protegidos.
settings.edit_protected_branch=Editar settings.edit_protected_branch=Editar
identity_access=Identidade e acesso identity_access=Identidade e acesso
users=Contas de utilizador users=Contas de utilizador
organizations=Organizações organizations=Organizações
assets=Recursos de código
assets=Recursos do código-fonte
repositories=Repositórios repositories=Repositórios
hooks=Automatismos web hooks=Automatismos web
integrations=Integrações integrations=Integrações
dashboard.mcache_structures_usage=Uso das estruturas MCache dashboard.mcache_structures_usage=Uso das estruturas MCache
dashboard.mcache_structures_obtained=Estruturas MCache obtidas dashboard.mcache_structures_obtained=Estruturas MCache obtidas
dashboard.profiling_bucket_hash_table_obtained=Perfil obtido da tabela de hash do balde dashboard.profiling_bucket_hash_table_obtained=Perfil obtido da tabela de hash do balde
dashboard.gc_metadata_obtained=Metadados da recolha de lixo obtidos
dashboard.gc_metadata_obtained=Metadados obtidos da recolha de lixo
dashboard.other_system_allocation_obtained=Outras alocações de sistema obtidas dashboard.other_system_allocation_obtained=Outras alocações de sistema obtidas
dashboard.next_gc_recycle=Próxima reciclagem da recolha de lixo dashboard.next_gc_recycle=Próxima reciclagem da recolha de lixo
dashboard.last_gc_time=Tempo decorrido desde a última recolha de lixo dashboard.last_gc_time=Tempo decorrido desde a última recolha de lixo
dashboard.total_gc_time=Pausa total da recolha de lixo dashboard.total_gc_time=Pausa total da recolha de lixo
dashboard.total_gc_pause=Pausa total da recolha de lixo dashboard.total_gc_pause=Pausa total da recolha de lixo
dashboard.last_gc_pause=Última pausa da recolha de lixo dashboard.last_gc_pause=Última pausa da recolha de lixo
dashboard.gc_times=Tempos da recolha de lixo
dashboard.gc_times=N.º de recolhas de lixo
dashboard.delete_old_actions=Eliminar todas as operações antigas da base de dados dashboard.delete_old_actions=Eliminar todas as operações antigas da base de dados
dashboard.delete_old_actions.started=Foi iniciado o processo de eliminação de todas as operações antigas da base de dados. dashboard.delete_old_actions.started=Foi iniciado o processo de eliminação de todas as operações antigas da base de dados.
dashboard.update_checker=Verificador de novas versões dashboard.update_checker=Verificador de novas versões
auths.attribute_mail=Atributo do email auths.attribute_mail=Atributo do email
auths.attribute_ssh_public_key=Atributo da chave pública SSH auths.attribute_ssh_public_key=Atributo da chave pública SSH
auths.attribute_avatar=Atributo do avatar auths.attribute_avatar=Atributo do avatar
auths.attributes_in_bind=Buscar os atributos no contexto de Bind DN
auths.attributes_in_bind=Buscar atributos no contexto do Bind DN
auths.allow_deactivate_all=Permitir que um resultado de pesquisa vazio desabilite todos os utilizadores auths.allow_deactivate_all=Permitir que um resultado de pesquisa vazio desabilite todos os utilizadores
auths.use_paged_search=Usar pesquisa paginada auths.use_paged_search=Usar pesquisa paginada
auths.search_page_size=Tamanho da página auths.search_page_size=Tamanho da página
config.session_provider=Fornecedor da sessão config.session_provider=Fornecedor da sessão
config.provider_config=Configuração do fornecedor config.provider_config=Configuração do fornecedor
config.cookie_name=Nome do cookie config.cookie_name=Nome do cookie
config.gc_interval_time=Intervalo da recolha do lixo
config.gc_interval_time=Intervalo de tempo entre recolhas do lixo
config.session_life_time=Tempo de vida da sessão config.session_life_time=Tempo de vida da sessão
config.https_only=Apenas HTTPS config.https_only=Apenas HTTPS
config.cookie_life_time=Tempo de vida do cookie config.cookie_life_time=Tempo de vida do cookie

+ 0
- 1
options/locale/locale_ru-RU.ini Переглянути файл

download_archive=Скачать репозиторий download_archive=Скачать репозиторий
more_operations=Ещё действия more_operations=Ещё действия


no_desc=Нет описания
quick_guide=Краткое руководство quick_guide=Краткое руководство
clone_this_repo=Клонировать репозиторий clone_this_repo=Клонировать репозиторий
cite_this_repo=Сослаться на этот репозиторий cite_this_repo=Сослаться на этот репозиторий

+ 0
- 1
options/locale/locale_si-LK.ini Переглянути файл

fork=දෙබලක fork=දෙබලක
download_archive=කෝෂ්ඨය බාගන්න download_archive=කෝෂ්ඨය බාගන්න


no_desc=සවිස්තරයක් නැත
quick_guide=ඉක්මන් මාර්ගෝපදේශය quick_guide=ඉක්මන් මාර්ගෝපදේශය
clone_this_repo=මෙම ගබඩාව පරිගණක ක්රිඩාවට සමාන clone_this_repo=මෙම ගබඩාව පරිගණක ක්රිඩාවට සමාන
create_new_repo_command=විධාන රේඛාවේ නව ගබඩාවක් නිර්මාණය කිරීම create_new_repo_command=විධාන රේඛාවේ නව ගබඩාවක් නිර්මාණය කිරීම

+ 0
- 1
options/locale/locale_sk-SK.ini Переглянути файл

download_archive=Stiahnuť repozitár download_archive=Stiahnuť repozitár
more_operations=Viac operácií more_operations=Viac operácií


no_desc=Bez popisu
quick_guide=Rýchly sprievodca quick_guide=Rýchly sprievodca
clone_this_repo=Klonovať tento repozitár clone_this_repo=Klonovať tento repozitár
create_new_repo_command=Vytvoriť nový repozitár v príkazovom riadku create_new_repo_command=Vytvoriť nový repozitár v príkazovom riadku

+ 0
- 1
options/locale/locale_sv-SE.ini Переглянути файл

fork=Förgrening fork=Förgrening
download_archive=Ladda Ned Utvecklingskatalogen download_archive=Ladda Ned Utvecklingskatalogen


no_desc=Ingen beskrivning
quick_guide=Snabbguide quick_guide=Snabbguide
clone_this_repo=Klona detta repo clone_this_repo=Klona detta repo
create_new_repo_command=Skapa en ny utvecklingskatalog på kommandoraden create_new_repo_command=Skapa en ny utvecklingskatalog på kommandoraden

+ 0
- 1
options/locale/locale_tr-TR.ini Переглянути файл

download_archive=Depoyu İndir download_archive=Depoyu İndir
more_operations=Daha Fazla İşlem more_operations=Daha Fazla İşlem


no_desc=Açıklama Yok
quick_guide=Hızlı Başlangıç Kılavuzu quick_guide=Hızlı Başlangıç Kılavuzu
clone_this_repo=Bu depoyu klonla clone_this_repo=Bu depoyu klonla
cite_this_repo=Bu depoya atıf ver cite_this_repo=Bu depoya atıf ver

+ 0
- 1
options/locale/locale_uk-UA.ini Переглянути файл

fork=Форк fork=Форк
download_archive=Скачати репозиторій download_archive=Скачати репозиторій


no_desc=Без опису
quick_guide=Короткий посібник quick_guide=Короткий посібник
clone_this_repo=Кнонувати цей репозиторій clone_this_repo=Кнонувати цей репозиторій
create_new_repo_command=Створити новий репозиторій з командного рядка create_new_repo_command=Створити новий репозиторій з командного рядка

+ 0
- 1
options/locale/locale_zh-CN.ini Переглянути файл

download_archive=下载此仓库 download_archive=下载此仓库
more_operations=更多操作 more_operations=更多操作


no_desc=暂无描述
quick_guide=快速帮助 quick_guide=快速帮助
clone_this_repo=克隆当前仓库 clone_this_repo=克隆当前仓库
cite_this_repo=引用此仓库 cite_this_repo=引用此仓库

+ 0
- 1
options/locale/locale_zh-HK.ini Переглянути файл

star=收藏 star=收藏
fork=複製 fork=複製


no_desc=暫無描述
quick_guide=快速幫助 quick_guide=快速幫助
clone_this_repo=複製當前儲存庫 clone_this_repo=複製當前儲存庫
create_new_repo_command=從命令列建立新儲存庫。 create_new_repo_command=從命令列建立新儲存庫。

+ 0
- 1
options/locale/locale_zh-TW.ini Переглянути файл

download_archive=下載此儲存庫 download_archive=下載此儲存庫
more_operations=更多操作 more_operations=更多操作


no_desc=暫無描述
quick_guide=快速幫助 quick_guide=快速幫助
clone_this_repo=Clone 此儲存庫 clone_this_repo=Clone 此儲存庫
cite_this_repo=引用此儲存庫 cite_this_repo=引用此儲存庫

+ 4
- 4
package-lock.json Переглянути файл

"esbuild-loader": "4.1.0", "esbuild-loader": "4.1.0",
"escape-goat": "4.0.0", "escape-goat": "4.0.0",
"fast-glob": "3.3.2", "fast-glob": "3.3.2",
"htmx.org": "1.9.11",
"htmx.org": "1.9.12",
"idiomorph": "0.3.0", "idiomorph": "0.3.0",
"jquery": "3.7.1", "jquery": "3.7.1",
"katex": "0.16.10", "katex": "0.16.10",
} }
}, },
"node_modules/htmx.org": { "node_modules/htmx.org": {
"version": "1.9.11",
"resolved": "https://registry.npmjs.org/htmx.org/-/htmx.org-1.9.11.tgz",
"integrity": "sha512-WlVuICn8dfNOOgYmdYzYG8zSnP3++AdHkMHooQAzGZObWpVXYathpz/I37ycF4zikR6YduzfCvEcxk20JkIUsw=="
"version": "1.9.12",
"resolved": "https://registry.npmjs.org/htmx.org/-/htmx.org-1.9.12.tgz",
"integrity": "sha512-VZAohXyF7xPGS52IM8d1T1283y+X4D+Owf3qY1NZ9RuBypyu9l8cGsxUMAG5fEAb/DhT7rDoJ9Hpu5/HxFD3cw=="
}, },
"node_modules/human-signals": { "node_modules/human-signals": {
"version": "5.0.0", "version": "5.0.0",

+ 1
- 1
package.json Переглянути файл

"esbuild-loader": "4.1.0", "esbuild-loader": "4.1.0",
"escape-goat": "4.0.0", "escape-goat": "4.0.0",
"fast-glob": "3.3.2", "fast-glob": "3.3.2",
"htmx.org": "1.9.11",
"htmx.org": "1.9.12",
"idiomorph": "0.3.0", "idiomorph": "0.3.0",
"jquery": "3.7.1", "jquery": "3.7.1",
"katex": "0.16.10", "katex": "0.16.10",

+ 0
- 1
routers/api/actions/artifacts.go Переглянути файл



var task *actions.ActionTask var task *actions.ActionTask
if err == nil { if err == nil {

task, err = actions.GetTaskByID(req.Context(), tID) task, err = actions.GetTaskByID(req.Context(), tID)
if err != nil { if err != nil {
log.Error("Error runner api getting task by ID: %v", err) log.Error("Error runner api getting task by ID: %v", err)

+ 2
- 2
routers/api/packages/alpine/alpine.go Переглянути файл

return return
} }


upload, close, err := ctx.UploadStream()
upload, needToClose, err := ctx.UploadStream()
if err != nil { if err != nil {
apiError(ctx, http.StatusInternalServerError, err) apiError(ctx, http.StatusInternalServerError, err)
return return
} }
if close {
if needToClose {
defer upload.Close() defer upload.Close()
} }



+ 0
- 0
routers/api/packages/conan/conan.go Переглянути файл


Деякі файли не було показано, через те що забагато файлів було змінено

Завантаження…
Відмінити
Зберегти