diff --git a/.gitea/issue_template.md b/.gitea/issue_template.md index 95b97e4de5..9ad186cca7 100644 --- a/.gitea/issue_template.md +++ b/.gitea/issue_template.md @@ -5,7 +5,7 @@ 2. Please ask questions or configuration/deploy problems on our Discord server (https://discord.gg/gitea) or forum (https://discourse.gitea.io). 3. Please take a moment to check that your issue doesn't already exist. - 4. Make sure it's not mentioned in the FAQ (https://docs.gitea.io/en-us/faq) + 4. Make sure it's not mentioned in the FAQ (https://docs.gitea.com/help/faq) 5. Please give all relevant information below for bug reports, because incomplete details will be handled as an invalid report. --> @@ -26,7 +26,7 @@ - [ ] No - Log gist: - + ## Description diff --git a/.github/ISSUE_TEMPLATE/bug-report.yaml b/.github/ISSUE_TEMPLATE/bug-report.yaml index 9dacad0d5f..1004c55de3 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.yaml +++ b/.github/ISSUE_TEMPLATE/bug-report.yaml @@ -1,6 +1,6 @@ name: Bug Report description: Found something you weren't expecting? Report it here! -labels: kind/bug +labels: ["kind/bug"] body: - type: markdown attributes: @@ -14,12 +14,9 @@ body: server (https://discord.gg/gitea) or forum (https://discourse.gitea.io). 3. Make sure you are using the latest release and take a moment to check that your issue hasn't been reported before. - 4. Make sure it's not mentioned in the FAQ (https://docs.gitea.io/en-us/faq) - 5. Please give all relevant information below for bug reports, because + 4. Make sure it's not mentioned in the FAQ (https://docs.gitea.com/help/faq) + 5. It's really important to provide pertinent details and logs (https://docs.gitea.com/help/support), incomplete details will be handled as an invalid report. - 6. In particular it's really important to provide pertinent logs. You must give us DEBUG level logs. - Please read https://docs.gitea.io/en-us/logging-configuration/#debugging-problems - In addition, if your problem relates to git commands set `RUN_MODE=dev` at the top of app.ini - type: textarea id: description attributes: @@ -50,7 +47,7 @@ body: attributes: value: | It's really important to provide pertinent logs - Please read https://docs.gitea.io/en-us/logging-configuration/#debugging-problems + Please read https://docs.gitea.com/administration/logging-config#collecting-logs-for-help In addition, if your problem relates to git commands set `RUN_MODE=dev` at the top of app.ini - type: input id: logs @@ -89,6 +86,6 @@ body: description: What database system are you running? options: - PostgreSQL - - MySQL + - MySQL/MariaDB - MSSQL - SQLite diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 9bb5bb8e88..e769873f47 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -8,9 +8,9 @@ contact_links: about: Please ask questions and discuss configuration or deployment problems here. - name: Discourse Forum url: https://discourse.gitea.io - about: Questions and configuration or deployment problems can also be discussed on our forum. + about: Questions and configuration or deployment problems can also be discussed on our forum. - name: Frequently Asked Questions - url: https://docs.gitea.io/en-us/faq + url: https://docs.gitea.com/help/faq about: Please check if your question isn't mentioned here. - name: Crowdin Translations url: https://crowdin.com/project/gitea diff --git a/.github/ISSUE_TEMPLATE/ui.bug-report.yaml b/.github/ISSUE_TEMPLATE/ui.bug-report.yaml index 80db52d7f1..d5c41bb836 100644 --- a/.github/ISSUE_TEMPLATE/ui.bug-report.yaml +++ b/.github/ISSUE_TEMPLATE/ui.bug-report.yaml @@ -13,12 +13,12 @@ body: 2. Please ask questions or configuration/deploy problems on our Discord server (https://discord.gg/gitea) or forum (https://discourse.gitea.io). 3. Please take a moment to check that your issue doesn't already exist. - 4. Make sure it's not mentioned in the FAQ (https://docs.gitea.io/en-us/faq) + 4. Make sure it's not mentioned in the FAQ (https://docs.gitea.com/help/faq) 5. Please give all relevant information below for bug reports, because incomplete details will be handled as an invalid report. 6. In particular it's really important to provide pertinent logs. If you are certain that this is a javascript error, show us the javascript console. If the error appears to relate to Gitea the server you must also give us - DEBUG level logs. (See https://docs.gitea.io/en-us/logging-configuration/#debugging-problems) + DEBUG level logs. (See https://docs.gitea.com/administration/logging-config#collecting-logs-for-help) - type: textarea id: description attributes: diff --git a/.github/workflows/files-changed.yml b/.github/workflows/files-changed.yml index f9156d668d..b9277d3109 100644 --- a/.github/workflows/files-changed.yml +++ b/.github/workflows/files-changed.yml @@ -43,6 +43,8 @@ jobs: - "go.mod" - "go.sum" - "Makefile" + - ".golangci.yml" + - ".editorconfig" frontend: - "**/*.js" @@ -51,16 +53,21 @@ jobs: - "package.json" - "package-lock.json" - "Makefile" + - ".eslintrc.yaml" + - ".stylelintrc.yaml" + - ".npmrc" docs: - "**/*.md" - "docs/**" + - ".markdownlint.yaml" actions: - ".github/workflows/*" templates: - "templates/**/*.tmpl" + - "pyproject.toml" - "poetry.lock" docker: @@ -72,3 +79,6 @@ jobs: swagger: - "templates/swagger/v1_json.tmpl" - "Makefile" + - "package.json" + - "package-lock.json" + - ".spectral.yml" diff --git a/.golangci.yml b/.golangci.yml index 7c35bdd2a8..069dc13c99 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -29,7 +29,7 @@ linters: fast: false run: - go: "1.20" + go: "1.21" timeout: 10m skip-dirs: - node_modules @@ -75,7 +75,7 @@ linters-settings: - name: modifies-value-receiver gofumpt: extra-rules: true - lang-version: "1.20" + lang-version: "1.21" depguard: rules: main: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4e6f0a34fa..5cd83a4898 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -60,7 +60,7 @@ ## Introduction This document explains how to contribute changes to the Gitea project. \ -It assumes you have followed the [installation instructions](https://docs.gitea.io/en-us/). \ +It assumes you have followed the [installation instructions](https://docs.gitea.com/category/installation). \ Sensitive security-related issues should be reported to [security@gitea.io](mailto:security@gitea.io). For configuring IDEs for Gitea development, see the [contributed IDE configurations](contrib/ide/). diff --git a/README.md b/README.md index ea4bb787c7..767a7858e4 100644 --- a/README.md +++ b/README.md @@ -79,14 +79,14 @@ or if SQLite support is required: The `build` target is split into two sub-targets: -- `make backend` which requires [Go Stable](https://go.dev/dl/), required version is defined in [go.mod](/go.mod). -- `make frontend` which requires [Node.js LTS](https://nodejs.org/en/download/) or greater and Internet connectivity to download npm dependencies. +- `make backend` which requires [Go Stable](https://go.dev/dl/), the required version is defined in [go.mod](/go.mod). +- `make frontend` which requires [Node.js LTS](https://nodejs.org/en/download/) or greater. -When building from the official source tarballs which include pre-built frontend files, the `frontend` target will not be triggered, making it possible to build without Node.js and Internet connectivity. +Internet connectivity is required to download the go and npm modules. When building from the official source tarballs which include pre-built frontend files, the `frontend` target will not be triggered, making it possible to build without Node.js. Parallelism (`make -j `) is not supported. -More info: https://docs.gitea.io/en-us/install-from-source/ +More info: https://docs.gitea.com/installation/install-from-source ## Using diff --git a/README_ZH.md b/README_ZH.md index 28da7af535..866b85e999 100644 --- a/README_ZH.md +++ b/README_ZH.md @@ -68,7 +68,7 @@ Gitea 的首要目标是创建一个极易安装,运行非常快速,安装 ## 文档 -关于如何安装请访问我们的 [文档站](https://docs.gitea.io/zh-cn/),如果没有找到对应的文档,你也可以通过 [Discord - 英文](https://discord.gg/gitea) 和 QQ群 328432459 来和我们交流。 +关于如何安装请访问我们的 [文档站](https://docs.gitea.com/zh-cn/category/installation),如果没有找到对应的文档,你也可以通过 [Discord - 英文](https://discord.gg/gitea) 和 QQ群 328432459 来和我们交流。 ## 贡献流程 diff --git a/build/backport-locales.go b/build/backport-locales.go index 0346215348..d112dd72bd 100644 --- a/build/backport-locales.go +++ b/build/backport-locales.go @@ -12,6 +12,7 @@ import ( "path/filepath" "strings" + "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/setting" ) @@ -58,7 +59,7 @@ func main() { // use old en-US as the base, and copy the new translations to the old locales enUsOld := inisOld["options/locale/locale_en-US.ini"] - brokenWarned := map[string]bool{} + brokenWarned := make(container.Set[string]) for path, iniOld := range inisOld { if iniOld == enUsOld { continue @@ -77,7 +78,7 @@ func main() { broken := oldStr != "" && strings.Count(oldStr, "%") != strings.Count(newStr, "%") broken = broken || strings.Contains(oldStr, "\n") || strings.Contains(oldStr, "\n") if broken { - brokenWarned[secOld.Name()+"."+keyEnUs.Name()] = true + brokenWarned.Add(secOld.Name() + "." + keyEnUs.Name()) fmt.Println("----") fmt.Printf("WARNING: skip broken locale: %s , [%s] %s\n", path, secEnUS.Name(), keyEnUs.Name()) fmt.Printf("\told: %s\n", strings.ReplaceAll(oldStr, "\n", "\\n")) @@ -103,7 +104,7 @@ func main() { broken = broken || strings.HasPrefix(str, "`\"") broken = broken || strings.Count(str, `"`)%2 == 1 broken = broken || strings.Count(str, "`")%2 == 1 - if broken && !brokenWarned[sec.Name()+"."+key.Name()] { + if broken && !brokenWarned.Contains(sec.Name()+"."+key.Name()) { fmt.Printf("WARNING: found broken locale: %s , [%s] %s\n", path, sec.Name(), key.Name()) fmt.Printf("\tstr: %s\n", strings.ReplaceAll(str, "\n", "\\n")) fmt.Println("----") diff --git a/build/generate-go-licenses.go b/build/generate-go-licenses.go index c3b40c226f..84ba39025c 100644 --- a/build/generate-go-licenses.go +++ b/build/generate-go-licenses.go @@ -15,6 +15,8 @@ import ( "regexp" "sort" "strings" + + "code.gitea.io/gitea/modules/container" ) // regexp is based on go-license, excluding README and NOTICE @@ -55,20 +57,14 @@ func main() { // yml // // It could be removed once we have a better regex. - excludedExt := map[string]bool{ - ".gitignore": true, - ".go": true, - ".mod": true, - ".sum": true, - ".toml": true, - ".yml": true, - } + excludedExt := container.SetOf(".gitignore", ".go", ".mod", ".sum", ".toml", ".yml") + var paths []string err := filepath.WalkDir(base, func(path string, entry fs.DirEntry, err error) error { if err != nil { return err } - if entry.IsDir() || !licenseRe.MatchString(entry.Name()) || excludedExt[filepath.Ext(entry.Name())] { + if entry.IsDir() || !licenseRe.MatchString(entry.Name()) || excludedExt.Contains(filepath.Ext(entry.Name())) { return nil } paths = append(paths, path) diff --git a/cmd/cert.go b/cmd/cert.go index 9ae5ed06ce..38241d71a3 100644 --- a/cmd/cert.go +++ b/cmd/cert.go @@ -43,7 +43,7 @@ Outputs to 'cert.pem' and 'key.pem' and will overwrite existing files.`, }, &cli.IntFlag{ Name: "rsa-bits", - Value: 2048, + Value: 3072, Usage: "Size of RSA key to generate. Ignored if --ecdsa-curve is set", }, &cli.StringFlag{ diff --git a/contrib/gitea-monitoring-mixin/config.libsonnet b/contrib/gitea-monitoring-mixin/config.libsonnet index 55297949e4..446fc09a41 100644 --- a/contrib/gitea-monitoring-mixin/config.libsonnet +++ b/contrib/gitea-monitoring-mixin/config.libsonnet @@ -7,7 +7,7 @@ dashboardTimezone: 'default', dashboardRefresh: '1m', - // please see https://docs.gitea.io/en-us/config-cheat-sheet/#metrics-metrics + // please see https://docs.gitea.com/administration/config-cheat-sheet#metrics-metrics // Show issue by repository metrics with format gitea_issues_by_repository{repository="org/repo"} 5. // Requires Gitea 1.16.0 with ENABLED_ISSUE_BY_REPOSITORY set to true. showIssuesByRepository: true, diff --git a/contrib/legal/privacy.html.sample b/contrib/legal/privacy.html.sample index 22fc40fa3c..50972b2a3e 100644 --- a/contrib/legal/privacy.html.sample +++ b/contrib/legal/privacy.html.sample @@ -37,7 +37,7 @@

With your Consent

-

We share your User Personal Information, if you consent, after letting you know what information will be shared, with whom, and why. For example, if you allow third party applications to access your Account using OAuth2 providers, we share all information associated with your Account, including private repos and organizations. You may also direct us through your action on Your Gitea Instance to share your User Personal Information, such as when joining an Organization.

+

We share your User Personal Information, if you consent, after letting you know what information will be shared, with whom, and why. For example, if you allow third party applications to access your Account using OAuth2 providers, we share all information associated with your Account, including private repos and organizations. You may also direct us through your action on Your Gitea Instance to share your User Personal Information, such as when joining an Organization.

With Service Providers

@@ -144,7 +144,7 @@

Data Portability

-

As a Your Gitea Instance User, you can always take your data with you. You can clone your repositories to your computer, or you can perform migrations using the provided interfaces, for example.

+

As a Your Gitea Instance User, you can always take your data with you. You can clone your repositories to your computer, or you can perform migrations using the provided interfaces, for example.

Data Retention and Deletion of Data

@@ -183,7 +183,7 @@

Changes to this Privacy Policy

-

Although most changes are likely to be minor, Your Gitea Instance may change our Privacy Statement from time to time. We will provide notification to Users of material changes to this Privacy Statement through our Website at least 30 days prior to the change taking effect by posting a notice on our home page or sending email to the primary email address specified in your account.

+

Although most changes are likely to be minor, Your Gitea Instance may change our Privacy Statement from time to time. We will provide notification to Users of material changes to this Privacy Statement through our Website at least 30 days prior to the change taking effect by posting a notice on our home page or sending email to the primary email address specified in your account.

Contact

diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini index a3d24b1961..96a0a3ede9 100644 --- a/custom/conf/app.example.ini +++ b/custom/conf/app.example.ini @@ -1339,7 +1339,7 @@ LEVEL = Info ;; Define allowed algorithms and their minimum key length (use -1 to disable a type) ;ED25519 = 256 ;ECDSA = 256 -;RSA = 2047 ; we allow 2047 here because an otherwise valid 2048 bit RSA key can be reported as having 2047 bit length +;RSA = 3071 ; we allow 3071 here because an otherwise valid 3072 bit RSA key can be reported as having 3071 bit length ;DSA = -1 ; set to 1024 to switch on ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/docker/README.md b/docker/README.md index ef05032ee6..a6d7c9a843 100644 --- a/docker/README.md +++ b/docker/README.md @@ -4,4 +4,4 @@ Dockerfile is found in root of repository. Docker image can be found on [docker hub](https://hub.docker.com/r/gitea/gitea) -Documentation on using docker image can be found on [Gitea Docs site](https://docs.gitea.io/en-us/install-with-docker/) +Documentation on using docker image can be found on [Gitea Docs site](https://docs.gitea.com/installation/install-with-docker-rootless) diff --git a/docker/root/etc/s6/openssh/setup b/docker/root/etc/s6/openssh/setup index f43544d655..dbb3bafd35 100755 --- a/docker/root/etc/s6/openssh/setup +++ b/docker/root/etc/s6/openssh/setup @@ -11,7 +11,7 @@ fi if [ ! -f /data/ssh/ssh_host_rsa_key ]; then echo "Generating /data/ssh/ssh_host_rsa_key..." - ssh-keygen -t rsa -b 2048 -f /data/ssh/ssh_host_rsa_key -N "" > /dev/null + ssh-keygen -t rsa -b 3072 -f /data/ssh/ssh_host_rsa_key -N "" > /dev/null fi if [ ! -f /data/ssh/ssh_host_ecdsa_key ]; then diff --git a/docs/content/administration/command-line.en-us.md b/docs/content/administration/command-line.en-us.md index 91c6c1e626..05caff90e4 100644 --- a/docs/content/administration/command-line.en-us.md +++ b/docs/content/administration/command-line.en-us.md @@ -313,7 +313,7 @@ directory and will overwrite any existing files. - `--ecdsa-curve value`: ECDSA curve to use to generate a key. Optional. Valid options are P224, P256, P384, P521. - `--rsa-bits value`: Size of RSA key to generate. Optional. Ignored if --ecdsa-curve is - set. (default: 2048). + set. (default: 3072). - `--start-date value`: Creation date. Optional. (format: `Jan 1 15:04:05 2011`). - `--duration value`: Duration which the certificate is valid for. Optional. (default: 8760h0m0s) - `--ca`: If provided, this cert generates it's own certificate authority. Optional. diff --git a/docs/content/administration/command-line.zh-cn.md b/docs/content/administration/command-line.zh-cn.md index bd3d0c72bc..6e6bcb6082 100644 --- a/docs/content/administration/command-line.zh-cn.md +++ b/docs/content/administration/command-line.zh-cn.md @@ -295,7 +295,7 @@ menu: - 选项: - `--host value`:逗号分隔的主机名和IP地址列表,此证书适用于这些主机。支持使用通配符。必填。 - `--ecdsa-curve value`:用于生成密钥的ECDSA曲线。可选。有效选项为P224、P256、P384、P521。 - - `--rsa-bits value`:要生成的RSA密钥的大小。可选。如果设置了--ecdsa-curve,则忽略此选项。(默认值:2048)。 + - `--rsa-bits value`:要生成的RSA密钥的大小。可选。如果设置了--ecdsa-curve,则忽略此选项。(默认值:3072)。 - `--start-date value`:证书的创建日期。可选。(格式:`Jan 1 15:04:05 2011`)。 - `--duration value`:证书有效期。可选。(默认值:8760h0m0s) - `--ca`:如果提供此选项,则证书将生成自己的证书颁发机构。可选。 diff --git a/docs/content/administration/config-cheat-sheet.en-us.md b/docs/content/administration/config-cheat-sheet.en-us.md index 968b47c230..311a2b2b75 100644 --- a/docs/content/administration/config-cheat-sheet.en-us.md +++ b/docs/content/administration/config-cheat-sheet.en-us.md @@ -681,7 +681,7 @@ Define allowed algorithms and their minimum key length (use -1 to disable a type - `ED25519`: **256** - `ECDSA`: **256** -- `RSA`: **2047**: We set 2047 here because an otherwise valid 2048 RSA key can be reported as 2047 length. +- `RSA`: **3071**: We set 3071 here because an otherwise valid 3072 RSA key can be reported as 3071 length. - `DSA`: **-1**: DSA is now disabled by default. Set to **1024** to re-enable but ensure you may need to reconfigure your SSHD provider ## Webhook (`webhook`) diff --git a/docs/content/administration/config-cheat-sheet.zh-cn.md b/docs/content/administration/config-cheat-sheet.zh-cn.md index 87e45aa693..39121908c2 100644 --- a/docs/content/administration/config-cheat-sheet.zh-cn.md +++ b/docs/content/administration/config-cheat-sheet.zh-cn.md @@ -648,7 +648,7 @@ Gitea 创建以下非唯一队列: - `ED25519`:**256** - `ECDSA`:**256** -- `RSA`:**2047**:我们在这里设置为2047,因为一个其他方面有效的2048 RSA密钥可能被报告为2047长度。 +- `RSA`:**3071**:我们在这里设置为2047,因为一个其他方面有效的3072 RSA密钥可能被报告为3071长度。 - `DSA`:**-1**:默认情况下禁用DSA。设置为**1024**以重新启用,但请注意可能需要重新配置您的SSHD提供者 ## Webhook (`webhook`) diff --git a/docs/content/administration/customizing-gitea.en-us.md b/docs/content/administration/customizing-gitea.en-us.md index 8be9d8ef63..4c2d7ed0c4 100644 --- a/docs/content/administration/customizing-gitea.en-us.md +++ b/docs/content/administration/customizing-gitea.en-us.md @@ -36,7 +36,7 @@ Application settings can be found in file `CustomConf` which is by default, Again `gitea help` will allow you review this variable and you can override it using the `--config` option on the `gitea` binary. -- [Quick Cheat Sheet](https://docs.gitea.io/en-us/config-cheat-sheet/) +- [Quick Cheat Sheet](administration/config-cheat-sheet.md) - [Complete List](https://github.com/go-gitea/gitea/blob/main/custom/conf/app.example.ini) If the `CustomPath` folder can't be found despite checking `gitea help`, check the `GITEA_CUSTOM` @@ -44,7 +44,7 @@ environment variable; this can be used to override the default path to something `GITEA_CUSTOM` might, for example, be set by an init script. You can check whether the value is set under the "Configuration" tab on the site administration page. -- [List of Environment Variables](https://docs.gitea.io/en-us/environment-variables/) +- [List of Environment Variables](administration/environment-variables.md) **Note:** Gitea must perform a full restart to see configuration changes. @@ -84,7 +84,7 @@ for C++ repositories, we want to replace `options/gitignore/C++`. To do this, a must be placed in `$GITEA_CUSTOM/options/gitignore/C++` (see about the location of the `CustomPath` directory at the top of this document). -Every single page of Gitea can be changed. Dynamic content is generated using [go templates](https://golang.org/pkg/html/template/), +Every single page of Gitea can be changed. Dynamic content is generated using [go templates](https://pkg.go.dev/html/template), which can be modified by placing replacements below the `$GITEA_CUSTOM/templates` directory. To obtain any embedded file (including templates), the [`gitea embedded` tool](administration/cmd-embedded.md) can be used. Alternatively, they can be found in the [`templates`](https://github.com/go-gitea/gitea/tree/main/templates) directory of Gitea source (Note: the example link is from the `main` branch. Make sure to use templates compatible with the release you are using). @@ -108,7 +108,7 @@ just place it under your "$GITEA_CUSTOM/public/assets/" directory (for instance To match the current style, the link should have the class name "item", and you can use `{{AppSubUrl}}` to get the base URL: `Impressum` -For more information, see [Adding Legal Pages](https://docs.gitea.io/en-us/adding-legal-pages). +For more information, see [Adding Legal Pages](administration/adding-legal-pages.md). You can add new tabs in the same way, putting them in `extra_tabs.tmpl`. The exact HTML needed to match the style of other tabs is in the file @@ -371,10 +371,10 @@ A full list of supported emoji's is at [emoji list](https://gitea.com/gitea/gite ## Customizing the look of Gitea The default built-in themes are `gitea` (light), `arc-green` (dark), and `auto` (chooses light or dark depending on operating system settings). -The default theme can be changed via `DEFAULT_THEME` in the [ui](https://docs.gitea.io/en-us/config-cheat-sheet/#ui-ui) section of `app.ini`. +The default theme can be changed via `DEFAULT_THEME` in the [ui](administration/config-cheat-sheet.md#ui-ui) section of `app.ini`. Gitea also has support for user themes, which means every user can select which theme should be used. -The list of themes a user can choose from can be configured with the `THEMES` value in the [ui](https://docs.gitea.io/en-us/config-cheat-sheet/#ui-ui) section of `app.ini`. +The list of themes a user can choose from can be configured with the `THEMES` value in the [ui](administration/config-cheat-sheet.md#ui-ui) section of `app.ini`. To make a custom theme available to all users: diff --git a/docs/content/administration/customizing-gitea.zh-cn.md b/docs/content/administration/customizing-gitea.zh-cn.md index 77b2fd8312..2babf03da7 100644 --- a/docs/content/administration/customizing-gitea.zh-cn.md +++ b/docs/content/administration/customizing-gitea.zh-cn.md @@ -23,13 +23,13 @@ Gitea 引用 `custom` 目录中的自定义配置文件来覆盖配置、模板 将会自动创建包括 `custom/` 在内的必要应用目录,应用本身的配置存放在 `custom/conf/app.ini` 当中。在发行版中可能会以 `/etc/gitea/` 的形式为 `custom` 设置一个符号链接,查看配置详情请移步: -- [快速备忘单](https://docs.gitea.io/en-us/config-cheat-sheet/) +- [快速备忘单](administration/config-cheat-sheet.md) - [完整配置清单](https://github.com/go-gitea/gitea/blob/main/custom/conf/app.example.ini) 如果您在 binary 同目录下无法找到 `custom` 文件夹,请检查您的 `GITEA_CUSTOM` 环境变量配置, 因为它可能被配置到了其他地方(可能被一些启动脚本设置指定了目录)。 -- [环境变量清单](https://docs.gitea.io/en-us/specific-variables/) +- [环境变量清单](administration/environment-variables.md) **注:** 必须完全重启 Gitea 以使配置生效。 @@ -87,4 +87,4 @@ Gitea 引用 `custom` 目录中的自定义配置文件来覆盖配置、模板 ## 更改 Gitea 外观 Gitea 目前由两种内置主题,分别为默认 `gitea` 主题和深色主题 `arc-green`,您可以通过修改 -`app.ini` [ui](https://docs.gitea.io/en-us/config-cheat-sheet/#ui-ui) 部分的 `DEFAULT_THEME` 的值来变更至一个可用的 Gitea 外观。 +`app.ini` [ui](administration/config-cheat-sheet.md#ui-ui) 部分的 `DEFAULT_THEME` 的值来变更至一个可用的 Gitea 外观。 diff --git a/docs/content/administration/fail2ban-setup.en-us.md b/docs/content/administration/fail2ban-setup.en-us.md index 5c0101ef97..c90f88c886 100644 --- a/docs/content/administration/fail2ban-setup.en-us.md +++ b/docs/content/administration/fail2ban-setup.en-us.md @@ -119,7 +119,7 @@ proxy_set_header X-Real-IP $remote_addr; The security options in `app.ini` need to be adjusted to allow the interpretation of the headers as well as the list of IP addresses and networks that describe trusted proxy servers -(See the [configuration cheat sheet](https://docs.gitea.io/en-us/config-cheat-sheet/#security-security) for more information). +(See the [configuration cheat sheet](administration/config-cheat-sheet.md#security-security) for more information). ``` REVERSE_PROXY_LIMIT = 1 diff --git a/docs/content/administration/fail2ban-setup.zh-cn.md b/docs/content/administration/fail2ban-setup.zh-cn.md index 2ca0b3a790..457afba959 100644 --- a/docs/content/administration/fail2ban-setup.zh-cn.md +++ b/docs/content/administration/fail2ban-setup.zh-cn.md @@ -91,4 +91,4 @@ REVERSE_PROXY_TRUSTED_PROXIES = 127.0.0.0/8,::1/128 `REVERSE_PROXY_LIMIT` 限制反向代理服务器的层数,设置为 `0` 表示不使用这些标头。 `REVERSE_PROXY_TRUSTED_PROXIES` 表示受信任的反向代理服务器网络地址, 经过该网络地址转发来的流量会经过解析 `X-Real-IP` 头部得到真实客户端地址。 -(参考 [configuration cheat sheet](https://docs.gitea.io/en-us/config-cheat-sheet/#security-security)) +(参考 [configuration cheat sheet](administration/config-cheat-sheet.md#security-security)) diff --git a/docs/content/administration/mail-templates.en-us.md b/docs/content/administration/mail-templates.en-us.md index d66108f26a..a129453e1a 100644 --- a/docs/content/administration/mail-templates.en-us.md +++ b/docs/content/administration/mail-templates.en-us.md @@ -18,7 +18,7 @@ menu: # Mail templates To craft the e-mail subject and contents for certain operations, Gitea can be customized by using templates. The templates -for these functions are located under the [`custom` directory](https://docs.gitea.io/en-us/customizing-gitea/). +for these functions are located under the [`custom` directory](administration/customizing-gitea.md). Gitea has an internal template that serves as default in case there's no custom alternative. Custom templates are loaded when Gitea starts. Changes made to them are not recognized until Gitea is restarted again. @@ -165,7 +165,7 @@ If the template fails to render, it will be noticed only at the moment the mail A default subject is used if the subject template fails, and whatever was rendered successfully from the the _mail body_ is used, disregarding the rest. -Please check [Gitea's logs](https://docs.gitea.io/en-us/logging-configuration/) for error messages in case of trouble. +Please check [Gitea's logs](administration/logging-config.md) for error messages in case of trouble. ## Example diff --git a/docs/content/administration/mail-templates.zh-cn.md b/docs/content/administration/mail-templates.zh-cn.md index 49f8b97789..6a31d65b0a 100644 --- a/docs/content/administration/mail-templates.zh-cn.md +++ b/docs/content/administration/mail-templates.zh-cn.md @@ -17,7 +17,7 @@ menu: # 邮件模板 -为了定制特定操作的电子邮件主题和内容,可以使用模板来自定义 Gitea。这些功能的模板位于 [`custom` 目录](https://docs.gitea.io/en-us/customizing-gitea/) 下。 +为了定制特定操作的电子邮件主题和内容,可以使用模板来自定义 Gitea。这些功能的模板位于 [`custom` 目录](administration/customizing-gitea.md) 下。 如果没有自定义的替代方案,Gitea 将使用内部模板作为默认模板。 自定义模板在 Gitea 启动时加载。对它们的更改在 Gitea 重新启动之前不会被识别。 @@ -148,7 +148,7 @@ _主题_ 和 _邮件正文_ 由 [Golang的模板引擎](https://golang.org/pkg/t 如果模板无法呈现,则只有在发送邮件时才会注意到。 如果主题模板失败,将使用默认主题,如果从 _邮件正文_ 中成功呈现了任何内容,则将使用该内容,忽略其他内容。 -如果遇到问题,请检查 [Gitea的日志](https://docs.gitea.io/en-us/logging-configuration/) 以获取错误消息。 +如果遇到问题,请检查 [Gitea的日志](administration/logging-config.md) 以获取错误消息。 ## 示例 diff --git a/docs/content/administration/repo-indexer.en-us.md b/docs/content/administration/repo-indexer.en-us.md index 71eb4ffbc4..6dec2d63fa 100644 --- a/docs/content/administration/repo-indexer.en-us.md +++ b/docs/content/administration/repo-indexer.en-us.md @@ -19,7 +19,7 @@ menu: ## Setting up the repository indexer -Gitea can search through the files of the repositories by enabling this function in your [`app.ini`](https://docs.gitea.io/en-us/config-cheat-sheet/): +Gitea can search through the files of the repositories by enabling this function in your [`app.ini`](administration/config-cheat-sheet.md): ```ini [indexer] diff --git a/docs/content/administration/repo-indexer.zh-cn.md b/docs/content/administration/repo-indexer.zh-cn.md index 157bfd97f4..d70a617ae1 100644 --- a/docs/content/administration/repo-indexer.zh-cn.md +++ b/docs/content/administration/repo-indexer.zh-cn.md @@ -19,7 +19,7 @@ menu: ## 设置仓库索引器 -通过在您的 [`app.ini`](https://docs.gitea.io/en-us/config-cheat-sheet/) 中启用此功能,Gitea 可以通过仓库的文件进行搜索: +通过在您的 [`app.ini`](administration/config-cheat-sheet.md) 中启用此功能,Gitea 可以通过仓库的文件进行搜索: ```ini [indexer] diff --git a/docs/content/development/api-usage.en-us.md b/docs/content/development/api-usage.en-us.md index a49c145ba1..465f4d380c 100644 --- a/docs/content/development/api-usage.en-us.md +++ b/docs/content/development/api-usage.en-us.md @@ -21,7 +21,7 @@ menu: By default, `ENABLE_SWAGGER` is true, and `MAX_RESPONSE_ITEMS` is set to 50. See [Config Cheat -Sheet](https://docs.gitea.io/en-us/config-cheat-sheet/) for more +Sheet](administration/config-cheat-sheet.md) for more information. ## Authentication @@ -76,7 +76,7 @@ interface: `Settings | Applications | Generate New Token`. ## OAuth2 Provider -Access tokens obtained from Gitea's [OAuth2 provider](https://docs.gitea.io/en-us/oauth2-provider) are accepted by these methods: +Access tokens obtained from Gitea's [OAuth2 provider](development/oauth2-provider.md) are accepted by these methods: - `Authorization bearer ...` header in HTTP headers - `token=...` parameter in URL query string diff --git a/docs/content/development/api-usage.zh-cn.md b/docs/content/development/api-usage.zh-cn.md index ca5909e5a7..c7eeceeb7d 100644 --- a/docs/content/development/api-usage.zh-cn.md +++ b/docs/content/development/api-usage.zh-cn.md @@ -20,7 +20,7 @@ menu: ## 开启/配置 API 访问 通常情况下, `ENABLE_SWAGGER` 默认开启并且参数 `MAX_RESPONSE_ITEMS` 默认为 50。您可以从 [Config Cheat -Sheet](https://docs.gitea.io/en-us/config-cheat-sheet/) 中获取更多配置相关信息。 +Sheet](administration/config-cheat-sheet.md) 中获取更多配置相关信息。 ## 通过 API 认证 diff --git a/docs/content/development/oauth2-provider.en-us.md b/docs/content/development/oauth2-provider.en-us.md index 81fc04bdcf..053c1d8d7e 100644 --- a/docs/content/development/oauth2-provider.en-us.md +++ b/docs/content/development/oauth2-provider.en-us.md @@ -148,7 +148,7 @@ For public clients, a redirect URI of a loopback IP address such as `http://127. The `REDIRECT_URI` in the `access_token` request must match the `REDIRECT_URI` in the `authorize` request. -3. Use the `access_token` to make [API requests](https://docs.gitea.io/en-us/api-usage#oauth2) to access the user's resources. +3. Use the `access_token` to make [API requests](development/api-usage.md#oauth2-provider) to access the user's resources. ### Public client (PKCE) @@ -210,4 +210,4 @@ After you have generated this values, you can continue with your request. The `REDIRECT_URI` in the `access_token` request must match the `REDIRECT_URI` in the `authorize` request. -3. Use the `access_token` to make [API requests](https://docs.gitea.io/en-us/api-usage#oauth2) to access the user's resources. +3. Use the `access_token` to make [API requests](development/api-usage.md#oauth2-provider) to access the user's resources. diff --git a/docs/content/development/oauth2-provider.zh-cn.md b/docs/content/development/oauth2-provider.zh-cn.md index 6584bac37b..80d8469414 100644 --- a/docs/content/development/oauth2-provider.zh-cn.md +++ b/docs/content/development/oauth2-provider.zh-cn.md @@ -134,4 +134,4 @@ Gitea 支持私密和公共客户端类型,[参见 RFC 6749](https://datatrack `access_token` 请求中的 `REDIRECT_URI` 必须与 `authorize` 请求中的 `REDIRECT_URI` 相符。 -3. 使用 `access_token` 来构造 [API 请求](https://docs.gitea.io/en-us/api-usage#oauth2) 以读写用户的资源。 +3. 使用 `access_token` 来构造 [API 请求](development/api-usage.md#oauth2-provider) 以读写用户的资源。 diff --git a/docs/content/development/oauth2-provider.zh-tw.md b/docs/content/development/oauth2-provider.zh-tw.md index dac6e684bd..dd9f04f03c 100644 --- a/docs/content/development/oauth2-provider.zh-tw.md +++ b/docs/content/development/oauth2-provider.zh-tw.md @@ -93,4 +93,4 @@ Gitea 支援作為 OAuth2 提供者,能讓第三方程式能在使用者同意 `access_token` 請求中的 `REDIRECT_URI` 必須符合 `authorize` 請求中的 `REDIRECT_URI`。 -1. 發送 [API requests](https://docs.gitea.io/en-us/api-usage#oauth2) 時使用 `access_token` 以存取使用者的資源。 +1. 發送 [API requests](development/api-usage.md#oauth2-provider) 時使用 `access_token` 以存取使用者的資源。 diff --git a/docs/content/help/support.en-us.md b/docs/content/help/support.en-us.md index 24afe67bb8..db735b8124 100644 --- a/docs/content/help/support.en-us.md +++ b/docs/content/help/support.en-us.md @@ -22,34 +22,57 @@ menu: - [Discourse Forum](https://discourse.gitea.io/) - [Matrix](https://matrix.to/#/#gitea-space:matrix.org) - NOTE: Most of the Matrix channels are bridged with their counterpart in Discord and may experience some degree of flakiness with the bridge process. +- Chinese Support + - [Discourse Chinese Category](https://discourse.gitea.io/c/5-category/5) + - QQ Group 328432459 + +# Bug Report + +If you found a bug, please [create an issue on GitHub](https://github.com/go-gitea/gitea/issues). **NOTE:** When asking for support, it may be a good idea to have the following available so that the person helping has all the info they need: 1. Your `app.ini` (with any sensitive data scrubbed as necessary). -2. The Gitea logs, and any other appropriate log files for the situation. - - When using systemd, use `journalctl --lines 1000 --unit gitea` to collect logs. - - When using docker, use `docker logs --tail 1000 ` to collect logs. - - By default, the logs are outputted to console. If you need to collect logs from files, - you could copy the following config into your `app.ini` (remove all other `[log]` sections), - then you can find the `*.log` files in Gitea's log directory (default: `%(GITEA_WORK_DIR)/log`). - - ```ini - ; To show all SQL logs, you can also set LOG_SQL=true in the [database] section - [log] - LEVEL=debug - MODE=console,file - ``` - -3. Any error messages you are seeing. -4. When possible, try to replicate the issue on [try.gitea.io](https://try.gitea.io) and include steps so that others can reproduce the issue. - - This will greatly improve the chance that the root of the issue can be quickly discovered and resolved. -5. If you encounter slow/hanging/deadlock problems, please report the stack trace when the problem occurs. +2. Any error messages you are seeing. +3. The Gitea logs, and all other related logs for the situation. + - It's more useful to collect `trace` / `debug` level logs (see the next section). + - When using systemd, use `journalctl --lines 1000 --unit gitea` to collect logs. + - When using docker, use `docker logs --tail 1000 ` to collect logs. +4. Reproducible steps so that others could reproduce and understand the problem more quickly and easily. + - [try.gitea.io](https://try.gitea.io) could be used to reproduce the problem. +5. If you encounter slow/hanging/deadlock problems, please report the stacktrace when the problem occurs. Go to the "Site Admin" -> "Monitoring" -> "Stacktrace" -> "Download diagnosis report". -## Bugs +# Advanced Bug Report Tips -If you found a bug, please create an [issue on GitHub](https://github.com/go-gitea/gitea/issues). +## More Config Options for Logs -## Chinese Support +By default, the logs are outputted to console with `info` level. +If you need to set log level and/or collect logs from files, +you could just copy the following config into your `app.ini` (remove all other `[log]` sections), +then you will find the `*.log` files in Gitea's log directory (default: `%(GITEA_WORK_DIR)/log`). -Support for the Chinese language is provided at [Our discourse](https://discourse.gitea.io/c/5-category/5) or QQ Group 328432459. +```ini +; To show all SQL logs, you can also set LOG_SQL=true in the [database] section +[log] +LEVEL=debug +MODE=console,file +``` + +## Collecting Stacktrace by Command Line + +Gitea could use Golang's pprof handler and toolchain to collect stacktrace and other runtime information. + +If the web UI stops working, you could try to collect the stacktrace by command line: + +1. Set `app.ini`: + + ``` + [server] + ENABLE_PPROF = true + ``` + +2. Restart Gitea + +3. Try to trigger the bug, when the requests get stuck for a while, + use `curl` or browser to visit: `http://127.0.0.1:6060/debug/pprof/goroutine?debug=1` to get the stacktrace. diff --git a/docs/content/help/support.zh-tw.md b/docs/content/help/support.zh-tw.md index 70f4db01b3..511b362417 100644 --- a/docs/content/help/support.zh-tw.md +++ b/docs/content/help/support.zh-tw.md @@ -28,7 +28,7 @@ menu: 3. 您看到的任何錯誤訊息 4. 儘可能地在 [try.gitea.io](https://try.gitea.io) 觸發您的問題並記下步驟,以便其他人能重現那個問題。 - 這將讓我們更有機會快速地找出問題的根源並解決它。 -5. 堆棧跟踪,[請參考英文文檔](https://docs.gitea.io/en-us/seek-help/) +5. 堆棧跟踪,[請參考英文文檔](https://docs.gitea.com/help/support) ## 錯誤回報 diff --git a/docs/content/installation/with-docker-rootless.en-us.md b/docs/content/installation/with-docker-rootless.en-us.md index fc99819d7f..10f1212217 100644 --- a/docs/content/installation/with-docker-rootless.en-us.md +++ b/docs/content/installation/with-docker-rootless.en-us.md @@ -254,7 +254,7 @@ documented above, please note that `db` must be used as the database hostname. # Customization -Customization files described [here](https://docs.gitea.io/en-us/customizing-gitea/) should +Customization files described [here](administration/customizing-gitea.md) should be placed in `/var/lib/gitea/custom` directory. If using host volumes, it's quite easy to access these files; for named volumes, this is done through another container or by direct access at `/var/lib/docker/volumes/gitea_gitea/_/var_lib_gitea`. The configuration file will be saved at @@ -313,7 +313,7 @@ services: - GITEA__mailer__PASSWD="""${GITEA__mailer__PASSWD:?GITEA__mailer__PASSWD not set}""" ``` -To set required TOKEN and SECRET values, consider using Gitea's built-in [generate utility functions](https://docs.gitea.io/en-us/command-line/#generate). +To set required TOKEN and SECRET values, consider using Gitea's built-in [generate utility functions](administration/command-line.md#generate). # SSH Container Passthrough @@ -342,7 +342,7 @@ Once the wrapper is in place, you can make it the shell for the `git` user: sudo usermod -s /usr/local/bin/gitea-shell git ``` -Now that all the SSH commands are forwarded to the container, you need to set up the SSH authentication on the host. This is done by leveraging the [SSH AuthorizedKeysCommand](https://docs.gitea.io/en-us/command-line/#keys) to match the keys against those accepted by Gitea. Add the following block to `/etc/ssh/sshd_config`, on the host: +Now that all the SSH commands are forwarded to the container, you need to set up the SSH authentication on the host. This is done by leveraging the [SSH AuthorizedKeysCommand](administration/command-line.md#keys) to match the keys against those accepted by Gitea. Add the following block to `/etc/ssh/sshd_config`, on the host: ```bash Match User git diff --git a/docs/content/installation/with-docker-rootless.zh-cn.md b/docs/content/installation/with-docker-rootless.zh-cn.md index 9d5013eaa0..70bc32dc12 100644 --- a/docs/content/installation/with-docker-rootless.zh-cn.md +++ b/docs/content/installation/with-docker-rootless.zh-cn.md @@ -281,7 +281,7 @@ services: - GITEA__mailer__PASSWD="""${GITEA__mailer__PASSWD:?GITEA__mailer__PASSWD not set}""" ``` -要设置所需的 TOKEN 和 SECRET 值,可以使用 Gitea 的内置[生成使用函数](https://docs.gitea.io/en-us/command-line/#generate). +要设置所需的 TOKEN 和 SECRET 值,可以使用 Gitea 的内置[生成使用函数](administration/command-line.md#generate). # SSH 容器透传 @@ -310,7 +310,7 @@ sudo chmod +x /usr/local/bin/gitea-shell sudo usermod -s /usr/local/bin/gitea-shell git ``` -现在,所有的 SSH 命令都会被转发到容器,您需要在主机上设置 SSH 认证。这可以通过利用 [SSH AuthorizedKeysCommand](https://docs.gitea.io/en-us/command-line/#keys) 来匹配 Gitea 接受的密钥。在主机的 `/etc/ssh/sshd_config` 文件中添加以下代码块: +现在,所有的 SSH 命令都会被转发到容器,您需要在主机上设置 SSH 认证。这可以通过利用 [SSH AuthorizedKeysCommand](administration/command-line.md#keys) 来匹配 Gitea 接受的密钥。在主机的 `/etc/ssh/sshd_config` 文件中添加以下代码块: ```bash Match User git diff --git a/docs/content/installation/with-docker.en-us.md b/docs/content/installation/with-docker.en-us.md index f5a09e9fc5..e67f5bccb2 100644 --- a/docs/content/installation/with-docker.en-us.md +++ b/docs/content/installation/with-docker.en-us.md @@ -261,7 +261,7 @@ documented above, please note that `db` must be used as the database hostname. ## Customization -Customization files described [here](https://docs.gitea.io/en-us/customizing-gitea/) should +Customization files described [here](administration/customizing-gitea.md) should be placed in `/data/gitea` directory. If using host volumes, it's quite easy to access these files; for named volumes, this is done through another container or by direct access at `/var/lib/docker/volumes/gitea_gitea/_data`. The configuration file will be saved at @@ -309,7 +309,7 @@ services: - GITEA__mailer__PASSWD="""${GITEA__mailer__PASSWD:?GITEA__mailer__PASSWD not set}""" ``` -Gitea will generate new secrets/tokens for every new installation automatically and write them into the app.ini. If you want to set the secrets/tokens manually, you can use the following docker commands to use of Gitea's built-in [generate utility functions](https://docs.gitea.io/en-us/command-line/#generate). Do not lose/change your SECRET_KEY after the installation, otherwise the encrypted data can not be decrypted anymore. +Gitea will generate new secrets/tokens for every new installation automatically and write them into the app.ini. If you want to set the secrets/tokens manually, you can use the following docker commands to use of Gitea's built-in [generate utility functions](administration/command-line.md#generate). Do not lose/change your SECRET_KEY after the installation, otherwise the encrypted data can not be decrypted anymore. The following commands will output a new `SECRET_KEY` and `INTERNAL_TOKEN` to `stdout`, which you can then place in your environment variables. @@ -553,7 +553,7 @@ In this option, the idea is that the host SSH uses an `AuthorizedKeysCommand` in Now all attempts to login as the `git` user on the host will be forwarded to the docker - including the `SSH_ORIGINAL_COMMAND`. We now need to set-up SSH authentication on the host. -We will do this by leveraging the [SSH AuthorizedKeysCommand](https://docs.gitea.io/en-us/command-line/#keys) to match the keys against those accepted by Gitea. +We will do this by leveraging the [SSH AuthorizedKeysCommand](administration/command-line.md#keys) to match the keys against those accepted by Gitea. Add the following block to `/etc/ssh/sshd_config`, on the host: diff --git a/docs/content/installation/with-docker.fr-fr.md b/docs/content/installation/with-docker.fr-fr.md index 432add9afd..46b562e14e 100644 --- a/docs/content/installation/with-docker.fr-fr.md +++ b/docs/content/installation/with-docker.fr-fr.md @@ -103,7 +103,7 @@ Vous devriez maintenant avoir deux conteneurs Docker pour Gitea et PostgreSQL pl # Personnalisation -Les fichier personnalisés ([voir les instructions](https://docs.gitea.io/en-us/customizing-gitea/)) peuvent être placés dans le répertoire `/data/gitea`. +Les fichier personnalisés ([voir les instructions](administration/customizing-gitea.md)) peuvent être placés dans le répertoire `/data/gitea`. Le fichier de configuration sera sauvegardé à l'emplacement suivant : `/data/gitea/conf/app.ini` diff --git a/docs/content/installation/with-docker.zh-cn.md b/docs/content/installation/with-docker.zh-cn.md index d8cbc2f950..fcaff1c119 100644 --- a/docs/content/installation/with-docker.zh-cn.md +++ b/docs/content/installation/with-docker.zh-cn.md @@ -260,7 +260,7 @@ MySQL 或 PostgreSQL 容器将需要分别创建。 ## 自定义 -[此处](https://docs.gitea.io/zh-cn/customizing-gitea/)描述的定制文件应放在 `/data/gitea` 目录中。如果使用主机卷,则访问这些文件非常容易;对于命名卷,可以通过另一个容器或通过直接访问 `/var/lib/docker/volumes/gitea_gitea/_data` 来完成。安装后,配置文件将保存在 `/data/gitea/conf/app.ini` 中。 +[此处](administration/customizing-gitea.md)描述的定制文件应放在 `/data/gitea` 目录中。如果使用主机卷,则访问这些文件非常容易;对于命名卷,可以通过另一个容器或通过直接访问 `/var/lib/docker/volumes/gitea_gitea/_data` 来完成。安装后,配置文件将保存在 `/data/gitea/conf/app.ini` 中。 ## 升级 @@ -293,7 +293,7 @@ services: - GITEA__mailer__PASSWD="""${GITEA__mailer__PASSWD:?GITEA__mailer__PASSWD not set}""" ``` -Gitea 将为每次新安装自动生成新的 `SECRET_KEY` 并将它们写入 `app.ini`。 如果您想手动设置 `SECRET_KEY`,您可以使用以下 docker 命令来使用 Gitea 内置的[方法](https://docs.gitea.io/en-us/command-line/#generate)生成 `SECRET_KEY`。 安装后请妥善保管您的 `SECRET_KEY`,如若丢失则无法解密已加密的数据。 +Gitea 将为每次新安装自动生成新的 `SECRET_KEY` 并将它们写入 `app.ini`。 如果您想手动设置 `SECRET_KEY`,您可以使用以下 docker 命令来使用 Gitea 内置的[方法](administration/command-line.md#generate)生成 `SECRET_KEY`。 安装后请妥善保管您的 `SECRET_KEY`,如若丢失则无法解密已加密的数据。 以下命令将向 `stdout` 输出一个新的 `SECRET_KEY` 和 `INTERNAL_TOKEN`,然后您可以将其放入环境变量中。 diff --git a/docs/content/usage/actions/design.en-us.md b/docs/content/usage/actions/design.en-us.md index d4c90d5404..8394e822dc 100644 --- a/docs/content/usage/actions/design.en-us.md +++ b/docs/content/usage/actions/design.en-us.md @@ -112,7 +112,7 @@ You can configure your Gitea instance to fetch actions or images from your intra In fact, your Gitea instance can serve as both the actions marketplace and the image registry. You can mirror actions repositories from GitHub to your Gitea instance, and use them as normal. -And [Gitea Container Registry](https://docs.gitea.io/en-us/usage/packages/container/) can be used as a Docker image registry. +And [Gitea Container Registry](usage/packages/container.md) can be used as a Docker image registry. ### Connection 4, job containers to internet diff --git a/docs/content/usage/actions/design.zh-cn.md b/docs/content/usage/actions/design.zh-cn.md index 995e9f0b7f..06f600f391 100644 --- a/docs/content/usage/actions/design.zh-cn.md +++ b/docs/content/usage/actions/design.zh-cn.md @@ -113,7 +113,7 @@ act runner 必须能够连接到Gitea以接收任务并发送执行结果回来 实际上,您的Gitea实例可以同时充当 Actions 市场和镜像注册表。 您可以将GitHub上的Actions仓库镜像到您的Gitea实例,并将其用作普通Actions。 -而 [Gitea 容器注册表](https://docs.gitea.io/en-us/usage/packages/container/) 可用作Docker镜像注册表。 +而 [Gitea 容器注册表](usage/packages/container.md) 可用作Docker镜像注册表。 ### 连接 4,Job容器到互联网 diff --git a/docs/content/usage/actions/faq.en-us.md b/docs/content/usage/actions/faq.en-us.md index d665b05b4b..031509a033 100644 --- a/docs/content/usage/actions/faq.en-us.md +++ b/docs/content/usage/actions/faq.en-us.md @@ -61,7 +61,7 @@ For example: Be careful, the `https://` or `http://` prefix is necessary! Alternatively, if you want your runners to download actions from GitHub or your own Gitea instance by default, you can configure it by setting `[actions].DEFAULT_ACTIONS_URL`. -See [Configuration Cheat Sheet](https://docs.gitea.io/en-us/config-cheat-sheet/#actions-actions). +See [Configuration Cheat Sheet](administration/config-cheat-sheet.md#actions-actions). This is one of the differences from GitHub Actions, but it should allow users much more flexibility in how they run Actions. @@ -69,7 +69,7 @@ This is one of the differences from GitHub Actions, but it should allow users mu Runners have no more permissions than simply connecting to your Gitea instance. When any runner receives a job to run, it will temporarily gain limited permission to the repository associated with the job. -If you want to give more permissions to the runner, allowing it to access more private repositories or external systems, you can pass [secrets](https://docs.gitea.io/en-us/usage/secrets/) to it. +If you want to give more permissions to the runner, allowing it to access more private repositories or external systems, you can pass [secrets](usage/secrets.md) to it. Refined permission control to Actions is a complicated job. In the future, we will add more options to Gitea to make it more configurable, such as allowing more write access to repositories or read access to all repositories in the same organization. diff --git a/docs/content/usage/actions/faq.zh-cn.md b/docs/content/usage/actions/faq.zh-cn.md index cdfea6d11d..f5dc8e179b 100644 --- a/docs/content/usage/actions/faq.zh-cn.md +++ b/docs/content/usage/actions/faq.zh-cn.md @@ -61,7 +61,7 @@ DEFAULT_REPO_UNITS = ...,repo.actions 注意,`https://`或`http://`前缀是必需的! 另外,如果您希望您的Runner默认从GitHub或您自己的Gitea实例下载Actions,可以通过设置 `[actions].DEFAULT_ACTIONS_URL`进行配置。 -参见[配置速查表](https://docs.gitea.io/en-us/config-cheat-sheet/#actions-actions)。 +参见[配置速查表](administration/config-cheat-sheet.md#actions-actions)。 这是与GitHub Actions的一个区别,但它应该允许用户以更灵活的方式运行Actions。 @@ -69,7 +69,7 @@ DEFAULT_REPO_UNITS = ...,repo.actions Runner仅具有连接到您的Gitea实例的权限。 当任何Runner接收到要运行的Job时,它将临时获得与Job关联的仓库的有限权限。 -如果您想为Runner提供更多权限,允许它访问更多私有仓库或外部系统,您可以向其传递[密钥](https://docs.gitea.io/en-us/usage/secrets/)。 +如果您想为Runner提供更多权限,允许它访问更多私有仓库或外部系统,您可以向其传递[密钥](usage/secrets.md)。 对于 Actions 的细粒度权限控制是一项复杂的工作。 在未来,我们将添加更多选项以使Gitea更可配置,例如允许对仓库进行更多写访问或对同一组织中的所有仓库进行读访问。 diff --git a/docs/content/usage/authentication.en-us.md b/docs/content/usage/authentication.en-us.md index d366414219..4ab5cad3e1 100644 --- a/docs/content/usage/authentication.en-us.md +++ b/docs/content/usage/authentication.en-us.md @@ -197,7 +197,7 @@ administrative user. field is set to `mail.com`, then Gitea will expect the `user email` field for an authenticated GIT instance to be `gituser@mail.com`.[^2] -**Note**: PAM support is added via [build-time flags](https://docs.gitea.io/en-us/install-from-source/#build), +**Note**: PAM support is added via [build-time flags](installation/install-from-source.md#build), and the official binaries provided do not have this enabled. PAM requires that the necessary libpam dynamic library be available and the necessary PAM development headers be accessible to the compiler. diff --git a/docs/content/usage/authentication.zh-cn.md b/docs/content/usage/authentication.zh-cn.md index ea9f03b9a0..e465bc3f23 100644 --- a/docs/content/usage/authentication.zh-cn.md +++ b/docs/content/usage/authentication.zh-cn.md @@ -162,7 +162,7 @@ PAM提供了一种机制,通过对用户进行PAM认证来自动将其添加 - PAM电子邮件域:用户认证时要附加的电子邮件后缀。例如,如果登录系统期望一个名为gituse的用户, 并且将此字段设置为mail.com,那么Gitea在验证一个GIT实例的用户时将期望user emai字段为gituser@mail.com[^2]。 -**Note**: PAM 支持通过[build-time flags](https://docs.gitea.io/en-us/install-from-source/#build)添加, +**Note**: PAM 支持通过[build-time flags](installation/install-from-source.md#build)添加, 而官方提供的二进制文件通常不会默认启用此功能。PAM需要确保系统上有必要的libpam动态库,并且编译器可以访问必要的PAM开发头文件。 [^1]: 例如,在Debian "Bullseye"上使用标准Linux登录,可以使用`common-session-noninteractive`。这个值对于其他版本的Debian, diff --git a/go.mod b/go.mod index 968c0663e0..1996d29a84 100644 --- a/go.mod +++ b/go.mod @@ -90,6 +90,7 @@ require ( github.com/prometheus/client_golang v1.16.0 github.com/quasoft/websspi v1.1.2 github.com/redis/go-redis/v9 v9.0.5 + github.com/robfig/cron/v3 v3.0.1 github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 github.com/sassoftware/go-rpmutils v0.2.0 github.com/sergi/go-diff v1.3.1 @@ -254,7 +255,6 @@ require ( github.com/rhysd/actionlint v1.6.25 // indirect github.com/rivo/uniseg v0.4.4 // indirect github.com/robfig/cron v1.2.0 // indirect - github.com/robfig/cron/v3 v3.0.1 // indirect github.com/rogpeppe/go-internal v1.11.0 // indirect github.com/rs/xid v1.5.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect diff --git a/models/actions/schedule.go b/models/actions/schedule.go new file mode 100644 index 0000000000..b0bc40dadc --- /dev/null +++ b/models/actions/schedule.go @@ -0,0 +1,120 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package actions + +import ( + "context" + "time" + + "code.gitea.io/gitea/models/db" + repo_model "code.gitea.io/gitea/models/repo" + user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/timeutil" + webhook_module "code.gitea.io/gitea/modules/webhook" + + "github.com/robfig/cron/v3" +) + +// ActionSchedule represents a schedule of a workflow file +type ActionSchedule struct { + ID int64 + Title string + Specs []string + RepoID int64 `xorm:"index"` + Repo *repo_model.Repository `xorm:"-"` + OwnerID int64 `xorm:"index"` + WorkflowID string + TriggerUserID int64 + TriggerUser *user_model.User `xorm:"-"` + Ref string + CommitSHA string + Event webhook_module.HookEventType + EventPayload string `xorm:"LONGTEXT"` + Content []byte + Created timeutil.TimeStamp `xorm:"created"` + Updated timeutil.TimeStamp `xorm:"updated"` +} + +func init() { + db.RegisterModel(new(ActionSchedule)) +} + +// GetSchedulesMapByIDs returns the schedules by given id slice. +func GetSchedulesMapByIDs(ids []int64) (map[int64]*ActionSchedule, error) { + schedules := make(map[int64]*ActionSchedule, len(ids)) + return schedules, db.GetEngine(db.DefaultContext).In("id", ids).Find(&schedules) +} + +// GetReposMapByIDs returns the repos by given id slice. +func GetReposMapByIDs(ids []int64) (map[int64]*repo_model.Repository, error) { + repos := make(map[int64]*repo_model.Repository, len(ids)) + return repos, db.GetEngine(db.DefaultContext).In("id", ids).Find(&repos) +} + +var cronParser = cron.NewParser(cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow | cron.Descriptor) + +// CreateScheduleTask creates new schedule task. +func CreateScheduleTask(ctx context.Context, rows []*ActionSchedule) error { + // Return early if there are no rows to insert + if len(rows) == 0 { + return nil + } + + // Begin transaction + ctx, committer, err := db.TxContext(ctx) + if err != nil { + return err + } + defer committer.Close() + + // Loop through each schedule row + for _, row := range rows { + // Create new schedule row + if err = db.Insert(ctx, row); err != nil { + return err + } + + // Loop through each schedule spec and create a new spec row + now := time.Now() + + for _, spec := range row.Specs { + // Parse the spec and check for errors + schedule, err := cronParser.Parse(spec) + if err != nil { + continue // skip to the next spec if there's an error + } + + // Insert the new schedule spec row + if err = db.Insert(ctx, &ActionScheduleSpec{ + RepoID: row.RepoID, + ScheduleID: row.ID, + Spec: spec, + Next: timeutil.TimeStamp(schedule.Next(now).Unix()), + }); err != nil { + return err + } + } + } + + // Commit transaction + return committer.Commit() +} + +func DeleteScheduleTaskByRepo(ctx context.Context, id int64) error { + ctx, committer, err := db.TxContext(ctx) + if err != nil { + return err + } + defer committer.Close() + + if _, err := db.GetEngine(ctx).Delete(&ActionSchedule{RepoID: id}); err != nil { + return err + } + + if _, err := db.GetEngine(ctx).Delete(&ActionScheduleSpec{RepoID: id}); err != nil { + return err + } + + return committer.Commit() +} diff --git a/models/actions/schedule_list.go b/models/actions/schedule_list.go new file mode 100644 index 0000000000..e873c05ec3 --- /dev/null +++ b/models/actions/schedule_list.go @@ -0,0 +1,94 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package actions + +import ( + "context" + + "code.gitea.io/gitea/models/db" + repo_model "code.gitea.io/gitea/models/repo" + user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/container" + + "xorm.io/builder" +) + +type ScheduleList []*ActionSchedule + +// GetUserIDs returns a slice of user's id +func (schedules ScheduleList) GetUserIDs() []int64 { + ids := make(container.Set[int64], len(schedules)) + for _, schedule := range schedules { + ids.Add(schedule.TriggerUserID) + } + return ids.Values() +} + +func (schedules ScheduleList) GetRepoIDs() []int64 { + ids := make(container.Set[int64], len(schedules)) + for _, schedule := range schedules { + ids.Add(schedule.RepoID) + } + return ids.Values() +} + +func (schedules ScheduleList) LoadTriggerUser(ctx context.Context) error { + userIDs := schedules.GetUserIDs() + users := make(map[int64]*user_model.User, len(userIDs)) + if err := db.GetEngine(ctx).In("id", userIDs).Find(&users); err != nil { + return err + } + for _, schedule := range schedules { + if schedule.TriggerUserID == user_model.ActionsUserID { + schedule.TriggerUser = user_model.NewActionsUser() + } else { + schedule.TriggerUser = users[schedule.TriggerUserID] + } + } + return nil +} + +func (schedules ScheduleList) LoadRepos() error { + repoIDs := schedules.GetRepoIDs() + repos, err := repo_model.GetRepositoriesMapByIDs(repoIDs) + if err != nil { + return err + } + for _, schedule := range schedules { + schedule.Repo = repos[schedule.RepoID] + } + return nil +} + +type FindScheduleOptions struct { + db.ListOptions + RepoID int64 + OwnerID int64 +} + +func (opts FindScheduleOptions) toConds() builder.Cond { + cond := builder.NewCond() + if opts.RepoID > 0 { + cond = cond.And(builder.Eq{"repo_id": opts.RepoID}) + } + if opts.OwnerID > 0 { + cond = cond.And(builder.Eq{"owner_id": opts.OwnerID}) + } + + return cond +} + +func FindSchedules(ctx context.Context, opts FindScheduleOptions) (ScheduleList, int64, error) { + e := db.GetEngine(ctx).Where(opts.toConds()) + if !opts.ListAll && opts.PageSize > 0 && opts.Page >= 1 { + e.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize) + } + var schedules ScheduleList + total, err := e.Desc("id").FindAndCount(&schedules) + return schedules, total, err +} + +func CountSchedules(ctx context.Context, opts FindScheduleOptions) (int64, error) { + return db.GetEngine(ctx).Where(opts.toConds()).Count(new(ActionSchedule)) +} diff --git a/models/actions/schedule_spec.go b/models/actions/schedule_spec.go new file mode 100644 index 0000000000..91240459a0 --- /dev/null +++ b/models/actions/schedule_spec.go @@ -0,0 +1,50 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package actions + +import ( + "context" + + "code.gitea.io/gitea/models/db" + repo_model "code.gitea.io/gitea/models/repo" + "code.gitea.io/gitea/modules/timeutil" + + "github.com/robfig/cron/v3" +) + +// ActionScheduleSpec represents a schedule spec of a workflow file +type ActionScheduleSpec struct { + ID int64 + RepoID int64 `xorm:"index"` + Repo *repo_model.Repository `xorm:"-"` + ScheduleID int64 `xorm:"index"` + Schedule *ActionSchedule `xorm:"-"` + + // Next time the job will run, or the zero time if Cron has not been + // started or this entry's schedule is unsatisfiable + Next timeutil.TimeStamp `xorm:"index"` + // Prev is the last time this job was run, or the zero time if never. + Prev timeutil.TimeStamp + Spec string + + Created timeutil.TimeStamp `xorm:"created"` + Updated timeutil.TimeStamp `xorm:"updated"` +} + +func (s *ActionScheduleSpec) Parse() (cron.Schedule, error) { + return cronParser.Parse(s.Spec) +} + +func init() { + db.RegisterModel(new(ActionScheduleSpec)) +} + +func UpdateScheduleSpec(ctx context.Context, spec *ActionScheduleSpec, cols ...string) error { + sess := db.GetEngine(ctx).ID(spec.ID) + if len(cols) > 0 { + sess.Cols(cols...) + } + _, err := sess.Update(spec) + return err +} diff --git a/models/actions/schedule_spec_list.go b/models/actions/schedule_spec_list.go new file mode 100644 index 0000000000..d379490b4e --- /dev/null +++ b/models/actions/schedule_spec_list.go @@ -0,0 +1,106 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package actions + +import ( + "context" + + "code.gitea.io/gitea/models/db" + repo_model "code.gitea.io/gitea/models/repo" + "code.gitea.io/gitea/modules/container" + + "xorm.io/builder" +) + +type SpecList []*ActionScheduleSpec + +func (specs SpecList) GetScheduleIDs() []int64 { + ids := make(container.Set[int64], len(specs)) + for _, spec := range specs { + ids.Add(spec.ScheduleID) + } + return ids.Values() +} + +func (specs SpecList) LoadSchedules() error { + scheduleIDs := specs.GetScheduleIDs() + schedules, err := GetSchedulesMapByIDs(scheduleIDs) + if err != nil { + return err + } + for _, spec := range specs { + spec.Schedule = schedules[spec.ScheduleID] + } + + repoIDs := specs.GetRepoIDs() + repos, err := GetReposMapByIDs(repoIDs) + if err != nil { + return err + } + for _, spec := range specs { + spec.Repo = repos[spec.RepoID] + } + + return nil +} + +func (specs SpecList) GetRepoIDs() []int64 { + ids := make(container.Set[int64], len(specs)) + for _, spec := range specs { + ids.Add(spec.RepoID) + } + return ids.Values() +} + +func (specs SpecList) LoadRepos() error { + repoIDs := specs.GetRepoIDs() + repos, err := repo_model.GetRepositoriesMapByIDs(repoIDs) + if err != nil { + return err + } + for _, spec := range specs { + spec.Repo = repos[spec.RepoID] + } + return nil +} + +type FindSpecOptions struct { + db.ListOptions + RepoID int64 + Next int64 +} + +func (opts FindSpecOptions) toConds() builder.Cond { + cond := builder.NewCond() + if opts.RepoID > 0 { + cond = cond.And(builder.Eq{"repo_id": opts.RepoID}) + } + + if opts.Next > 0 { + cond = cond.And(builder.Lte{"next": opts.Next}) + } + + return cond +} + +func FindSpecs(ctx context.Context, opts FindSpecOptions) (SpecList, int64, error) { + e := db.GetEngine(ctx).Where(opts.toConds()) + if opts.PageSize > 0 && opts.Page >= 1 { + e.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize) + } + var specs SpecList + total, err := e.Desc("id").FindAndCount(&specs) + if err != nil { + return nil, 0, err + } + + if err := specs.LoadSchedules(); err != nil { + return nil, 0, err + } + return specs, total, nil +} + +func CountSpecs(ctx context.Context, opts FindSpecOptions) (int64, error) { + return db.GetEngine(ctx).Where(opts.toConds()).Count(new(ActionScheduleSpec)) +} diff --git a/models/fixtures/email_address.yml b/models/fixtures/email_address.yml index e7df5fdc5f..1c32379b60 100644 --- a/models/fixtures/email_address.yml +++ b/models/fixtures/email_address.yml @@ -276,4 +276,12 @@ email: user2-2@example.com lower_email: user2-2@example.com is_activated: false - is_primary: false \ No newline at end of file + is_primary: false + +- + id: 36 + uid: 36 + email: abcde@gitea.com + lower_email: abcde@gitea.com + is_activated: true + is_primary: false diff --git a/models/fixtures/gpg_key.yml b/models/fixtures/gpg_key.yml index ca780a73aa..2d54313fdf 100644 --- a/models/fixtures/gpg_key.yml +++ b/models/fixtures/gpg_key.yml @@ -1 +1,23 @@ -[] # empty +- + id: 5 + owner_id: 36 + key_id: B15431642629B826 + primary_key_id: + content: xsDNBGTrY3UBDAC2HLBqmMplAV15qSnC7g1c4dV406f5EHNhFr95Nup2My6b2eafTlvedv77s8PT/I7F3fy4apOZs5A7w2SsPlLMcQ3ev4uGOsxRtkq5RLy1Yb6SNueX0Da2UVKR5KTC5Q6BWaqxwS0IjKOLZ/xz0Pbe/ClV3bZSKBEY2omkVo3Z0HZ771vB2clPRvGJ/IdeKOsZ3ZytSFXfyiJBdARmeSPmydXLil8+Ibq5iLAeow5PK8hK1TCOnKHzLWNqcNq70tyjoHvcGi70iGjoVEEUgPCLLuU8WmzTJwlvA3BuDzjtaO7TLo/jdE6iqkHtMSS8x+43sAH6hcFRCWAVh/0Uq7n36uGDfNxGnX3YrmX3LR9x5IsBES1rGGWbpxio4o5GIf/Xd+JgDd9rzJCqRuZ3/sW/TxK38htWaVNZV0kMkHUCTc1ctzWpCm635hbFCHBhPYIp+/z206khkAKDbz/CNuU91Wazsh7KO07wrwDtxfDDbInJ8TfHE2TGjzjQzgChfmcAEQEAAQ== + verified: true + can_sign: true + can_encrypt_comms: true + can_encrypt_storage: true + can_certify: true + +- + id: 6 + owner_id: 36 + key_id: EE3AF48454AFD619 + primary_key_id: B15431642629B826 + content: zsDNBGTrY3UBDADsHrzuOicQaPdUQm0+0UNrs92cESm/j/4yBBUk+sfLZAo6J99c4eh4nAQzzZ7al080rYKB0G+7xoRz1eHcQH6zrVcqB8KYtf/sdY47WaMiMyxM+kTSvzp7tsv7QuSQZ0neUEXRyYMz5ttBfIjWUd+3NDItuHyB+MtNWlS3zXgaUbe5VifqKaNmzN0Ye4yXTKcpypE3AOqPVz+iIFv3c6TmsqLHJaR4VoicCleAqLyF/28WsJO7M9dDW+EM3MZVnsVpycTURyHAJGfSk10waQZAaRwmarCN/q0KEJ+aEAK/SRliUneBZoMO5hY5iBeG432tofwaQqAahPv9uXIb1n2JEMKwnMlMA9UGD1AcDbywfj1m/ZGBBw95i4Ekkfn43RvV3THr7uJU/dRqqP+iic4MwpUrOxqELW/kmeHXlBcNbZZhEEvwRoW7U2/9eeuog4nRleRJ0pi/xOP9wmxkKjaIPIK3phdBtEpVk4w/UTAWNdyIIrFggukeAnZFyGJwlm8AEQEAAQ== + verified: true + can_sign: true + can_encrypt_comms: true + can_encrypt_storage: true + can_certify: true diff --git a/models/fixtures/user.yml b/models/fixtures/user.yml index c7c5c024be..f24d098a7e 100644 --- a/models/fixtures/user.yml +++ b/models/fixtures/user.yml @@ -1301,7 +1301,7 @@ lower_name: limited_org36 name: limited_org36 full_name: Limited Org 36 - email: limited_org36@example.com + email: abcde@gitea.com keep_email_private: false email_notifications_preference: enabled passwd: ZogKvWdyEx:password @@ -1320,7 +1320,7 @@ allow_create_organization: true prohibit_login: false avatar: avatar22 - avatar_email: limited_org36@example.com + avatar_email: abcde@gitea.com use_custom_avatar: false num_followers: 0 num_following: 0 diff --git a/models/issues/comment.go b/models/issues/comment.go index e781931261..17e579b455 100644 --- a/models/issues/comment.go +++ b/models/issues/comment.go @@ -23,6 +23,7 @@ import ( "code.gitea.io/gitea/modules/references" "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/timeutil" + "code.gitea.io/gitea/modules/translation" "code.gitea.io/gitea/modules/util" "xorm.io/builder" @@ -181,40 +182,32 @@ func (t CommentType) HasAttachmentSupport() bool { return false } -// RoleDescriptor defines comment tag type -type RoleDescriptor int +// RoleInRepo presents the user's participation in the repo +type RoleInRepo string + +// RoleDescriptor defines comment "role" tags +type RoleDescriptor struct { + IsPoster bool + RoleInRepo RoleInRepo +} // Enumerate all the role tags. const ( - RoleDescriptorNone RoleDescriptor = iota - RoleDescriptorPoster - RoleDescriptorWriter - RoleDescriptorOwner + RoleRepoOwner RoleInRepo = "owner" + RoleRepoMember RoleInRepo = "member" + RoleRepoCollaborator RoleInRepo = "collaborator" + RoleRepoFirstTimeContributor RoleInRepo = "first_time_contributor" + RoleRepoContributor RoleInRepo = "contributor" ) -// WithRole enable a specific tag on the RoleDescriptor. -func (rd RoleDescriptor) WithRole(role RoleDescriptor) RoleDescriptor { - return rd | (1 << role) +// LocaleString returns the locale string name of the role +func (r RoleInRepo) LocaleString(lang translation.Locale) string { + return lang.Tr("repo.issues.role." + string(r)) } -func stringToRoleDescriptor(role string) RoleDescriptor { - switch role { - case "Poster": - return RoleDescriptorPoster - case "Writer": - return RoleDescriptorWriter - case "Owner": - return RoleDescriptorOwner - default: - return RoleDescriptorNone - } -} - -// HasRole returns if a certain role is enabled on the RoleDescriptor. -func (rd RoleDescriptor) HasRole(role string) bool { - roleDescriptor := stringToRoleDescriptor(role) - bitValue := rd & (1 << roleDescriptor) - return (bitValue > 0) +// LocaleHelper returns the locale tooltip of the role +func (r RoleInRepo) LocaleHelper(lang translation.Locale) string { + return lang.Tr("repo.issues.role." + string(r) + "_helper") } // Comment represents a comment in commit and issue page. diff --git a/models/issues/pull_list.go b/models/issues/pull_list.go index 3b2416900b..c4506ef150 100644 --- a/models/issues/pull_list.go +++ b/models/issues/pull_list.go @@ -199,3 +199,16 @@ func (prs PullRequestList) GetIssueIDs() []int64 { } return issueIDs } + +// HasMergedPullRequestInRepo returns whether the user(poster) has merged pull-request in the repo +func HasMergedPullRequestInRepo(ctx context.Context, repoID, posterID int64) (bool, error) { + return db.GetEngine(ctx). + Join("INNER", "pull_request", "pull_request.issue_id = issue.id"). + Where("repo_id=?", repoID). + And("poster_id=?", posterID). + And("is_pull=?", true). + And("pull_request.has_merged=?", true). + Select("issue.id"). + Limit(1). + Get(new(Issue)) +} diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go index 87c597b573..9f4acda236 100644 --- a/models/migrations/migrations.go +++ b/models/migrations/migrations.go @@ -526,6 +526,8 @@ var migrations = []Migration{ NewMigration("Allow archiving labels", v1_21.AddArchivedUnixColumInLabelTable), // v272 -> v273 NewMigration("Add Version to ActionRun table", v1_21.AddVersionToActionRunTable), + // v273 -> v274 + NewMigration("Add Action Schedule Table", v1_21.AddActionScheduleTable), } // GetCurrentDBVersion returns the current db version diff --git a/models/migrations/v1_16/v210.go b/models/migrations/v1_16/v210.go index 4e55afccc1..533bb4bf80 100644 --- a/models/migrations/v1_16/v210.go +++ b/models/migrations/v1_16/v210.go @@ -4,7 +4,6 @@ package v1_16 //nolint import ( - "crypto/elliptic" "encoding/base32" "fmt" "strings" @@ -123,13 +122,17 @@ func RemigrateU2FCredentials(x *xorm.Engine) error { if err != nil { continue } + pubKey, err := parsed.PubKey.ECDH() + if err != nil { + continue + } remigrated := &webauthnCredential{ ID: reg.ID, Name: reg.Name, LowerName: strings.ToLower(reg.Name), UserID: reg.UserID, CredentialID: base32.HexEncoding.EncodeToString(parsed.KeyHandle), - PublicKey: elliptic.Marshal(elliptic.P256(), parsed.PubKey.X, parsed.PubKey.Y), + PublicKey: pubKey.Bytes(), AttestationType: "fido-u2f", AAGUID: []byte{}, SignCount: reg.Counter, diff --git a/models/migrations/v1_21/v273.go b/models/migrations/v1_21/v273.go new file mode 100644 index 0000000000..61c79f4a76 --- /dev/null +++ b/models/migrations/v1_21/v273.go @@ -0,0 +1,45 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package v1_21 //nolint +import ( + "code.gitea.io/gitea/modules/timeutil" + + "xorm.io/xorm" +) + +func AddActionScheduleTable(x *xorm.Engine) error { + type ActionSchedule struct { + ID int64 + Title string + Specs []string + RepoID int64 `xorm:"index"` + OwnerID int64 `xorm:"index"` + WorkflowID string + TriggerUserID int64 + Ref string + CommitSHA string + Event string + EventPayload string `xorm:"LONGTEXT"` + Content []byte + Created timeutil.TimeStamp `xorm:"created"` + Updated timeutil.TimeStamp `xorm:"updated"` + } + + type ActionScheduleSpec struct { + ID int64 + RepoID int64 `xorm:"index"` + ScheduleID int64 `xorm:"index"` + Spec string + Next timeutil.TimeStamp `xorm:"index"` + Prev timeutil.TimeStamp + + Created timeutil.TimeStamp `xorm:"created"` + Updated timeutil.TimeStamp `xorm:"updated"` + } + + return x.Sync( + new(ActionSchedule), + new(ActionScheduleSpec), + ) +} diff --git a/models/repo.go b/models/repo.go index 7579d2ad73..74a88d4c48 100644 --- a/models/repo.go +++ b/models/repo.go @@ -170,6 +170,8 @@ func DeleteRepository(doer *user_model.User, uid, repoID int64) error { &actions_model.ActionRunJob{RepoID: repoID}, &actions_model.ActionRun{RepoID: repoID}, &actions_model.ActionRunner{RepoID: repoID}, + &actions_model.ActionScheduleSpec{RepoID: repoID}, + &actions_model.ActionSchedule{RepoID: repoID}, &actions_model.ActionArtifact{RepoID: repoID}, ); err != nil { return fmt.Errorf("deleteBeans: %w", err) diff --git a/models/repo/pushmirror.go b/models/repo/pushmirror.go index dad9a9d388..3edbcceb9b 100644 --- a/models/repo/pushmirror.go +++ b/models/repo/pushmirror.go @@ -128,7 +128,7 @@ func GetPushMirrorsByRepoID(ctx context.Context, repoID int64, listOptions db.Li func GetPushMirrorsSyncedOnCommit(ctx context.Context, repoID int64) ([]*PushMirror, error) { mirrors := make([]*PushMirror, 0, 10) return mirrors, db.GetEngine(ctx). - Where("repo_id=? AND sync_on_commit=?", repoID, true). + Where("repo_id = ? AND sync_on_commit = ?", repoID, true). Find(&mirrors) } diff --git a/models/repo/release.go b/models/repo/release.go index a00585111e..191475d541 100644 --- a/models/repo/release.go +++ b/models/repo/release.go @@ -133,6 +133,11 @@ func (r *Release) HTMLURL() string { return r.Repo.HTMLURL() + "/releases/tag/" + util.PathEscapeSegments(r.TagName) } +// APIUploadURL the api url to upload assets to a release. release must have attributes loaded +func (r *Release) APIUploadURL() string { + return r.APIURL() + "/assets" +} + // Link the relative url for a release on the web UI. release must have attributes loaded func (r *Release) Link() string { return r.Repo.Link() + "/releases/tag/" + util.PathEscapeSegments(r.TagName) diff --git a/models/secret/secret.go b/models/secret/secret.go index c9c95e82d3..1cb816e9db 100644 --- a/models/secret/secret.go +++ b/models/secret/secret.go @@ -6,12 +6,14 @@ package secret import ( "context" "errors" + "fmt" "strings" "code.gitea.io/gitea/models/db" secret_module "code.gitea.io/gitea/modules/secret" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/timeutil" + "code.gitea.io/gitea/modules/util" "xorm.io/builder" ) @@ -26,6 +28,25 @@ type Secret struct { CreatedUnix timeutil.TimeStamp `xorm:"created NOT NULL"` } +// ErrSecretNotFound represents a "secret not found" error. +type ErrSecretNotFound struct { + Name string +} + +// IsErrSecretNotFound checks if an error is a ErrSecretNotFound. +func IsErrSecretNotFound(err error) bool { + _, ok := err.(ErrSecretNotFound) + return ok +} + +func (err ErrSecretNotFound) Error() string { + return fmt.Sprintf("secret was not found [name: %s]", err.Name) +} + +func (err ErrSecretNotFound) Unwrap() error { + return util.ErrNotExist +} + // newSecret Creates a new already encrypted secret func newSecret(ownerID, repoID int64, name, data string) *Secret { return &Secret{ @@ -93,3 +114,77 @@ func FindSecrets(ctx context.Context, opts FindSecretsOptions) ([]*Secret, error func CountSecrets(ctx context.Context, opts *FindSecretsOptions) (int64, error) { return db.GetEngine(ctx).Where(opts.toConds()).Count(new(Secret)) } + +// UpdateSecret changes org or user reop secret. +func UpdateSecret(ctx context.Context, orgID, repoID int64, name, data string) error { + sc := new(Secret) + name = strings.ToUpper(name) + has, err := db.GetEngine(ctx). + Where("owner_id=?", orgID). + And("repo_id=?", repoID). + And("name=?", name). + Get(sc) + if err != nil { + return err + } else if !has { + return ErrSecretNotFound{Name: name} + } + + encrypted, err := secret_module.EncryptSecret(setting.SecretKey, data) + if err != nil { + return err + } + + sc.Data = encrypted + _, err = db.GetEngine(ctx).ID(sc.ID).Cols("data").Update(sc) + return err +} + +// DeleteSecret deletes secret from an organization. +func DeleteSecret(ctx context.Context, orgID, repoID int64, name string) error { + sc := new(Secret) + has, err := db.GetEngine(ctx). + Where("owner_id=?", orgID). + And("repo_id=?", repoID). + And("name=?", strings.ToUpper(name)). + Get(sc) + if err != nil { + return err + } else if !has { + return ErrSecretNotFound{Name: name} + } + + if _, err := db.GetEngine(ctx).ID(sc.ID).Delete(new(Secret)); err != nil { + return fmt.Errorf("Delete: %w", err) + } + + return nil +} + +// CreateOrUpdateSecret creates or updates a secret and returns true if it was created +func CreateOrUpdateSecret(ctx context.Context, orgID, repoID int64, name, data string) (bool, error) { + sc := new(Secret) + name = strings.ToUpper(name) + has, err := db.GetEngine(ctx). + Where("owner_id=?", orgID). + And("repo_id=?", repoID). + And("name=?", name). + Get(sc) + if err != nil { + return false, err + } + + if !has { + _, err = InsertEncryptedSecret(ctx, orgID, repoID, name, data) + if err != nil { + return false, err + } + return true, nil + } + + if err := UpdateSecret(ctx, orgID, repoID, name, data); err != nil { + return false, err + } + + return false, nil +} diff --git a/models/unit/unit.go b/models/unit/unit.go index 5f5e20de1e..ca3e7f999d 100644 --- a/models/unit/unit.go +++ b/models/unit/unit.go @@ -9,6 +9,7 @@ import ( "strings" "code.gitea.io/gitea/models/perm" + "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" ) @@ -318,14 +319,13 @@ var ( // FindUnitTypes give the unit key names and return valid unique units and invalid keys func FindUnitTypes(nameKeys ...string) (res []Type, invalidKeys []string) { - m := map[Type]struct{}{} + m := make(container.Set[Type]) for _, key := range nameKeys { t := TypeFromKey(key) if t == TypeInvalid { invalidKeys = append(invalidKeys, key) - } else if _, ok := m[t]; !ok { + } else if m.Add(t) { res = append(res, t) - m[t] = struct{}{} } } return res, invalidKeys diff --git a/modules/actions/workflows.go b/modules/actions/workflows.go index de340a74ec..408fdb8f8e 100644 --- a/modules/actions/workflows.go +++ b/modules/actions/workflows.go @@ -95,18 +95,25 @@ func GetEventsFromContent(content []byte) ([]*jobparser.Event, error) { return events, nil } -func DetectWorkflows(gitRepo *git.Repository, commit *git.Commit, triggedEvent webhook_module.HookEventType, payload api.Payloader) ([]*DetectedWorkflow, error) { +func DetectWorkflows( + gitRepo *git.Repository, + commit *git.Commit, + triggedEvent webhook_module.HookEventType, + payload api.Payloader, +) ([]*DetectedWorkflow, []*DetectedWorkflow, error) { entries, err := ListWorkflows(commit) if err != nil { - return nil, err + return nil, nil, err } workflows := make([]*DetectedWorkflow, 0, len(entries)) + schedules := make([]*DetectedWorkflow, 0, len(entries)) for _, entry := range entries { content, err := GetContentFromEntry(entry) if err != nil { - return nil, err + return nil, nil, err } + events, err := GetEventsFromContent(content) if err != nil { log.Warn("ignore invalid workflow %q: %v", entry.Name(), err) @@ -114,6 +121,14 @@ func DetectWorkflows(gitRepo *git.Repository, commit *git.Commit, triggedEvent w } for _, evt := range events { log.Trace("detect workflow %q for event %#v matching %q", entry.Name(), evt, triggedEvent) + if evt.IsSchedule() { + dwf := &DetectedWorkflow{ + EntryName: entry.Name(), + TriggerEvent: evt.Name, + Content: content, + } + schedules = append(schedules, dwf) + } if detectMatched(gitRepo, commit, triggedEvent, payload, evt) { dwf := &DetectedWorkflow{ EntryName: entry.Name(), @@ -125,7 +140,7 @@ func DetectWorkflows(gitRepo *git.Repository, commit *git.Commit, triggedEvent w } } - return workflows, nil + return workflows, schedules, nil } func detectMatched(gitRepo *git.Repository, commit *git.Commit, triggedEvent webhook_module.HookEventType, payload api.Payloader, evt *jobparser.Event) bool { diff --git a/modules/activitypub/user_settings.go b/modules/activitypub/user_settings.go index 20b3d759c2..ef9bc0a864 100644 --- a/modules/activitypub/user_settings.go +++ b/modules/activitypub/user_settings.go @@ -8,7 +8,7 @@ import ( "code.gitea.io/gitea/modules/util" ) -const rsaBits = 2048 +const rsaBits = 3072 // GetKeyPair function returns a user's private and public keys func GetKeyPair(user *user_model.User) (pub, priv string, err error) { diff --git a/modules/assetfs/layered.go b/modules/assetfs/layered.go index d69732f81b..9678d23ad6 100644 --- a/modules/assetfs/layered.go +++ b/modules/assetfs/layered.go @@ -14,6 +14,7 @@ import ( "sort" "time" + "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/process" "code.gitea.io/gitea/modules/util" @@ -130,7 +131,7 @@ func readDir(layer *Layer, name string) ([]fs.FileInfo, error) { // * false: only directories will be returned. // The returned files are sorted by name. func (l *LayeredFS) ListFiles(name string, fileMode ...bool) ([]string, error) { - fileMap := map[string]bool{} + fileSet := make(container.Set[string]) for _, layer := range l.layers { infos, err := readDir(layer, name) if err != nil { @@ -138,14 +139,11 @@ func (l *LayeredFS) ListFiles(name string, fileMode ...bool) ([]string, error) { } for _, info := range infos { if shouldInclude(info, fileMode...) { - fileMap[info.Name()] = true + fileSet.Add(info.Name()) } } } - files := make([]string, 0, len(fileMap)) - for file := range fileMap { - files = append(files, file) - } + files := fileSet.Values() sort.Strings(files) return files, nil } @@ -161,7 +159,7 @@ func (l *LayeredFS) ListAllFiles(name string, fileMode ...bool) ([]string, error } func listAllFiles(layers []*Layer, name string, fileMode ...bool) ([]string, error) { - fileMap := map[string]bool{} + fileSet := make(container.Set[string]) var list func(dir string) error list = func(dir string) error { for _, layer := range layers { @@ -172,7 +170,7 @@ func listAllFiles(layers []*Layer, name string, fileMode ...bool) ([]string, err for _, info := range infos { path := util.PathJoinRelX(dir, info.Name()) if shouldInclude(info, fileMode...) { - fileMap[path] = true + fileSet.Add(path) } if info.IsDir() { if err = list(path); err != nil { @@ -186,10 +184,7 @@ func listAllFiles(layers []*Layer, name string, fileMode ...bool) ([]string, err if err := list(name); err != nil { return nil, err } - var files []string - for file := range fileMap { - files = append(files, file) - } + files := fileSet.Values() sort.Strings(files) return files, nil } diff --git a/modules/context/context.go b/modules/context/context.go index 98c1a9bdb6..47ad310b09 100644 --- a/modules/context/context.go +++ b/modules/context/context.go @@ -107,6 +107,29 @@ func GetValidateContext(req *http.Request) (ctx *ValidateContext) { return ctx } +func NewTemplateContextForWeb(ctx *Context) TemplateContext { + tmplCtx := NewTemplateContext(ctx) + tmplCtx["Locale"] = ctx.Base.Locale + tmplCtx["AvatarUtils"] = templates.NewAvatarUtils(ctx) + return tmplCtx +} + +func NewWebContext(base *Base, render Render, session session.Store) *Context { + ctx := &Context{ + Base: base, + Render: render, + Session: session, + + Cache: mc.GetCache(), + Link: setting.AppSubURL + strings.TrimSuffix(base.Req.URL.EscapedPath(), "/"), + Repo: &Repository{PullRequest: &PullRequest{}}, + Org: &Organization{}, + } + ctx.TemplateContext = NewTemplateContextForWeb(ctx) + ctx.Flash = &middleware.Flash{DataStore: ctx, Values: url.Values{}} + return ctx +} + // Contexter initializes a classic context for a request. func Contexter() func(next http.Handler) http.Handler { rnd := templates.HTMLRenderer() @@ -127,21 +150,8 @@ func Contexter() func(next http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { base, baseCleanUp := NewBaseContext(resp, req) - ctx := &Context{ - Base: base, - Cache: mc.GetCache(), - Link: setting.AppSubURL + strings.TrimSuffix(req.URL.EscapedPath(), "/"), - Render: rnd, - Session: session.GetSession(req), - Repo: &Repository{PullRequest: &PullRequest{}}, - Org: &Organization{}, - } defer baseCleanUp() - - // TODO: "install.go" also shares the same logic, which should be refactored to a general function - ctx.TemplateContext = NewTemplateContext(ctx) - ctx.TemplateContext["Locale"] = ctx.Locale - ctx.TemplateContext["AvatarUtils"] = templates.NewAvatarUtils(ctx) + ctx := NewWebContext(base, rnd, session.GetSession(req)) ctx.Data.MergeFrom(middleware.CommonTemplateContextData()) ctx.Data["Context"] = ctx // TODO: use "ctx" in template and remove this @@ -172,8 +182,7 @@ func Contexter() func(next http.Handler) http.Handler { } } - // prepare an empty Flash message for current request - ctx.Flash = &middleware.Flash{DataStore: ctx, Values: url.Values{}} + // if there are new messages in the ctx.Flash, write them into cookie ctx.Resp.Before(func(resp ResponseWriter) { if val := ctx.Flash.Encode(); val != "" { middleware.SetSiteCookie(ctx.Resp, CookieNameFlash, val, 0) diff --git a/modules/context/package.go b/modules/context/package.go index be50e0a991..c0813fb2da 100644 --- a/modules/context/package.go +++ b/modules/context/package.go @@ -154,12 +154,10 @@ func PackageContexter() func(next http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { base, baseCleanUp := NewBaseContext(resp, req) - ctx := &Context{ - Base: base, - Render: renderer, // it is still needed when rendering 500 page in a package handler - } defer baseCleanUp() + // it is still needed when rendering 500 page in a package handler + ctx := NewWebContext(base, renderer, nil) ctx.Base.AppendContextValue(WebContextKey, ctx) next.ServeHTTP(ctx.Resp, ctx.Req) }) diff --git a/modules/doctor/fix8312.go b/modules/doctor/fix8312.go new file mode 100644 index 0000000000..8de3113663 --- /dev/null +++ b/modules/doctor/fix8312.go @@ -0,0 +1,61 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package doctor + +import ( + "context" + + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/models/db" + org_model "code.gitea.io/gitea/models/organization" + "code.gitea.io/gitea/models/perm" + "code.gitea.io/gitea/modules/log" + + "xorm.io/builder" +) + +func fixOwnerTeamCreateOrgRepo(ctx context.Context, logger log.Logger, autofix bool) error { + count := 0 + + err := db.Iterate( + ctx, + builder.Eq{"authorize": perm.AccessModeOwner, "can_create_org_repo": false}, + func(ctx context.Context, team *org_model.Team) error { + team.CanCreateOrgRepo = true + count++ + + if !autofix { + return nil + } + + return models.UpdateTeam(team, false, false) + }, + ) + if err != nil { + logger.Critical("Unable to iterate across repounits to fix incorrect can_create_org_repo: Error %v", err) + return err + } + + if !autofix { + if count == 0 { + logger.Info("Found no team with incorrect can_create_org_repo") + } else { + logger.Warn("Found %d teams with incorrect can_create_org_repo", count) + } + return nil + } + logger.Info("Fixed %d teams with incorrect can_create_org_repo", count) + + return nil +} + +func init() { + Register(&Check{ + Title: "Check for incorrect can_create_org_repo for org owner teams", + Name: "fix-owner-team-create-org-repo", + IsDefault: false, + Run: fixOwnerTeamCreateOrgRepo, + Priority: 7, + }) +} diff --git a/modules/git/commit.go b/modules/git/commit.go index c44882d886..b09be25ba0 100644 --- a/modules/git/commit.go +++ b/modules/git/commit.go @@ -495,7 +495,7 @@ func GetCommitFileStatus(ctx context.Context, repoPath, commitID string) (*Commi }() stderr := new(bytes.Buffer) - err := NewCommand(ctx, "log", "--name-status", "-c", "--pretty=format:", "--parents", "--no-renames", "-z", "-1").AddDynamicArguments(commitID).Run(&RunOpts{ + err := NewCommand(ctx, "log", "--name-status", "-m", "--pretty=format:", "--first-parent", "--no-renames", "-z", "-1").AddDynamicArguments(commitID).Run(&RunOpts{ Dir: repoPath, Stdout: w, Stderr: stderr, diff --git a/modules/git/commit_test.go b/modules/git/commit_test.go index acf4beb029..ac586fdf09 100644 --- a/modules/git/commit_test.go +++ b/modules/git/commit_test.go @@ -255,3 +255,26 @@ func TestParseCommitFileStatus(t *testing.T) { assert.Equal(t, kase.modified, fileStatus.Modified) } } + +func TestGetCommitFileStatusMerges(t *testing.T) { + bareRepo1Path := filepath.Join(testReposDir, "repo6_merge") + + commitFileStatus, err := GetCommitFileStatus(DefaultContext, bareRepo1Path, "022f4ce6214973e018f02bf363bf8a2e3691f699") + assert.NoError(t, err) + + expected := CommitFileStatus{ + []string{ + "add_file.txt", + }, + []string{ + "to_remove.txt", + }, + []string{ + "to_modify.txt", + }, + } + + assert.Equal(t, commitFileStatus.Added, expected.Added) + assert.Equal(t, commitFileStatus.Removed, expected.Removed) + assert.Equal(t, commitFileStatus.Modified, expected.Modified) +} diff --git a/modules/git/log_name_status.go b/modules/git/log_name_status.go index 70f6ef9dbb..7519e32b90 100644 --- a/modules/git/log_name_status.go +++ b/modules/git/log_name_status.go @@ -374,27 +374,25 @@ heaploop: break heaploop } parentRemaining.Remove(current.CommitID) - if current.Paths != nil { - for i, found := range current.Paths { - if !found { - continue + for i, found := range current.Paths { + if !found { + continue + } + changed[i] = false + if results[i] == "" { + results[i] = current.CommitID + if err := repo.LastCommitCache.Put(headRef, path.Join(treepath, paths[i]), current.CommitID); err != nil { + return nil, err } - changed[i] = false - if results[i] == "" { - results[i] = current.CommitID - if err := repo.LastCommitCache.Put(headRef, path.Join(treepath, paths[i]), current.CommitID); err != nil { + delete(path2idx, paths[i]) + remaining-- + if results[0] == "" { + results[0] = current.CommitID + if err := repo.LastCommitCache.Put(headRef, treepath, current.CommitID); err != nil { return nil, err } - delete(path2idx, paths[i]) + delete(path2idx, "") remaining-- - if results[0] == "" { - results[0] = current.CommitID - if err := repo.LastCommitCache.Put(headRef, treepath, current.CommitID); err != nil { - return nil, err - } - delete(path2idx, "") - remaining-- - } } } } diff --git a/modules/git/tests/repos/repo6_merge/HEAD b/modules/git/tests/repos/repo6_merge/HEAD new file mode 100644 index 0000000000..b870d82622 --- /dev/null +++ b/modules/git/tests/repos/repo6_merge/HEAD @@ -0,0 +1 @@ +ref: refs/heads/main diff --git a/modules/git/tests/repos/repo6_merge/objects/02/2f4ce6214973e018f02bf363bf8a2e3691f699 b/modules/git/tests/repos/repo6_merge/objects/02/2f4ce6214973e018f02bf363bf8a2e3691f699 new file mode 100644 index 0000000000..0778a1c88c Binary files /dev/null and b/modules/git/tests/repos/repo6_merge/objects/02/2f4ce6214973e018f02bf363bf8a2e3691f699 differ diff --git a/modules/git/tests/repos/repo6_merge/objects/05/45879290cc368a8becebc4aa34002c52d5fecc b/modules/git/tests/repos/repo6_merge/objects/05/45879290cc368a8becebc4aa34002c52d5fecc new file mode 100644 index 0000000000..c71794f329 Binary files /dev/null and b/modules/git/tests/repos/repo6_merge/objects/05/45879290cc368a8becebc4aa34002c52d5fecc differ diff --git a/modules/git/tests/repos/repo6_merge/objects/1e/5d0a65fe099ef12d24b28f783896e4b8172576 b/modules/git/tests/repos/repo6_merge/objects/1e/5d0a65fe099ef12d24b28f783896e4b8172576 new file mode 100644 index 0000000000..365f368d35 Binary files /dev/null and b/modules/git/tests/repos/repo6_merge/objects/1e/5d0a65fe099ef12d24b28f783896e4b8172576 differ diff --git a/modules/git/tests/repos/repo6_merge/objects/37/d35c7ed39e4e16d0b579a5b995b7e30b0e9411 b/modules/git/tests/repos/repo6_merge/objects/37/d35c7ed39e4e16d0b579a5b995b7e30b0e9411 new file mode 100644 index 0000000000..83890b5d0b --- /dev/null +++ b/modules/git/tests/repos/repo6_merge/objects/37/d35c7ed39e4e16d0b579a5b995b7e30b0e9411 @@ -0,0 +1,2 @@ +xM +1 a=E$LAī'iRg~_u1N@mZ)g2Dj*_fs 4amnp>>!#1;p]xuyIwN4 \ No newline at end of file diff --git a/modules/git/tests/repos/repo6_merge/objects/38/ec3e0cdc88bde01014bda4a5dd9fc835f41439 b/modules/git/tests/repos/repo6_merge/objects/38/ec3e0cdc88bde01014bda4a5dd9fc835f41439 new file mode 100644 index 0000000000..582d98ce30 --- /dev/null +++ b/modules/git/tests/repos/repo6_merge/objects/38/ec3e0cdc88bde01014bda4a5dd9fc835f41439 @@ -0,0 +1,2 @@ +xM +0a9M2 U =yv/ 0:@$UѬ.[> lIsx 8'TRĤS,$x. ֚=n[נus!+9BGvfm ur> \ No newline at end of file diff --git a/modules/git/tests/repos/repo6_merge/objects/66/7e0fbc6bc02c2285d17f542e89b23c0fa5482b b/modules/git/tests/repos/repo6_merge/objects/66/7e0fbc6bc02c2285d17f542e89b23c0fa5482b new file mode 100644 index 0000000000..d7faff6bf2 Binary files /dev/null and b/modules/git/tests/repos/repo6_merge/objects/66/7e0fbc6bc02c2285d17f542e89b23c0fa5482b differ diff --git a/modules/git/tests/repos/repo6_merge/objects/9f/d90b1d524c0fea776ed5e6476da02ea1740597 b/modules/git/tests/repos/repo6_merge/objects/9f/d90b1d524c0fea776ed5e6476da02ea1740597 new file mode 100644 index 0000000000..8ac2814c0f Binary files /dev/null and b/modules/git/tests/repos/repo6_merge/objects/9f/d90b1d524c0fea776ed5e6476da02ea1740597 differ diff --git a/modules/git/tests/repos/repo6_merge/objects/ae/4b035e7c4afbc000576cee3f713ea0c2f1e1e2 b/modules/git/tests/repos/repo6_merge/objects/ae/4b035e7c4afbc000576cee3f713ea0c2f1e1e2 new file mode 100644 index 0000000000..c0f6b1334f --- /dev/null +++ b/modules/git/tests/repos/repo6_merge/objects/ae/4b035e7c4afbc000576cee3f713ea0c2f1e1e2 @@ -0,0 +1,5 @@ +xM +1 @a=E?i*mǑxO=x.lORZ80\[*%b +&q IȚ灝 +7ԋF쨜wcuzx?0 +1 :m6LS>& \ No newline at end of file diff --git a/modules/git/tests/repos/repo6_merge/objects/ba/2906d0666cf726c7eaadd2cd3db615dedfdf3a b/modules/git/tests/repos/repo6_merge/objects/ba/2906d0666cf726c7eaadd2cd3db615dedfdf3a new file mode 100644 index 0000000000..edef2a7f0c Binary files /dev/null and b/modules/git/tests/repos/repo6_merge/objects/ba/2906d0666cf726c7eaadd2cd3db615dedfdf3a differ diff --git a/modules/git/tests/repos/repo6_merge/objects/c1/a95c2eff8151c6d1437a0d5d3322a73ff38fb8 b/modules/git/tests/repos/repo6_merge/objects/c1/a95c2eff8151c6d1437a0d5d3322a73ff38fb8 new file mode 100644 index 0000000000..353d86ccdb Binary files /dev/null and b/modules/git/tests/repos/repo6_merge/objects/c1/a95c2eff8151c6d1437a0d5d3322a73ff38fb8 differ diff --git a/modules/git/tests/repos/repo6_merge/objects/cc/d1d4d594029e68c388ecef5aa3063fa1055831 b/modules/git/tests/repos/repo6_merge/objects/cc/d1d4d594029e68c388ecef5aa3063fa1055831 new file mode 100644 index 0000000000..5449d726cd Binary files /dev/null and b/modules/git/tests/repos/repo6_merge/objects/cc/d1d4d594029e68c388ecef5aa3063fa1055831 differ diff --git a/modules/git/tests/repos/repo6_merge/objects/cd/fc1aaf7a149151cb7bff639fafe05668d4bbd2 b/modules/git/tests/repos/repo6_merge/objects/cd/fc1aaf7a149151cb7bff639fafe05668d4bbd2 new file mode 100644 index 0000000000..030eb980f6 Binary files /dev/null and b/modules/git/tests/repos/repo6_merge/objects/cd/fc1aaf7a149151cb7bff639fafe05668d4bbd2 differ diff --git a/modules/git/tests/repos/repo6_merge/objects/d1/792641396ff7630d35fbb0b74b86b0c71bca77 b/modules/git/tests/repos/repo6_merge/objects/d1/792641396ff7630d35fbb0b74b86b0c71bca77 new file mode 100644 index 0000000000..867e4c0b86 Binary files /dev/null and b/modules/git/tests/repos/repo6_merge/objects/d1/792641396ff7630d35fbb0b74b86b0c71bca77 differ diff --git a/modules/git/tests/repos/repo6_merge/objects/ec/d11d8da0f25eaa99f64a37a82da98685f381e2 b/modules/git/tests/repos/repo6_merge/objects/ec/d11d8da0f25eaa99f64a37a82da98685f381e2 new file mode 100644 index 0000000000..52d300cdf1 Binary files /dev/null and b/modules/git/tests/repos/repo6_merge/objects/ec/d11d8da0f25eaa99f64a37a82da98685f381e2 differ diff --git a/modules/git/tests/repos/repo6_merge/objects/fa/49b077972391ad58037050f2a75f74e3671e92 b/modules/git/tests/repos/repo6_merge/objects/fa/49b077972391ad58037050f2a75f74e3671e92 new file mode 100644 index 0000000000..112998d425 Binary files /dev/null and b/modules/git/tests/repos/repo6_merge/objects/fa/49b077972391ad58037050f2a75f74e3671e92 differ diff --git a/modules/git/tests/repos/repo6_merge/refs/heads/main b/modules/git/tests/repos/repo6_merge/refs/heads/main new file mode 100644 index 0000000000..adf9e86717 --- /dev/null +++ b/modules/git/tests/repos/repo6_merge/refs/heads/main @@ -0,0 +1 @@ +022f4ce6214973e018f02bf363bf8a2e3691f699 diff --git a/modules/git/tests/repos/repo6_merge/refs/heads/merge/add_file b/modules/git/tests/repos/repo6_merge/refs/heads/merge/add_file new file mode 100644 index 0000000000..035ad11863 --- /dev/null +++ b/modules/git/tests/repos/repo6_merge/refs/heads/merge/add_file @@ -0,0 +1 @@ +ae4b035e7c4afbc000576cee3f713ea0c2f1e1e2 diff --git a/modules/git/tests/repos/repo6_merge/refs/heads/merge/modify_file b/modules/git/tests/repos/repo6_merge/refs/heads/merge/modify_file new file mode 100644 index 0000000000..f0d5105f54 --- /dev/null +++ b/modules/git/tests/repos/repo6_merge/refs/heads/merge/modify_file @@ -0,0 +1 @@ +d1792641396ff7630d35fbb0b74b86b0c71bca77 diff --git a/modules/git/tests/repos/repo6_merge/refs/heads/merge/remove_file b/modules/git/tests/repos/repo6_merge/refs/heads/merge/remove_file new file mode 100644 index 0000000000..ada3c8b3f4 --- /dev/null +++ b/modules/git/tests/repos/repo6_merge/refs/heads/merge/remove_file @@ -0,0 +1 @@ +38ec3e0cdc88bde01014bda4a5dd9fc835f41439 diff --git a/modules/log/event_writer_base.go b/modules/log/event_writer_base.go index b8b13e4bc4..c327c48ca2 100644 --- a/modules/log/event_writer_base.go +++ b/modules/log/event_writer_base.go @@ -90,7 +90,7 @@ func (b *EventWriterBaseImpl) Run(ctx context.Context) { if exprRegexp != nil { fileLineCaller := fmt.Sprintf("%s:%d:%s", event.Origin.Filename, event.Origin.Line, event.Origin.Caller) - matched := exprRegexp.Match([]byte(fileLineCaller)) || exprRegexp.Match([]byte(event.Origin.MsgSimpleText)) + matched := exprRegexp.MatchString(fileLineCaller) || exprRegexp.MatchString(event.Origin.MsgSimpleText) if !matched { continue } diff --git a/modules/notification/notification.go b/modules/notification/notification.go index 99e1a06ebd..50b2694d9c 100644 --- a/modules/notification/notification.go +++ b/modules/notification/notification.go @@ -16,7 +16,6 @@ import ( "code.gitea.io/gitea/modules/notification/base" "code.gitea.io/gitea/modules/notification/indexer" "code.gitea.io/gitea/modules/notification/mail" - "code.gitea.io/gitea/modules/notification/mirror" "code.gitea.io/gitea/modules/notification/ui" "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/setting" @@ -38,7 +37,6 @@ func NewContext() { } RegisterNotifier(indexer.NewNotifier()) RegisterNotifier(action.NewNotifier()) - RegisterNotifier(mirror.NewNotifier()) } // NotifyNewWikiPage notifies creating new wiki pages to notifiers diff --git a/modules/packages/conda/metadata_test.go b/modules/packages/conda/metadata_test.go index 2038ca370c..2bb114f030 100644 --- a/modules/packages/conda/metadata_test.go +++ b/modules/packages/conda/metadata_test.go @@ -19,9 +19,9 @@ const ( packageName = "gitea" packageVersion = "1.0.1" description = "Package Description" - projectURL = "https://gitea.io" - repositoryURL = "https://gitea.io/gitea/gitea" - documentationURL = "https://docs.gitea.io" + projectURL = "https://gitea.com" + repositoryURL = "https://gitea.com/gitea/gitea" + documentationURL = "https://docs.gitea.com" ) func TestParsePackage(t *testing.T) { diff --git a/modules/packages/container/metadata_test.go b/modules/packages/container/metadata_test.go index 195b3982cb..665499b2e6 100644 --- a/modules/packages/container/metadata_test.go +++ b/modules/packages/container/metadata_test.go @@ -17,9 +17,9 @@ func TestParseImageConfig(t *testing.T) { description := "Image Description" author := "Gitea" license := "MIT" - projectURL := "https://gitea.io" + projectURL := "https://gitea.com" repositoryURL := "https://gitea.com/gitea" - documentationURL := "https://docs.gitea.io" + documentationURL := "https://docs.gitea.com" configOCI := `{"config": {"labels": {"` + labelAuthors + `": "` + author + `", "` + labelLicenses + `": "` + license + `", "` + labelURL + `": "` + projectURL + `", "` + labelSource + `": "` + repositoryURL + `", "` + labelDocumentation + `": "` + documentationURL + `", "` + labelDescription + `": "` + description + `"}}, "history": [{"created_by": "do it 1"}, {"created_by": "dummy #(nop) do it 2"}]}` diff --git a/modules/packages/pub/metadata_test.go b/modules/packages/pub/metadata_test.go index 4c0c3f0ec6..8f9126e0c9 100644 --- a/modules/packages/pub/metadata_test.go +++ b/modules/packages/pub/metadata_test.go @@ -18,9 +18,9 @@ const ( packageName = "gitea" packageVersion = "1.0.1" description = "Package Description" - projectURL = "https://gitea.io" - repositoryURL = "https://gitea.io/gitea/gitea" - documentationURL = "https://docs.gitea.io" + projectURL = "https://gitea.com" + repositoryURL = "https://gitea.com/gitea/gitea" + documentationURL = "https://docs.gitea.com" ) const pubspecContent = `name: ` + packageName + ` diff --git a/modules/repository/branch.go b/modules/repository/branch.go index bffadd62f4..8daceecb44 100644 --- a/modules/repository/branch.go +++ b/modules/repository/branch.go @@ -106,15 +106,15 @@ func SyncRepoBranchesWithRepo(ctx context.Context, repo *repo_model.Repository, return int64(len(allBranches)), nil } - if err := db.WithTx(ctx, func(subCtx context.Context) error { + if err := db.WithTx(ctx, func(ctx context.Context) error { if len(toAdd) > 0 { - if err := git_model.AddBranches(subCtx, toAdd); err != nil { + if err := git_model.AddBranches(ctx, toAdd); err != nil { return err } } for _, b := range toUpdate { - if _, err := db.GetEngine(subCtx).ID(b.ID). + if _, err := db.GetEngine(ctx).ID(b.ID). Cols("commit_id, commit_message, pusher_id, commit_time, is_deleted"). Update(b); err != nil { return err @@ -122,7 +122,7 @@ func SyncRepoBranchesWithRepo(ctx context.Context, repo *repo_model.Repository, } if len(toRemove) > 0 { - if err := git_model.DeleteBranches(subCtx, repo.ID, doerID, toRemove); err != nil { + if err := git_model.DeleteBranches(ctx, repo.ID, doerID, toRemove); err != nil { return err } } diff --git a/modules/setting/service.go b/modules/setting/service.go index 595ea6528f..74a7e90f7c 100644 --- a/modules/setting/service.go +++ b/modules/setting/service.go @@ -223,7 +223,7 @@ func loadServiceFrom(rootCfg ConfigProvider) { Service.UserDeleteWithCommentsMaxTime = sec.Key("USER_DELETE_WITH_COMMENTS_MAX_TIME").MustDuration(0) sec.Key("VALID_SITE_URL_SCHEMES").MustString("http,https") Service.ValidSiteURLSchemes = sec.Key("VALID_SITE_URL_SCHEMES").Strings(",") - schemes := make([]string, len(Service.ValidSiteURLSchemes)) + schemes := make([]string, 0, len(Service.ValidSiteURLSchemes)) for _, scheme := range Service.ValidSiteURLSchemes { scheme = strings.ToLower(strings.TrimSpace(scheme)) if scheme != "" { diff --git a/modules/setting/ssh.go b/modules/setting/ssh.go index bbb7f5ab6c..ea387e521f 100644 --- a/modules/setting/ssh.go +++ b/modules/setting/ssh.go @@ -60,7 +60,7 @@ var SSH = struct { ServerMACs: []string{"hmac-sha2-256-etm@openssh.com", "hmac-sha2-256", "hmac-sha1"}, KeygenPath: "", MinimumKeySizeCheck: true, - MinimumKeySizes: map[string]int{"ed25519": 256, "ed25519-sk": 256, "ecdsa": 256, "ecdsa-sk": 256, "rsa": 2047}, + MinimumKeySizes: map[string]int{"ed25519": 256, "ed25519-sk": 256, "ecdsa": 256, "ecdsa-sk": 256, "rsa": 3071}, ServerHostKeys: []string{"ssh/gitea.rsa", "ssh/gogs.rsa"}, AuthorizedKeysCommandTemplate: "{{.AppPath}} --config={{.CustomConf}} serv key-{{.Key.ID}}", PerWriteTimeout: PerWriteTimeout, diff --git a/modules/structs/release.go b/modules/structs/release.go index 3fe40389b1..c7378645c2 100644 --- a/modules/structs/release.go +++ b/modules/structs/release.go @@ -18,6 +18,7 @@ type Release struct { HTMLURL string `json:"html_url"` TarURL string `json:"tarball_url"` ZipURL string `json:"zipball_url"` + UploadURL string `json:"upload_url"` IsDraft bool `json:"draft"` IsPrerelease bool `json:"prerelease"` // swagger:strfmt date-time diff --git a/modules/structs/secret.go b/modules/structs/secret.go index c707eb2278..a0673ca08c 100644 --- a/modules/structs/secret.go +++ b/modules/structs/secret.go @@ -14,14 +14,11 @@ type Secret struct { Created time.Time `json:"created_at"` } -// CreateSecretOption options when creating secret +// CreateOrUpdateSecretOption options when creating or updating secret // swagger:model -type CreateSecretOption struct { - // Name of the secret to create +type CreateOrUpdateSecretOption struct { + // Data of the secret to update // // required: true - // unique: true - Name string `json:"name" binding:"Required;AlphaDashDot;MaxSize(100)"` - // Data of the secret to create Data string `json:"data" binding:"Required"` } diff --git a/modules/templates/util_dict.go b/modules/templates/util_dict.go index 79c307b68d..8d6376b522 100644 --- a/modules/templates/util_dict.go +++ b/modules/templates/util_dict.go @@ -9,6 +9,7 @@ import ( "html/template" "reflect" + "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/setting" ) @@ -51,7 +52,7 @@ func dict(args ...any) (map[string]any, error) { return m, nil } -func dumpVarMarshalable(v any, dumped map[uintptr]bool) (ret any, ok bool) { +func dumpVarMarshalable(v any, dumped container.Set[uintptr]) (ret any, ok bool) { if v == nil { return nil, true } @@ -61,11 +62,10 @@ func dumpVarMarshalable(v any, dumped map[uintptr]bool) (ret any, ok bool) { } if e.CanAddr() { addr := e.UnsafeAddr() - if dumped[addr] { + if !dumped.Add(addr) { return "[dumped]", false } - dumped[addr] = true - defer delete(dumped, addr) + defer dumped.Remove(addr) } switch e.Kind() { case reflect.Bool, reflect.String, @@ -107,7 +107,7 @@ func dumpVar(v any) template.HTML { if setting.IsProd { return "
dumpVar: only available in dev mode
" } - m, ok := dumpVarMarshalable(v, map[uintptr]bool{}) + m, ok := dumpVarMarshalable(v, make(container.Set[uintptr])) var dumpStr string jsonBytes, err := json.MarshalIndent(m, "", " ") if err != nil { diff --git a/modules/test/context_tests.go b/modules/test/context_tests.go index 92d7f8b22b..83e6117bcf 100644 --- a/modules/test/context_tests.go +++ b/modules/test/context_tests.go @@ -45,14 +45,12 @@ func MockContext(t *testing.T, reqPath string) (*context.Context, *httptest.Resp resp := httptest.NewRecorder() req := mockRequest(t, reqPath) base, baseCleanUp := context.NewBaseContext(resp, req) + _ = baseCleanUp // during test, it doesn't need to do clean up. TODO: this can be improved later base.Data = middleware.GetContextData(req.Context()) base.Locale = &translation.MockLocale{} - ctx := &context.Context{ - Base: base, - Render: &mockRender{}, - Flash: &middleware.Flash{Values: url.Values{}}, - } - _ = baseCleanUp // during test, it doesn't need to do clean up. TODO: this can be improved later + + ctx := context.NewWebContext(base, &MockRender{}, nil) + ctx.Flash = &middleware.Flash{Values: url.Values{}} chiCtx := chi.NewRouteContext() ctx.Base.AppendContextValue(chi.RouteCtxKey, chiCtx) @@ -148,13 +146,13 @@ func LoadGitRepo(t *testing.T, ctx *context.Context) { assert.NoError(t, err) } -type mockRender struct{} +type MockRender struct{} -func (tr *mockRender) TemplateLookup(tmpl string, _ gocontext.Context) (templates.TemplateExecutor, error) { +func (tr *MockRender) TemplateLookup(tmpl string, _ gocontext.Context) (templates.TemplateExecutor, error) { return nil, nil } -func (tr *mockRender) HTML(w io.Writer, status int, _ string, _ any, _ gocontext.Context) error { +func (tr *MockRender) HTML(w io.Writer, status int, _ string, _ any, _ gocontext.Context) error { if resp, ok := w.(http.ResponseWriter); ok { resp.WriteHeader(status) } diff --git a/options/license/GNU-compiler-exception b/options/license/GNU-compiler-exception new file mode 100644 index 0000000000..684833ffb4 --- /dev/null +++ b/options/license/GNU-compiler-exception @@ -0,0 +1,6 @@ +As a special exception, if you link this library with files +compiled with a GNU compiler to produce an executable, this +does not cause the resulting executable to be covered by +the GNU General Public License. This exception does not +however invalidate any other reasons why the executable +file might be covered by the GNU General Public License. diff --git a/options/license/MIT-testregex b/options/license/MIT-testregex new file mode 100644 index 0000000000..321b4bf9bb --- /dev/null +++ b/options/license/MIT-testregex @@ -0,0 +1,17 @@ + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of THIS SOFTWARE FILE (the "Software"), to deal in the Software + * without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following disclaimer: + * + * THIS SOFTWARE IS PROVIDED BY AT&T ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL AT&T BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/options/license/SANE-exception b/options/license/SANE-exception new file mode 100644 index 0000000000..198a8c67cc --- /dev/null +++ b/options/license/SANE-exception @@ -0,0 +1,20 @@ +As a special exception, the authors of sane-airscan give permission for +additional uses of the libraries contained in this release of sane-airscan. + +The exception is that, if you link a sane-airscan library with other files +to produce an executable, this does not by itself cause the +resulting executable to be covered by the GNU General Public +License. Your use of that executable is in no way restricted on +account of linking the sane-airscan library code into it. + +This exception does not, however, invalidate any other reasons why +the executable file might be covered by the GNU General Public +License. + +If you submit changes to sane-airscan to the maintainers to be included in +a subsequent release, you agree by submitting the changes that +those changes may be distributed with this exception intact. + +If you write modifications of your own for sane-airscan, it is your choice +whether to permit this exception to apply to your modifications. +If you do not wish that, delete this exception notice. diff --git a/options/license/Soundex b/options/license/Soundex index b745f29db5..16c3fa7664 100644 --- a/options/license/Soundex +++ b/options/license/Soundex @@ -1,7 +1,9 @@ -# (c) Copyright 1998-2007 by Mark Mielke # # Freedom to use -these sources for whatever you want, as long as credit # is -given where credit is due, is hereby granted. You may make -modifications # where you see fit but leave this copyright -somewhere visible. As well, try # to initial any changes you -make so that if I like the changes I can # incorporate them -into later versions. # # - Mark Mielke +(c) Copyright 1998-2007 by Mark Mielke + +Freedom to use these sources for whatever you want, as long as credit +is given where credit is due, is hereby granted. You may make modifications +where you see fit but leave this copyright somewhere visible. As well, try +to initial any changes you make so that if I like the changes I can +incorporate them into later versions. + + - Mark Mielke diff --git a/options/license/TTYP0 b/options/license/TTYP0 new file mode 100644 index 0000000000..3df2e4c4e9 --- /dev/null +++ b/options/license/TTYP0 @@ -0,0 +1,29 @@ +THE TTYP0 LICENSE + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this font software and associated files (the "Software"), +to deal in the Software without restriction, including without +limitation the rights to use, copy, modify, merge, publish, +distribute, embed, sublicense, and/or sell copies of the Software, +and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +(1) The above copyright notice, this permission notice, and the + disclaimer below shall be included in all copies or substantial + portions of the Software. + +(2) If the design of any glyphs in the fonts that are contained in the + Software or generated during the installation process is modified + or if additional glyphs are added to the fonts, the fonts + must be renamed. The new names may not contain the word "UW", + irrespective of capitalisation; the new names may contain the word + "ttyp0", irrespective of capitalisation, only if preceded by a + foundry name different from "UW". + +THE 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 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, 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 IN THE SOFTWARE. diff --git a/options/license/libpng-2.0 b/options/license/libpng-2.0 index 1cc8b0deb3..72ae6e44ef 100644 --- a/options/license/libpng-2.0 +++ b/options/license/libpng-2.0 @@ -10,7 +10,7 @@ PNG Reference Library License version 2 The software is supplied "as is", without warranty of any kind, express or implied, including, without limitation, the warranties of merchantability, fitness for a particular purpose, title, and -non-infringement. In no even shall the Copyright owners, or +non-infringement. In no event shall the Copyright owners, or anyone distributing the software, be liable for any damages or other liability, whether in contract, tort or otherwise, arising from, out of, or in connection with the software, or the use or diff --git a/options/license/stunnel-exception b/options/license/stunnel-exception new file mode 100644 index 0000000000..5e38d00a0f --- /dev/null +++ b/options/license/stunnel-exception @@ -0,0 +1,5 @@ +Linking stunnel statically or dynamically with other modules is making a combined work based on stunnel. Thus, the terms and conditions of the GNU General Public License cover the whole combination. + +In addition, as a special exception, the copyright holder of stunnel gives you permission to combine stunnel with free software programs or libraries that are released under the GNU LGPL and with code included in the standard release of OpenSSL under the OpenSSL License (or modified versions of such code, with unchanged license). You may copy and distribute such a system following the terms of the GNU GPL for stunnel and the licenses of the other code concerned. + +Note that people who make modified versions of stunnel are not obligated to grant this special exception for their modified versions; it is their choice whether to do so. The GNU General Public License gives permission to release a modified version without this exception; this exception also makes it possible to release a modified version which carries forward this exception. diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 587a2b14bc..f8e068fe19 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -181,7 +181,7 @@ network_error = Network error [startpage] app_desc = A painless, self-hosted Git service install = Easy to install -install_desc = Simply run the binary for your platform, ship it with Docker, or get it packaged. +install_desc = Simply run the binary for your platform, ship it with Docker, or get it packaged. platform = Cross-platform platform_desc = Gitea runs anywhere Go can compile for: Windows, macOS, Linux, ARM, etc. Choose the one you love! lightweight = Lightweight @@ -317,6 +317,7 @@ filter_by_team_repositories = Filter by team repositories feed_of = Feed of "%s" show_archived = Archived +archived = Archived show_both_archived_unarchived = Showing both archived and unarchived show_only_archived = Showing only archived show_only_unarchived = Showing only unarchived @@ -598,7 +599,6 @@ overview = Overview following = Following follow = Follow unfollow = Unfollow -heatmap.loading = Loading Heatmap… user_bio = Biography disabled_public_activity = This user has disabled the public visibility of the activity. email_visibility.limited = Your email address is visible to all authenticated users @@ -970,6 +970,7 @@ trust_model_helper_collaborator_committer = Collaborator+Committer: Trust signat trust_model_helper_default = Default: Use the default trust model for this installation create_repo = Create Repository default_branch = Default Branch +default_branch_label = default default_branch_helper = The default branch is the base branch for pull requests and code commits. mirror_prune = Prune mirror_prune_desc = Remove obsolete remote-tracking references @@ -1480,9 +1481,18 @@ issues.ref_reopening_from = `referenced a pull request %[4]s tha issues.ref_closed_from = `closed this issue %[4]s %[2]s` issues.ref_reopened_from = `reopened this issue %[4]s %[2]s` issues.ref_from = `from %[1]s` -issues.poster = Poster -issues.collaborator = Collaborator -issues.owner = Owner +issues.author = Author +issues.author_helper = This user is the author. +issues.role.owner = Owner +issues.role.owner_helper = This user is the owner of this repository. +issues.role.member = Member +issues.role.member_helper = This user is a member of the organization owning this repository. +issues.role.collaborator = Collaborator +issues.role.collaborator_helper = This user has been invited to collaborate on the repository. +issues.role.first_time_contributor = First-time contributor +issues.role.first_time_contributor_helper = This is the first contribution of this user to the repository. +issues.role.contributor = Contributor +issues.role.contributor_helper = This user has previously committed to the repository. issues.re_request_review=Re-request review issues.is_stale = There have been changes to this PR since this review issues.remove_request_review=Remove review request @@ -2319,7 +2329,7 @@ settings.tags.protection.allowed.teams = Allowed teams settings.tags.protection.allowed.noone = No One settings.tags.protection.create = Protect Tag settings.tags.protection.none = There are no protected tags. -settings.tags.protection.pattern.description = You can use a single name or a glob pattern or regular expression to match multiple tags. Read more in the protected tags guide. +settings.tags.protection.pattern.description = You can use a single name or a glob pattern or regular expression to match multiple tags. Read more in the protected tags guide. settings.bot_token = Bot Token settings.chat_id = Chat ID settings.thread_id = Thread ID @@ -2756,6 +2766,7 @@ dashboard.gc_lfs = Garbage collect LFS meta objects dashboard.stop_zombie_tasks = Stop zombie tasks dashboard.stop_endless_tasks = Stop endless tasks dashboard.cancel_abandoned_jobs = Cancel abandoned jobs +dashboard.start_schedule_tasks = Start schedule tasks dashboard.sync_branch.started = Branches Sync started dashboard.rebuild_issue_indexer = Rebuild issue indexer @@ -2859,12 +2870,12 @@ packages.size = Size packages.published = Published defaulthooks = Default Webhooks -defaulthooks.desc = Webhooks automatically make HTTP POST requests to a server when certain Gitea events trigger. Webhooks defined here are defaults and will be copied into all new repositories. Read more in the webhooks guide. +defaulthooks.desc = Webhooks automatically make HTTP POST requests to a server when certain Gitea events trigger. Webhooks defined here are defaults and will be copied into all new repositories. Read more in the webhooks guide. defaulthooks.add_webhook = Add Default Webhook defaulthooks.update_webhook = Update Default Webhook systemhooks = System Webhooks -systemhooks.desc = Webhooks automatically make HTTP POST requests to a server when certain Gitea events trigger. Webhooks defined here will act on all repositories on the system, so please consider any performance implications this may have. Read more in the webhooks guide. +systemhooks.desc = Webhooks automatically make HTTP POST requests to a server when certain Gitea events trigger. Webhooks defined here will act on all repositories on the system, so please consider any performance implications this may have. Read more in the webhooks guide. systemhooks.add_webhook = Add System Webhook systemhooks.update_webhook = Update System Webhook @@ -2969,7 +2980,7 @@ auths.tip.google_plus = Obtain OAuth2 client credentials from the Google API con auths.tip.openid_connect = Use the OpenID Connect Discovery URL (/.well-known/openid-configuration) to specify the endpoints auths.tip.twitter = Go to https://dev.twitter.com/apps, create an application and ensure that the “Allow this application to be used to Sign in with Twitter” option is enabled auths.tip.discord = Register a new application on https://discordapp.com/developers/applications/me -auths.tip.gitea = Register a new OAuth2 application. Guide can be found at https://docs.gitea.io/en-us/oauth2-provider/ +auths.tip.gitea = Register a new OAuth2 application. Guide can be found at https://docs.gitea.com/development/oauth2-provider auths.tip.yandex = Create a new application at https://oauth.yandex.com/client/new. Select following permissions from the "Yandex.Passport API" section: "Access to email address", "Access to user avatar" and "Access to username, first name and surname, gender" auths.tip.mastodon = Input a custom instance URL for the mastodon instance you want to authenticate with (or use the default one) auths.edit = Edit Authentication Source diff --git a/options/locale/locale_ja-JP.ini b/options/locale/locale_ja-JP.ini index 864dc52e2d..eef01aced1 100644 --- a/options/locale/locale_ja-JP.ini +++ b/options/locale/locale_ja-JP.ini @@ -167,6 +167,7 @@ string.desc=Z - A [error] occurred=エラーが発生しました. +report_message=Gitea のバグが疑われる場合は、GitHubでIssueを検索して、見つからなければ新しいIssueを作成してください。 missing_csrf=不正なリクエスト: CSRFトークンが不明です invalid_csrf=不正なリクエスト: CSRFトークンが無効です not_found=ターゲットが見つかりませんでした。 @@ -353,6 +354,7 @@ remember_me=このデバイスで自動サインイン forgot_password_title=パスワードを忘れた forgot_password=パスワードをお忘れですか? sign_up_now=アカウントが必要ですか? 今すぐ登録しましょう。 +sign_up_successful=アカウントは無事に作成されました。ようこそ! confirmation_mail_sent_prompt=%s に確認メールを送信しました。 %s以内に受信トレイを確認し、登録手続きを完了してください。 must_change_password=パスワードの更新 allow_password_change=ユーザーはパスワードの変更が必要 (推奨) @@ -360,6 +362,7 @@ reset_password_mail_sent_prompt=%s に確認メールを送信しまし active_your_account=アカウントの有効化 account_activated=アカウントがアクティベートされました prohibit_login=サインイン禁止 +prohibit_login_desc=あなたのアカウントはサインインを禁止されています。 サイト管理者にお問い合わせください。 resent_limit_prompt=少し前に、あなたからアクティベーションメールが要求されています。 3分待ったのち、もう一度試してください。 has_unconfirmed_mail=こんにちは %s さん、あなたのメール アドレス (%s) は確認がとれていません。 確認メールを受け取っていない場合や、改めて送信したい場合は、下のボタンをクリックしてください。 resend_mail=アクティベーションメールを再送信するにはここをクリック @@ -369,6 +372,7 @@ reset_password=アカウントの回復 invalid_code=確認コードが無効か期限切れです。 invalid_password=アカウントの作成に使用されたパスワードと一致しません。 reset_password_helper=アカウント回復 +reset_password_wrong_user=あなたは %s でサインイン中ですが、アカウント回復のリンクは %s のものです。 password_too_short=%d文字未満のパスワードは設定できません。 non_local_account=ローカルユーザーでない場合はGiteaのWebインターフェースからパスワードを変更することはできません。 verify=確認 @@ -393,6 +397,7 @@ openid_connect_title=既存のアカウントに接続 openid_connect_desc=選択したOpenID URIは未登録です。 ここで新しいアカウントと関連付けます。 openid_register_title=アカウント新規作成 openid_register_desc=選択したOpenID URIは未登録です。 ここで新しいアカウントと関連付けます。 +openid_signin_desc=OpenID URIを入力します。例: alice.openid.example.org または https://openid.example.org/alice disable_forgot_password_mail=メール送信設定が無いためアカウントの回復は無効になっています。 サイト管理者にお問い合わせください。 disable_forgot_password_mail_admin=アカウントの回復はメール送信が設定済みの場合だけ使用できます。 アカウントの回復を有効にするにはメール送信を設定してください。 email_domain_blacklisted=あなたのメールアドレスでは登録することはできません。 @@ -403,6 +408,7 @@ authorize_application_description=アクセスを許可すると、このアプ authorize_title=`"%s"にあなたのアカウントへのアクセスを許可しますか?` authorization_failed=認可失敗 sspi_auth_failed=SSPI認証に失敗しました +password_pwned=あなたが選択したパスワードは、過去の情報漏洩事件で流出した盗まれたパスワードのリストに含まれています。 別のパスワードでもう一度試してください。 また他の登録でもこのパスワードからの変更を検討してください。 password_pwned_err=HaveIBeenPwnedへのリクエストを完了できませんでした [mail] @@ -417,6 +423,7 @@ activate_account.text_1=こんにちは、%[1]s さん。 %[2]s へのご activate_account.text_2=あなたのアカウントを有効化するため、%s以内に次のリンクをクリックしてください: activate_email=メール アドレスを確認します +activate_email.title=%s さん、メールアドレス確認をお願いします activate_email.text=あなたのメールアドレスを確認するため、%s以内に次のリンクをクリックしてください: register_notify=Giteaへようこそ @@ -608,9 +615,12 @@ delete=アカウントを削除 twofa=2要素認証 account_link=連携アカウント organization=組織 +uid=UID webauthn=セキュリティキー public_profile=公開プロフィール +biography_placeholder=自己紹介してください!(Markdownを使うことができます) +profile_desc=あなたのプロフィールが他のユーザーにどのように表示されるかを制御します。あなたのプライマリメールアドレスは、通知、パスワードの回復、WebベースのGit操作に使用されます。 password_username_disabled=非ローカルユーザーのユーザー名は変更できません。詳細はサイト管理者にお問い合わせください。 full_name=フルネーム website=Webサイト @@ -662,6 +672,7 @@ update_user_avatar_success=ユーザーのアバターを更新しました。 change_password=パスワードを更新 old_password=現在のパスワード new_password=新しいパスワード +retype_new_password=新しいパスワードの確認 password_incorrect=現在のパスワードが正しくありません。 change_password_success=パスワードを更新しました。 今後は新しいパスワードを使ってサインインしてください。 password_change_disabled=ローカルユーザーでない場合は、GiteaのWebインターフェースからパスワードを変更することはできません。 @@ -670,6 +681,7 @@ emails=メールアドレス manage_emails=メールアドレスの管理 manage_themes=デフォルトのテーマを選択 manage_openid=OpenIDアドレスの管理 +email_desc=プライマリメールアドレスは、通知、パスワードの回復、さらにメールアドレスを隠さない場合は、WebベースのGit操作にも使用されます。 theme_desc=この設定がサイト全体のデフォルトのテーマとなります。 primary=プライマリー activated=アクティベート済み @@ -695,6 +707,7 @@ add_email_success=新しいメールアドレスを追加しました。 email_preference_set_success=メール設定を保存しました。 add_openid_success=新しいOpenIDアドレスを追加しました。 keep_email_private=メールアドレスを隠す +keep_email_private_popup=これによりプロフィールでメールアドレスが隠され、Webインターフェースでのプルリクエスト作成やファイル編集でもメールアドレスが隠されます。 プッシュ済みのコミットは変更されません。 openid_desc=OpenIDを使うと外部プロバイダーに認証を委任することができます。 manage_ssh_keys=SSHキーの管理 @@ -773,7 +786,9 @@ ssh_disabled=SSHは無効です ssh_signonly=SSHは現在無効になっているため、これらのキーはコミット署名の検証にのみ使用されます。 ssh_externally_managed=このユーザー用に外部で管理されているSSHキーです manage_social=関連付けられているソーシャルアカウントを管理 +social_desc=これらのソーシャルアカウントで、あなたのアカウントにサインインできます。 すべて自分が知っているものであることを確認してください。 unbind=連携の解除 +unbind_success=ソーシャルアカウントの登録を削除しました。 manage_access_token=アクセストークンの管理 generate_new_token=新しいトークンを生成 @@ -805,6 +820,8 @@ remove_oauth2_application_desc=OAuth2アプリケーションを削除すると remove_oauth2_application_success=アプリケーションを削除しました。 create_oauth2_application=新しいOAuth2アプリケーションの作成 create_oauth2_application_button=アプリケーション作成 +create_oauth2_application_success=新しいOAuth2アプリケーションを作成しました。 +update_oauth2_application_success=OAuth2アプリケーションを更新しました。 oauth2_application_name=アプリケーション名 oauth2_confidential_client=コンフィデンシャルクライアント。 ウェブアプリのように秘密情報を機密にできるアプリの場合に選択します。 デスクトップアプリやモバイルアプリなどのネイティブアプリには選択しないでください。 oauth2_redirect_uris=リダイレクトURI (複数可)。 URIごとに改行してください。 @@ -813,19 +830,24 @@ oauth2_client_id=クライアントID oauth2_client_secret=クライアント シークレット oauth2_regenerate_secret=シークレットを再生成 oauth2_regenerate_secret_hint=シークレットを紛失? +oauth2_client_secret_hint=このページから移動したりページを更新すると、二度とこのシークレットは表示されません。 シークレットを保存したことを確認してください。 oauth2_application_edit=編集 oauth2_application_create_description=OAuth2アプリケーションで、サードパーティアプリケーションがこのインスタンス上のユーザーアカウントにアクセスできるようになります。 +oauth2_application_remove_description=OAuth2アプリケーションを削除すると、このインスタンス上の許可されたユーザーアカウントへのアクセスができなくなります。 続行しますか? authorized_oauth2_applications=許可済みOAuth2アプリケーション +authorized_oauth2_applications_description=これらのサードパーティ アプリケーションに、あなたのGiteaアカウントへのアクセスを許可しています。 不要になったアプリケーションはアクセス権を取り消すようにしてください。 revoke_key=取り消し revoke_oauth2_grant=アクセス権の取り消し revoke_oauth2_grant_description=このサードパーティ アプリケーションのアクセス権を取り消し、アプリケーションがあなたのデータへアクセスすることを防ぎます。 続行しますか? +revoke_oauth2_grant_success=アクセス権を取り消しました。 twofa_desc=2要素認証はアカウントのセキュリティを強化します。 twofa_is_enrolled=このアカウントは2要素認証が有効になっています。 twofa_not_enrolled=このアカウントは2要素認証が設定されていません。 twofa_disable=2要素認証を無効にする twofa_scratch_token_regenerate=スクラッチトークンを再生成 +twofa_scratch_token_regenerated=あなたのスクラッチトークンは %s になりました。 安全な場所に保管してください。 二度と表示されません。 twofa_enroll=2要素認証の開始 twofa_disable_note=2要素認証は必要に応じて無効にできます。 twofa_disable_desc=2要素認証を無効にするとアカウントのセキュリティが低下します。 続行しますか? @@ -1871,6 +1893,7 @@ settings.mirror_settings.last_update=最終更新 settings.mirror_settings.push_mirror.none=プッシュミラーは設定されていません settings.mirror_settings.push_mirror.remote_url=リモートGitリポジトリのURL settings.mirror_settings.push_mirror.add=プッシュミラーを追加 +settings.mirror_settings.push_mirror.edit_sync_time=ミラー同期の間隔を編集 settings.sync_mirror=今すぐ同期 settings.mirror_sync_in_progress=ミラー同期を実行しています。 しばらくあとでまた確認してください。 diff --git a/options/locale/locale_tr-TR.ini b/options/locale/locale_tr-TR.ini index 5a5838546c..d0250b5b34 100644 --- a/options/locale/locale_tr-TR.ini +++ b/options/locale/locale_tr-TR.ini @@ -4,6 +4,7 @@ explore=Keşfet help=Yardım logo=Logo sign_in=Giriş Yap +sign_in_with_provider=%s ile oturum aç sign_in_or=veya sign_out=Çıkış Yap sign_up=Kaydol @@ -79,6 +80,7 @@ milestones=Kilometre Taşları ok=Tamam cancel=İptal +retry=Yeniden Dene rerun=Yeniden çalıştır rerun_all=Tüm görevleri yeniden çalıştır save=Kaydet @@ -91,6 +93,7 @@ edit=Düzenle enabled=Aktifleştirilmiş disabled=Devre Dışı +locked=Kilitli copy=Kopyala copy_url=URL'yi kopyala @@ -128,7 +131,9 @@ concept_user_organization=Organizasyon show_timestamps=Zaman damgalarını göster show_log_seconds=Saniyeleri göster show_full_screen=Tam ekran göster +download_logs=Günlükleri indir +confirm_delete_selected=Tüm seçili öğeleri gerçekten silmek istiyor musunuz? name=İsim value=Değer @@ -167,6 +172,7 @@ string.desc=Z - A [error] occurred=Bir hata oluştu +report_message=Bunun bir Gitea hatası olduğunu düşünüyorsanız, lütfen GitHub sayfasında sorunu arayın veya gerekiyorsa yeni bir sorun oluşturun. missing_csrf=Hatalı İstek: CSRF anahtarı yok invalid_csrf=Hatalı İstek: geçersiz CSRF erişim anahtarı not_found=Hedef bulunamadı. @@ -220,6 +226,7 @@ repo_path_helper=Tüm uzak Git depoları bu dizine kaydedilecektir. lfs_path=Git LFS Kök Yolu lfs_path_helper=Git LFS tarafından izlenen dosyalar bu dizinde saklanacaktır. LFS'yi devre dışı bırakmak için boş bırakın. run_user=Şu Kullanıcı Olarak Çalıştır +run_user_helper=Gitea'nin çalışacağı işletim sistemi kullanıcı adı. Bu kullanıcının depo kök yoluna erişiminin olması gerektiğini unutmayın. domain=Sunucu Alan Adı domain_helper=Sunucu için alan adı veya ana bilgisayar adresi. ssh_port=SSH Sunucu Portu @@ -291,6 +298,8 @@ invalid_password_algorithm=Hatalı parola hash algoritması password_algorithm_helper=Parola hash algoritmasını ayarlayın. Algoritmalar değişen gereksinimlere ve güce sahiptirler. argon2 algoritması iyi özelliklere sahip olmasına rağmen fazla miktarda bellek kullanır ve küçük sistemler için uygun olmayabilir. enable_update_checker=Güncelleme Denetleyicisini Etkinleştir enable_update_checker_helper=Düzenli olarak gitea.io'ya bağlanarak yeni yayınlanan sürümleri denetler. +env_config_keys=Ortam Yapılandırma +env_config_keys_prompt=Aşağıdaki ortam değişkenleri de yapılandırma dosyanıza eklenecektir: [home] uname_holder=Kullanıcı Adı veya E-Posta Adresi @@ -353,6 +362,7 @@ remember_me=Bu Aygıtı hatırla forgot_password_title=Şifremi unuttum forgot_password=Şifrenizi mi unuttunuz? sign_up_now=Bir hesaba mı ihtiyacınız var? Hemen kaydolun. +sign_up_successful=Hesap başarılı bir şekilde oluşturuldu. Hoşgeldiniz! confirmation_mail_sent_prompt=Yeni onay e-postası %s adresine gönderildi. Lütfen gelen kutunuzu bir sonraki %s e kadar kontrol edip kayıt işlemini tamamlayın. must_change_password=Parolanızı güncelleyin allow_password_change=Kullanıcıyı parola değiştirmeye zorla (önerilen) @@ -360,6 +370,7 @@ reset_password_mail_sent_prompt=%s adresine bir onay e-postası gönderil active_your_account=Hesabınızı Etkinleştirin account_activated=Hesap etkinleştirildi prohibit_login=Oturum Açma Yasağı +prohibit_login_desc=Hesabınız ile oturum açmanız yasaklanmış, lütfen site yöneticinizle iletişime geçin. resent_limit_prompt=Zaten bir etkinleştirme e-postası talep ettiniz. Lütfen 3 dakika bekleyip tekrar deneyin. has_unconfirmed_mail=Merhaba %s, doğrulanmamış bir e-posta adresin var (%s). Bir doğrulama e-postası almadıysanız ya da yenisine ihtiyacınız varsa lütfen aşağıdaki düğmeye tıklayın. resend_mail=Etkinleştirme e-postasını tekrar almak için buraya tıklayın @@ -369,6 +380,7 @@ reset_password=Hesap Kurtarma invalid_code=Doğrulama kodunuz geçersiz veya süresi dolmuş. invalid_password=Parolanız hesap oluşturulurken kullanılan parolayla eşleşmiyor. reset_password_helper=Hesabı Kurtar +reset_password_wrong_user=%s olarak oturum açmışsınız, ancak hesap kurtarma bağlantısı %s için password_too_short=Parolanız en az %d karakter uzunluğunda olmalıdır. non_local_account=Yerel olmayan kullanıcılar parolalarını Gitea web arayüzünden güncelleyemezler. verify=Doğrula @@ -393,6 +405,7 @@ openid_connect_title=Mevcut olan bir hesaba bağlan openid_connect_desc=Seçilen OpenID URI'si bilinmiyor. Burada yeni bir hesapla ilişkilendir. openid_register_title=Yeni hesap oluştur openid_register_desc=Seçilen OpenID URI'si bilinmiyor. Burada yeni bir hesapla ilişkilendir. +openid_signin_desc=OpenID URI'nizi girin. Örneğin: alice.openid.example.org veya https://openid.example.org/alice. disable_forgot_password_mail=E posta ayarlanmadığından hesap kurtarma devre dışı. Site yöneticinizle iletişime geçin. disable_forgot_password_mail_admin=Hesap kurtarma sadece e posta ayarlıyken kullanılabilir. Hesap kurtarmayı etkinleştirmek için lütfen e posta ayarlayın. email_domain_blacklisted=Bu e-posta adresinizle kayıt olamazsınız. @@ -402,7 +415,9 @@ authorize_application_created_by=Bu uygulama %s tarafından oluşturuldu. authorize_application_description=Erişime izin verirseniz, özel depolar ve organizasyonlar da dahil olmak üzere tüm hesap bilgilerinize erişebilir ve yazabilir. authorize_title=Hesabınıza erişmesi için "%s" yetkilendirilsin mi? authorization_failed=Yetkilendirme başarısız oldu +authorization_failed_desc=Geçersiz bir istek tespit ettiğimiz için yetkilendirme başarısız oldu. Lütfen izin vermeye çalıştığınız uygulamanın sağlayıcısı ile iletişim kurun. sspi_auth_failed=SSPI kimlik doğrulaması başarısız oldu +password_pwned=Seçtiğiniz parola, daha önce herkese açık veri ihlallerinde açığa çıkan bir çalınan parola listesindedir. Lütfen farklı bir parola ile tekrar deneyin ve başka yerlerde de bu parolayı değiştirmeyi düşünün. password_pwned_err=HaveIBeenPwned'e yapılan istek tamamlanamadı [mail] @@ -417,6 +432,7 @@ activate_account.text_1=Merhaba %[1]s, %[2]s kaydınızı yaptırdığın activate_account.text_2=Hesabınızı etkinleştirmek için lütfen %s içinde linke tıklayın: activate_email=E-posta adresinizi doğrulayın +activate_email.title=%s, lütfen e-posta adresinizi doğrulayın activate_email.text=E posta adresinizi doğrulamak için lütfen %s içinde linke tıklayın: register_notify=Gitea'ya Hoş Geldiniz @@ -587,6 +603,8 @@ user_bio=Biyografi disabled_public_activity=Bu kullanıcı, etkinliğin herkese görünür olmasını devre dışı bıraktı. email_visibility.limited=E-posta adresiniz giriş yapmış tüm kullanıcılar tarafından görünür email_visibility.private=E-posta adresiniz sadece siz veya yöneticiler tarafından görünür +show_on_map=Bu yeri harita üzerinde göster +settings=Kullanıcı Ayarları form.name_reserved=`"%s" kullanıcı adı rezerve edilmiş.` form.name_pattern_not_allowed=Kullanıcı adında "%s" deseni kullanılamaz. @@ -608,9 +626,13 @@ delete=Hesabı Sil twofa=İki Aşamalı Doğrulama account_link=Bağlı Hesaplar organization=Organizasyonlar +uid=UID webauthn=Güvenlik Anahtarları public_profile=Herkese Açık Profil +biography_placeholder=Bize kendiniz hakkında birşeyler söyleyin! (Markdown kullanabilirsiniz) +location_placeholder=Yaklaşık konumunuzu başkalarıyla paylaşın +profile_desc=Profilinizin başkalarına nasıl gösterildiğini yönetin. Ana e-posta adresiniz bildirimler, parola kurtarma ve web tabanlı Git işlemleri için kullanılacaktır. password_username_disabled=Yerel olmayan kullanıcılara kullanıcı adlarını değiştirme izni verilmemiştir. Daha fazla bilgi edinmek için lütfen site yöneticisi ile iletişime geçiniz. full_name=Ad Soyad website=Web Sitesi @@ -622,6 +644,8 @@ update_language_not_found=`"%s" dili mevcut değil.` update_language_success=Dil güncellendi. update_profile_success=Profil resminiz güncellendi. change_username=Kullanıcı adınız değiştirildi. +change_username_prompt=Not: Kullanıcı adınızı değiştirirseniz, hesap URL'niz de değişecektir. +change_username_redirect_prompt=Eski kullanıcı adı, başkası tarafından kullanılana kadar yönlendirilecektir. continue=Devam Et cancel=İptal language=Dil @@ -646,6 +670,7 @@ comment_type_group_project=Proje comment_type_group_issue_ref=Konu referansı saved_successfully=Ayarlarınız başarılı bir şekilde kaydedildi. privacy=Gizlilik +keep_activity_private=Etkinliği profil sayfasında gizle keep_activity_private_popup=Etkinliği yalnızca siz ve yöneticiler için görünür hale getirir lookup_avatar_by_mail=Avatarı E-posta Adresine Göre Ara @@ -662,6 +687,7 @@ update_user_avatar_success=Kullanıcının avatarı güncellendi. change_password=Parolayı Güncelle old_password=Mevcut Parola new_password=Yeni Parola +retype_new_password=Yeni Parolayı Onayla password_incorrect=Mevcut parola hatalı. change_password_success=Parolanız güncelleştirildi. Şu andan itibaren yeni parolanızı kullanarak oturum açın. password_change_disabled=Yerel olmayan kullanıcılar parolalarını Gitea web arayüzünden güncelleyemezler. @@ -670,6 +696,7 @@ emails=E-Posta Adresleri manage_emails=E-posta Adreslerini Yönet manage_themes=Varsayılan temayı seç manage_openid=OpenID Adreslerini Yönet +email_desc=Ana e-posta adresiniz bildirimler, parola kurtarma ve gizlenmemişse eğer web tabanlı Git işlemleri için kullanılacaktır. theme_desc=Bu, sitedeki varsayılan temanız olacak. primary=Birincil activated=Aktifleştirildi @@ -677,6 +704,7 @@ requires_activation=Etkinleştirme gerekiyor primary_email=Birincil Yap activate_email=Etkinleştirme Gönder activations_pending=Bekleyen Etkinleştirmeler +can_not_add_email_activations_pending=Bekleyen etkinleştirme var, yeni bir e-posta adresi eklemek istiyorsanız birkaç dakika içinde tekrar deneyin. delete_email=Kaldır email_deletion=E-posta Adresini Kaldır email_deletion_desc=E-posta adresi ve ilgili bilgiler hesabınızdan kaldırılacak. Bu e-posta adresi tarafından yapılan işlemeler değişmeden kalacaktır. Devam edilsin mi? @@ -695,6 +723,7 @@ add_email_success=Yeni e-posta adresi eklendi. email_preference_set_success=E-posta tercihi başarıyla ayarlandı. add_openid_success=Yeni OpenID adresi eklendi. keep_email_private=E-posta Adresini Gizle +keep_email_private_popup=Bu, e-posta adresinizi profilde, değişiklik isteği yaptığınızda veya web arayüzünde dosya düzenlediğinizde gizleyecektir. İtilen işlemeler değişmeyecektir. openid_desc=OpenID, kimlik doğrulama işlemini harici bir sağlayıcıya devretmenize olanak sağlar. manage_ssh_keys=SSH Anahtarlarını Yönet @@ -773,7 +802,9 @@ ssh_disabled=SSH devre dışı bırakıldı ssh_signonly=SSH şu an devre dışı, dolayısıyla bu anahtarlar sadece işlem imza doğrulama için kullanılıyor. ssh_externally_managed=Bu SSH anahtarı, bu kullanıcı için harici olarak yönetiliyor manage_social=Bağlanmış Sosyal Hesapları Yönet +social_desc=Bu sosyal hesaplar oturum açmanız için kullanılabilir. Hepsini tanıdığınızdan emin olun. unbind=Bağlantıyı Kaldır +unbind_success=Sosyal hesap başarılı bir şekilde kaldırıldı. manage_access_token=Erişim Jetonlarını Yönet generate_new_token=Yeni Erişim Anahtarı Üret @@ -794,6 +825,8 @@ permissions_access_all=Tümü (herkese açık, özel ve sınırlı) select_permissions=İzinleri seçin permission_no_access=Erişim Yok permission_read=Okunmuş +permission_write=Okuma ve Yazma +access_token_desc=Seçili token izinleri, yetkilendirmeyi ilgili API yollarıyla sınırlandıracaktır. Daha fazla bilgi için belgeleri okuyun. at_least_one_permission=Bir token oluşturmak için en azından bir izin seçmelisiniz permissions_list=İzinler: @@ -805,6 +838,8 @@ remove_oauth2_application_desc=Bir OAuth2 uygulamasının kaldırılması, imzal remove_oauth2_application_success=Uygulama silindi. create_oauth2_application=Yeni bir OAuth2 Uygulaması Oluştur create_oauth2_application_button=Uygulama Oluştur +create_oauth2_application_success=Yeni bir OAuth2 uygulamasını başarıyla oluşturdunuz. +update_oauth2_application_success=OAuth2 uygulamasını başarıyla güncellediniz. oauth2_application_name=Uygulama Adı oauth2_confidential_client=Güvenli İstemci. Web uygulamaları gibi sırları güvende tutan uygulamalar için bunu seçin. Masaüstü ve mobil uygulamaları da içeren doğal uygulamalar için seçmeyin. oauth2_redirect_uris=Yönlendirme URI'leri. Lütfen her bir URI'yi yeni bir satıra yazın. @@ -813,19 +848,25 @@ oauth2_client_id=İstemci Kimliği oauth2_client_secret=İstemci Gizliliği oauth2_regenerate_secret=Gizliliği Yeniden Oluştur oauth2_regenerate_secret_hint=Gizliliğini mi kaybettin? +oauth2_client_secret_hint=Bu sayfadan ayrıldıktan veya yeniledikten sonra gizliliği göremeyeceksiniz. Kaydettiğinizden emin olun. oauth2_application_edit=Düzenle oauth2_application_create_description=OAuth2 uygulamaları, üçüncü taraf uygulamanıza bu durumda kullanıcı hesaplarına erişim sağlar. +oauth2_application_remove_description=Bir OAuth2 uygulamasının kaldırılması, bu sunucudaki yetkili kullanıcı hesaplarına erişmesini önler. Devam edilsin mi? +oauth2_application_locked=Gitea kimi OAuth2 uygulamalarının başlangıçta ön kaydını, yapılandırmada etkinleştirilmişse yapabilir. Beklenmeyen davranışı önlemek için bunlar ne düzenlenmeli ne de kaldırılmalı. Daha fazla bilgi için OAuth2 belgesine bakın. authorized_oauth2_applications=Yetkili OAuth2 Uygulamaları +authorized_oauth2_applications_description=Kişisel Gitea hesabınıza bu üçüncü parti uygulamalara erişim izni verdiniz. Lütfen artık ihtiyaç duyulmayan uygulamalara erişimi iptal edin. revoke_key=İptal Et revoke_oauth2_grant=Erişimi İptal Et revoke_oauth2_grant_description=Bu üçüncü taraf uygulamasına erişimin iptal edilmesi bu uygulamanın verilerinize erişmesini önleyecektir. Emin misiniz? +revoke_oauth2_grant_success=Erişim başarıyla kaldırıldı. twofa_desc=İki faktörlü kimlik doğrulama, hesabınızın güvenliğini artırır. twofa_is_enrolled=Hesabınız şu anda iki faktörlü kimlik doğrulaması içinde kaydedilmiş. twofa_not_enrolled=Hesabınız şu anda iki faktörlü kimlik doğrulaması içinde kaydedilmemiş. twofa_disable=İki Aşamalı Doğrulamayı Devre Dışı Bırak twofa_scratch_token_regenerate=Geçici Kodu Yeniden Üret +twofa_scratch_token_regenerated=Geçici kodunuz şimdi %s. Güvenli bir yerde saklayın, tekrar gösterilmeyecektir. twofa_enroll=İki Faktörlü Kimlik Doğrulamaya Kaydolun twofa_disable_note=Gerekirse iki faktörlü kimlik doğrulamayı devre dışı bırakabilirsiniz. twofa_disable_desc=İki faktörlü kimlik doğrulamayı devre dışı bırakmak hesabınızı daha az güvenli hale getirir. Devam edilsin mi? @@ -852,8 +893,10 @@ remove_account_link=Bağlantılı Hesabı Kaldır remove_account_link_desc=Bağlantılı bir hesabı kaldırmak, onunla Gitea hesabınıza erişimi iptal edecektir. Devam edilsin mi? remove_account_link_success=Bağlantılı hesap kaldırıldı. +hooks.desc=Sahip olduğunuz tüm depolar için tetiklenecek web istemcileri ekle. orgs_none=Herhangi bir organizasyonun bir üyesi değilsiniz. +repos_none=Herhangi bir depoya sahip değilsiniz. delete_account=Hesabınızı Silin delete_prompt=Bu işlem kullanıcı hesabınızı kalıcı olarak siler. Bu işlem GERİ ALINAMAZ. @@ -872,9 +915,12 @@ visibility=Kullanıcı görünürlüğü visibility.public=Herkese Açık visibility.public_tooltip=Herkes tarafından görünür visibility.limited=Sınırlı +visibility.limited_tooltip=Sadece oturum açmış kullanıcılar tarafından görünür visibility.private=Özel +visibility.private_tooltip=Sadece katıldığınız organizasyonların üyeleri tarafından görünür [repo] +new_repo_helper=Bir depo, sürüm geçmişi dahil tüm proje dosyalarını içerir. Zaten başka bir yerde mi barındırıyorsunuz? Depoyu taşıyın. owner=Sahibi owner_helper=Bazı organizasyonlar, en çok depo sayısı sınırı nedeniyle açılır menüde görünmeyebilir. repo_name=Depo İsmi @@ -886,6 +932,7 @@ template_helper=Depoyu şablon yap template_description=Şablon depoları, kullanıcıların aynı dizin yapısı, dosyaları ve isteğe bağlı ayarlarla yeni depoları oluşturmasına izin verir. visibility=Görünürlük visibility_description=Yalnızca sahibi veya haklara sahip organizasyon üyeleri onu görebilecek. +visibility_helper=Depoyu özel yap visibility_helper_forced=Site yöneticiniz, yeni depoları gizli olmaya zorluyor. visibility_fork_helper=(Bunu değiştirmek tüm çatallamaları etkileyecektir.) clone_helper=Klonlama konusunda yardıma mı ihtiyacınız var? Yardım adresini ziyaret edin. @@ -894,6 +941,7 @@ fork_from=Buradan Çatalla already_forked=%s deposunu zaten çatalladınız fork_to_different_account=Başka bir hesaba çatalla fork_visibility_helper=Çatallanmış bir deponun görünürlüğü değiştirilemez. +fork_no_valid_owners=Geçerli bir sahibi olmadığı için bu depo çatallanamaz. use_template=Bu şablonu kullan clone_in_vsc=VS Code'ta klonla download_zip=ZIP indir @@ -930,6 +978,8 @@ mirror_interval_invalid=Yansı süre aralığı geçerli değil. mirror_sync_on_commit=İşlemeler gönderildiğinde senkronize et mirror_address=URL'den Klonla mirror_address_desc=Yetkilendirme bölümüne gerekli tüm kimlik bilgilerini girin. +mirror_address_url_invalid=Sağlanan URL geçersiz. URL'nin tüm bileşenleri doğru olarak girilmelidir. +mirror_address_protocol_invalid=Sağlanan URL geçersiz. Yalnızca http(s):// veya git:// konumları yansı olabilir. mirror_lfs=Büyük Dosya Depolama (LFS) mirror_lfs_desc=LFS verisinin yansılamasını etkinleştir. mirror_lfs_endpoint=LFS Uç Noktası @@ -961,6 +1011,8 @@ transfer.accept=Aktarımı Kabul Et transfer.accept_desc=`"%s" tarafına aktar` transfer.reject=Aktarımı Reddet transfer.reject_desc=`"%s" tarafına aktarımı iptal et` +transfer.no_permission_to_accept=Bu aktarımı kabul etme izniniz yok. +transfer.no_permission_to_reject=Bu aktarımı reddetme izniniz yok. desc.private=Özel desc.public=Genel @@ -981,6 +1033,8 @@ template.issue_labels=Konu Etiketleri template.one_item=En az bir şablon öğesi seçmelisiniz template.invalid=Bir şablon deposu seçmelisiniz +archive.title=Bu depo arşivlendi. Dosyaları görüntüleyebilir ve klonlayabilirsiniz ama işleme gönderemez veya konu veya değişiklik isteği açamazsınız. +archive.title_date=Bu depo %s tarihinde arşivlendi. Dosyaları görüntüleyebilir ve klonlayabilirsiniz ama işleme gönderemez veya konu veya değişiklik isteği açamazsınız. archive.issue.nocomment=Bu depo arşivlendi. Konular bölümünde yorum yapamazsınız. archive.pull.nocomment=Bu depo arşivlendi. Değişiklik istekleri bölümünde yorum yapamazsınız. @@ -997,6 +1051,7 @@ migrate_options_lfs=LFS dosyalarını taşı migrate_options_lfs_endpoint.label=LFS Uç Noktası migrate_options_lfs_endpoint.description=Taşıma, LFS sunucusunu belirlemek için Git uzak sunucusunu kullanmaya çalışacak. Eğer LFS veri deposu başka yerdeyse özel bir uç nokta da belirtebilirsiniz. migrate_options_lfs_endpoint.description.local=Yerel bir sunucu yolu da destekleniyor. +migrate_options_lfs_endpoint.placeholder=Boş bırakılırsa, uç nokta klon URL'sinden türetilecektir migrate_items=Göç Öğeleri migrate_items_wiki=Wiki migrate_items_milestones=Kilometre Taşları @@ -1099,6 +1154,10 @@ file_view_rendered=Oluşturulanları Görüntüle file_view_raw=Ham Görünüm file_permalink=Kalıcı Bağlantı file_too_large=Bu dosya görüntülemek için çok büyük. +invisible_runes_header=`Bu dosya görünmez Evrensel Kodlu karakter içeriyor` +invisible_runes_description=`Bu dosya, insanlar tarafından ayırt edilemeyen ama bir bilgisayar tarafından farklı bir şekilde işlenebilecek görünmez evrensel kodlu karakter içeriyor. Eğer bunu kasıtlı olarak yaptıysanız bu uyarıyı yok sayabilirsiniz. Gizli karakterleri göstermek için Kaçış Karakterli düğmesine tıklayın.` +ambiguous_runes_header=`Bu dosya muğlak Evrensel Kodlu karakter içeriyor` +ambiguous_runes_description=`Bu dosya, başka karakterlerle karıştırılabilecek evrensel kodlu karakter içeriyor. Eğer bunu kasıtlı olarak yaptıysanız bu uyarıyı yok sayabilirsiniz. Gizli karakterleri göstermek için Kaçış Karakterli düğmesine tıklayın.` invisible_runes_line=`Bu satırda görünmez evrensel kodlu karakter var` ambiguous_runes_line=`Bu satırda muğlak evrensel kodlu karakter var` ambiguous_character=`%[1]c [U+%04[1]X], %[2]c [U+%04[2]X] ile karıştırılabilir` @@ -1111,11 +1170,15 @@ video_not_supported_in_browser=Tarayıcınız HTML5 'video' etiketini desteklemi audio_not_supported_in_browser=Tarayıcınız HTML5 'audio' etiketini desteklemiyor. stored_lfs=Git LFS ile depolandı symbolic_link=Sembolik Bağlantı +executable_file=Çalıştırılabilir Dosya commit_graph=İşleme Grafiği commit_graph.select=Dalları seç commit_graph.hide_pr_refs=Değişiklik İsteklerini Gizle commit_graph.monochrome=Siyah Beyaz commit_graph.color=Renk +commit.contained_in=Bu işleme şunda yer alıyor: +commit.contained_in_default_branch=Bu işleme varsayılan dalın bir parçasıdır +commit.load_referencing_branches_and_tags=Bu işlemeyi kullanan dal ve etiketleri yükle blame=Suçlama download_file=Dosya indir normal_view=Normal Görünüm @@ -1330,6 +1393,7 @@ issues.delete_branch_at=`%s dalı silindi %s` issues.filter_label=Etiket issues.filter_label_exclude=`Etiketleri hariç tutmak için alt + tıkla/enter kullanın` issues.filter_label_no_select=Tüm etiketler +issues.filter_label_select_no_label=Etiket yok issues.filter_milestone=Kilometre Taşı issues.filter_milestone_all=Tüm kilometre taşları issues.filter_milestone_none=Kilometre taşı yok @@ -1363,6 +1427,7 @@ issues.filter_sort.moststars=En çok yıldızlılar issues.filter_sort.feweststars=En az yıldızlılar issues.filter_sort.mostforks=En çok çatallananlar issues.filter_sort.fewestforks=En az çatallananlar +issues.keyword_search_unavailable=Anahtar kelime ile arama şu an mevcut değil. Lütfen site yöneticisiyle iletişime geçin. issues.action_open=Açık issues.action_close=Kapat issues.action_label=Etiket @@ -1383,6 +1448,7 @@ issues.next=Sonraki issues.open_title=Açık issues.closed_title=Kapalı issues.draft_title=Taslak +issues.num_comments_1=%d yorum issues.num_comments=%d yorum issues.commented_at=`%s yorum yaptı` issues.delete_comment_confirm=Bu yorumu silmek istediğinizden emin misiniz? @@ -1391,6 +1457,7 @@ issues.context.quote_reply=Alıntı Cevapla issues.context.reference_issue=Yeni konuda referans issues.context.edit=Düzenle issues.context.delete=Sil +issues.no_content=Herhangi bir açıklama sağlanmadı. issues.close=Konuyu Kapat issues.comment_pull_merged_at=%[1]s işlemesi, %[2]s dalına birleştirildi %[3]s issues.comment_manually_pull_merged_at=%[1]s işlemesi, %[2]s dalına elle birleştirildi %[3]s @@ -1425,6 +1492,8 @@ issues.label_title=Etiket adı issues.label_description=Etiket açıklaması issues.label_color=Etiket rengi issues.label_exclusive=Özel +issues.label_archive=Etiketi Arşivle +issues.label_archive_tooltip=Arşivlenmiş etiketler, bir konuya etiket uygulanırken etiket aramasından çıkarılıyor. Bir konudaki mevcut etiketler bundan etkilenmiyor, böylece eskimiş etiketleri bilgi kaybına uğramadan kullanımdan kaldırabiliyorsunuz. issues.label_exclusive_desc=Kapsam/öğe etiketini, diğer kapsam/ etiketleriyle ayrışık olacak şekilde adlandırın. issues.label_exclusive_warning=Çakışan kapsamlı etiketler, bir konu veya değişiklik isteği etiketleri düzenlenirken kaldırılacaktır. issues.label_count=%d etiket @@ -1479,6 +1548,7 @@ issues.tracking_already_started=`başka bir konuda zaten zaman issues.stop_tracking=Zamanlayıcıyı Bitir issues.stop_tracking_history=`%s çalışmayı durdurdu` issues.cancel_tracking=Yoksay +issues.cancel_tracking_history=`%s zaman takibini iptal etti` issues.add_time=El ile Zaman Ekle issues.del_time=Bu zaman kaydını sil issues.add_time_short=Zaman Ekle @@ -1502,6 +1572,7 @@ issues.due_date_form=yyyy-aa-gg issues.due_date_form_add=Bitiş tarihi ekle issues.due_date_form_edit=Düzenle issues.due_date_form_remove=Kaldır +issues.due_date_not_writer=Bir konunun bitiş tarihini güncellemek için bu depoda yazma iznine ihtiyacınız var. issues.due_date_not_set=Bitiş tarihi atanmadı. issues.due_date_added=bitiş tarihini %s olarak %s ekledi issues.due_date_modified=bitiş tarihini %[2]s iken %[1]s olarak %[3]s değiştirdi @@ -1557,6 +1628,9 @@ issues.review.pending.tooltip=Bu yorum başkaları tarafından görünmüyor. Be issues.review.review=Gözden Geçir issues.review.reviewers=Gözden Geçirenler issues.review.outdated=Eskimiş +issues.review.outdated_description=Bu yorum yapıldığından beri içerik değişti +issues.review.option.show_outdated_comments=Zaman aşımına uğramış yorumları göster +issues.review.option.hide_outdated_comments=Zaman aşımına uğramış yorumları gizle issues.review.show_outdated=Eskiyi göster issues.review.hide_outdated=Eskiyi gizle issues.review.show_resolved=Çözülenleri göster @@ -1596,6 +1670,13 @@ pulls.switch_comparison_type=Karşılaştırma türünü değiştir pulls.switch_head_and_base=Ana ve temeli değiştir pulls.filter_branch=Dal filtrele pulls.no_results=Sonuç bulunamadı. +pulls.show_all_commits=Tüm işlemeleri göster +pulls.show_changes_since_your_last_review=Son incelemenizden sonraki değişiklikleri göster +pulls.showing_only_single_commit=Sadece %[1]s işlemesindeki değişiklikler gösteriliyor +pulls.showing_specified_commit_range=%[1]s..%[2]s arasındaki değişiklikler gösteriliyor +pulls.select_commit_hold_shift_for_range=İşleme seç. Bir aralık seçmek için Shift'e basılı tutup tıklayın +pulls.review_only_possible_for_full_diff=İnceleme sadece tam fark görüntülemede mümkündür +pulls.filter_changes_by_commit=İşleme ile süz pulls.nothing_to_compare=Bu dallar eşit. Değişiklik isteği oluşturmaya gerek yok. pulls.nothing_to_compare_and_allow_empty_pr=Bu dallar eşittir. Bu Dİ boş olacak. pulls.has_pull_request=`Bu dallar arasında zaten bir değişiklik isteği var: %[2]s#%[3]d` @@ -1627,6 +1708,12 @@ pulls.is_empty=Bu daldaki değişiklikler zaten hedef dalda mevcut. Bu boş bir pulls.required_status_check_failed=Bazı gerekli denetimler başarılı olmadı. pulls.required_status_check_missing=Gerekli bazı kontroller eksik. pulls.required_status_check_administrator=Yönetici olarak, bu değişiklik isteğini yine de birleştirebilirsiniz. +pulls.blocked_by_approvals=Bu değişiklik isteğinin henüz yeterli onayı yok. %d onay var, %d onay gerekiyor. +pulls.blocked_by_rejection=Bu değişiklik isteğinde resmi bir inceleyici tarafından istenen değişiklikler var. +pulls.blocked_by_official_review_requests=Bu değişiklik isteğinde resmi inceleme istekleri var. +pulls.blocked_by_outdated_branch=Bu değişiklik isteği eskidiği için engellendi. +pulls.blocked_by_changed_protected_files_1=Bu değişiklik isteği, korumalı bir dosyayı değiştirdiği için engellendi: +pulls.blocked_by_changed_protected_files_n=Bu değişiklik isteği, korumalı dosyaları değiştirdiği için engellendi: pulls.can_auto_merge_desc=Bu değişiklik isteği otomatik olarak birleştirilebilir. pulls.cannot_auto_merge_desc=Bu değişiklik isteği, çakışmalar nedeniyle otomatik olarak birleştirilemiyor. pulls.cannot_auto_merge_helper=Çakışmaları çözmek için el ile birleştirin. @@ -1701,7 +1788,9 @@ pulls.auto_merge_canceled_schedule_comment=`bu değişiklik isteğinin, tüm den pulls.delete.title=Bu değişiklik isteği silinsin mi? pulls.delete.text=Bu değişiklik isteğini gerçekten silmek istiyor musunuz? (Bu işlem tüm içeriği kalıcı olarak silecektir. Arşivde tutma niyetiniz varsa silmek yerine kapatmayı düşünün) +pulls.recently_pushed_new_branches=%[1]s dalına ittiniz %[2]s +pull.deleted_branch=(silindi): %s milestones.new=Yeni Kilometre Taşı milestones.closed=Kapalı %s @@ -1709,6 +1798,7 @@ milestones.update_ago=%s tarihinde güncellendi milestones.no_due_date=Bitiş tarihi yok milestones.open=Aç milestones.close=Kapat +milestones.new_subheader=Kilometre taşları konuları yönetmenize ve gelişmelerini takip etmenize yardımcı olur. milestones.completeness=%d%% Tamamlandı milestones.create=Kilometre Taşı Oluştur milestones.title=Başlık @@ -1732,6 +1822,19 @@ milestones.filter_sort.most_complete=En çok tamamlama milestones.filter_sort.most_issues=En çok konu milestones.filter_sort.least_issues=En az konu +signing.will_sign=Bu işleme "%s" anahtarıyla imzalanacak. +signing.wont_sign.error=İşlemenin imzalanıp imzalanamayacağını kontrol ederken bir hata oluştu. +signing.wont_sign.nokey=Bu işlemeyi imzalamak için anahtar yok. +signing.wont_sign.never=İşlemeler asla imzalanmaz. +signing.wont_sign.always=İşlemeler her zaman imzalanır. +signing.wont_sign.pubkey=Hesabınızla ilişkilendirilmiş bir ortak anahtarınız olmadığı için işleme imzalanmayacak. +signing.wont_sign.twofa=İşlemelerin imzalanması için iki aşamalı kimlik doğrulamayı etkinleştirmelisiniz. +signing.wont_sign.parentsigned=Üst işleme imzalanmadığı için bu işleme imzalanmayacak. +signing.wont_sign.basesigned=Temel işleme imzalanmadığı için birleştirme imzalanmayacak. +signing.wont_sign.headsigned=Ana işleme imzalanmadığı için birleştirme imzalanmayacak. +signing.wont_sign.commitssigned=İlişkili tüm işlemeler imzalanmadığı için birleştirme imzalanmayacak. +signing.wont_sign.approved=Değişiklik İsteği onaylanmadığı için birleştirme imzalanmayacak. +signing.wont_sign.not_signed_in=Oturum açmadınız. ext_wiki=Harici Vikiye Erişim ext_wiki.desc=Harici bir wiki'ye bağlantı. @@ -1861,7 +1964,9 @@ settings.mirror_settings.docs.disabled_push_mirror.info=Gönderme yansıları si settings.mirror_settings.docs.no_new_mirrors=Deponuz değişiklikleri başka bir depoyla yansılıyor. Şu an başka bir yansı oluşturamayacağınızı unutmayın. settings.mirror_settings.docs.can_still_use=Her ne kadar mevcut yansıları değiştiremiyor veya yeni yansı oluşturamıyor olsanız da, hala mevcut yansıyı kullanabilirsiniz. settings.mirror_settings.docs.pull_mirror_instructions=Çekme yansıyı kurmak için, lütfen şuraya danışın: +settings.mirror_settings.docs.more_information_if_disabled=İtme ve çekme yansıları hakkında daha fazla bilgiye şuradan ulaşabilirsiniz: settings.mirror_settings.docs.doc_link_title=Depoların yansısını nasıl oluştururum? +settings.mirror_settings.docs.doc_link_pull_section=belgelerin "uzak bir depodan çekmek" bölümü. settings.mirror_settings.docs.pulling_remote_title=Uzak bir depodan çekmek settings.mirror_settings.mirrored_repository=Yansıtılmış depo settings.mirror_settings.direction=Yön @@ -1871,6 +1976,7 @@ settings.mirror_settings.last_update=Son güncelleme settings.mirror_settings.push_mirror.none=Yapılandırılmış yansı gönderimi yok settings.mirror_settings.push_mirror.remote_url=Git Uzak Depo URL'si settings.mirror_settings.push_mirror.add=Yansı Gönderimi Ekle +settings.mirror_settings.push_mirror.edit_sync_time=Yansı eşzamanlama aralığını düzenle settings.sync_mirror=Şimdi Eşitle settings.mirror_sync_in_progress=Yansı senkronizasyonu devam ediyor. Bir dakika sonra tekrar kontrol edin. @@ -1940,6 +2046,7 @@ settings.transfer.rejected=Depo aktarımı reddedildi. settings.transfer.success=Depo aktarımı başarıyla tamamlandı. settings.transfer_abort=Aktarımı iptal et settings.transfer_abort_invalid=Var olmayan bir depo aktarımını iptal edemezsiniz. +settings.transfer_abort_success=%s tarafına yapılan depo aktarımı başarıyla iptal edildi. settings.transfer_desc=Bu depoyu bir kullanıcıya veya yönetici haklarına sahip olduğunuz bir organizasyona aktarın. settings.transfer_form_title=Onaylamak için depo adını girin: settings.transfer_in_progress=Şu anda devam etmekte olan bir aktarım mevcut. Eğer bu depoyu başka bir kullanıcıya aktarmak istiyorsanız mevcut aktarımı iptal edin. @@ -2210,16 +2317,23 @@ settings.tags.protection.none=Korumalı etiket yok. settings.tags.protection.pattern.description=Birden çok etiketi eşleştirmek için tek bir ad, glob deseni veya normal ifade kullanabilirsiniz. Daha fazlası için korumalı etiketler rehberini okuyun. settings.bot_token=Bot Jetonu settings.chat_id=Sohbet Kimliği +settings.thread_id=İş Parçacığı ID settings.matrix.homeserver_url=Ev sunucusu URL'si settings.matrix.room_id=Oda Kimliği settings.matrix.message_type=Mesaj Türü settings.archive.button=Depoyu Arşivle settings.archive.header=Bu Depoyu Arşivle +settings.archive.text=Depoyu arşivlemek onu tamamen salt okunur yapacaktır. Panoda gizlenecektir. Hiç kimse (siz bile) yeni işleme yapamayacak veya yeni konu veya değişiklik isteği açamayacak. settings.archive.success=Depo başarıyla arşivlendi. settings.archive.error=Depoyu arşivlemeye çalışırken bir hata oluştu. Daha fazla ayrıntı için günlüğe bakın. settings.archive.error_ismirror=Yansılanmış bir depoyu arşivleyemezsiniz. settings.archive.branchsettings_unavailable=Depo arşivlenirse dal ayarları kullanılamaz. settings.archive.tagsettings_unavailable=Depo arşivlenmişse etiket ayarları kullanılamaz. +settings.unarchive.button=Depoyu Arşivden Çıkar +settings.unarchive.header=Bu Depoyu Arşivden Çıkar +settings.unarchive.text=Depoyu arşivden çıkarmak, yeni sorunların ve değişiklik isteklerinin yanı sıra işleme ve itme yeteneğini de geri kazandıracaktır. +settings.unarchive.success=Depo başarıyla arşivden çıkarıldı. +settings.unarchive.error=Depoyu arşivden çıkarmaya çalışırken bir hata oluştu. Daha fazla ayrıntı için günlüğe bakın. settings.update_avatar_success=Depo resmi güncellendi. settings.lfs=LFS settings.lfs_filelist=Bu depoda barındırılan LFS dosyaları @@ -2286,6 +2400,7 @@ diff.show_more=Daha Fazla Göster diff.load=Fark Yükle diff.generated=üretilen diff.vendored=sağlanmış +diff.comment.add_line_comment=Satır yorum ekle diff.comment.placeholder=Yorum Yap diff.comment.markdown_info=Markdown ile şekillendirme desteklenir. diff.comment.add_single_comment=Bir yorum ekle @@ -2342,6 +2457,7 @@ release.edit_release=Sürümü Güncelle release.delete_release=Sürümü Sil release.delete_tag=Etiketi Sil release.deletion=Sürümü Sil +release.deletion_desc=Bir sürümü silmek onu sadece Gitea'dan kaldırır. Git etiketini, deponuzun içeriğini veya geçmişini etkilemez. Devam edilsin mi? release.deletion_success=Sürüm silindi. release.deletion_tag_desc=Bu etiket depodan silinecek. Depo içeriği ve geçmişi değişmeden kalır. Devam edilsin mi? release.deletion_tag_success=Etiket silindi. @@ -2362,6 +2478,7 @@ branch.already_exists=`"%s" isimli bir dal zaten mevcut.` branch.delete_head=Sil branch.delete=`"%s" Dalını Sil` branch.delete_html=Bölüm Sil +branch.delete_desc=Bir dalı silmek kalıcıdır. Her ne kadar silinen dal tamamen kaldırılana kadar çok kısa bir süre yaşamını sürdürse de, çoğu durumda bu işlem GERİ ALINAMAZ. Devam edilsin mi? branch.deletion_success=`"%s" dalı silindi.` branch.deletion_failed=`"%s" dalı silinemedi.` branch.delete_branch_has_new_commits=`"%s" dalı silinemedi çünkü birleştirme sonrasında yeni işlemeler eklendi.` @@ -2401,6 +2518,7 @@ tag.create_success=`"%s" etiketi oluşturuldu.` topic.manage_topics=Konuları Yönet topic.done=Bitti topic.count_prompt=25'ten fazla konu seçemezsiniz +topic.format_prompt=Konular bir harf veya rakamla başlamalı, kısa çizgi ('-') ve nokta ('.') içerebilir ve en fazla 35 karakter uzunluğunda olabilir. Harfler küçük harf olmalıdır. find_file.go_to_file=Dosyaya git find_file.no_matching=Eşleşen dosya bulunamadı @@ -2439,6 +2557,7 @@ form.create_org_not_allowed=Organizasyon oluşturmanıza izin verilmiyor. settings=Ayarlar settings.options=Organizasyon settings.full_name=Tam İsim +settings.email=İletişim E-posta settings.website=Web Sitesi settings.location=Lokasyon settings.permission=İzinler @@ -2452,6 +2571,7 @@ settings.visibility.private_shortname=Özel settings.update_settings=Ayarları Güncelle settings.update_setting_success=Organizasyon ayarları güncellendi. +settings.change_orgname_prompt=Not: Organizasyon adını değiştirmek organizasyonunuzun URL'sini de değiştirecek ve eski ismi serbest bıracaktır. settings.change_orgname_redirect_prompt=Eski ad, talep edilene kadar yeniden yönlendirilecektir. settings.update_avatar_success=Organizasyonun resmi güncellendi. settings.delete=Organizasyonu Sil @@ -2527,15 +2647,19 @@ teams.all_repositories_helper=Takımın tüm depolara erişimi vardır. Bunu se teams.all_repositories_read_permission_desc=Bu takım tüm depolara Okuma erişimi sağlar: üyeler depoları görüntüleyebilir ve kopyalayabilir. teams.all_repositories_write_permission_desc=Bu takım tüm depolara Yazma erişimi sağlar: üyeler depolardan okuyabilir ve depolara itebilir. teams.all_repositories_admin_permission_desc=Bu takım tüm depolara Yönetici erişimi sağlar: üyeler depolardan okuyabilir, itebilir ve katkıcıları ekleyebilir. +teams.invite.title=%s takımına (Organizasyon: %s) katılmaya davet edildiniz. teams.invite.by=%s tarafından davet edildi teams.invite.description=Takıma katılmak için aşağıdaki düğmeye tıklayın. [admin] dashboard=Pano +identity_access=Kimlik ve Erişim users=Kullanıcı Hesapları organizations=Organizasyonlar +assets=Kod Varlıkları repositories=Depolar hooks=Web İstemcileri +integrations=Bütünleştirmeler authentication=Yetkilendirme Kaynakları emails=Kullanıcı E-postaları config=Yapılandırma @@ -2544,6 +2668,7 @@ monitor=İzleme first_page=İlk last_page=Son total=Toplam: %d +settings=Yönetici Ayarları dashboard.new_version_hint=Gitea %s şimdi hazır, %s çalıştırıyorsunuz. Ayrıntılar için blog'a bakabilirsiniz. dashboard.statistic=Özet @@ -2556,11 +2681,13 @@ dashboard.clean_unbind_oauth=Bağsız OAuth bağlantılarını temizle dashboard.clean_unbind_oauth_success=Tüm bağsız OAuth bağlantıları silindi. dashboard.task.started=Görev Başlatıldı: %[1]s dashboard.task.process=Görev: %[1]s +dashboard.task.cancelled=Görev: %[1]s iptal edildi: %[3]s dashboard.task.error=Görevde Hata: %[1]s: %[3]s dashboard.task.finished=Görev: %[1]s %[2]s tarafından başlatıldı ve bitti dashboard.task.unknown=Bilinmeyen görev: %[1]s dashboard.cron.started=Cron Başlatıldı: %[1]s dashboard.cron.process=Cron: %[1]s +dashboard.cron.cancelled=Cron: %[1]s iptal edildi: %[3]s dashboard.cron.error=Cron Hatası: %s: %[3]s dashboard.cron.finished=Cron: %[1]s bitti dashboard.delete_inactive_accounts=Etkinleştirilmemiş tüm hesapları sil @@ -2570,6 +2697,7 @@ dashboard.delete_repo_archives.started=Tüm depo arşivlerini silme görevi baş dashboard.delete_missing_repos=Git dosyaları eksik olan tüm depoları sil dashboard.delete_missing_repos.started=Git dosyaları eksik olan tüm depoları silme görevi başladı. dashboard.delete_generated_repository_avatars=Oluşturulan depo resimlerini sil +dashboard.sync_repo_branches=Eşzamanlama git verisinden veritabanlarına dalları kaçırdı dashboard.update_mirrors=Yansıları Güncelle dashboard.repo_health_check=Tüm depoların sağlığını denetle dashboard.check_repo_stats=Tüm depo istatistiklerini denetle @@ -2623,6 +2751,7 @@ dashboard.gc_lfs=LFS üst nesnelerin atıklarını temizle dashboard.stop_zombie_tasks=Zombi görevleri durdur dashboard.stop_endless_tasks=Daimi görevleri durdur dashboard.cancel_abandoned_jobs=Terkedilmiş görevleri iptal et +dashboard.sync_branch.started=Dal Eşzamanlaması başladı users.user_manage_panel=Kullanıcı Hesap Yönetimi users.new_account=Yeni Kullanıcı Hesabı @@ -2708,10 +2837,12 @@ repos.stars=Yıldızlar repos.forks=Çatallar repos.issues=Konular repos.size=Boyut +repos.lfs_size=LFS Boyutu packages.package_manage_panel=Paket Yönetimi packages.total_size=Toplam Boyut: %s packages.unreferenced_size=Referanssız Boyut: %s +packages.cleanup=Süresi dolmuş veriyi temizle packages.owner=Sahibi packages.creator=Oluşturan packages.name=İsim @@ -2820,6 +2951,7 @@ auths.sspi_default_language=Varsayılan kullanıcı dili auths.sspi_default_language_helper=SSPI kimlik doğrulama yöntemi tarafından otomatik olarak oluşturulan kullanıcılar için varsayılan dil. Dili otomatik olarak algılamayı tercih ederseniz boş bırakın. auths.tips=İpuçları auths.tips.oauth2.general=OAuth2 Kimlik Doğrulama +auths.tips.oauth2.general.tip=Yeni bir OAuth2 kimlik doğrulama kaydederken, geri çağırma/yönlendirme URL'si şu olmalıdır: auths.tip.oauth2_provider=OAuth2 Sağlayıcısı auths.tip.bitbucket=https://bitbucket.org/account/user//oauth-consumers/new adında yeni bir OAuth tüketicisi kaydedin ve 'Hesap' - 'Oku' iznini ekleyin auths.tip.nextcloud=Aşağıdaki "Ayarlar -> Güvenlik -> OAuth 2.0 istemcisi" menüsünü kullanarak örneğinize yeni bir OAuth tüketicisi kaydedin @@ -2861,6 +2993,7 @@ config.disable_router_log=Yönlendirici Log'larını Devre Dışı Bırak config.run_user=Şu Kullanıcı Olarak Çalıştır config.run_mode=Çalıştırma Modu config.git_version=Git Sürümü +config.app_data_path=Uygulama Veri Yolu config.repo_root_path=Depo Kök Yolu config.lfs_root_path=LFS Kök Dizini config.log_file_root_path=Günlük Dosyası Yolu @@ -3136,6 +3269,7 @@ desc=Depo paketlerini yönet. empty=Henüz hiçbir paket yok. empty.documentation=Paket kütüğü hakkında daha fazla bilgi için, belgeye bakabilirsiniz. empty.repo=Bir paket yüklediniz ama burada gösterilmiyor mu? Paket ayarlarına gidin ve bu depoya bağlantı verin. +registry.documentation=%s kütüğü hakkında daha fazla bilgi için, belgeye bakabilirsiniz. filter.type=Tür filter.type.all=Tümü filter.no_result=Filtreniz herhangi bir sonuç döndürmedi. @@ -3225,6 +3359,8 @@ pub.install=Paketi Dart ile kurmak için, şu komutu çalıştırın: pypi.requires=Gereken Python pypi.install=Paketi pip ile kurmak için, şu komutu çalıştırın: rpm.registry=Bu kütüğü komut satırını kullanarak kurun: +rpm.distros.redhat=RedHat tabanlı dağıtımlarda +rpm.distros.suse=SUSE tabanlı dağıtımlarda rpm.install=Paketi kurmak için, aşağıdaki komutu çalıştırın: rubygems.install=Paketi gem ile kurmak için, şu komutu çalıştırın: rubygems.install2=veya paketi Gemfile dosyasına ekleyin: @@ -3249,14 +3385,17 @@ settings.delete.success=Paket silindi. settings.delete.error=Paket silinemedi. owner.settings.cargo.title=Cargo Kayıt Dizini owner.settings.cargo.initialize=Dizini İlkle +owner.settings.cargo.initialize.description=Cargo kütüğünü kullanmak için özel bir Git depo indeksi gerekiyor. Bu seçeneği kullanmak depoyu (yeniden) oluşturacak ve otomatik olarak yapılandıracaktır. owner.settings.cargo.initialize.error=Cargo dizini oluşturma başarısız oldu: %v owner.settings.cargo.initialize.success=Cargo dizini başarıyla oluşturuldu. owner.settings.cargo.rebuild=Dizini yeniden oluştur +owner.settings.cargo.rebuild.description=İndeks, depolanmış Cargo paketleriyle eşzamanlanmamışsa yeniden oluşturma yararlı olabilir. owner.settings.cargo.rebuild.error=Cargo dizinini yeniden oluşturma başarısız oldu: %v owner.settings.cargo.rebuild.success=Cargo dizini başarıyla yeniden oluşturuldu. owner.settings.cleanuprules.title=Temizleme Kurallarını Yönet owner.settings.cleanuprules.add=Temizleme Kuralı Ekle owner.settings.cleanuprules.edit=Temizleme Kuralı Düzenle +owner.settings.cleanuprules.none=Temizleme kuralı yok. Lütfen belgelere danışın. owner.settings.cleanuprules.preview=Temizleme Kuralı Önizleme owner.settings.cleanuprules.preview.overview=%d paketin kaldırılması planlandı. owner.settings.cleanuprules.preview.none=Temizleme kuralı herhangi bir paketle eşleşmiyor. @@ -3275,6 +3414,7 @@ owner.settings.cleanuprules.success.update=Temizleme kuralı güncellendi. owner.settings.cleanuprules.success.delete=Temizleme kuralı silindi. owner.settings.chef.title=Chef Kütüğü owner.settings.chef.keypair=Anahtar çifti üret +owner.settings.chef.keypair.description=Chef kütüğünde kimlik doğrulaması için bir anahtar çifti gereklidir. Eğer daha önce bir anahtar çifti ürettiyseniz, yeni bir anahtar çifti üretmek eski anahtar çiftini ıskartaya çıkartacaktır. [secrets] secrets=Gizlilikler @@ -3301,6 +3441,7 @@ status.waiting=Bekleniyor status.running=Çalışıyor status.success=Başarılı status.failure=Başarısız +status.cancelled=İptal edildi status.skipped=Atlandı status.blocked=Engellendi @@ -3317,6 +3458,7 @@ runners.labels=Etiketler runners.last_online=Son Görülme Zamanı runners.runner_title=Çalıştırıcı runners.task_list=Bu çalıştırıcıdaki son görevler +runners.task_list.no_tasks=Henüz bir görev yok. runners.task_list.run=Çalıştır runners.task_list.status=Durum runners.task_list.repository=Depo @@ -3341,13 +3483,38 @@ runners.reset_registration_token_success=Çalıştırıcı kayıt belirteci baş runs.all_workflows=Tüm İş Akışları runs.commit=İşle +runs.pushed_by=iten runs.invalid_workflow_helper=İş akışı yapılandırma dosyası geçersiz. Lütfen yapılandırma dosyanızı denetleyin: %s runs.no_matching_runner_helper=Eşleşen çalıştırıcı yok: %s +runs.actor=Aktör runs.status=Durum +runs.actors_no_select=Tüm aktörler +runs.status_no_select=Tüm durumlar +runs.no_results=Eşleşen sonuç yok. +runs.no_runs=İş akışı henüz hiç çalıştırılmadı. +workflow.disable=İş Akışını Devre Dışı Bırak +workflow.disable_success='%s' iş akışı başarıyla devre dışı bırakıldı. +workflow.enable=İş Akışını Etkinleştir +workflow.enable_success='%s' iş akışı başarıyla etkinleştirildi. need_approval_desc=Değişiklik isteği çatalında iş akışı çalıştırmak için onay gerekiyor. +variables=Değişkenler +variables.management=Değişken Yönetimi +variables.creation=Değişken Ekle +variables.none=Henüz hiçbir değişken yok. +variables.deletion=Değişkeni kaldır +variables.deletion.description=Bir değişkeni kaldırma kalıcıdır ve geri alınamaz. Devam edilsin mi? +variables.description=Değişkenler belirli işlemlere aktarılacaktır, bunun dışında okunamaz. +variables.id_not_exist=%d kimlikli değişken mevcut değil. +variables.edit=Değişkeni Düzenle +variables.deletion.failed=Değişken kaldırılamadı. +variables.deletion.success=Değişken kaldırıldı. +variables.creation.failed=Değişken eklenemedi. +variables.creation.success=`"%s" değişkeni eklendi.` +variables.update.failed=Değişken düzenlenemedi. +variables.update.success=Değişken düzenlendi. [projects] type-1.display_name=Kişisel Proje @@ -3355,5 +3522,10 @@ type-2.display_name=Depo Projesi type-3.display_name=Organizasyon Projesi [git.filemode] +changed_filemode=%[1]s → %[2]s +directory=Dizin +normal_file=Normal dosya +executable_file=Çalıştırılabilir dosya symbolic_link=Sembolik Bağlantı +submodule=Alt modül diff --git a/package-lock.json b/package-lock.json index 8e6a560a3b..dda23bcbd4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,21 +17,21 @@ "@primer/octicons": "19.6.0", "@webcomponents/custom-elements": "1.6.0", "add-asset-webpack-plugin": "2.0.1", - "ansi_up": "6.0.0", + "ansi_up": "6.0.2", "asciinema-player": "3.5.0", "clippie": "4.0.6", "css-loader": "6.8.1", "dropzone": "6.0.0-beta.2", "easymde": "2.18.0", - "esbuild-loader": "4.0.0", + "esbuild-loader": "4.0.2", "escape-goat": "4.0.0", "fast-glob": "3.3.1", - "jquery": "3.7.0", + "jquery": "3.7.1", "jquery.are-you-sure": "1.9.0", "katex": "0.16.8", "license-checker-webpack-plugin": "0.2.1", "lightningcss-loader": "2.1.0", - "mermaid": "10.3.1", + "mermaid": "10.4.0", "mini-css-extract-plugin": "2.7.6", "minimatch": "9.0.3", "monaco-editor": "0.41.0", @@ -58,18 +58,19 @@ "@eslint-community/eslint-plugin-eslint-comments": "4.1.0", "@playwright/test": "1.37.1", "@stoplight/spectral-cli": "6.10.1", - "@vitejs/plugin-vue": "4.3.1", - "eslint": "8.47.0", + "@vitejs/plugin-vue": "4.3.4", + "eslint": "8.48.0", "eslint-plugin-array-func": "3.1.8", "eslint-plugin-custom-elements": "0.0.8", - "eslint-plugin-import": "2.28.0", + "eslint-plugin-import": "2.28.1", "eslint-plugin-jquery": "1.5.1", "eslint-plugin-no-jquery": "2.7.0", "eslint-plugin-no-use-extend-native": "0.5.0", "eslint-plugin-regexp": "1.15.0", - "eslint-plugin-sonarjs": "0.20.0", + "eslint-plugin-sonarjs": "0.21.0", "eslint-plugin-unicorn": "48.0.1", "eslint-plugin-vue": "9.17.0", + "eslint-plugin-vue-scoped-css": "2.5.0", "eslint-plugin-wc": "1.5.0", "jsdom": "22.1.0", "markdownlint-cli": "0.35.0", @@ -79,9 +80,9 @@ "stylelint-declaration-strict-value": "1.9.2", "stylelint-stylistic": "0.4.3", "svgo": "3.0.2", - "updates": "14.3.5", + "updates": "14.4.0", "vite-string-plugin": "1.1.2", - "vitest": "0.34.2" + "vitest": "0.34.3" }, "engines": { "node": ">= 16.0.0" @@ -991,9 +992,9 @@ } }, "node_modules/@eslint/js": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.47.0.tgz", - "integrity": "sha512-P6omY1zv5MItm93kLM8s2vr1HICJH8v0dvddDhysbIuZ+vcjOHg5Zbkf1mTkcmi2JA9oBG2anOkRnW8WJTS8Og==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.48.0.tgz", + "integrity": "sha512-ZSjtmelB7IJfWD2Fvb7+Z+ChTIKWq6kjda95fLcQKNS5aheVHn4IkfgRQE3sIIzTcSLwLcLZUD9UBt+V7+h+Pw==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1145,9 +1146,9 @@ } }, "node_modules/@jest/schemas": { - "version": "29.6.0", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.0.tgz", - "integrity": "sha512-rxLjXyJBTL4LQeJW3aKo0M/+GkCOXsO+8i9Iu7eDb6KwtP65ayoDsitrdPBtujxQ88k4wI2FNYfa6TOGwSn6cQ==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", "dev": true, "dependencies": { "@sinclair/typebox": "^0.27.8" @@ -2029,9 +2030,9 @@ "dev": true }, "node_modules/@vitejs/plugin-vue": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-4.3.1.tgz", - "integrity": "sha512-tUBEtWcF7wFtII7ayNiLNDTCE1X1afySEo+XNVMNkFXaThENyCowIEX095QqbJZGTgoOcSVDJGlnde2NG4jtbQ==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-4.3.4.tgz", + "integrity": "sha512-ciXNIHKPriERBisHFBvnTbfKa6r9SAesOYXeGDzgegcvy9Q4xdScSHAmKbNT0M3O0S9LKhIf5/G+UYG4NnnzYw==", "dev": true, "engines": { "node": "^14.18.0 || >=16.0.0" @@ -2042,13 +2043,13 @@ } }, "node_modules/@vitest/expect": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-0.34.2.tgz", - "integrity": "sha512-EZm2dMNlLyIfDMha17QHSQcg2KjeAZaXd65fpPzXY5bvnfx10Lcaz3N55uEe8PhF+w4pw+hmrlHLLlRn9vkBJg==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-0.34.3.tgz", + "integrity": "sha512-F8MTXZUYRBVsYL1uoIft1HHWhwDbSzwAU9Zgh8S6WFC3YgVb4AnFV2GXO3P5Em8FjEYaZtTnQYoNwwBrlOMXgg==", "dev": true, "dependencies": { - "@vitest/spy": "0.34.2", - "@vitest/utils": "0.34.2", + "@vitest/spy": "0.34.3", + "@vitest/utils": "0.34.3", "chai": "^4.3.7" }, "funding": { @@ -2056,12 +2057,12 @@ } }, "node_modules/@vitest/runner": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-0.34.2.tgz", - "integrity": "sha512-8ydGPACVX5tK3Dl0SUwxfdg02h+togDNeQX3iXVFYgzF5odxvaou7HnquALFZkyVuYskoaHUOqOyOLpOEj5XTA==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-0.34.3.tgz", + "integrity": "sha512-lYNq7N3vR57VMKMPLVvmJoiN4bqwzZ1euTW+XXYH5kzr3W/+xQG3b41xJn9ChJ3AhYOSoweu974S1V3qDcFESA==", "dev": true, "dependencies": { - "@vitest/utils": "0.34.2", + "@vitest/utils": "0.34.3", "p-limit": "^4.0.0", "pathe": "^1.1.1" }, @@ -2097,9 +2098,9 @@ } }, "node_modules/@vitest/snapshot": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-0.34.2.tgz", - "integrity": "sha512-qhQ+xy3u4mwwLxltS4Pd4SR+XHv4EajiTPNY3jkIBLUApE6/ce72neJPSUQZ7bL3EBuKI+NhvzhGj3n5baRQUQ==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-0.34.3.tgz", + "integrity": "sha512-QyPaE15DQwbnIBp/yNJ8lbvXTZxS00kRly0kfFgAD5EYmCbYcA+1EEyRalc93M0gosL/xHeg3lKAClIXYpmUiQ==", "dev": true, "dependencies": { "magic-string": "^0.30.1", @@ -2111,9 +2112,9 @@ } }, "node_modules/@vitest/snapshot/node_modules/magic-string": { - "version": "0.30.2", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.2.tgz", - "integrity": "sha512-lNZdu7pewtq/ZvWUp9Wpf/x7WzMTsR26TWV03BRZrXFsv+BI6dy8RAiKgm1uM/kyR0rCfUcqvOlXKG66KhIGug==", + "version": "0.30.3", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.3.tgz", + "integrity": "sha512-B7xGbll2fG/VjP+SWg4sX3JynwIU0mjoTc6MPpKNuIvftk6u6vqhDnk1R80b8C2GBR6ywqy+1DcKBrevBg+bmw==", "dev": true, "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" @@ -2123,9 +2124,9 @@ } }, "node_modules/@vitest/spy": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-0.34.2.tgz", - "integrity": "sha512-yd4L9OhfH6l0Av7iK3sPb3MykhtcRN5c5K5vm1nTbuN7gYn+yvUVVsyvzpHrjqS7EWqn9WsPJb7+0c3iuY60tA==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-0.34.3.tgz", + "integrity": "sha512-N1V0RFQ6AI7CPgzBq9kzjRdPIgThC340DGjdKdPSE8r86aUSmeliTUgkTqLSgtEwWWsGfBQ+UetZWhK0BgJmkQ==", "dev": true, "dependencies": { "tinyspy": "^2.1.1" @@ -2135,9 +2136,9 @@ } }, "node_modules/@vitest/utils": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-0.34.2.tgz", - "integrity": "sha512-Lzw+kAsTPubhoQDp1uVAOP6DhNia1GMDsI9jgB0yMn+/nDaPieYQ88lKqz/gGjSHL4zwOItvpehec9OY+rS73w==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-0.34.3.tgz", + "integrity": "sha512-kiSnzLG6m/tiT0XEl4U2H8JDBjFtwVlaE8I3QfGiMFR0QvnRDfYfdP3YvTBWM/6iJDAyaPY6yVQiCTUc7ZzTHA==", "dev": true, "dependencies": { "diff-sequences": "^29.4.3", @@ -2606,9 +2607,9 @@ } }, "node_modules/ansi_up": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/ansi_up/-/ansi_up-6.0.0.tgz", - "integrity": "sha512-3lnYPwXYbCSXQw52OWemps5mgfYuNsX3R5fgkHXoUDpRe8Ex/ivir1AdQLUWkdpQLyQZS/v3ofY8JGQDORwuLQ==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/ansi_up/-/ansi_up-6.0.2.tgz", + "integrity": "sha512-3G3vKvl1ilEp7J1u6BmULpMA0xVoW/f4Ekqhl8RTrJrhEBkonKn5k3bUc5Xt+qDayA6iDX0jyUh3AbZjB/l0tw==", "engines": { "node": "*" } @@ -2836,6 +2837,18 @@ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", "dev": true }, + "node_modules/atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true, + "bin": { + "atob": "bin/atob.js" + }, + "engines": { + "node": ">= 4.5.0" + } + }, "node_modules/available-typed-arrays": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", @@ -3075,9 +3088,9 @@ ] }, "node_modules/chai": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.7.tgz", - "integrity": "sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==", + "version": "4.3.8", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.8.tgz", + "integrity": "sha512-vX4YvVVtxlfSZ2VecZgFUTU5qPCYsobVI2O9FmwEXBhDigYGQA6jRXCycIs1yJnnWbZ6/+a2zNIF5DfVCcJBFQ==", "dev": true, "dependencies": { "assertion-error": "^1.1.0", @@ -3350,6 +3363,17 @@ "node": ">= 8" } }, + "node_modules/css": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/css/-/css-3.0.0.tgz", + "integrity": "sha512-DG9pFfwOrzc+hawpmqX/dHYHJG+Bsdb0klhyi1sDneOgGOXy9wQIC8hzyVp1e4NRYDBdxcylvywPkkXCHAzTyQ==", + "dev": true, + "dependencies": { + "inherits": "^2.0.4", + "source-map": "^0.6.1", + "source-map-resolve": "^0.6.0" + } + }, "node_modules/css-color-names": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", @@ -4092,6 +4116,15 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/decode-uri-component": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", + "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", + "dev": true, + "engines": { + "node": ">=0.10" + } + }, "node_modules/deep-eql": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", @@ -4189,9 +4222,9 @@ } }, "node_modules/diff-sequences": { - "version": "29.4.3", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.4.3.tgz", - "integrity": "sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", "dev": true, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -4563,12 +4596,12 @@ } }, "node_modules/esbuild-loader": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/esbuild-loader/-/esbuild-loader-4.0.0.tgz", - "integrity": "sha512-J7TJWyHV2YHmflZaXLZ0Vf4wYmixDyGTw26bt4Ok+XOqSyYA4VWAVt2zJGqIfCA7TwZRDKN8hvus4akN2yAbmA==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/esbuild-loader/-/esbuild-loader-4.0.2.tgz", + "integrity": "sha512-kj88m0yrtTEJDeUEF+3TZsq7t9VPzQQj7UmXAzUbIaipoYSrd0UxKAcg4l9CBgP8uVoploiw+nKr8DIv6Y9gXw==", "dependencies": { "esbuild": "^0.19.0", - "get-tsconfig": "^4.6.2", + "get-tsconfig": "^4.7.0", "loader-utils": "^2.0.4", "webpack-sources": "^1.4.3" }, @@ -4611,15 +4644,15 @@ } }, "node_modules/eslint": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.47.0.tgz", - "integrity": "sha512-spUQWrdPt+pRVP1TTJLmfRNJJHHZryFmptzcafwSvHsceV81djHOdnEeDmkdotZyLNjDhrOasNK8nikkoG1O8Q==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.48.0.tgz", + "integrity": "sha512-sb6DLeIuRXxeM1YljSe1KEx9/YYeZFQWcV8Rq9HfigmdDEugjLEVEa1ozDjL6YDjBpQHPJxJzze+alxi4T3OLg==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.2", - "@eslint/js": "^8.47.0", + "@eslint/js": "8.48.0", "@humanwhocodes/config-array": "^0.11.10", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", @@ -4732,9 +4765,9 @@ } }, "node_modules/eslint-plugin-import": { - "version": "2.28.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.28.0.tgz", - "integrity": "sha512-B8s/n+ZluN7sxj9eUf7/pRFERX0r5bnFA2dCaLHy2ZeaQEAz0k+ZZkFWRFHJAqxfxQDx6KLv9LeIki7cFdwW+Q==", + "version": "2.28.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.28.1.tgz", + "integrity": "sha512-9I9hFlITvOV55alzoKBI+K9q74kv0iKMeY6av5+umsNwayt59fz692daGyjR+oStBQgx6nwR9rXldDev3Clw+A==", "dev": true, "dependencies": { "array-includes": "^3.1.6", @@ -4746,13 +4779,12 @@ "eslint-import-resolver-node": "^0.3.7", "eslint-module-utils": "^2.8.0", "has": "^1.0.3", - "is-core-module": "^2.12.1", + "is-core-module": "^2.13.0", "is-glob": "^4.0.3", "minimatch": "^3.1.2", "object.fromentries": "^2.0.6", "object.groupby": "^1.0.0", "object.values": "^1.1.6", - "resolve": "^1.22.3", "semver": "^6.3.1", "tsconfig-paths": "^3.14.2" }, @@ -4871,9 +4903,9 @@ } }, "node_modules/eslint-plugin-sonarjs": { - "version": "0.20.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-sonarjs/-/eslint-plugin-sonarjs-0.20.0.tgz", - "integrity": "sha512-BRhZ7BY/oTr6DDaxvx58ReTg7R+J8T+Y2ZVGgShgpml25IHBTIG7EudUtHuJD1zhtMgUEt59x3VNvUQRo2LV6w==", + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-sonarjs/-/eslint-plugin-sonarjs-0.21.0.tgz", + "integrity": "sha512-oezUDfFT5S6j3rQheZ4DLPrbetPmMS7zHIKWGHr0CM3g5JgyZroz1FpIKa4jV83NsGpmgIeagpokWDKIJzRQmw==", "dev": true, "engines": { "node": ">=14" @@ -4935,6 +4967,31 @@ "eslint": "^6.2.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/eslint-plugin-vue-scoped-css": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-vue-scoped-css/-/eslint-plugin-vue-scoped-css-2.5.0.tgz", + "integrity": "sha512-vR+raYNE1aQ69lS1lZGiKoz8rXFI3MWf2fxrfns/XCQ0XT5sIguhDtQS+9JmUQJClenLDEe2CQx7P+eeSdF4cA==", + "dev": true, + "dependencies": { + "eslint-utils": "^3.0.0", + "lodash": "^4.17.21", + "postcss": "^8.4.6", + "postcss-safe-parser": "^6.0.0", + "postcss-scss": "^4.0.3", + "postcss-selector-parser": "^6.0.9", + "postcss-styl": "^0.12.0" + }, + "engines": { + "node": "^12.22 || ^14.17 || >=16" + }, + "funding": { + "url": "https://github.com/sponsors/ota-meshi" + }, + "peerDependencies": { + "eslint": ">=5.0.0", + "vue-eslint-parser": ">=7.1.0" + } + }, "node_modules/eslint-plugin-wc": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/eslint-plugin-wc/-/eslint-plugin-wc-1.5.0.tgz", @@ -4964,6 +5021,33 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^2.0.0" + }, + "engines": { + "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=5" + } + }, + "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, "node_modules/eslint-visitor-keys": { "version": "3.4.3", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", @@ -5104,6 +5188,12 @@ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true + }, "node_modules/fast-glob": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", @@ -6372,9 +6462,9 @@ } }, "node_modules/jquery": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.0.tgz", - "integrity": "sha512-umpJ0/k8X0MvD1ds0P9SfowREz2LenHsQaxSohMZ5OMNEU2r0tf8pdeEFTHMFxWVxKNyU9rTtK3CWzUCTKJUeQ==" + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz", + "integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==" }, "node_modules/jquery.are-you-sure": { "version": "1.9.0", @@ -6961,6 +7051,12 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "node_modules/lodash.sortedlastindex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/lodash.sortedlastindex/-/lodash.sortedlastindex-4.1.0.tgz", + "integrity": "sha512-s8xEQdsp2Tu5zUqVdFSe9C0kR8YlnAJYLqMdkh+pIRBRxF6/apWseLdHl3/+jv2I61dhPwtI/Ff+EqvCpc+N8w==", + "dev": true + }, "node_modules/lodash.template": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz", @@ -7320,9 +7416,9 @@ } }, "node_modules/mermaid": { - "version": "10.3.1", - "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-10.3.1.tgz", - "integrity": "sha512-hkenh7WkuRWPcob3oJtrN3W+yzrrIYuWF1OIfk/d0xGE8UWlvDhfexaHmDwwe8DKQgqMLI8DWEPwGprxkumjuw==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-10.4.0.tgz", + "integrity": "sha512-4QCQLp79lvz7UZxow5HUX7uWTPJOaQBVExduo91tliXC7v78i6kssZOPHxLL+Xs30KU72cpPn3g3imw/xm/gaw==", "dependencies": { "@braintree/sanitize-url": "^6.0.1", "@types/d3-scale": "^4.0.3", @@ -7872,15 +7968,15 @@ } }, "node_modules/mlly": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.4.0.tgz", - "integrity": "sha512-ua8PAThnTwpprIaU47EPeZ/bPUVp2QYBbWMphUQpVdBI3Lgqzm5KZQ45Agm3YJedHXaIHl6pBGabaLSUPPSptg==", + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.4.1.tgz", + "integrity": "sha512-SCDs78Q2o09jiZiE2WziwVBEqXQ02XkGdUy45cbJf+BpYRIjArXRJ1Wbowxkb+NaM9DWvS3UC9GiO/6eqvQ/pg==", "dev": true, "dependencies": { - "acorn": "^8.9.0", + "acorn": "^8.10.0", "pathe": "^1.1.1", "pkg-types": "^1.0.3", - "ufo": "^1.1.2" + "ufo": "^1.3.0" } }, "node_modules/monaco-editor": { @@ -8628,6 +8724,32 @@ "postcss": "^8.3.3" } }, + "node_modules/postcss-scss": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/postcss-scss/-/postcss-scss-4.0.7.tgz", + "integrity": "sha512-xPv2GseoyXPa58Nro7M73ZntttusuCmZdeOojUFR5PZDz2BR62vfYx1w9TyOnp1+nYFowgOMipsCBhxzVkAEPw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss-scss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.4.19" + } + }, "node_modules/postcss-selector-parser": { "version": "6.0.13", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz", @@ -8640,6 +8762,25 @@ "node": ">=4" } }, + "node_modules/postcss-styl": { + "version": "0.12.3", + "resolved": "https://registry.npmjs.org/postcss-styl/-/postcss-styl-0.12.3.tgz", + "integrity": "sha512-8I7Cd8sxiEITIp32xBK4K/Aj1ukX6vuWnx8oY/oAH35NfQI4OZaY5nd68Yx8HeN5S49uhQ6DL0rNk0ZBu/TaLg==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "fast-diff": "^1.2.0", + "lodash.sortedlastindex": "^4.1.0", + "postcss": "^7.0.27 || ^8.0.0", + "stylus": "^0.57.0" + }, + "engines": { + "node": "^8.10.0 || ^10.13.0 || ^11.10.1 || >=12.13.0" + }, + "funding": { + "url": "https://opencollective.com/stylus" + } + }, "node_modules/postcss-value-parser": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", @@ -8655,12 +8796,12 @@ } }, "node_modules/pretty-format": { - "version": "29.6.2", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.6.2.tgz", - "integrity": "sha512-1q0oC8eRveTg5nnBEWMXAU2qpv65Gnuf2eCQzSjxpWFkPaPARwqZZDGuNE0zPAZfTCHzIk3A8dIjwlQKKLphyg==", + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.6.3.tgz", + "integrity": "sha512-ZsBgjVhFAj5KeK+nHfF1305/By3lechHQSMWCTl8iHSbfOm2TN5nHEtFc/+W7fAyUeCs2n5iow72gld4gW0xDw==", "dev": true, "dependencies": { - "@jest/schemas": "^29.6.0", + "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", "react-is": "^18.0.0" }, @@ -9256,6 +9397,12 @@ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, + "node_modules/sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "dev": true + }, "node_modules/saxes": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", @@ -9471,6 +9618,17 @@ "node": ">=0.10.0" } }, + "node_modules/source-map-resolve": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.6.0.tgz", + "integrity": "sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w==", + "deprecated": "See https://github.com/lydell/source-map-resolve#deprecated", + "dev": true, + "dependencies": { + "atob": "^2.1.2", + "decode-uri-component": "^0.2.0" + } + }, "node_modules/source-map-support": { "version": "0.5.21", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", @@ -9838,6 +9996,35 @@ "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.0.tgz", "integrity": "sha512-E87pIogpwUsUwXw7dNyU4QDjdgVMy52m+XEOPEKUn161cCzWjjhPSQhByfd1CcNvrOLnXQ6OnnZDwnJrz/Z4YQ==" }, + "node_modules/stylus": { + "version": "0.57.0", + "resolved": "https://registry.npmjs.org/stylus/-/stylus-0.57.0.tgz", + "integrity": "sha512-yOI6G8WYfr0q8v8rRvE91wbxFU+rJPo760Va4MF6K0I6BZjO4r+xSynkvyPBP9tV1CIEUeRsiidjIs2rzb1CnQ==", + "dev": true, + "dependencies": { + "css": "^3.0.0", + "debug": "^4.3.2", + "glob": "^7.1.6", + "safer-buffer": "^2.1.2", + "sax": "~1.2.4", + "source-map": "^0.7.3" + }, + "bin": { + "stylus": "bin/stylus" + }, + "engines": { + "node": "*" + } + }, + "node_modules/stylus/node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, "node_modules/superstruct": { "version": "0.10.13", "resolved": "https://registry.npmjs.org/superstruct/-/superstruct-0.10.13.tgz", @@ -10323,9 +10510,9 @@ "dev": true }, "node_modules/ufo": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.2.0.tgz", - "integrity": "sha512-RsPyTbqORDNDxqAdQPQBpgqhWle1VcTSou/FraClYlHf6TZnQcGslpLcAphNR+sQW4q5lLWLbOsRlh9j24baQg==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.3.0.tgz", + "integrity": "sha512-bRn3CsoojyNStCZe0BG0Mt4Nr/4KF+rhFlnNXybgqt5pXHNFRlqinSoQaTrGyzE4X8aHplSb+TorH+COin9Yxw==", "dev": true }, "node_modules/uint8-to-base64": { @@ -10399,9 +10586,9 @@ } }, "node_modules/updates": { - "version": "14.3.5", - "resolved": "https://registry.npmjs.org/updates/-/updates-14.3.5.tgz", - "integrity": "sha512-kx1sm2RXd9guF3lAmAaC8mpfAlG5iSPHiPkSJtEC5d/Gaa+NoxwxcuySb0c5pBFlzuGGU8ZxxQ0qRl9HLfwRRg==", + "version": "14.4.0", + "resolved": "https://registry.npmjs.org/updates/-/updates-14.4.0.tgz", + "integrity": "sha512-fAB49LEq46XlJfQmLDWHt3Yt7XpSAxj1GwO6MxgEMHlGbhyGLSNu2hPYuSzipNRhO7phJNp8UDi0kikn/RAwwQ==", "dev": true, "bin": { "updates": "bin/updates.js" @@ -10548,9 +10735,9 @@ } }, "node_modules/vite-node": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-0.34.2.tgz", - "integrity": "sha512-JtW249Zm3FB+F7pQfH56uWSdlltCo1IOkZW5oHBzeQo0iX4jtC7o1t9aILMGd9kVekXBP2lfJBEQt9rBh07ebA==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-0.34.3.tgz", + "integrity": "sha512-+0TzJf1g0tYXj6tR2vEyiA42OPq68QkRZCu/ERSo2PtsDJfBpDyEfuKbRvLmZqi/CgC7SCBtyC+WjTGNMRIaig==", "dev": true, "dependencies": { "cac": "^6.7.14", @@ -10985,19 +11172,19 @@ } }, "node_modules/vitest": { - "version": "0.34.2", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-0.34.2.tgz", - "integrity": "sha512-WgaIvBbjsSYMq/oiMlXUI7KflELmzM43BEvkdC/8b5CAod4ryAiY2z8uR6Crbi5Pjnu5oOmhKa9sy7uk6paBxQ==", + "version": "0.34.3", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-0.34.3.tgz", + "integrity": "sha512-7+VA5Iw4S3USYk+qwPxHl8plCMhA5rtfwMjgoQXMT7rO5ldWcdsdo3U1QD289JgglGK4WeOzgoLTsGFu6VISyQ==", "dev": true, "dependencies": { "@types/chai": "^4.3.5", "@types/chai-subset": "^1.3.3", "@types/node": "*", - "@vitest/expect": "0.34.2", - "@vitest/runner": "0.34.2", - "@vitest/snapshot": "0.34.2", - "@vitest/spy": "0.34.2", - "@vitest/utils": "0.34.2", + "@vitest/expect": "0.34.3", + "@vitest/runner": "0.34.3", + "@vitest/snapshot": "0.34.3", + "@vitest/spy": "0.34.3", + "@vitest/utils": "0.34.3", "acorn": "^8.9.0", "acorn-walk": "^8.2.0", "cac": "^6.7.14", @@ -11012,7 +11199,7 @@ "tinybench": "^2.5.0", "tinypool": "^0.7.0", "vite": "^3.0.0 || ^4.0.0", - "vite-node": "0.34.2", + "vite-node": "0.34.3", "why-is-node-running": "^2.2.2" }, "bin": { diff --git a/package.json b/package.json index bc35d77e33..e4f1743feb 100644 --- a/package.json +++ b/package.json @@ -16,21 +16,21 @@ "@primer/octicons": "19.6.0", "@webcomponents/custom-elements": "1.6.0", "add-asset-webpack-plugin": "2.0.1", - "ansi_up": "6.0.0", + "ansi_up": "6.0.2", "asciinema-player": "3.5.0", "clippie": "4.0.6", "css-loader": "6.8.1", "dropzone": "6.0.0-beta.2", "easymde": "2.18.0", - "esbuild-loader": "4.0.0", + "esbuild-loader": "4.0.2", "escape-goat": "4.0.0", "fast-glob": "3.3.1", - "jquery": "3.7.0", + "jquery": "3.7.1", "jquery.are-you-sure": "1.9.0", "katex": "0.16.8", "license-checker-webpack-plugin": "0.2.1", "lightningcss-loader": "2.1.0", - "mermaid": "10.3.1", + "mermaid": "10.4.0", "mini-css-extract-plugin": "2.7.6", "minimatch": "9.0.3", "monaco-editor": "0.41.0", @@ -57,18 +57,19 @@ "@eslint-community/eslint-plugin-eslint-comments": "4.1.0", "@playwright/test": "1.37.1", "@stoplight/spectral-cli": "6.10.1", - "@vitejs/plugin-vue": "4.3.1", - "eslint": "8.47.0", + "@vitejs/plugin-vue": "4.3.4", + "eslint": "8.48.0", "eslint-plugin-array-func": "3.1.8", "eslint-plugin-custom-elements": "0.0.8", - "eslint-plugin-import": "2.28.0", + "eslint-plugin-import": "2.28.1", "eslint-plugin-jquery": "1.5.1", "eslint-plugin-no-jquery": "2.7.0", "eslint-plugin-no-use-extend-native": "0.5.0", "eslint-plugin-regexp": "1.15.0", - "eslint-plugin-sonarjs": "0.20.0", + "eslint-plugin-sonarjs": "0.21.0", "eslint-plugin-unicorn": "48.0.1", "eslint-plugin-vue": "9.17.0", + "eslint-plugin-vue-scoped-css": "2.5.0", "eslint-plugin-wc": "1.5.0", "jsdom": "22.1.0", "markdownlint-cli": "0.35.0", @@ -78,9 +79,9 @@ "stylelint-declaration-strict-value": "1.9.2", "stylelint-stylistic": "0.4.3", "svgo": "3.0.2", - "updates": "14.3.5", + "updates": "14.4.0", "vite-string-plugin": "1.1.2", - "vitest": "0.34.2" + "vitest": "0.34.3" }, "browserslist": [ "defaults", diff --git a/routers/api/actions/runner/utils.go b/routers/api/actions/runner/utils.go index e95df7a00f..24432ab6b2 100644 --- a/routers/api/actions/runner/utils.go +++ b/routers/api/actions/runner/utils.go @@ -10,6 +10,7 @@ import ( actions_model "code.gitea.io/gitea/models/actions" secret_model "code.gitea.io/gitea/models/secret" actions_module "code.gitea.io/gitea/modules/actions" + "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/log" @@ -55,8 +56,12 @@ func pickTask(ctx context.Context, runner *actions_model.ActionRunner) (*runnerv func getSecretsOfTask(ctx context.Context, task *actions_model.ActionTask) map[string]string { secrets := map[string]string{} + + secrets["GITHUB_TOKEN"] = task.Token + secrets["GITEA_TOKEN"] = task.Token + if task.Job.Run.IsForkPullRequest && task.Job.Run.TriggerEvent != actions_module.GithubEventPullRequestTarget { - // ignore secrets for fork pull request + // ignore secrets for fork pull request, except GITHUB_TOKEN and GITEA_TOKEN which are automatically generated. // for the tasks triggered by pull_request_target event, they could access the secrets because they will run in the context of the base branch // see the documentation: https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_target return secrets @@ -82,13 +87,6 @@ func getSecretsOfTask(ctx context.Context, task *actions_model.ActionTask) map[s } } - if _, ok := secrets["GITHUB_TOKEN"]; !ok { - secrets["GITHUB_TOKEN"] = task.Token - } - if _, ok := secrets["GITEA_TOKEN"]; !ok { - secrets["GITEA_TOKEN"] = task.Token - } - return secrets } @@ -200,10 +198,7 @@ func findTaskNeeds(ctx context.Context, task *actions_model.ActionTask) (map[str if len(task.Job.Needs) == 0 { return nil, nil } - needs := map[string]struct{}{} - for _, v := range task.Job.Needs { - needs[v] = struct{}{} - } + needs := container.SetOf(task.Job.Needs...) jobs, _, err := actions_model.FindRunJobs(ctx, actions_model.FindRunJobOptions{RunID: task.Job.RunID}) if err != nil { @@ -212,7 +207,7 @@ func findTaskNeeds(ctx context.Context, task *actions_model.ActionTask) (map[str ret := make(map[string]*runnerv1.TaskNeed, len(needs)) for _, job := range jobs { - if _, ok := needs[job.JobID]; !ok { + if !needs.Contains(job.JobID) { continue } if job.TaskID == 0 || !job.Status.IsDone() { diff --git a/routers/api/packages/cargo/cargo.go b/routers/api/packages/cargo/cargo.go index a2e835df57..8c370339cd 100644 --- a/routers/api/packages/cargo/cargo.go +++ b/routers/api/packages/cargo/cargo.go @@ -16,6 +16,8 @@ import ( "code.gitea.io/gitea/modules/log" packages_module "code.gitea.io/gitea/modules/packages" cargo_module "code.gitea.io/gitea/modules/packages/cargo" + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/routers/api/packages/helper" "code.gitea.io/gitea/services/convert" @@ -48,7 +50,7 @@ func apiError(ctx *context.Context, status int, obj any) { // https://rust-lang.github.io/rfcs/2789-sparse-index.html func RepositoryConfig(ctx *context.Context) { - ctx.JSON(http.StatusOK, cargo_service.BuildConfig(ctx.Package.Owner)) + ctx.JSON(http.StatusOK, cargo_service.BuildConfig(ctx.Package.Owner, setting.Service.RequireSignInView || ctx.Package.Owner.Visibility != structs.VisibleTypePublic)) } func EnumeratePackageVersions(ctx *context.Context) { diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index 3ec1b8a1fa..77c97fca1c 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -933,6 +933,10 @@ func Routes() *web.Route { m.Post("/accept", repo.AcceptTransfer) m.Post("/reject", repo.RejectTransfer) }, reqToken()) + m.Group("/actions/secrets", func() { + m.Combo("/{secretname}"). + Put(reqToken(), reqOwner(), bind(api.CreateOrUpdateSecretOption{}), repo.CreateOrUpdateSecret) + }) m.Group("/hooks/git", func() { m.Combo("").Get(repo.ListGitHooks) m.Group("/{id}", func() { @@ -1303,7 +1307,9 @@ func Routes() *web.Route { }) m.Group("/actions/secrets", func() { m.Get("", reqToken(), reqOrgOwnership(), org.ListActionsSecrets) - m.Post("", reqToken(), reqOrgOwnership(), bind(api.CreateSecretOption{}), org.CreateOrgSecret) + m.Combo("/{secretname}"). + Put(reqToken(), reqOrgOwnership(), bind(api.CreateOrUpdateSecretOption{}), org.CreateOrUpdateSecret). + Delete(reqToken(), reqOrgOwnership(), org.DeleteOrgSecret) }) m.Group("/public_members", func() { m.Get("", org.ListPublicMembers) diff --git a/routers/api/v1/org/action.go b/routers/api/v1/org/action.go index 7659191946..0bf741e825 100644 --- a/routers/api/v1/org/action.go +++ b/routers/api/v1/org/action.go @@ -12,7 +12,6 @@ import ( "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/utils" "code.gitea.io/gitea/routers/web/shared/actions" - "code.gitea.io/gitea/services/convert" ) // ListActionsSecrets list an organization's actions secrets @@ -74,11 +73,11 @@ func listActionsSecrets(ctx *context.APIContext) { ctx.JSON(http.StatusOK, apiSecrets) } -// CreateOrgSecret create one secret of the organization -func CreateOrgSecret(ctx *context.APIContext) { - // swagger:operation POST /orgs/{org}/actions/secrets organization createOrgSecret +// create or update one secret of the organization +func CreateOrUpdateSecret(ctx *context.APIContext) { + // swagger:operation PUT /orgs/{org}/actions/secrets/{secretname} organization updateOrgSecret // --- - // summary: Create a secret in an organization + // summary: Create or Update a secret value in an organization // consumes: // - application/json // produces: @@ -89,27 +88,80 @@ func CreateOrgSecret(ctx *context.APIContext) { // description: name of organization // type: string // required: true + // - name: secretname + // in: path + // description: name of the secret + // type: string + // required: true // - name: body // in: body // schema: - // "$ref": "#/definitions/CreateSecretOption" + // "$ref": "#/definitions/CreateOrUpdateSecretOption" // responses: // "201": - // "$ref": "#/responses/Secret" + // description: response when creating a secret + // "204": + // description: response when updating a secret // "400": // "$ref": "#/responses/error" - // "404": - // "$ref": "#/responses/notFound" // "403": // "$ref": "#/responses/forbidden" - opt := web.GetForm(ctx).(*api.CreateSecretOption) - s, err := secret_model.InsertEncryptedSecret( - ctx, ctx.Org.Organization.ID, 0, opt.Name, actions.ReserveLineBreakForTextarea(opt.Data), - ) + secretName := ctx.Params(":secretname") + if err := actions.NameRegexMatch(secretName); err != nil { + ctx.Error(http.StatusBadRequest, "CreateOrUpdateSecret", err) + return + } + opt := web.GetForm(ctx).(*api.CreateOrUpdateSecretOption) + isCreated, err := secret_model.CreateOrUpdateSecret(ctx, ctx.Org.Organization.ID, 0, secretName, opt.Data) if err != nil { - ctx.Error(http.StatusInternalServerError, "InsertEncryptedSecret", err) + ctx.Error(http.StatusInternalServerError, "CreateOrUpdateSecret", err) + return + } + if isCreated { + ctx.Status(http.StatusCreated) return } - ctx.JSON(http.StatusCreated, convert.ToSecret(s)) + ctx.Status(http.StatusNoContent) +} + +// DeleteOrgSecret delete one secret of the organization +func DeleteOrgSecret(ctx *context.APIContext) { + // swagger:operation DELETE /orgs/{org}/actions/secrets/{secretname} organization deleteOrgSecret + // --- + // summary: Delete a secret in an organization + // consumes: + // - application/json + // produces: + // - application/json + // parameters: + // - name: org + // in: path + // description: name of organization + // type: string + // required: true + // - name: secretname + // in: path + // description: name of the secret + // type: string + // required: true + // responses: + // "204": + // description: delete one secret of the organization + // "403": + // "$ref": "#/responses/forbidden" + secretName := ctx.Params(":secretname") + err := secret_model.DeleteSecret( + ctx, ctx.Org.Organization.ID, 0, secretName, + ) + if secret_model.IsErrSecretNotFound(err) { + ctx.NotFound(err) + return + } + if err != nil { + ctx.Error(http.StatusInternalServerError, "DeleteSecret", err) + return + } + + ctx.Status(http.StatusNoContent) } diff --git a/routers/api/v1/repo/action.go b/routers/api/v1/repo/action.go new file mode 100644 index 0000000000..015c731a75 --- /dev/null +++ b/routers/api/v1/repo/action.go @@ -0,0 +1,75 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package repo + +import ( + "net/http" + + secret_model "code.gitea.io/gitea/models/secret" + "code.gitea.io/gitea/modules/context" + api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/routers/web/shared/actions" +) + +// create or update one secret of the repository +func CreateOrUpdateSecret(ctx *context.APIContext) { + // swagger:operation PUT /repos/{owner}/{repo}/actions/secrets/{secretname} repository updateRepoSecret + // --- + // summary: Create or Update a secret value in a repository + // consumes: + // - application/json + // produces: + // - application/json + // parameters: + // - name: owner + // in: path + // description: owner of the repository + // type: string + // required: true + // - name: repo + // in: path + // description: name of the repository + // type: string + // required: true + // - name: secretname + // in: path + // description: name of the secret + // type: string + // required: true + // - name: body + // in: body + // schema: + // "$ref": "#/definitions/CreateOrUpdateSecretOption" + // responses: + // "201": + // description: response when creating a secret + // "204": + // description: response when updating a secret + // "400": + // "$ref": "#/responses/error" + // "403": + // "$ref": "#/responses/forbidden" + + owner := ctx.Repo.Owner + repo := ctx.Repo.Repository + + secretName := ctx.Params(":secretname") + if err := actions.NameRegexMatch(secretName); err != nil { + ctx.Error(http.StatusBadRequest, "CreateOrUpdateSecret", err) + return + } + opt := web.GetForm(ctx).(*api.CreateOrUpdateSecretOption) + isCreated, err := secret_model.CreateOrUpdateSecret(ctx, owner.ID, repo.ID, secretName, opt.Data) + if err != nil { + ctx.Error(http.StatusInternalServerError, "CreateOrUpdateSecret", err) + return + } + if isCreated { + ctx.Status(http.StatusCreated) + return + } + + ctx.Status(http.StatusNoContent) +} diff --git a/routers/api/v1/repo/branch.go b/routers/api/v1/repo/branch.go index 577776dadd..cdc176b8e4 100644 --- a/routers/api/v1/repo/branch.go +++ b/routers/api/v1/repo/branch.go @@ -768,7 +768,8 @@ func EditBranchProtection(ctx *context.APIContext) { if form.EnableStatusCheck != nil { protectBranch.EnableStatusCheck = *form.EnableStatusCheck } - if protectBranch.EnableStatusCheck { + + if form.StatusCheckContexts != nil { protectBranch.StatusCheckContexts = form.StatusCheckContexts } diff --git a/routers/api/v1/repo/mirror.go b/routers/api/v1/repo/mirror.go index 9d8497927e..60f1bfe0d3 100644 --- a/routers/api/v1/repo/mirror.go +++ b/routers/api/v1/repo/mirror.go @@ -14,7 +14,6 @@ import ( repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unit" "code.gitea.io/gitea/modules/context" - mirror_module "code.gitea.io/gitea/modules/mirror" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" @@ -70,7 +69,7 @@ func MirrorSync(ctx *context.APIContext) { return } - mirror_module.AddPullMirrorToQueue(repo.ID) + mirror_service.AddPullMirrorToQueue(repo.ID) ctx.Status(http.StatusOK) } diff --git a/routers/api/v1/repo/pull.go b/routers/api/v1/repo/pull.go index a507c1f44d..58f2fc69ce 100644 --- a/routers/api/v1/repo/pull.go +++ b/routers/api/v1/repo/pull.go @@ -326,11 +326,9 @@ func CreatePullRequest(ctx *context.APIContext) { return } - labelIDs = make([]int64, len(form.Labels)) - orgLabelIDs := make([]int64, len(form.Labels)) - - for i := range labels { - labelIDs[i] = labels[i].ID + labelIDs = make([]int64, 0, len(labels)) + for _, label := range labels { + labelIDs = append(labelIDs, label.ID) } if ctx.Repo.Owner.IsOrganization() { @@ -340,12 +338,12 @@ func CreatePullRequest(ctx *context.APIContext) { return } - for i := range orgLabels { - orgLabelIDs[i] = orgLabels[i].ID + orgLabelIDs := make([]int64, 0, len(orgLabels)) + for _, orgLabel := range orgLabels { + orgLabelIDs = append(orgLabelIDs, orgLabel.ID) } + labelIDs = append(labelIDs, orgLabelIDs...) } - - labelIDs = append(labelIDs, orgLabelIDs...) } if form.Milestone > 0 { diff --git a/routers/api/v1/swagger/options.go b/routers/api/v1/swagger/options.go index 8e7e6ec3df..6f7859df62 100644 --- a/routers/api/v1/swagger/options.go +++ b/routers/api/v1/swagger/options.go @@ -189,5 +189,5 @@ type swaggerParameterBodies struct { UpdateRepoAvatarOptions api.UpdateRepoAvatarOption // in:body - CreateSecretOption api.CreateSecretOption + CreateOrUpdateSecretOption api.CreateOrUpdateSecretOption } diff --git a/routers/install/install.go b/routers/install/install.go index 99ea7b0738..6d60dfdca3 100644 --- a/routers/install/install.go +++ b/routers/install/install.go @@ -60,17 +60,9 @@ func Contexter() func(next http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { base, baseCleanUp := context.NewBaseContext(resp, req) - ctx := &context.Context{ - Base: base, - Flash: &middleware.Flash{}, - Render: rnd, - Session: session.GetSession(req), - } defer baseCleanUp() - ctx.TemplateContext = context.NewTemplateContext(ctx) - ctx.TemplateContext["Locale"] = ctx.Locale - + ctx := context.NewWebContext(base, rnd, session.GetSession(req)) ctx.AppendContextValue(context.WebContextKey, ctx) ctx.Data.MergeFrom(middleware.CommonTemplateContextData()) ctx.Data.MergeFrom(middleware.ContextData{ @@ -189,7 +181,7 @@ func checkDatabase(ctx *context.Context, form *forms.InstallForm) bool { if err = db.InitEngine(ctx); err != nil { if strings.Contains(err.Error(), `Unknown database type: sqlite3`) { ctx.Data["Err_DbType"] = true - ctx.RenderWithErr(ctx.Tr("install.sqlite3_not_available", "https://docs.gitea.io/en-us/install-from-binary/"), tplInstall, form) + ctx.RenderWithErr(ctx.Tr("install.sqlite3_not_available", "https://docs.gitea.com/installation/install-from-binary"), tplInstall, form) } else { ctx.Data["Err_DbSetting"] = true ctx.RenderWithErr(ctx.Tr("install.invalid_db_setting", err), tplInstall, form) diff --git a/routers/private/hook_verification.go b/routers/private/hook_verification.go index caf3874ec3..8604789529 100644 --- a/routers/private/hook_verification.go +++ b/routers/private/hook_verification.go @@ -28,23 +28,31 @@ func verifyCommits(oldCommitID, newCommitID string, repo *git.Repository, env [] _ = stdoutWriter.Close() }() + var command *git.Command + if oldCommitID == git.EmptySHA { + // When creating a new branch, the oldCommitID is empty, by using "newCommitID --not --all": + // List commits that are reachable by following the newCommitID, exclude "all" existing heads/tags commits + // So, it only lists the new commits received, doesn't list the commits already present in the receiving repository + command = git.NewCommand(repo.Ctx, "rev-list").AddDynamicArguments(newCommitID).AddArguments("--not", "--all") + } else { + command = git.NewCommand(repo.Ctx, "rev-list").AddDynamicArguments(oldCommitID + "..." + newCommitID) + } // This is safe as force pushes are already forbidden - err = git.NewCommand(repo.Ctx, "rev-list").AddDynamicArguments(oldCommitID + "..." + newCommitID). - Run(&git.RunOpts{ - Env: env, - Dir: repo.Path, - Stdout: stdoutWriter, - PipelineFunc: func(ctx context.Context, cancel context.CancelFunc) error { - _ = stdoutWriter.Close() - err := readAndVerifyCommitsFromShaReader(stdoutReader, repo, env) - if err != nil { - log.Error("%v", err) - cancel() - } - _ = stdoutReader.Close() - return err - }, - }) + err = command.Run(&git.RunOpts{ + Env: env, + Dir: repo.Path, + Stdout: stdoutWriter, + PipelineFunc: func(ctx context.Context, cancel context.CancelFunc) error { + _ = stdoutWriter.Close() + err := readAndVerifyCommitsFromShaReader(stdoutReader, repo, env) + if err != nil { + log.Error("%v", err) + cancel() + } + _ = stdoutReader.Close() + return err + }, + }) if err != nil && !isErrUnverifiedCommit(err) { log.Error("Unable to check commits from %s to %s in %s: %v", oldCommitID, newCommitID, repo.Path, err) } diff --git a/routers/private/hook_verification_test.go b/routers/private/hook_verification_test.go new file mode 100644 index 0000000000..cd0c05ff50 --- /dev/null +++ b/routers/private/hook_verification_test.go @@ -0,0 +1,43 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package private + +import ( + "context" + "testing" + + "code.gitea.io/gitea/models/unittest" + "code.gitea.io/gitea/modules/git" + + "github.com/stretchr/testify/assert" +) + +var testReposDir = "tests/repos/" + +func TestVerifyCommits(t *testing.T) { + unittest.PrepareTestEnv(t) + + gitRepo, err := git.OpenRepository(context.Background(), testReposDir+"repo1_hook_verification") + defer gitRepo.Close() + assert.NoError(t, err) + + testCases := []struct { + base, head string + verified bool + }{ + {"72920278f2f999e3005801e5d5b8ab8139d3641c", "d766f2917716d45be24bfa968b8409544941be32", true}, + {git.EmptySHA, "93eac826f6188f34646cea81bf426aa5ba7d3bfe", true}, // New branch with verified commit + {"9779d17a04f1e2640583d35703c62460b2d86e0a", "72920278f2f999e3005801e5d5b8ab8139d3641c", false}, + {git.EmptySHA, "9ce3f779ae33f31fce17fac3c512047b75d7498b", false}, // New branch with unverified commit + } + + for _, tc := range testCases { + err = verifyCommits(tc.base, tc.head, gitRepo, nil) + if tc.verified { + assert.NoError(t, err) + } else { + assert.Error(t, err) + } + } +} diff --git a/routers/private/main_test.go b/routers/private/main_test.go new file mode 100644 index 0000000000..700af6ec8d --- /dev/null +++ b/routers/private/main_test.go @@ -0,0 +1,17 @@ +// Copyright 2017 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package private + +import ( + "path/filepath" + "testing" + + "code.gitea.io/gitea/models/unittest" +) + +func TestMain(m *testing.M) { + unittest.MainTest(m, &unittest.TestOptions{ + GiteaRootPath: filepath.Join("..", ".."), + }) +} diff --git a/routers/private/tests/repos/repo1_hook_verification/HEAD b/routers/private/tests/repos/repo1_hook_verification/HEAD new file mode 100644 index 0000000000..b870d82622 --- /dev/null +++ b/routers/private/tests/repos/repo1_hook_verification/HEAD @@ -0,0 +1 @@ +ref: refs/heads/main diff --git a/routers/private/tests/repos/repo1_hook_verification/config b/routers/private/tests/repos/repo1_hook_verification/config new file mode 100644 index 0000000000..64280b806c --- /dev/null +++ b/routers/private/tests/repos/repo1_hook_verification/config @@ -0,0 +1,6 @@ +[core] + repositoryformatversion = 0 + filemode = false + bare = true + symlinks = false + ignorecase = true diff --git a/routers/private/tests/repos/repo1_hook_verification/info/refs b/routers/private/tests/repos/repo1_hook_verification/info/refs new file mode 100644 index 0000000000..ee593c4db2 --- /dev/null +++ b/routers/private/tests/repos/repo1_hook_verification/info/refs @@ -0,0 +1 @@ +d766f2917716d45be24bfa968b8409544941be32 refs/heads/main diff --git a/routers/private/tests/repos/repo1_hook_verification/logs/HEAD b/routers/private/tests/repos/repo1_hook_verification/logs/HEAD new file mode 100644 index 0000000000..5c549b9b4e --- /dev/null +++ b/routers/private/tests/repos/repo1_hook_verification/logs/HEAD @@ -0,0 +1 @@ +0000000000000000000000000000000000000000 d766f2917716d45be24bfa968b8409544941be32 Gitea 1693148474 +0800 push diff --git a/routers/private/tests/repos/repo1_hook_verification/logs/refs/heads/main b/routers/private/tests/repos/repo1_hook_verification/logs/refs/heads/main new file mode 100644 index 0000000000..5c549b9b4e --- /dev/null +++ b/routers/private/tests/repos/repo1_hook_verification/logs/refs/heads/main @@ -0,0 +1 @@ +0000000000000000000000000000000000000000 d766f2917716d45be24bfa968b8409544941be32 Gitea 1693148474 +0800 push diff --git a/routers/private/tests/repos/repo1_hook_verification/objects/08/cbc8f0e903b0916025ae7577564b7ed39ecb2c b/routers/private/tests/repos/repo1_hook_verification/objects/08/cbc8f0e903b0916025ae7577564b7ed39ecb2c new file mode 100644 index 0000000000..d55278f9d4 Binary files /dev/null and b/routers/private/tests/repos/repo1_hook_verification/objects/08/cbc8f0e903b0916025ae7577564b7ed39ecb2c differ diff --git a/routers/private/tests/repos/repo1_hook_verification/objects/0b/5987362fe3fabdd4406babdc819642ee2f5a2a b/routers/private/tests/repos/repo1_hook_verification/objects/0b/5987362fe3fabdd4406babdc819642ee2f5a2a new file mode 100644 index 0000000000..d8b6020bc0 Binary files /dev/null and b/routers/private/tests/repos/repo1_hook_verification/objects/0b/5987362fe3fabdd4406babdc819642ee2f5a2a differ diff --git a/routers/private/tests/repos/repo1_hook_verification/objects/13/b0f23f673b161f4b5cb66f051cb93c99729e1e b/routers/private/tests/repos/repo1_hook_verification/objects/13/b0f23f673b161f4b5cb66f051cb93c99729e1e new file mode 100644 index 0000000000..77936d8241 Binary files /dev/null and b/routers/private/tests/repos/repo1_hook_verification/objects/13/b0f23f673b161f4b5cb66f051cb93c99729e1e differ diff --git a/routers/private/tests/repos/repo1_hook_verification/objects/23/33a51fdb238b7023a62ae3dcc58994061a7c86 b/routers/private/tests/repos/repo1_hook_verification/objects/23/33a51fdb238b7023a62ae3dcc58994061a7c86 new file mode 100644 index 0000000000..5ec09ac7bd Binary files /dev/null and b/routers/private/tests/repos/repo1_hook_verification/objects/23/33a51fdb238b7023a62ae3dcc58994061a7c86 differ diff --git a/routers/private/tests/repos/repo1_hook_verification/objects/2b/df04adb23d2b40b6085efb230856e5e2a775b7 b/routers/private/tests/repos/repo1_hook_verification/objects/2b/df04adb23d2b40b6085efb230856e5e2a775b7 new file mode 100644 index 0000000000..355b88e95e Binary files /dev/null and b/routers/private/tests/repos/repo1_hook_verification/objects/2b/df04adb23d2b40b6085efb230856e5e2a775b7 differ diff --git a/routers/private/tests/repos/repo1_hook_verification/objects/65/a457425a679cbe9adf0d2741785d3ceabb44a7 b/routers/private/tests/repos/repo1_hook_verification/objects/65/a457425a679cbe9adf0d2741785d3ceabb44a7 new file mode 100644 index 0000000000..ba1f06fc0e Binary files /dev/null and b/routers/private/tests/repos/repo1_hook_verification/objects/65/a457425a679cbe9adf0d2741785d3ceabb44a7 differ diff --git a/routers/private/tests/repos/repo1_hook_verification/objects/72/920278f2f999e3005801e5d5b8ab8139d3641c b/routers/private/tests/repos/repo1_hook_verification/objects/72/920278f2f999e3005801e5d5b8ab8139d3641c new file mode 100644 index 0000000000..4705fbf6b1 --- /dev/null +++ b/routers/private/tests/repos/repo1_hook_verification/objects/72/920278f2f999e3005801e5d5b8ab8139d3641c @@ -0,0 +1,2 @@ +xK +1] AS$"32 ooW{!`JC%. $r]sѱe$mM)(O`btlE[:;4H1_rayl~EL@cXvM":MۃG_}? \ No newline at end of file diff --git a/routers/private/tests/repos/repo1_hook_verification/objects/8b/903ede7c494725624bf842ec890f6342dababd b/routers/private/tests/repos/repo1_hook_verification/objects/8b/903ede7c494725624bf842ec890f6342dababd new file mode 100644 index 0000000000..fd3c3a488b Binary files /dev/null and b/routers/private/tests/repos/repo1_hook_verification/objects/8b/903ede7c494725624bf842ec890f6342dababd differ diff --git a/routers/private/tests/repos/repo1_hook_verification/objects/93/eac826f6188f34646cea81bf426aa5ba7d3bfe b/routers/private/tests/repos/repo1_hook_verification/objects/93/eac826f6188f34646cea81bf426aa5ba7d3bfe new file mode 100644 index 0000000000..80abd3ade1 Binary files /dev/null and b/routers/private/tests/repos/repo1_hook_verification/objects/93/eac826f6188f34646cea81bf426aa5ba7d3bfe differ diff --git a/routers/private/tests/repos/repo1_hook_verification/objects/97/79d17a04f1e2640583d35703c62460b2d86e0a b/routers/private/tests/repos/repo1_hook_verification/objects/97/79d17a04f1e2640583d35703c62460b2d86e0a new file mode 100644 index 0000000000..7f3293a8bc --- /dev/null +++ b/routers/private/tests/repos/repo1_hook_verification/objects/97/79d17a04f1e2640583d35703c62460b2d86e0a @@ -0,0 +1,2 @@ +x1 +!ES{AwGGa 9EQg W#AZ/p((Bhۼ&:pLY`U-z\ZM:xJ/G}:3 \ No newline at end of file diff --git a/routers/private/tests/repos/repo1_hook_verification/objects/9c/e3f779ae33f31fce17fac3c512047b75d7498b b/routers/private/tests/repos/repo1_hook_verification/objects/9c/e3f779ae33f31fce17fac3c512047b75d7498b new file mode 100644 index 0000000000..61a9ee8391 Binary files /dev/null and b/routers/private/tests/repos/repo1_hook_verification/objects/9c/e3f779ae33f31fce17fac3c512047b75d7498b differ diff --git a/routers/private/tests/repos/repo1_hook_verification/objects/a9/f76e70a663e40091749a97eeac5f57a6fec141 b/routers/private/tests/repos/repo1_hook_verification/objects/a9/f76e70a663e40091749a97eeac5f57a6fec141 new file mode 100644 index 0000000000..fb79dc96b9 Binary files /dev/null and b/routers/private/tests/repos/repo1_hook_verification/objects/a9/f76e70a663e40091749a97eeac5f57a6fec141 differ diff --git a/routers/private/tests/repos/repo1_hook_verification/objects/ba/0caedd359ebe310ef431335576e20f2b84e9b9 b/routers/private/tests/repos/repo1_hook_verification/objects/ba/0caedd359ebe310ef431335576e20f2b84e9b9 new file mode 100644 index 0000000000..1801a7f127 Binary files /dev/null and b/routers/private/tests/repos/repo1_hook_verification/objects/ba/0caedd359ebe310ef431335576e20f2b84e9b9 differ diff --git a/routers/private/tests/repos/repo1_hook_verification/objects/bb/87653e0819460e79b5f075f2563f583cbbf18c b/routers/private/tests/repos/repo1_hook_verification/objects/bb/87653e0819460e79b5f075f2563f583cbbf18c new file mode 100644 index 0000000000..a765d66758 Binary files /dev/null and b/routers/private/tests/repos/repo1_hook_verification/objects/bb/87653e0819460e79b5f075f2563f583cbbf18c differ diff --git a/routers/private/tests/repos/repo1_hook_verification/objects/cb/a4c30c196a0e03e7bdf6eeb8393d14b9d073aa b/routers/private/tests/repos/repo1_hook_verification/objects/cb/a4c30c196a0e03e7bdf6eeb8393d14b9d073aa new file mode 100644 index 0000000000..c7de09f9ac --- /dev/null +++ b/routers/private/tests/repos/repo1_hook_verification/objects/cb/a4c30c196a0e03e7bdf6eeb8393d14b9d073aa @@ -0,0 +1,3 @@ +xA +0E]$L xL2] + \}e[:{MZ5b8$v fR37];ˆbt 3$,tXG>m p1w͗-7pĄpZsDZL̾Le@ \ No newline at end of file diff --git a/routers/private/tests/repos/repo1_hook_verification/objects/d7/66f2917716d45be24bfa968b8409544941be32 b/routers/private/tests/repos/repo1_hook_verification/objects/d7/66f2917716d45be24bfa968b8409544941be32 new file mode 100644 index 0000000000..154458468c --- /dev/null +++ b/routers/private/tests/repos/repo1_hook_verification/objects/d7/66f2917716d45be24bfa968b8409544941be32 @@ -0,0 +1,3 @@ +xˮFE3+zn%44!%QxۀsA` 8{2IMjdf2"$e +-( !J"a@BaHo3V<$/)$JJDBH{ # RROnfFO +q[2̇~zjjL}prmFqh `@ث՘f?3[7) ^uֿ,l7zr|&Ou49:Qj1x6Q%tsV| (V,aL,G~r@`$[! Xˊep[8 o(kZγyeйYkd63;3 Ri ދdYDk91V]/C#&poFb}uW&]+m xaqdIX3 3KI#i_rgĩ7=`@[&A̤Lo3~M8MGt>xvQ(aWo"srzeŭ}QD֨fK)mr>>̚$F8x ^J k{mczI*^Mb m6M~hp {0 ]€?nUwgɠJ б<72 \ No newline at end of file diff --git a/routers/private/tests/repos/repo1_hook_verification/objects/e3/7e5d19823e42fad252f6341b1f77a7bc6ee451 b/routers/private/tests/repos/repo1_hook_verification/objects/e3/7e5d19823e42fad252f6341b1f77a7bc6ee451 new file mode 100644 index 0000000000..b3f925e817 Binary files /dev/null and b/routers/private/tests/repos/repo1_hook_verification/objects/e3/7e5d19823e42fad252f6341b1f77a7bc6ee451 differ diff --git a/routers/private/tests/repos/repo1_hook_verification/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 b/routers/private/tests/repos/repo1_hook_verification/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 new file mode 100644 index 0000000000..7112238943 Binary files /dev/null and b/routers/private/tests/repos/repo1_hook_verification/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 differ diff --git a/routers/private/tests/repos/repo1_hook_verification/objects/e8/4e452c3a20c02dee17ec2f63c0cb9ef6c759eb b/routers/private/tests/repos/repo1_hook_verification/objects/e8/4e452c3a20c02dee17ec2f63c0cb9ef6c759eb new file mode 100644 index 0000000000..24580f88e3 Binary files /dev/null and b/routers/private/tests/repos/repo1_hook_verification/objects/e8/4e452c3a20c02dee17ec2f63c0cb9ef6c759eb differ diff --git a/routers/private/tests/repos/repo1_hook_verification/objects/info/packs b/routers/private/tests/repos/repo1_hook_verification/objects/info/packs new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/routers/private/tests/repos/repo1_hook_verification/objects/info/packs @@ -0,0 +1 @@ + diff --git a/routers/private/tests/repos/repo1_hook_verification/objects/tmp_objdir-incoming-a21648/08/cbc8f0e903b0916025ae7577564b7ed39ecb2c b/routers/private/tests/repos/repo1_hook_verification/objects/tmp_objdir-incoming-a21648/08/cbc8f0e903b0916025ae7577564b7ed39ecb2c new file mode 100644 index 0000000000..d55278f9d4 Binary files /dev/null and b/routers/private/tests/repos/repo1_hook_verification/objects/tmp_objdir-incoming-a21648/08/cbc8f0e903b0916025ae7577564b7ed39ecb2c differ diff --git a/routers/private/tests/repos/repo1_hook_verification/objects/tmp_objdir-incoming-a21648/0b/5987362fe3fabdd4406babdc819642ee2f5a2a b/routers/private/tests/repos/repo1_hook_verification/objects/tmp_objdir-incoming-a21648/0b/5987362fe3fabdd4406babdc819642ee2f5a2a new file mode 100644 index 0000000000..d8b6020bc0 Binary files /dev/null and b/routers/private/tests/repos/repo1_hook_verification/objects/tmp_objdir-incoming-a21648/0b/5987362fe3fabdd4406babdc819642ee2f5a2a differ diff --git a/routers/private/tests/repos/repo1_hook_verification/objects/tmp_objdir-incoming-a21648/13/b0f23f673b161f4b5cb66f051cb93c99729e1e b/routers/private/tests/repos/repo1_hook_verification/objects/tmp_objdir-incoming-a21648/13/b0f23f673b161f4b5cb66f051cb93c99729e1e new file mode 100644 index 0000000000..77936d8241 Binary files /dev/null and b/routers/private/tests/repos/repo1_hook_verification/objects/tmp_objdir-incoming-a21648/13/b0f23f673b161f4b5cb66f051cb93c99729e1e differ diff --git a/routers/private/tests/repos/repo1_hook_verification/objects/tmp_objdir-incoming-a21648/23/33a51fdb238b7023a62ae3dcc58994061a7c86 b/routers/private/tests/repos/repo1_hook_verification/objects/tmp_objdir-incoming-a21648/23/33a51fdb238b7023a62ae3dcc58994061a7c86 new file mode 100644 index 0000000000..5ec09ac7bd Binary files /dev/null and b/routers/private/tests/repos/repo1_hook_verification/objects/tmp_objdir-incoming-a21648/23/33a51fdb238b7023a62ae3dcc58994061a7c86 differ diff --git a/routers/private/tests/repos/repo1_hook_verification/objects/tmp_objdir-incoming-a21648/8b/903ede7c494725624bf842ec890f6342dababd b/routers/private/tests/repos/repo1_hook_verification/objects/tmp_objdir-incoming-a21648/8b/903ede7c494725624bf842ec890f6342dababd new file mode 100644 index 0000000000..fd3c3a488b Binary files /dev/null and b/routers/private/tests/repos/repo1_hook_verification/objects/tmp_objdir-incoming-a21648/8b/903ede7c494725624bf842ec890f6342dababd differ diff --git a/routers/private/tests/repos/repo1_hook_verification/objects/tmp_objdir-incoming-a21648/93/eac826f6188f34646cea81bf426aa5ba7d3bfe b/routers/private/tests/repos/repo1_hook_verification/objects/tmp_objdir-incoming-a21648/93/eac826f6188f34646cea81bf426aa5ba7d3bfe new file mode 100644 index 0000000000..80abd3ade1 Binary files /dev/null and b/routers/private/tests/repos/repo1_hook_verification/objects/tmp_objdir-incoming-a21648/93/eac826f6188f34646cea81bf426aa5ba7d3bfe differ diff --git a/routers/private/tests/repos/repo1_hook_verification/objects/tmp_objdir-incoming-a21648/9c/e3f779ae33f31fce17fac3c512047b75d7498b b/routers/private/tests/repos/repo1_hook_verification/objects/tmp_objdir-incoming-a21648/9c/e3f779ae33f31fce17fac3c512047b75d7498b new file mode 100644 index 0000000000..61a9ee8391 Binary files /dev/null and b/routers/private/tests/repos/repo1_hook_verification/objects/tmp_objdir-incoming-a21648/9c/e3f779ae33f31fce17fac3c512047b75d7498b differ diff --git a/routers/private/tests/repos/repo1_hook_verification/objects/tmp_objdir-incoming-a21648/a9/f76e70a663e40091749a97eeac5f57a6fec141 b/routers/private/tests/repos/repo1_hook_verification/objects/tmp_objdir-incoming-a21648/a9/f76e70a663e40091749a97eeac5f57a6fec141 new file mode 100644 index 0000000000..fb79dc96b9 Binary files /dev/null and b/routers/private/tests/repos/repo1_hook_verification/objects/tmp_objdir-incoming-a21648/a9/f76e70a663e40091749a97eeac5f57a6fec141 differ diff --git a/routers/private/tests/repos/repo1_hook_verification/objects/tmp_objdir-incoming-a21648/bb/87653e0819460e79b5f075f2563f583cbbf18c b/routers/private/tests/repos/repo1_hook_verification/objects/tmp_objdir-incoming-a21648/bb/87653e0819460e79b5f075f2563f583cbbf18c new file mode 100644 index 0000000000..a765d66758 Binary files /dev/null and b/routers/private/tests/repos/repo1_hook_verification/objects/tmp_objdir-incoming-a21648/bb/87653e0819460e79b5f075f2563f583cbbf18c differ diff --git a/routers/private/tests/repos/repo1_hook_verification/objects/tmp_objdir-incoming-a21648/cb/a4c30c196a0e03e7bdf6eeb8393d14b9d073aa b/routers/private/tests/repos/repo1_hook_verification/objects/tmp_objdir-incoming-a21648/cb/a4c30c196a0e03e7bdf6eeb8393d14b9d073aa new file mode 100644 index 0000000000..c7de09f9ac --- /dev/null +++ b/routers/private/tests/repos/repo1_hook_verification/objects/tmp_objdir-incoming-a21648/cb/a4c30c196a0e03e7bdf6eeb8393d14b9d073aa @@ -0,0 +1,3 @@ +xA +0E]$L xL2] + \}e[:{MZ5b8$v fR37];ˆbt 3$,tXG>m p1w͗-7pĄpZsDZL̾Le@ \ No newline at end of file diff --git a/routers/private/tests/repos/repo1_hook_verification/refs/heads/main b/routers/private/tests/repos/repo1_hook_verification/refs/heads/main new file mode 100644 index 0000000000..2186e820a7 --- /dev/null +++ b/routers/private/tests/repos/repo1_hook_verification/refs/heads/main @@ -0,0 +1 @@ +d766f2917716d45be24bfa968b8409544941be32 diff --git a/routers/private/tests/repos/repo1_hook_verification_dummy_gpg_key.txt b/routers/private/tests/repos/repo1_hook_verification_dummy_gpg_key.txt new file mode 100644 index 0000000000..3061c75dcb --- /dev/null +++ b/routers/private/tests/repos/repo1_hook_verification_dummy_gpg_key.txt @@ -0,0 +1,127 @@ +# GPG key for abcde@gitea.com + +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQGNBGTrY3UBDAC2HLBqmMplAV15qSnC7g1c4dV406f5EHNhFr95Nup2My6b2eaf +Tlvedv77s8PT/I7F3fy4apOZs5A7w2SsPlLMcQ3ev4uGOsxRtkq5RLy1Yb6SNueX +0Da2UVKR5KTC5Q6BWaqxwS0IjKOLZ/xz0Pbe/ClV3bZSKBEY2omkVo3Z0HZ771vB +2clPRvGJ/IdeKOsZ3ZytSFXfyiJBdARmeSPmydXLil8+Ibq5iLAeow5PK8hK1TCO +nKHzLWNqcNq70tyjoHvcGi70iGjoVEEUgPCLLuU8WmzTJwlvA3BuDzjtaO7TLo/j +dE6iqkHtMSS8x+43sAH6hcFRCWAVh/0Uq7n36uGDfNxGnX3YrmX3LR9x5IsBES1r +GGWbpxio4o5GIf/Xd+JgDd9rzJCqRuZ3/sW/TxK38htWaVNZV0kMkHUCTc1ctzWp +Cm635hbFCHBhPYIp+/z206khkAKDbz/CNuU91Wazsh7KO07wrwDtxfDDbInJ8TfH +E2TGjzjQzgChfmcAEQEAAbQXYWJjZGUgPGFiY2RlQGdpdGVhLmNvbT6JAc4EEwEI +ADgWIQRo/BkcvP70fnQCv16xVDFkJim4JgUCZOtjdQIbAwULCQgHAgYVCgkICwIE +FgIDAQIeAQIXgAAKCRCxVDFkJim4Js6+C/9yIjHqcyM88hQAYQUoiPYfgJ0f2NsD +Ai/XypyDaFbRy9Wqm3oKvMr9L9G5xgOXshjRaRWOpODAwLmtVrJfOV5BhxLEcBcO +2hDdM3ycp8Gt7+Fx/o0cUjPiiC18hh3K5LRfeE7oYynSJDgjoDNuzIMuyoWuJPNc ++IcE4roND55qyyyC9ObrTLz1GgGm1bXtkHhZ1NdOfQ4q8M48K39Jn7pmnmSX3R74 +CSU6flh/o9AtzGLjU70JUOLFcWnR5D0iEI8mOsdfEHr+p+CvDVG9l4unPhMunT+Q +OUwV2DEmqo9P+yIert1ucVTDoSf+FrRaKUHg8r1Tt6T4/4GyIeSxG72NImK0h8jz ++bADPZhxuG4UR1Mj8bilqhWgODFPi/5DrDsNMWq1pEvjn6f4pCUx0IDTnPTniOXt +afXtAD4Rz0rwJWYqgeJFHgjXzaxBiOE1bhS26NPEvyAa0T9Tj3E73ICMESAmVad2 +JqO/mVxkLDGWdpXM7qB8bO2YGMOplrTvWaa5AY0EZOtjdQEMAOwevO46JxBo91RC +bT7RQ2uz3ZwRKb+P/jIEFST6x8tkCjon31zh6HicBDPNntqXTzStgoHQb7vGhHPV +4dxAfrOtVyoHwpi1/+x1jjtZoyIzLEz6RNK/Onu2y/tC5JBnSd5QRdHJgzPm20F8 +iNZR37c0Mi24fIH4y01aVLfNeBpRt7lWJ+opo2bM3Rh7jJdMpynKkTcA6o9XP6Ig +W/dzpOayosclpHhWiJwKV4CovIX/bxawk7sz10Nb4QzcxlWexWnJxNRHIcAkZ9KT +XTBpBkBpHCZqsI3+rQoQn5oQAr9JGWJSd4Fmgw7mFjmIF4bjfa2h/BpCoBqE+/25 +chvWfYkQwrCcyUwD1QYPUBwNvLB+PWb9kYEHD3mLgSSR+fjdG9XdMevu4lT91Gqo +/6KJzgzClSs7GoQtb+SZ4deUFw1tlmEQS/BGhbtTb/1566iDidGV5EnSmL/E4/3C +bGQqNog8gremF0G0SlWTjD9RMBY13IgisWCC6R4CdkXIYnCWbwARAQABiQG2BBgB +CAAgFiEEaPwZHLz+9H50Ar9esVQxZCYpuCYFAmTrY3UCGwwACgkQsVQxZCYpuCb1 +AAv/dI5YtGxBXaHAMj+lOLmZi5w4t0M7Zafa8tNnWrBwj4KixiXEt52i5YKxuaVD +3+/cMqidSDp0M5Cxx0wcmnmg+mdFFcowtXIXuk1TGTcHcOCPoXgF6gfoGimNNE1A +w1+EnC4/TbjMCKEM7b2QZ7/CgkBxZJWbScN4Jtawory9LEQqo0/epYJwf+79GHIJ +rpODAPiPJEMKmlej23KyoFuusOi17C0vHCf3GZNj4F2So3LOrcs51qTlOum2MdL5 +oTdqffatzs6p4u5bHBxyRugQlQggTRSK+TXLdxnFXr9ukXjIC2mFir7CCnZHw4e+ +2JwZfaAom0ZX+pLwrReSop4BPPU2YDzt3XCUk0S9kpiOsN7iFWUMCFreIE50DOxt +9406kSGopYKVaifbDl4MdLXM4v+oucLe7/yOViT/dm4FcIytIR+jzC8MaLQTB23e +uzm2wOjI1YOwv7Il6PWZyDdU+tyzXcaJ7wSFBeQFZZtqph2TItCeV04HoaKHHc25 +4akc +=OYIo +-----END PGP PUBLIC KEY BLOCK----- + +-----BEGIN PGP PRIVATE KEY BLOCK----- + +lQWGBGTrY3UBDAC2HLBqmMplAV15qSnC7g1c4dV406f5EHNhFr95Nup2My6b2eaf +Tlvedv77s8PT/I7F3fy4apOZs5A7w2SsPlLMcQ3ev4uGOsxRtkq5RLy1Yb6SNueX +0Da2UVKR5KTC5Q6BWaqxwS0IjKOLZ/xz0Pbe/ClV3bZSKBEY2omkVo3Z0HZ771vB +2clPRvGJ/IdeKOsZ3ZytSFXfyiJBdARmeSPmydXLil8+Ibq5iLAeow5PK8hK1TCO +nKHzLWNqcNq70tyjoHvcGi70iGjoVEEUgPCLLuU8WmzTJwlvA3BuDzjtaO7TLo/j +dE6iqkHtMSS8x+43sAH6hcFRCWAVh/0Uq7n36uGDfNxGnX3YrmX3LR9x5IsBES1r +GGWbpxio4o5GIf/Xd+JgDd9rzJCqRuZ3/sW/TxK38htWaVNZV0kMkHUCTc1ctzWp +Cm635hbFCHBhPYIp+/z206khkAKDbz/CNuU91Wazsh7KO07wrwDtxfDDbInJ8TfH +E2TGjzjQzgChfmcAEQEAAf4HAwKN54iG/XBl5/UViAmmiESRj3u+uJC9EztalVbj +156bjamUHBYIoCH4SBB0l0bR/o9ZN3vE4ZvyF3OyJ0AKF9epjWIuz7S+QIm1NLzk +IqwRyfGPsktwtZOF1CsathN4RyJL5/3nB9g4BLYfRARe9lwU0C0HQjBwAVj8m6RN ++wMTHZqW7tUN75npgPRLUI30H3GPVm3yLfS88Ol8nd31r7V0JsXZ2/mM9CWF4sUy +o1DW3P/rBn49s/x2qL/acEL+5PK7suFBP8Pjp5cwGjnSehoWeOclXgstkg3OEryY +2JP74muDVmaEVOAk7wiRjUD7HYuEOm/MbphFyen7QtO8WtN3IRKgNm19v5Skd4AF +NW9ZAdQOk2yHw7zyRk7HOPmEbEstbyE1RYWIfgZGjJlEJ2DI5ABwVJJ3W6DRPiZ3 +owd/JxBUVu/wigIjbg6z6ZQd/bn1XwKyhyTtgyTyILzE1gqtO7xs1XmK3wcww794 +cVLjqSnAdaeXMt4P+sDA17Wqky0f/jQ9kq7/tv7ipq9jvp9RaQ1ccRsz+mGgBVl+ +oLg4klKN47ZQGt0SQpLzHLL8SHzY0dz5US+Z2J+hdZia6jEmfilY9r4WPe7djMYz +Na908DmcbjfAg4XHPqVRXjgraUiT2YTo2LOV2dHn7550hJ/JshpOVqrJUrjhCgDN +usEMK3KXJkFvf6zflMv3t8HMD2SGBfpCJSwDaW+mrmtpR6a5laoZxg/009qZqgpj +FuenLuZmgYrHXozMXllwi6MLvSE/ioXrK4fqvpAwzOk6ArqZdWfxoJDYNQKXVL7z +Arniq9Ctaag8hr5T+JoZ9wNPNVF/LuEwPTWDur4qpU07KqWt9OFKPsEDNzxVZfNM +vtSCYvQ1uUH3CbPLQvPpd5TnyhjwKYtTzyW4OcuZHrWIZp9fZi5QdhWxobqGQiBk ++nRNFe0FPVEN0VcNdYJIDKcDLsOYCkGy08tucZnbKtr8JaK7XBSOo9Frg1i/j4Aa +GnXWlkMTVAkuxLZPATTOgdBoYmHMYKQvw31aFBrf3QU9c3EEg9UPYFMErVIeBHBB +BS+E7QZToHScCG1zezlr4rdqarkz0Yvzc3aduoSAOJHDf/Il+tOkepMne1y5fi72 +5UT1yWGbXXkTCV/pM6s0pLaEvNHmGvPQ6VGbJ//5w+42PFD1d7yEai53OgSZNs7B ++Ie/6Vq5GYzTM0bT3/o7/O1Zi56y791YKaas9wgxOhmMIZ0hsTecQJLJZGotUlOv +V7fZUhPRc4ksUeCyM3G0E89ilFtY6NuPcWQ8yMeS4sRRLmie+iaT+kNvAqL5mXvg +WNLhFIXPC1gpGLB8lpT5YEY647aPjQEig7QXYWJjZGUgPGFiY2RlQGdpdGVhLmNv +bT6JAc4EEwEIADgWIQRo/BkcvP70fnQCv16xVDFkJim4JgUCZOtjdQIbAwULCQgH +AgYVCgkICwIEFgIDAQIeAQIXgAAKCRCxVDFkJim4Js6+C/9yIjHqcyM88hQAYQUo +iPYfgJ0f2NsDAi/XypyDaFbRy9Wqm3oKvMr9L9G5xgOXshjRaRWOpODAwLmtVrJf +OV5BhxLEcBcO2hDdM3ycp8Gt7+Fx/o0cUjPiiC18hh3K5LRfeE7oYynSJDgjoDNu +zIMuyoWuJPNc+IcE4roND55qyyyC9ObrTLz1GgGm1bXtkHhZ1NdOfQ4q8M48K39J +n7pmnmSX3R74CSU6flh/o9AtzGLjU70JUOLFcWnR5D0iEI8mOsdfEHr+p+CvDVG9 +l4unPhMunT+QOUwV2DEmqo9P+yIert1ucVTDoSf+FrRaKUHg8r1Tt6T4/4GyIeSx +G72NImK0h8jz+bADPZhxuG4UR1Mj8bilqhWgODFPi/5DrDsNMWq1pEvjn6f4pCUx +0IDTnPTniOXtafXtAD4Rz0rwJWYqgeJFHgjXzaxBiOE1bhS26NPEvyAa0T9Tj3E7 +3ICMESAmVad2JqO/mVxkLDGWdpXM7qB8bO2YGMOplrTvWaadBYYEZOtjdQEMAOwe +vO46JxBo91RCbT7RQ2uz3ZwRKb+P/jIEFST6x8tkCjon31zh6HicBDPNntqXTzSt +goHQb7vGhHPV4dxAfrOtVyoHwpi1/+x1jjtZoyIzLEz6RNK/Onu2y/tC5JBnSd5Q +RdHJgzPm20F8iNZR37c0Mi24fIH4y01aVLfNeBpRt7lWJ+opo2bM3Rh7jJdMpynK +kTcA6o9XP6IgW/dzpOayosclpHhWiJwKV4CovIX/bxawk7sz10Nb4QzcxlWexWnJ +xNRHIcAkZ9KTXTBpBkBpHCZqsI3+rQoQn5oQAr9JGWJSd4Fmgw7mFjmIF4bjfa2h +/BpCoBqE+/25chvWfYkQwrCcyUwD1QYPUBwNvLB+PWb9kYEHD3mLgSSR+fjdG9Xd +Mevu4lT91Gqo/6KJzgzClSs7GoQtb+SZ4deUFw1tlmEQS/BGhbtTb/1566iDidGV +5EnSmL/E4/3CbGQqNog8gremF0G0SlWTjD9RMBY13IgisWCC6R4CdkXIYnCWbwAR +AQAB/gcDAgtreHsdznsa9bAha2g+J5zygs7rp95KvqRm4SGrgWPnngMewrHXrJAx +REUQFbOYJKvb6+SB47N8BTIh/nEY/B6dpvC36QSHB0XAgkktiOhdS2rTlrq+bKse +rZzoM/jbcxS3/cwi4VWH4lQhz7TLZtQxFZDuwyiik8/m5KscMxQrbYJg++4KpFQQ +En7RRUO0hEaYdnqQ9t3M8SWLwZn2yK3hzBE0gkQ8CJA3Zokv3DO7FSsAX823O25B +X7NgIpmbHCeYK6YV0gjQUKP1o3Sf7DhJzO1iltg0+obNTDl9RoeFgxTVORCdUlGA +kPdgoBbAGtadpZlCMThn7FlIn+ogqwQpAcoSTZjX31SOQBBpgMW9yf3GTNk2Nvrn +08zIA0hnUWFfc4VY6fbjbX5bF0jpoJ3XG6Hwa1VVRwQGFLxFV23TbZ+baLLuxEBx +A86XDC5zWFMwF/7aYL8oeXgoI+499u9G4Gw9G87va7rQXlTQJcHQRqu9YaGcxwOi +UslhNtVWz52iIURappUfFaGBRGUvtx2DOTgn4m099nnPaKDUiLmc4bFIHwzyA7Pl +RdAmLosrxSyIxHdlUOS/KshucXXKGVoYkJqGLXNQCY6x2zbyBPX9/a/0P59UP/WU +qwAHuGbXlToGhSKZzC8KmVs12tyQsAZ/47D+G29kEcRlaey1+N3Uor1jN7D66uyj +M1jYFhBudNIuuTR8sfrYjmbYIj8y0bgvF4RN6sU1padoTETadWNyIcFiRMZQ0oQd +KJBa3CxdqQZ2EU4a5jkA4UTQE13IySh7eNbYP5VwBgr3Z59gcbouKfFxKBhmPHF2 +BAmC0VXI2BgqKNqM6QgVj5UKrp41AX4D+iIhyKa0D3rapuIywXg1AtsrAlrOU/Ig +tQCj/a0NjIVJpLqVKBUdd4Eea69fDCJGIoaDNyp7qwo+nA1O2oDbc32EryJYUkHm +XMoLmx5y+/rxRsRevBv0ojwu3zsx2K93M1wHYd0z+SJsU8QGFinoFgYcmNp/tgMW +WtHBN4AijDuDSZAyG+MrWIj3NS4mbajx+utEIn3DC/ofFPlTmgX3OvpOPG1hnhBH +xSZUME+znOnqJMpUqnna4jbHEPwvRIXUY6InFKgl1Bu4grww/oo3qi7NwWL0Mcdy +qabWhdlEz5N/QBBPWVQllelgI+xTmZoCRUhh1mn+PM900vXXeM/DIALnxEXs9I/m +l4wPdLZlCdaKZS8vv33adyS6i9gWfI3NPWxZ2TyqC7nf5D5OK1zKSu3iWx17nXn2 +ak5hZnaXfzTxuZL3E8KZD/qsDm80c2PXFitogJTih37N6A8UQOJPtWbkfvPiwUvI +gw0oouggn0iJQVNoiQG2BBgBCAAgFiEEaPwZHLz+9H50Ar9esVQxZCYpuCYFAmTr +Y3UCGwwACgkQsVQxZCYpuCb1AAv/dI5YtGxBXaHAMj+lOLmZi5w4t0M7Zafa8tNn +WrBwj4KixiXEt52i5YKxuaVD3+/cMqidSDp0M5Cxx0wcmnmg+mdFFcowtXIXuk1T +GTcHcOCPoXgF6gfoGimNNE1Aw1+EnC4/TbjMCKEM7b2QZ7/CgkBxZJWbScN4Jtaw +ory9LEQqo0/epYJwf+79GHIJrpODAPiPJEMKmlej23KyoFuusOi17C0vHCf3GZNj +4F2So3LOrcs51qTlOum2MdL5oTdqffatzs6p4u5bHBxyRugQlQggTRSK+TXLdxnF +Xr9ukXjIC2mFir7CCnZHw4e+2JwZfaAom0ZX+pLwrReSop4BPPU2YDzt3XCUk0S9 +kpiOsN7iFWUMCFreIE50DOxt9406kSGopYKVaifbDl4MdLXM4v+oucLe7/yOViT/ +dm4FcIytIR+jzC8MaLQTB23euzm2wOjI1YOwv7Il6PWZyDdU+tyzXcaJ7wSFBeQF +ZZtqph2TItCeV04HoaKHHc254akc +=PPG4 +-----END PGP PRIVATE KEY BLOCK----- diff --git a/routers/web/repo/actions/actions.go b/routers/web/repo/actions/actions.go index b0f4b6f897..6284d21463 100644 --- a/routers/web/repo/actions/actions.go +++ b/routers/web/repo/actions/actions.go @@ -191,7 +191,7 @@ func List(ctx *context.Context) { ctx.Error(http.StatusInternalServerError, err.Error()) return } - ctx.Data["Actors"] = repo.MakeSelfOnTop(ctx, actors) + ctx.Data["Actors"] = repo.MakeSelfOnTop(ctx.Doer, actors) ctx.Data["StatusInfoList"] = actions_model.GetStatusInfoList(ctx) diff --git a/routers/web/repo/commit.go b/routers/web/repo/commit.go index 7513f9360b..9a620f6d37 100644 --- a/routers/web/repo/commit.go +++ b/routers/web/repo/commit.go @@ -81,7 +81,6 @@ func Commits(ctx *context.Context) { ctx.Data["Username"] = ctx.Repo.Owner.Name ctx.Data["Reponame"] = ctx.Repo.Repository.Name ctx.Data["CommitCount"] = commitsCount - ctx.Data["RefName"] = ctx.Repo.RefName pager := context.NewPagination(int(commitsCount), pageSize, page, 5) pager.SetDefaultParams(ctx) @@ -157,7 +156,7 @@ func Graph(ctx *context.Context) { ctx.Data["Username"] = ctx.Repo.Owner.Name ctx.Data["Reponame"] = ctx.Repo.Repository.Name ctx.Data["CommitCount"] = commitsCount - ctx.Data["RefName"] = ctx.Repo.RefName + paginator := context.NewPagination(int(graphCommitsCount), setting.UI.GraphMaxCommitNum, page, 5) paginator.AddParam(ctx, "mode", "Mode") paginator.AddParam(ctx, "hide-pr-refs", "HidePRRefs") @@ -203,7 +202,6 @@ func SearchCommits(ctx *context.Context) { } ctx.Data["Username"] = ctx.Repo.Owner.Name ctx.Data["Reponame"] = ctx.Repo.Repository.Name - ctx.Data["RefName"] = ctx.Repo.RefName ctx.HTML(http.StatusOK, tplCommits) } @@ -247,7 +245,6 @@ func FileHistory(ctx *context.Context) { ctx.Data["Reponame"] = ctx.Repo.Repository.Name ctx.Data["FileName"] = fileName ctx.Data["CommitCount"] = commitsCount - ctx.Data["RefName"] = ctx.Repo.RefName pager := context.NewPagination(int(commitsCount), setting.Git.CommitsRangeSize, page, 5) pager.SetDefaultParams(ctx) diff --git a/routers/web/repo/compare.go b/routers/web/repo/compare.go index 4ceb52d039..aee3495612 100644 --- a/routers/web/repo/compare.go +++ b/routers/web/repo/compare.go @@ -460,7 +460,7 @@ func ParseCompareInfo(ctx *context.Context) *CompareInfo { rootRepo.ID != ci.HeadRepo.ID && rootRepo.ID != baseRepo.ID { canRead := access_model.CheckRepoUnitUser(ctx, rootRepo, ctx.Doer, unit.TypeCode) - if canRead && rootRepo.AllowsPulls() { + if canRead { ctx.Data["RootRepo"] = rootRepo if !fileOnly { branches, tags, err := getBranchesAndTagsForRepo(ctx, rootRepo) diff --git a/routers/web/repo/helper.go b/routers/web/repo/helper.go index fb5ada1bdb..f8cdefdc8e 100644 --- a/routers/web/repo/helper.go +++ b/routers/web/repo/helper.go @@ -7,16 +7,15 @@ import ( "sort" "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/context" ) -func MakeSelfOnTop(ctx *context.Context, users []*user.User) []*user.User { - if ctx.Doer != nil { +func MakeSelfOnTop(doer *user.User, users []*user.User) []*user.User { + if doer != nil { sort.Slice(users, func(i, j int) bool { if users[i].ID == users[j].ID { return false } - return users[i].ID == ctx.Doer.ID // if users[i] is self, put it before others, so less=true + return users[i].ID == doer.ID // if users[i] is self, put it before others, so less=true }) } return users diff --git a/routers/web/repo/helper_test.go b/routers/web/repo/helper_test.go index 226e2e81f4..978758e77f 100644 --- a/routers/web/repo/helper_test.go +++ b/routers/web/repo/helper_test.go @@ -7,21 +7,20 @@ import ( "testing" "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/context" "github.com/stretchr/testify/assert" ) func TestMakeSelfOnTop(t *testing.T) { - users := MakeSelfOnTop(&context.Context{}, []*user.User{{ID: 2}, {ID: 1}}) + users := MakeSelfOnTop(nil, []*user.User{{ID: 2}, {ID: 1}}) assert.Len(t, users, 2) assert.EqualValues(t, 2, users[0].ID) - users = MakeSelfOnTop(&context.Context{Doer: &user.User{ID: 1}}, []*user.User{{ID: 2}, {ID: 1}}) + users = MakeSelfOnTop(&user.User{ID: 1}, []*user.User{{ID: 2}, {ID: 1}}) assert.Len(t, users, 2) assert.EqualValues(t, 1, users[0].ID) - users = MakeSelfOnTop(&context.Context{Doer: &user.User{ID: 2}}, []*user.User{{ID: 2}, {ID: 1}}) + users = MakeSelfOnTop(&user.User{ID: 2}, []*user.User{{ID: 2}, {ID: 1}}) assert.Len(t, users, 2) assert.EqualValues(t, 2, users[0].ID) } diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index b04802e452..b8b5a2dff2 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -331,7 +331,7 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption uti ctx.ServerError("GetRepoAssignees", err) return } - ctx.Data["Assignees"] = MakeSelfOnTop(ctx, assigneeUsers) + ctx.Data["Assignees"] = MakeSelfOnTop(ctx.Doer, assigneeUsers) handleTeamMentions(ctx) if ctx.Written() { @@ -535,7 +535,7 @@ func RetrieveRepoMilestonesAndAssignees(ctx *context.Context, repo *repo_model.R ctx.ServerError("GetRepoAssignees", err) return } - ctx.Data["Assignees"] = MakeSelfOnTop(ctx, assigneeUsers) + ctx.Data["Assignees"] = MakeSelfOnTop(ctx.Doer, assigneeUsers) handleTeamMentions(ctx) } @@ -1228,47 +1228,70 @@ func NewIssuePost(ctx *context.Context) { } } -// roleDescriptor returns the Role Descriptor for a comment in/with the given repo, poster and issue +// roleDescriptor returns the role descriptor for a comment in/with the given repo, poster and issue func roleDescriptor(ctx stdCtx.Context, repo *repo_model.Repository, poster *user_model.User, issue *issues_model.Issue, hasOriginalAuthor bool) (issues_model.RoleDescriptor, error) { + roleDescriptor := issues_model.RoleDescriptor{} + if hasOriginalAuthor { - return issues_model.RoleDescriptorNone, nil + return roleDescriptor, nil } perm, err := access_model.GetUserRepoPermission(ctx, repo, poster) if err != nil { - return issues_model.RoleDescriptorNone, err - } - - // By default the poster has no roles on the comment. - roleDescriptor := issues_model.RoleDescriptorNone - - // Check if the poster is owner of the repo. - if perm.IsOwner() { - // If the poster isn't a admin, enable the owner role. - if !poster.IsAdmin { - roleDescriptor = roleDescriptor.WithRole(issues_model.RoleDescriptorOwner) - } else { - - // Otherwise check if poster is the real repo admin. - ok, err := access_model.IsUserRealRepoAdmin(repo, poster) - if err != nil { - return issues_model.RoleDescriptorNone, err - } - if ok { - roleDescriptor = roleDescriptor.WithRole(issues_model.RoleDescriptorOwner) - } - } - } - - // Is the poster can write issues or pulls to the repo, enable the Writer role. - // Only enable this if the poster doesn't have the owner role already. - if !roleDescriptor.HasRole("Owner") && perm.CanWriteIssuesOrPulls(issue.IsPull) { - roleDescriptor = roleDescriptor.WithRole(issues_model.RoleDescriptorWriter) + return roleDescriptor, err } // If the poster is the actual poster of the issue, enable Poster role. - if issue.IsPoster(poster.ID) { - roleDescriptor = roleDescriptor.WithRole(issues_model.RoleDescriptorPoster) + roleDescriptor.IsPoster = issue.IsPoster(poster.ID) + + // Check if the poster is owner of the repo. + if perm.IsOwner() { + // If the poster isn't an admin, enable the owner role. + if !poster.IsAdmin { + roleDescriptor.RoleInRepo = issues_model.RoleRepoOwner + return roleDescriptor, nil + } + + // Otherwise check if poster is the real repo admin. + ok, err := access_model.IsUserRealRepoAdmin(repo, poster) + if err != nil { + return roleDescriptor, err + } + if ok { + roleDescriptor.RoleInRepo = issues_model.RoleRepoOwner + return roleDescriptor, nil + } + } + + // If repo is organization, check Member role + if err := repo.LoadOwner(ctx); err != nil { + return roleDescriptor, err + } + if repo.Owner.IsOrganization() { + if isMember, err := organization.IsOrganizationMember(ctx, repo.Owner.ID, poster.ID); err != nil { + return roleDescriptor, err + } else if isMember { + roleDescriptor.RoleInRepo = issues_model.RoleRepoMember + return roleDescriptor, nil + } + } + + // If the poster is the collaborator of the repo + if isCollaborator, err := repo_model.IsCollaborator(ctx, repo.ID, poster.ID); err != nil { + return roleDescriptor, err + } else if isCollaborator { + roleDescriptor.RoleInRepo = issues_model.RoleRepoCollaborator + return roleDescriptor, nil + } + + hasMergedPR, err := issues_model.HasMergedPullRequestInRepo(ctx, repo.ID, poster.ID) + if err != nil { + return roleDescriptor, err + } else if hasMergedPR { + roleDescriptor.RoleInRepo = issues_model.RoleRepoContributor + } else { + // only display first time contributor in the first opening pull request + roleDescriptor.RoleInRepo = issues_model.RoleRepoFirstTimeContributor } return roleDescriptor, nil @@ -3602,7 +3625,7 @@ func issuePosters(ctx *context.Context, isPullList bool) { } } - posters = MakeSelfOnTop(ctx, posters) + posters = MakeSelfOnTop(ctx.Doer, posters) resp := &userSearchResponse{} resp.Results = make([]*userSearchInfo, len(posters)) diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go index e3854779fe..e697a0d5b6 100644 --- a/routers/web/repo/pull.go +++ b/routers/web/repo/pull.go @@ -956,7 +956,7 @@ func viewPullFiles(ctx *context.Context, specifiedStartCommit, specifiedEndCommi ctx.ServerError("GetRepoAssignees", err) return } - ctx.Data["Assignees"] = MakeSelfOnTop(ctx, assigneeUsers) + ctx.Data["Assignees"] = MakeSelfOnTop(ctx.Doer, assigneeUsers) handleTeamMentions(ctx) if ctx.Written() { diff --git a/routers/web/repo/release.go b/routers/web/repo/release.go index 957cf56972..138df45857 100644 --- a/routers/web/repo/release.go +++ b/routers/web/repo/release.go @@ -349,7 +349,7 @@ func NewRelease(ctx *context.Context) { ctx.ServerError("GetRepoAssignees", err) return } - ctx.Data["Assignees"] = MakeSelfOnTop(ctx, assigneeUsers) + ctx.Data["Assignees"] = MakeSelfOnTop(ctx.Doer, assigneeUsers) upload.AddUploadContext(ctx, "release") @@ -538,7 +538,7 @@ func EditRelease(ctx *context.Context) { ctx.ServerError("GetRepoAssignees", err) return } - ctx.Data["Assignees"] = MakeSelfOnTop(ctx, assigneeUsers) + ctx.Data["Assignees"] = MakeSelfOnTop(ctx.Doer, assigneeUsers) ctx.HTML(http.StatusOK, tplReleaseNew) } diff --git a/routers/web/repo/setting/setting.go b/routers/web/repo/setting/setting.go index 71c1939f29..af09e240d5 100644 --- a/routers/web/repo/setting/setting.go +++ b/routers/web/repo/setting/setting.go @@ -24,7 +24,6 @@ import ( "code.gitea.io/gitea/modules/indexer/stats" "code.gitea.io/gitea/modules/lfs" "code.gitea.io/gitea/modules/log" - mirror_module "code.gitea.io/gitea/modules/mirror" repo_module "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/structs" @@ -277,7 +276,7 @@ func SettingsPost(ctx *context.Context) { return } - mirror_module.AddPullMirrorToQueue(repo.ID) + mirror_service.AddPullMirrorToQueue(repo.ID) ctx.Flash.Info(ctx.Tr("repo.settings.mirror_sync_in_progress")) ctx.Redirect(repo.Link() + "/settings") @@ -294,7 +293,7 @@ func SettingsPost(ctx *context.Context) { return } - mirror_module.AddPushMirrorToQueue(m.ID) + mirror_service.AddPushMirrorToQueue(m.ID) ctx.Flash.Info(ctx.Tr("repo.settings.mirror_sync_in_progress")) ctx.Redirect(repo.Link() + "/settings") @@ -332,7 +331,7 @@ func SettingsPost(ctx *context.Context) { // If we observed its implementation in the context of `push-mirror-sync` where it // is evident that pushing to the queue is necessary for updates. // So, there are updates within the given interval, it is necessary to update the queue accordingly. - mirror_module.AddPushMirrorToQueue(m.ID) + mirror_service.AddPushMirrorToQueue(m.ID) ctx.Flash.Success(ctx.Tr("repo.settings.update_settings_success")) ctx.Redirect(repo.Link() + "/settings") diff --git a/routers/web/repo/setting/webhook.go b/routers/web/repo/setting/webhook.go index bbcc36ee60..33ea2c206b 100644 --- a/routers/web/repo/setting/webhook.go +++ b/routers/web/repo/setting/webhook.go @@ -44,7 +44,7 @@ func Webhooks(ctx *context.Context) { ctx.Data["PageIsSettingsHooks"] = true ctx.Data["BaseLink"] = ctx.Repo.RepoLink + "/settings/hooks" ctx.Data["BaseLinkNew"] = ctx.Repo.RepoLink + "/settings/hooks" - ctx.Data["Description"] = ctx.Tr("repo.settings.hooks_desc", "https://docs.gitea.io/en-us/webhooks/") + ctx.Data["Description"] = ctx.Tr("repo.settings.hooks_desc", "https://docs.gitea.com/usage/webhooks") ws, err := webhook.ListWebhooksByOpts(ctx, &webhook.ListWebhookOptions{RepoID: ctx.Repo.Repository.ID}) if err != nil { diff --git a/routers/web/user/home.go b/routers/web/user/home.go index d1a4877e6d..a7f6a52f1b 100644 --- a/routers/web/user/home.go +++ b/routers/web/user/home.go @@ -157,7 +157,7 @@ func Milestones(ctx *context.Context) { } repoOpts := repo_model.SearchRepoOptions{ - Actor: ctxUser, + Actor: ctx.Doer, OwnerID: ctxUser.ID, Private: true, AllPublic: false, // Include also all public repositories of users and public organisations @@ -449,7 +449,7 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) { // - Team has read permission to repository. repoOpts := &repo_model.SearchRepoOptions{ Actor: ctx.Doer, - OwnerID: ctx.Doer.ID, + OwnerID: ctxUser.ID, Private: true, AllPublic: false, AllLimited: false, @@ -567,12 +567,9 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) { // Remove repositories that should not be shown, // which are repositories that have no issues and are not selected by the user. - selectedReposMap := make(map[int64]struct{}, len(selectedRepoIDs)) - for _, repoID := range selectedRepoIDs { - selectedReposMap[repoID] = struct{}{} - } + selectedRepos := container.SetOf(selectedRepoIDs...) for k, v := range issueCountByRepo { - if _, ok := selectedReposMap[k]; !ok && v == 0 { + if v == 0 && !selectedRepos.Contains(k) { delete(issueCountByRepo, k) } } diff --git a/services/actions/notifier_helper.go b/services/actions/notifier_helper.go index 75c99ff19c..ff00e48c64 100644 --- a/services/actions/notifier_helper.go +++ b/services/actions/notifier_helper.go @@ -4,6 +4,7 @@ package actions import ( + "bytes" "context" "fmt" "strings" @@ -24,6 +25,7 @@ import ( "code.gitea.io/gitea/services/convert" "github.com/nektos/act/pkg/jobparser" + "github.com/nektos/act/pkg/model" ) var methodCtxKey struct{} @@ -143,15 +145,15 @@ func notify(ctx context.Context, input *notifyInput) error { } var detectedWorkflows []*actions_module.DetectedWorkflow - workflows, err := actions_module.DetectWorkflows(gitRepo, commit, input.Event, input.Payload) + actionsConfig := input.Repo.MustGetUnit(ctx, unit_model.TypeActions).ActionsConfig() + workflows, schedules, err := actions_module.DetectWorkflows(gitRepo, commit, input.Event, input.Payload) if err != nil { return fmt.Errorf("DetectWorkflows: %w", err) } + if len(workflows) == 0 { log.Trace("repo %s with commit %s couldn't find workflows", input.Repo.RepoPath(), commit.ID) } else { - actionsConfig := input.Repo.MustGetUnit(ctx, unit_model.TypeActions).ActionsConfig() - for _, wf := range workflows { if actionsConfig.IsWorkflowDisabled(wf.EntryName) { log.Trace("repo %s has disable workflows %s", input.Repo.RepoPath(), wf.EntryName) @@ -171,7 +173,7 @@ func notify(ctx context.Context, input *notifyInput) error { if err != nil { return fmt.Errorf("gitRepo.GetCommit: %w", err) } - baseWorkflows, err := actions_module.DetectWorkflows(gitRepo, baseCommit, input.Event, input.Payload) + baseWorkflows, _, err := actions_module.DetectWorkflows(gitRepo, baseCommit, input.Event, input.Payload) if err != nil { return fmt.Errorf("DetectWorkflows: %w", err) } @@ -186,7 +188,22 @@ func notify(ctx context.Context, input *notifyInput) error { } } + if err := handleSchedules(ctx, schedules, commit, input); err != nil { + return err + } + + return handleWorkflows(ctx, detectedWorkflows, commit, input, ref) +} + +func handleWorkflows( + ctx context.Context, + detectedWorkflows []*actions_module.DetectedWorkflow, + commit *git.Commit, + input *notifyInput, + ref string, +) error { if len(detectedWorkflows) == 0 { + log.Trace("repo %s with commit %s couldn't find workflows", input.Repo.RepoPath(), commit.ID) return nil } @@ -350,3 +367,86 @@ func ifNeedApproval(ctx context.Context, run *actions_model.ActionRun, repo *rep log.Trace("need approval because it's the first time user %d triggered actions", user.ID) return true, nil } + +func handleSchedules( + ctx context.Context, + detectedWorkflows []*actions_module.DetectedWorkflow, + commit *git.Commit, + input *notifyInput, +) error { + if len(detectedWorkflows) == 0 { + log.Trace("repo %s with commit %s couldn't find schedules", input.Repo.RepoPath(), commit.ID) + return nil + } + + branch, err := commit.GetBranchName() + if err != nil { + return err + } + if branch != input.Repo.DefaultBranch { + log.Trace("commit branch is not default branch in repo") + return nil + } + + rows, _, err := actions_model.FindSchedules(ctx, actions_model.FindScheduleOptions{RepoID: input.Repo.ID}) + if err != nil { + log.Error("FindCrons: %v", err) + return err + } + + if len(rows) > 0 { + if err := actions_model.DeleteScheduleTaskByRepo(ctx, input.Repo.ID); err != nil { + log.Error("DeleteCronTaskByRepo: %v", err) + } + } + + p, err := json.Marshal(input.Payload) + if err != nil { + return fmt.Errorf("json.Marshal: %w", err) + } + + crons := make([]*actions_model.ActionSchedule, 0, len(detectedWorkflows)) + for _, dwf := range detectedWorkflows { + // Check cron job condition. Only working in default branch + workflow, err := model.ReadWorkflow(bytes.NewReader(dwf.Content)) + if err != nil { + log.Error("ReadWorkflow: %v", err) + continue + } + schedules := workflow.OnSchedule() + if len(schedules) == 0 { + log.Warn("no schedule event") + continue + } + + run := &actions_model.ActionSchedule{ + Title: strings.SplitN(commit.CommitMessage, "\n", 2)[0], + RepoID: input.Repo.ID, + OwnerID: input.Repo.OwnerID, + WorkflowID: dwf.EntryName, + TriggerUserID: input.Doer.ID, + Ref: input.Ref, + CommitSHA: commit.ID.String(), + Event: input.Event, + EventPayload: string(p), + Specs: schedules, + Content: dwf.Content, + } + + // cancel running jobs if the event is push + if run.Event == webhook_module.HookEventPush { + // cancel running jobs of the same workflow + if err := actions_model.CancelRunningJobs( + ctx, + run.RepoID, + run.Ref, + run.WorkflowID, + ); err != nil { + log.Error("CancelRunningJobs: %v", err) + } + } + crons = append(crons, run) + } + + return actions_model.CreateScheduleTask(ctx, crons) +} diff --git a/services/actions/schedule_tasks.go b/services/actions/schedule_tasks.go new file mode 100644 index 0000000000..87131e0aab --- /dev/null +++ b/services/actions/schedule_tasks.go @@ -0,0 +1,135 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package actions + +import ( + "context" + "fmt" + "time" + + actions_model "code.gitea.io/gitea/models/actions" + "code.gitea.io/gitea/models/db" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/timeutil" + webhook_module "code.gitea.io/gitea/modules/webhook" + + "github.com/nektos/act/pkg/jobparser" +) + +// StartScheduleTasks start the task +func StartScheduleTasks(ctx context.Context) error { + return startTasks(ctx) +} + +// startTasks retrieves specifications in pages, creates a schedule task for each specification, +// and updates the specification's next run time and previous run time. +// The function returns an error if there's an issue with finding or updating the specifications. +func startTasks(ctx context.Context) error { + // Set the page size + pageSize := 50 + + // Retrieve specs in pages until all specs have been retrieved + now := time.Now() + for page := 1; ; page++ { + // Retrieve the specs for the current page + specs, _, err := actions_model.FindSpecs(ctx, actions_model.FindSpecOptions{ + ListOptions: db.ListOptions{ + Page: page, + PageSize: pageSize, + }, + Next: now.Unix(), + }) + if err != nil { + return fmt.Errorf("find specs: %w", err) + } + + // Loop through each spec and create a schedule task for it + for _, row := range specs { + // cancel running jobs if the event is push + if row.Schedule.Event == webhook_module.HookEventPush { + // cancel running jobs of the same workflow + if err := actions_model.CancelRunningJobs( + ctx, + row.RepoID, + row.Schedule.Ref, + row.Schedule.WorkflowID, + ); err != nil { + log.Error("CancelRunningJobs: %v", err) + } + } + + if err := CreateScheduleTask(ctx, row.Schedule); err != nil { + log.Error("CreateScheduleTask: %v", err) + return err + } + + // Parse the spec + schedule, err := row.Parse() + if err != nil { + log.Error("Parse: %v", err) + return err + } + + // Update the spec's next run time and previous run time + row.Prev = row.Next + row.Next = timeutil.TimeStamp(schedule.Next(now.Add(1 * time.Minute)).Unix()) + if err := actions_model.UpdateScheduleSpec(ctx, row, "prev", "next"); err != nil { + log.Error("UpdateScheduleSpec: %v", err) + return err + } + } + + // Stop if all specs have been retrieved + if len(specs) < pageSize { + break + } + } + + return nil +} + +// CreateScheduleTask creates a scheduled task from a cron action schedule. +// It creates an action run based on the schedule, inserts it into the database, and creates commit statuses for each job. +func CreateScheduleTask(ctx context.Context, cron *actions_model.ActionSchedule) error { + // Create a new action run based on the schedule + run := &actions_model.ActionRun{ + Title: cron.Title, + RepoID: cron.RepoID, + OwnerID: cron.OwnerID, + WorkflowID: cron.WorkflowID, + TriggerUserID: cron.TriggerUserID, + Ref: cron.Ref, + CommitSHA: cron.CommitSHA, + Event: cron.Event, + EventPayload: cron.EventPayload, + Status: actions_model.StatusWaiting, + } + + // Parse the workflow specification from the cron schedule + workflows, err := jobparser.Parse(cron.Content) + if err != nil { + return err + } + + // Insert the action run and its associated jobs into the database + if err := actions_model.InsertRun(ctx, run, workflows); err != nil { + return err + } + + // Retrieve the jobs for the newly created action run + jobs, _, err := actions_model.FindRunJobs(ctx, actions_model.FindRunJobOptions{RunID: run.ID}) + if err != nil { + return err + } + + // Create commit statuses for each job + for _, job := range jobs { + if err := createCommitStatus(ctx, job); err != nil { + return err + } + } + + // Return nil if no errors occurred + return nil +} diff --git a/services/auth/source/ldap/source_sync.go b/services/auth/source/ldap/source_sync.go index 43ee32c84b..df5eb60393 100644 --- a/services/auth/source/ldap/source_sync.go +++ b/services/auth/source/ldap/source_sync.go @@ -13,6 +13,7 @@ import ( "code.gitea.io/gitea/models/organization" user_model "code.gitea.io/gitea/models/user" auth_module "code.gitea.io/gitea/modules/auth" + "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/util" source_service "code.gitea.io/gitea/services/auth/source" @@ -41,7 +42,7 @@ func (source *Source) Sync(ctx context.Context, updateExisting bool) error { usernameUsers := make(map[string]*user_model.User, len(users)) mailUsers := make(map[string]*user_model.User, len(users)) - keepActiveUsers := make(map[int64]struct{}) + keepActiveUsers := make(container.Set[int64]) for _, u := range users { usernameUsers[u.LowerName] = u @@ -97,7 +98,7 @@ func (source *Source) Sync(ctx context.Context, updateExisting bool) error { } if usr != nil { - keepActiveUsers[usr.ID] = struct{}{} + keepActiveUsers.Add(usr.ID) } else if len(su.Username) == 0 { // we cannot create the user if su.Username is empty continue @@ -208,7 +209,7 @@ func (source *Source) Sync(ctx context.Context, updateExisting bool) error { // Deactivate users not present in LDAP if updateExisting { for _, usr := range users { - if _, ok := keepActiveUsers[usr.ID]; ok { + if keepActiveUsers.Contains(usr.ID) { continue } diff --git a/services/convert/release.go b/services/convert/release.go index d8aa46d432..bfff53e62f 100644 --- a/services/convert/release.go +++ b/services/convert/release.go @@ -22,6 +22,7 @@ func ToAPIRelease(ctx context.Context, repo *repo_model.Repository, r *repo_mode HTMLURL: r.HTMLURL(), TarURL: r.TarURL(), ZipURL: r.ZipURL(), + UploadURL: r.APIUploadURL(), IsDraft: r.IsDraft, IsPrerelease: r.IsPrerelease, CreatedAt: r.CreatedUnix.AsTime(), diff --git a/services/convert/release_test.go b/services/convert/release_test.go new file mode 100644 index 0000000000..201b27e16d --- /dev/null +++ b/services/convert/release_test.go @@ -0,0 +1,28 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package convert + +import ( + "testing" + + "code.gitea.io/gitea/models/db" + repo_model "code.gitea.io/gitea/models/repo" + "code.gitea.io/gitea/models/unittest" + + "github.com/stretchr/testify/assert" +) + +func TestRelease_ToRelease(t *testing.T) { + assert.NoError(t, unittest.PrepareTestDatabase()) + + repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) + release1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Release{ID: 1}) + release1.LoadAttributes(db.DefaultContext) + + apiRelease := ToAPIRelease(db.DefaultContext, repo1, release1) + assert.NotNil(t, apiRelease) + assert.EqualValues(t, 1, apiRelease.ID) + assert.EqualValues(t, "https://try.gitea.io/api/v1/repos/user2/repo1/releases/1", apiRelease.URL) + assert.EqualValues(t, "https://try.gitea.io/api/v1/repos/user2/repo1/releases/1/assets", apiRelease.UploadURL) +} diff --git a/services/cron/tasks_actions.go b/services/cron/tasks_actions.go index 30e8749a5e..0875792503 100644 --- a/services/cron/tasks_actions.go +++ b/services/cron/tasks_actions.go @@ -18,6 +18,7 @@ func initActionsTasks() { registerStopZombieTasks() registerStopEndlessTasks() registerCancelAbandonedJobs() + registerScheduleTasks() } func registerStopZombieTasks() { @@ -49,3 +50,16 @@ func registerCancelAbandonedJobs() { return actions_service.CancelAbandonedJobs(ctx) }) } + +// registerScheduleTasks registers a scheduled task that runs every minute to start any due schedule tasks. +func registerScheduleTasks() { + // Register the task with a unique name, enabled status, and schedule for every minute. + RegisterTaskFatal("start_schedule_tasks", &BaseConfig{ + Enabled: true, + RunAtStart: false, + Schedule: "@every 1m", + }, func(ctx context.Context, _ *user_model.User, cfg Config) error { + // Call the function to start schedule tasks and pass the context. + return actions_service.StartScheduleTasks(ctx) + }) +} diff --git a/services/markup/processorhelper_test.go b/services/markup/processorhelper_test.go index 2f48e03b22..d83e10903f 100644 --- a/services/markup/processorhelper_test.go +++ b/services/markup/processorhelper_test.go @@ -13,6 +13,7 @@ import ( "code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/models/user" gitea_context "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/test" "github.com/stretchr/testify/assert" ) @@ -41,7 +42,7 @@ func TestProcessorHelper(t *testing.T) { assert.NoError(t, err) base, baseCleanUp := gitea_context.NewBaseContext(httptest.NewRecorder(), req) defer baseCleanUp() - giteaCtx := &gitea_context.Context{Base: base} + giteaCtx := gitea_context.NewWebContext(base, &test.MockRender{}, nil) assert.True(t, ProcessorHelper().IsUsernameMentionable(giteaCtx, userPublic)) assert.False(t, ProcessorHelper().IsUsernameMentionable(giteaCtx, userPrivate)) diff --git a/services/migrations/gitlab.go b/services/migrations/gitlab.go index 76180a5159..f626036254 100644 --- a/services/migrations/gitlab.go +++ b/services/migrations/gitlab.go @@ -14,6 +14,7 @@ import ( "strings" "time" + "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/log" base "code.gitea.io/gitea/modules/migration" "code.gitea.io/gitea/modules/structs" @@ -673,16 +674,15 @@ func (g *GitlabDownloader) GetReviews(reviewable base.Reviewable) ([]*base.Revie func (g *GitlabDownloader) awardsToReactions(awards []*gitlab.AwardEmoji) []*base.Reaction { result := make([]*base.Reaction, 0, len(awards)) - uniqCheck := make(map[string]struct{}) + uniqCheck := make(container.Set[string]) for _, award := range awards { uid := fmt.Sprintf("%s%d", award.Name, award.User.ID) - if _, ok := uniqCheck[uid]; !ok { + if uniqCheck.Add(uid) { result = append(result, &base.Reaction{ UserID: int64(award.User.ID), UserName: award.User.Username, Content: award.Name, }) - uniqCheck[uid] = struct{}{} } } return result diff --git a/services/mirror/mirror.go b/services/mirror/mirror.go index abce1d3c2d..0fc871b214 100644 --- a/services/mirror/mirror.go +++ b/services/mirror/mirror.go @@ -10,21 +10,20 @@ import ( repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/log" - mirror_module "code.gitea.io/gitea/modules/mirror" "code.gitea.io/gitea/modules/queue" "code.gitea.io/gitea/modules/setting" ) // doMirrorSync causes this request to mirror itself -func doMirrorSync(ctx context.Context, req *mirror_module.SyncRequest) { +func doMirrorSync(ctx context.Context, req *SyncRequest) { if req.ReferenceID == 0 { log.Warn("Skipping mirror sync request, no mirror ID was specified") return } switch req.Type { - case mirror_module.PushMirrorType: + case PushMirrorType: _ = SyncPushMirror(ctx, req.ReferenceID) - case mirror_module.PullMirrorType: + case PullMirrorType: _ = SyncPullMirror(ctx, req.ReferenceID) default: log.Error("Unknown Request type in queue: %v for MirrorID[%d]", req.Type, req.ReferenceID) @@ -43,7 +42,7 @@ func Update(ctx context.Context, pullLimit, pushLimit int) error { handler := func(idx int, bean any) error { var repo *repo_model.Repository - var mirrorType mirror_module.SyncType + var mirrorType SyncType var referenceID int64 if m, ok := bean.(*repo_model.Mirror); ok { @@ -52,7 +51,7 @@ func Update(ctx context.Context, pullLimit, pushLimit int) error { return nil } repo = m.Repo - mirrorType = mirror_module.PullMirrorType + mirrorType = PullMirrorType referenceID = m.RepoID } else if m, ok := bean.(*repo_model.PushMirror); ok { if m.GetRepository() == nil { @@ -60,7 +59,7 @@ func Update(ctx context.Context, pullLimit, pushLimit int) error { return nil } repo = m.Repo - mirrorType = mirror_module.PushMirrorType + mirrorType = PushMirrorType referenceID = m.ID } else { log.Error("Unknown bean: %v", bean) @@ -75,9 +74,9 @@ func Update(ctx context.Context, pullLimit, pushLimit int) error { } // Push to the Queue - if err := mirror_module.PushToQueue(mirrorType, referenceID); err != nil { + if err := PushToQueue(mirrorType, referenceID); err != nil { if err == queue.ErrAlreadyInQueue { - if mirrorType == mirror_module.PushMirrorType { + if mirrorType == PushMirrorType { log.Trace("PushMirrors for %-v already queued for sync", repo) } else { log.Trace("PullMirrors for %-v already queued for sync", repo) @@ -120,7 +119,7 @@ func Update(ctx context.Context, pullLimit, pushLimit int) error { return nil } -func queueHandler(items ...*mirror_module.SyncRequest) []*mirror_module.SyncRequest { +func queueHandler(items ...*SyncRequest) []*SyncRequest { for _, req := range items { doMirrorSync(graceful.GetManager().ShutdownContext(), req) } @@ -129,5 +128,5 @@ func queueHandler(items ...*mirror_module.SyncRequest) []*mirror_module.SyncRequ // InitSyncMirrors initializes a go routine to sync the mirrors func InitSyncMirrors() { - mirror_module.StartSyncMirrors(queueHandler) + StartSyncMirrors(queueHandler) } diff --git a/services/mirror/mirror_push.go b/services/mirror/mirror_push.go index 2c1b00b60c..594d31df89 100644 --- a/services/mirror/mirror_push.go +++ b/services/mirror/mirror_push.go @@ -253,3 +253,15 @@ func pushAllLFSObjects(ctx context.Context, gitRepo *git.Repository, lfsClient l return nil } + +func syncPushMirrorWithSyncOnCommit(ctx context.Context, repoID int64) { + pushMirrors, err := repo_model.GetPushMirrorsSyncedOnCommit(ctx, repoID) + if err != nil { + log.Error("repo_model.GetPushMirrorsSyncedOnCommit failed: %v", err) + return + } + + for _, mirror := range pushMirrors { + AddPushMirrorToQueue(mirror.ID) + } +} diff --git a/modules/notification/mirror/mirror.go b/services/mirror/notifier.go similarity index 61% rename from modules/notification/mirror/mirror.go rename to services/mirror/notifier.go index 426e36ee58..e0e1b443e0 100644 --- a/modules/notification/mirror/mirror.go +++ b/services/mirror/notifier.go @@ -8,23 +8,21 @@ import ( repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" - "code.gitea.io/gitea/modules/log" - mirror_module "code.gitea.io/gitea/modules/mirror" + "code.gitea.io/gitea/modules/notification" "code.gitea.io/gitea/modules/notification/base" "code.gitea.io/gitea/modules/repository" ) +func init() { + notification.RegisterNotifier(&mirrorNotifier{}) +} + type mirrorNotifier struct { base.NullNotifier } var _ base.Notifier = &mirrorNotifier{} -// NewNotifier create a new mirrorNotifier notifier -func NewNotifier() base.Notifier { - return &mirrorNotifier{} -} - func (m *mirrorNotifier) NotifyPushCommits(ctx context.Context, _ *user_model.User, repo *repo_model.Repository, _ *repository.PushUpdateOptions, _ *repository.PushCommits) { syncPushMirrorWithSyncOnCommit(ctx, repo.ID) } @@ -32,15 +30,3 @@ func (m *mirrorNotifier) NotifyPushCommits(ctx context.Context, _ *user_model.Us func (m *mirrorNotifier) NotifySyncPushCommits(ctx context.Context, _ *user_model.User, repo *repo_model.Repository, _ *repository.PushUpdateOptions, _ *repository.PushCommits) { syncPushMirrorWithSyncOnCommit(ctx, repo.ID) } - -func syncPushMirrorWithSyncOnCommit(ctx context.Context, repoID int64) { - pushMirrors, err := repo_model.GetPushMirrorsSyncedOnCommit(ctx, repoID) - if err != nil { - log.Error("repo_model.GetPushMirrorsSyncedOnCommit failed: %v", err) - return - } - - for _, mirror := range pushMirrors { - mirror_module.AddPushMirrorToQueue(mirror.ID) - } -} diff --git a/modules/mirror/mirror.go b/services/mirror/queue.go similarity index 100% rename from modules/mirror/mirror.go rename to services/mirror/queue.go diff --git a/services/packages/cargo/index.go b/services/packages/cargo/index.go index 1c2d17b504..867cd796d3 100644 --- a/services/packages/cargo/index.go +++ b/services/packages/cargo/index.go @@ -21,6 +21,7 @@ import ( cargo_module "code.gitea.io/gitea/modules/packages/cargo" repo_module "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" files_service "code.gitea.io/gitea/services/repository/files" ) @@ -220,14 +221,16 @@ func getOrCreateIndexRepository(ctx context.Context, doer, owner *user_model.Use } type Config struct { - DownloadURL string `json:"dl"` - APIURL string `json:"api"` + DownloadURL string `json:"dl"` + APIURL string `json:"api"` + AuthRequired bool `json:"auth-required"` } -func BuildConfig(owner *user_model.User) *Config { +func BuildConfig(owner *user_model.User, isPrivate bool) *Config { return &Config{ - DownloadURL: setting.AppURL + "api/packages/" + owner.Name + "/cargo/api/v1/crates", - APIURL: setting.AppURL + "api/packages/" + owner.Name + "/cargo", + DownloadURL: setting.AppURL + "api/packages/" + owner.Name + "/cargo/api/v1/crates", + APIURL: setting.AppURL + "api/packages/" + owner.Name + "/cargo", + AuthRequired: isPrivate, } } @@ -239,7 +242,7 @@ func createOrUpdateConfigFile(ctx context.Context, repo *repo_model.Repository, "Initialize Cargo Config", func(t *files_service.TemporaryUploadRepository) error { var b bytes.Buffer - err := json.NewEncoder(&b).Encode(BuildConfig(owner)) + err := json.NewEncoder(&b).Encode(BuildConfig(owner, setting.Service.RequireSignInView || owner.Visibility != structs.VisibleTypePublic || repo.IsPrivate)) if err != nil { return err } diff --git a/services/packages/rpm/repository.go b/services/packages/rpm/repository.go index 774613509e..cfd70ec23e 100644 --- a/services/packages/rpm/repository.go +++ b/services/packages/rpm/repository.go @@ -232,7 +232,7 @@ func buildRepomd(pv *packages_model.PackageVersion, ownerID int64, data []*repoD } var buf bytes.Buffer - buf.Write([]byte(xml.Header)) + buf.WriteString(xml.Header) if err := xml.NewEncoder(&buf).Encode(&Repomd{ Xmlns: "http://linux.duke.edu/metadata/repo", XmlnsRpm: "http://linux.duke.edu/metadata/rpm", diff --git a/services/pull/pull.go b/services/pull/pull.go index 0b6194b143..d4352abafe 100644 --- a/services/pull/pull.go +++ b/services/pull/pull.go @@ -755,11 +755,11 @@ func GetSquashMergeCommitMessages(ctx context.Context, pr *issues_model.PullRequ } for _, author := range authors { - if _, err := stringBuilder.Write([]byte("Co-authored-by: ")); err != nil { + if _, err := stringBuilder.WriteString("Co-authored-by: "); err != nil { log.Error("Unable to write to string builder Error: %v", err) return "" } - if _, err := stringBuilder.Write([]byte(author)); err != nil { + if _, err := stringBuilder.WriteString(author); err != nil { log.Error("Unable to write to string builder Error: %v", err) return "" } diff --git a/services/pull/review.go b/services/pull/review.go index 58470142e1..6e088382f9 100644 --- a/services/pull/review.go +++ b/services/pull/review.go @@ -333,9 +333,9 @@ func DismissApprovalReviews(ctx context.Context, doer *user_model.User, pull *is return err } - return db.WithTx(ctx, func(subCtx context.Context) error { + return db.WithTx(ctx, func(ctx context.Context) error { for _, review := range reviews { - if err := issues_model.DismissReview(subCtx, review, true); err != nil { + if err := issues_model.DismissReview(ctx, review, true); err != nil { return err } diff --git a/services/repository/files/temp_repo.go b/services/repository/files/temp_repo.go index a086d15a4f..7f6b8137ae 100644 --- a/services/repository/files/temp_repo.go +++ b/services/repository/files/temp_repo.go @@ -114,12 +114,12 @@ func (t *TemporaryUploadRepository) LsFiles(filenames ...string) ([]string, erro return nil, err } - filelist := make([]string, len(filenames)) + fileList := make([]string, 0, len(filenames)) for _, line := range bytes.Split(stdOut.Bytes(), []byte{'\000'}) { - filelist = append(filelist, string(line)) + fileList = append(fileList, string(line)) } - return filelist, nil + return fileList, nil } // RemoveFilesFromIndex removes the given files from the index diff --git a/services/webhook/feishu.go b/services/webhook/feishu.go index 885cde3bc5..089f51952f 100644 --- a/services/webhook/feishu.go +++ b/services/webhook/feishu.go @@ -97,23 +97,40 @@ func (f *FeishuPayload) Push(p *api.PushPayload) (api.Payloader, error) { // Issue implements PayloadConvertor Issue method func (f *FeishuPayload) Issue(p *api.IssuePayload) (api.Payloader, error) { - text, issueTitle, attachmentText, _ := getIssuesPayloadInfo(p, noneLinkFormatter, true) - - return newFeishuTextPayload(issueTitle + "\r\n" + text + "\r\n\r\n" + attachmentText), nil + title, link, by, operator, result, assignees := getIssuesInfo(p) + var res api.Payloader + if assignees != "" { + if p.Action == api.HookIssueAssigned || p.Action == api.HookIssueUnassigned || p.Action == api.HookIssueMilestoned { + res = newFeishuTextPayload(fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n%s\n\n%s", title, link, by, operator, result, assignees, p.Issue.Body)) + } else { + res = newFeishuTextPayload(fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n\n%s", title, link, by, operator, assignees, p.Issue.Body)) + } + } else { + res = newFeishuTextPayload(fmt.Sprintf("%s\n%s\n%s\n%s\n\n%s", title, link, by, operator, p.Issue.Body)) + } + return res, nil } // IssueComment implements PayloadConvertor IssueComment method func (f *FeishuPayload) IssueComment(p *api.IssueCommentPayload) (api.Payloader, error) { - text, issueTitle, _ := getIssueCommentPayloadInfo(p, noneLinkFormatter, true) - - return newFeishuTextPayload(issueTitle + "\r\n" + text + "\r\n\r\n" + p.Comment.Body), nil + title, link, by, operator := getIssuesCommentInfo(p) + return newFeishuTextPayload(fmt.Sprintf("%s\n%s\n%s\n%s\n\n%s", title, link, by, operator, p.Comment.Body)), nil } // PullRequest implements PayloadConvertor PullRequest method func (f *FeishuPayload) PullRequest(p *api.PullRequestPayload) (api.Payloader, error) { - text, issueTitle, attachmentText, _ := getPullRequestPayloadInfo(p, noneLinkFormatter, true) - - return newFeishuTextPayload(issueTitle + "\r\n" + text + "\r\n\r\n" + attachmentText), nil + title, link, by, operator, result, assignees := getPullRequestInfo(p) + var res api.Payloader + if assignees != "" { + if p.Action == api.HookIssueAssigned || p.Action == api.HookIssueUnassigned || p.Action == api.HookIssueMilestoned { + res = newFeishuTextPayload(fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n%s\n\n%s", title, link, by, operator, result, assignees, p.PullRequest.Body)) + } else { + res = newFeishuTextPayload(fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n\n%s", title, link, by, operator, assignees, p.PullRequest.Body)) + } + } else { + res = newFeishuTextPayload(fmt.Sprintf("%s\n%s\n%s\n%s\n\n%s", title, link, by, operator, p.PullRequest.Body)) + } + return res, nil } // Review implements PayloadConvertor Review method diff --git a/services/webhook/feishu_test.go b/services/webhook/feishu_test.go index 84549c1fa5..a3182e82b0 100644 --- a/services/webhook/feishu_test.go +++ b/services/webhook/feishu_test.go @@ -72,7 +72,7 @@ func TestFeishuPayload(t *testing.T) { require.NotNil(t, pl) require.IsType(t, &FeishuPayload{}, pl) - assert.Equal(t, "#2 crash\r\n[test/repo] Issue opened: #2 crash by user1\r\n\r\nissue body", pl.(*FeishuPayload).Content.Text) + assert.Equal(t, "[Issue-test/repo #2]: opened\ncrash\nhttp://localhost:3000/test/repo/issues/2\nIssue by user1\nOperator: user1\nAssignees: user1\n\nissue body", pl.(*FeishuPayload).Content.Text) p.Action = api.HookIssueClosed pl, err = d.Issue(p) @@ -80,7 +80,7 @@ func TestFeishuPayload(t *testing.T) { require.NotNil(t, pl) require.IsType(t, &FeishuPayload{}, pl) - assert.Equal(t, "#2 crash\r\n[test/repo] Issue closed: #2 crash by user1", pl.(*FeishuPayload).Content.Text) + assert.Equal(t, "[Issue-test/repo #2]: closed\ncrash\nhttp://localhost:3000/test/repo/issues/2\nIssue by user1\nOperator: user1\nAssignees: user1\n\nissue body", pl.(*FeishuPayload).Content.Text) }) t.Run("IssueComment", func(t *testing.T) { @@ -92,7 +92,7 @@ func TestFeishuPayload(t *testing.T) { require.NotNil(t, pl) require.IsType(t, &FeishuPayload{}, pl) - assert.Equal(t, "#2 crash\r\n[test/repo] New comment on issue #2 crash by user1\r\n\r\nmore info needed", pl.(*FeishuPayload).Content.Text) + assert.Equal(t, "[Comment-test/repo #2]: created\ncrash\nhttp://localhost:3000/test/repo/issues/2\nIssue by user1\nOperator: user1\n\nmore info needed", pl.(*FeishuPayload).Content.Text) }) t.Run("PullRequest", func(t *testing.T) { @@ -104,7 +104,7 @@ func TestFeishuPayload(t *testing.T) { require.NotNil(t, pl) require.IsType(t, &FeishuPayload{}, pl) - assert.Equal(t, "#12 Fix bug\r\n[test/repo] Pull request opened: #12 Fix bug by user1\r\n\r\nfixes bug #2", pl.(*FeishuPayload).Content.Text) + assert.Equal(t, "[PullRequest-test/repo #12]: opened\nFix bug\nhttp://localhost:3000/test/repo/pulls/12\nPullRequest by user1\nOperator: user1\nAssignees: user1\n\nfixes bug #2", pl.(*FeishuPayload).Content.Text) }) t.Run("PullRequestComment", func(t *testing.T) { @@ -116,7 +116,7 @@ func TestFeishuPayload(t *testing.T) { require.NotNil(t, pl) require.IsType(t, &FeishuPayload{}, pl) - assert.Equal(t, "#12 Fix bug\r\n[test/repo] New comment on pull request #12 Fix bug by user1\r\n\r\nchanges requested", pl.(*FeishuPayload).Content.Text) + assert.Equal(t, "[Comment-test/repo #12]: created\nFix bug\nhttp://localhost:3000/test/repo/pulls/12\nPullRequest by user1\nOperator: user1\n\nchanges requested", pl.(*FeishuPayload).Content.Text) }) t.Run("Review", func(t *testing.T) { diff --git a/services/webhook/general.go b/services/webhook/general.go index b9cc3dc845..986467bc99 100644 --- a/services/webhook/general.go +++ b/services/webhook/general.go @@ -28,6 +28,69 @@ func htmlLinkFormatter(url, text string) string { return fmt.Sprintf(`%s`, html.EscapeString(url), html.EscapeString(text)) } +// getPullRequestInfo gets the information for a pull request +func getPullRequestInfo(p *api.PullRequestPayload) (title, link, by, operator, operateResult, assignees string) { + title = fmt.Sprintf("[PullRequest-%s #%d]: %s\n%s", p.Repository.FullName, p.PullRequest.Index, p.Action, p.PullRequest.Title) + assignList := p.PullRequest.Assignees + assignStringList := make([]string, len(assignList)) + + for i, user := range assignList { + assignStringList[i] = user.UserName + } + if p.Action == api.HookIssueAssigned { + operateResult = fmt.Sprintf("%s assign this to %s", p.Sender.UserName, assignList[len(assignList)-1].UserName) + } else if p.Action == api.HookIssueUnassigned { + operateResult = fmt.Sprintf("%s unassigned this for someone", p.Sender.UserName) + } else if p.Action == api.HookIssueMilestoned { + operateResult = fmt.Sprintf("%s/milestone/%d", p.Repository.HTMLURL, p.PullRequest.Milestone.ID) + } + link = p.PullRequest.HTMLURL + by = fmt.Sprintf("PullRequest by %s", p.PullRequest.Poster.UserName) + if len(assignStringList) > 0 { + assignees = fmt.Sprintf("Assignees: %s", strings.Join(assignStringList, ", ")) + } + operator = fmt.Sprintf("Operator: %s", p.Sender.UserName) + return title, link, by, operator, operateResult, assignees +} + +// getIssuesInfo gets the information for an issue +func getIssuesInfo(p *api.IssuePayload) (issueTitle, link, by, operator, operateResult, assignees string) { + issueTitle = fmt.Sprintf("[Issue-%s #%d]: %s\n%s", p.Repository.FullName, p.Issue.Index, p.Action, p.Issue.Title) + assignList := p.Issue.Assignees + assignStringList := make([]string, len(assignList)) + + for i, user := range assignList { + assignStringList[i] = user.UserName + } + if p.Action == api.HookIssueAssigned { + operateResult = fmt.Sprintf("%s assign this to %s", p.Sender.UserName, assignList[len(assignList)-1].UserName) + } else if p.Action == api.HookIssueUnassigned { + operateResult = fmt.Sprintf("%s unassigned this for someone", p.Sender.UserName) + } else if p.Action == api.HookIssueMilestoned { + operateResult = fmt.Sprintf("%s/milestone/%d", p.Repository.HTMLURL, p.Issue.Milestone.ID) + } + link = p.Issue.HTMLURL + by = fmt.Sprintf("Issue by %s", p.Issue.Poster.UserName) + if len(assignStringList) > 0 { + assignees = fmt.Sprintf("Assignees: %s", strings.Join(assignStringList, ", ")) + } + operator = fmt.Sprintf("Operator: %s", p.Sender.UserName) + return issueTitle, link, by, operator, operateResult, assignees +} + +// getIssuesCommentInfo gets the information for a comment +func getIssuesCommentInfo(p *api.IssueCommentPayload) (title, link, by, operator string) { + title = fmt.Sprintf("[Comment-%s #%d]: %s\n%s", p.Repository.FullName, p.Issue.Index, p.Action, p.Issue.Title) + link = p.Issue.HTMLURL + if p.IsPull { + by = fmt.Sprintf("PullRequest by %s", p.Issue.Poster.UserName) + } else { + by = fmt.Sprintf("Issue by %s", p.Issue.Poster.UserName) + } + operator = fmt.Sprintf("Operator: %s", p.Sender.UserName) + return title, link, by, operator +} + func getIssuesPayloadInfo(p *api.IssuePayload, linkFormatter linkFormatter, withSender bool) (string, string, string, int) { repoLink := linkFormatter(p.Repository.HTMLURL, p.Repository.FullName) issueTitle := fmt.Sprintf("#%d %s", p.Index, p.Issue.Title) diff --git a/services/webhook/general_test.go b/services/webhook/general_test.go index ba58ca4f90..64bd72f5a0 100644 --- a/services/webhook/general_test.go +++ b/services/webhook/general_test.go @@ -123,6 +123,10 @@ func issueTestPayload() *api.IssuePayload { HTMLURL: "http://localhost:3000/test/repo/issues/2", Title: "crash", Body: "issue body", + Poster: &api.User{ + UserName: "user1", + AvatarURL: "http://localhost:3000/user1/avatar", + }, Assignees: []*api.User{ { UserName: "user1", @@ -161,7 +165,11 @@ func issueCommentTestPayload() *api.IssueCommentPayload { URL: "http://localhost:3000/api/v1/repos/test/repo/issues/2", HTMLURL: "http://localhost:3000/test/repo/issues/2", Title: "crash", - Body: "this happened", + Poster: &api.User{ + UserName: "user1", + AvatarURL: "http://localhost:3000/user1/avatar", + }, + Body: "this happened", }, } } @@ -190,6 +198,10 @@ func pullRequestCommentTestPayload() *api.IssueCommentPayload { HTMLURL: "http://localhost:3000/test/repo/pulls/12", Title: "Fix bug", Body: "fixes bug #2", + Poster: &api.User{ + UserName: "user1", + AvatarURL: "http://localhost:3000/user1/avatar", + }, }, IsPull: true, } @@ -254,6 +266,10 @@ func pullRequestTestPayload() *api.PullRequestPayload { Title: "Fix bug", Body: "fixes bug #2", Mergeable: true, + Poster: &api.User{ + UserName: "user1", + AvatarURL: "http://localhost:3000/user1/avatar", + }, Assignees: []*api.User{ { UserName: "user1", diff --git a/templates/admin/notice.tmpl b/templates/admin/notice.tmpl index 5cd617a0b5..ac5851f853 100644 --- a/templates/admin/notice.tmpl +++ b/templates/admin/notice.tmpl @@ -17,7 +17,7 @@ {{range .Notices}} -
+
{{.ID}} {{$.locale.Tr .TrStr}} {{.Description}} diff --git a/templates/admin/stacktrace-row.tmpl b/templates/admin/stacktrace-row.tmpl index 15e51e4aca..b0e282bcc7 100644 --- a/templates/admin/stacktrace-row.tmpl +++ b/templates/admin/stacktrace-row.tmpl @@ -27,7 +27,7 @@
-
+
{{svg "octicon-code" 16}}{{.Description}}{{if gt .Count 1}} * {{.Count}}{{end}}
diff --git a/templates/base/head_navbar.tmpl b/templates/base/head_navbar.tmpl index 1ad7fd20bd..5a2d0ddb0b 100644 --- a/templates/base/head_navbar.tmpl +++ b/templates/base/head_navbar.tmpl @@ -48,7 +48,7 @@ {{template "custom/extra_links" .}} {{if not .IsSigned}} - {{.locale.Tr "help"}} + {{.locale.Tr "help"}} {{end}}
@@ -171,7 +171,7 @@ {{svg "octicon-tools"}} {{.locale.Tr "your_settings"}} - + {{svg "octicon-question"}} {{.locale.Tr "help"}} diff --git a/templates/code/searchresults.tmpl b/templates/code/searchresults.tmpl index 684e2860bf..faac696705 100644 --- a/templates/code/searchresults.tmpl +++ b/templates/code/searchresults.tmpl @@ -1,6 +1,6 @@ -
+
{{range $term := .SearchResultLanguages}} - + {{$term.Language}}
{{$term.Count}}
diff --git a/templates/devtest/flex-list.tmpl b/templates/devtest/flex-list.tmpl index f9087a5714..cbc2632139 100644 --- a/templates/devtest/flex-list.tmpl +++ b/templates/devtest/flex-list.tmpl @@ -1,8 +1,8 @@ {{template "base/head" .}} -
-
-

Flex List

+
+
+

Flex List (standalone)

@@ -84,6 +84,23 @@
+ +
+ +

Flex List (with "ui segment")

+
+
+
item 1
+
item 2
+
+
+
+

Flex List (with "ui segment")

+
+
item 1
+
item 2
+
+
{{template "base/footer" .}} diff --git a/templates/devtest/fomantic-modal.tmpl b/templates/devtest/fomantic-modal.tmpl index 799bab7594..b91b29f3cc 100644 --- a/templates/devtest/fomantic-modal.tmpl +++ b/templates/devtest/fomantic-modal.tmpl @@ -2,16 +2,33 @@
{{template "base/alert" .}} - - {{template "base/footer" .}} diff --git a/templates/install.tmpl b/templates/install.tmpl index 1716d0a5e4..c8e094b5a3 100644 --- a/templates/install.tmpl +++ b/templates/install.tmpl @@ -8,7 +8,7 @@
{{template "base/alert" .}} -

{{.locale.Tr "install.docker_helper" "https://docs.gitea.io/en-us/install-with-docker/" | Safe}}

+

{{.locale.Tr "install.docker_helper" "https://docs.gitea.com/installation/install-with-docker" | Safe}}

diff --git a/templates/org/create.tmpl b/templates/org/create.tmpl index 7e988ba0c7..966150aa93 100644 --- a/templates/org/create.tmpl +++ b/templates/org/create.tmpl @@ -19,15 +19,15 @@
- +
- +
- +
diff --git a/templates/org/home.tmpl b/templates/org/home.tmpl index 0bd744d2b8..72be948650 100644 --- a/templates/org/home.tmpl +++ b/templates/org/home.tmpl @@ -57,7 +57,7 @@ {{if .NumMembers}}

{{.locale.Tr "org.members"}} - {{.NumMembers}} {{svg "octicon-chevron-right"}} + {{.NumMembers}} {{svg "octicon-chevron-right"}}

{{$isMember := .IsOrganizationMember}} @@ -71,7 +71,7 @@ {{if .IsOrganizationMember}}
{{range .Teams}} diff --git a/templates/org/settings/options.tmpl b/templates/org/settings/options.tmpl index 03827e4c3f..ecc269481d 100644 --- a/templates/org/settings/options.tmpl +++ b/templates/org/settings/options.tmpl @@ -40,19 +40,19 @@
- +
- +
- +
diff --git a/templates/org/team/repositories.tmpl b/templates/org/team/repositories.tmpl index ab5f6c6e75..65eb64cb54 100644 --- a/templates/org/team/repositories.tmpl +++ b/templates/org/team/repositories.tmpl @@ -10,7 +10,7 @@ {{$canAddRemove := and $.IsOrganizationOwner (not $.Team.IncludesAllRepositories)}} {{if $canAddRemove}}
- + {{.CsrfTokenHtml}}
- +
diff --git a/templates/package/content/cargo.tmpl b/templates/package/content/cargo.tmpl index b3dfbae257..b7a34632e4 100644 --- a/templates/package/content/cargo.tmpl +++ b/templates/package/content/cargo.tmpl @@ -18,7 +18,7 @@ git-fetch-with-cli = true
cargo add {{.PackageDescriptor.Package.Name}}@{{.PackageDescriptor.Version.Version}}
- +
diff --git a/templates/package/content/chef.tmpl b/templates/package/content/chef.tmpl index 331b1fb49f..2596f8d493 100644 --- a/templates/package/content/chef.tmpl +++ b/templates/package/content/chef.tmpl @@ -11,7 +11,7 @@
knife supermarket install {{.PackageDescriptor.Package.Name}} {{.PackageDescriptor.Version.Version}}
- +
diff --git a/templates/package/content/composer.tmpl b/templates/package/content/composer.tmpl index c2a8bc9625..80997c583f 100644 --- a/templates/package/content/composer.tmpl +++ b/templates/package/content/composer.tmpl @@ -17,7 +17,7 @@
composer require {{.PackageDescriptor.Package.Name}}:{{.PackageDescriptor.Version.Version}}
- +
diff --git a/templates/package/content/conan.tmpl b/templates/package/content/conan.tmpl index 86f1c8ede3..c8904216af 100644 --- a/templates/package/content/conan.tmpl +++ b/templates/package/content/conan.tmpl @@ -11,7 +11,7 @@
conan install --remote=gitea {{.PackageDescriptor.Package.Name}}/{{.PackageDescriptor.Version.Version}}
- +
diff --git a/templates/package/content/conda.tmpl b/templates/package/content/conda.tmpl index 627e637e1a..23d640944b 100644 --- a/templates/package/content/conda.tmpl +++ b/templates/package/content/conda.tmpl @@ -16,7 +16,7 @@ default_channels:
conda install{{if $channel}} -c {{$channel}}{{end}} {{.PackageDescriptor.PackageProperties.GetByName "conda.name"}}={{.PackageDescriptor.Version.Version}}
- +
diff --git a/templates/package/content/container.tmpl b/templates/package/content/container.tmpl index 5aba2b6061..cba5d9e7c1 100644 --- a/templates/package/content/container.tmpl +++ b/templates/package/content/container.tmpl @@ -19,7 +19,7 @@
{{range .PackageDescriptor.Files}}{{if eq .File.LowerName "manifest.json"}}{{.Properties.GetByName "container.digest"}}{{end}}{{end}}
- +
diff --git a/templates/package/content/cran.tmpl b/templates/package/content/cran.tmpl index 38a58bc4e7..1f3200130c 100644 --- a/templates/package/content/cran.tmpl +++ b/templates/package/content/cran.tmpl @@ -11,7 +11,7 @@
install.packages("{{.PackageDescriptor.Package.Name}}")
- +
diff --git a/templates/package/content/debian.tmpl b/templates/package/content/debian.tmpl index ea146d170f..b222767035 100644 --- a/templates/package/content/debian.tmpl +++ b/templates/package/content/debian.tmpl @@ -16,7 +16,7 @@ sudo apt update
- +
diff --git a/templates/package/content/generic.tmpl b/templates/package/content/generic.tmpl index d3e4c7a62a..01012a5f0a 100644 --- a/templates/package/content/generic.tmpl +++ b/templates/package/content/generic.tmpl @@ -11,7 +11,7 @@ curl - + diff --git a/templates/package/content/go.tmpl b/templates/package/content/go.tmpl index 616698c414..db25cc2b92 100644 --- a/templates/package/content/go.tmpl +++ b/templates/package/content/go.tmpl @@ -7,7 +7,7 @@
GOPROXY= go install {{$.PackageDescriptor.Package.Name}}@{{$.PackageDescriptor.Version.Version}}
- +
diff --git a/templates/package/content/helm.tmpl b/templates/package/content/helm.tmpl index 11c05a12b6..44d01b6013 100644 --- a/templates/package/content/helm.tmpl +++ b/templates/package/content/helm.tmpl @@ -12,7 +12,7 @@ helm repo update
helm install {{.PackageDescriptor.Package.Name}} {{AppDomain}}/{{.PackageDescriptor.Package.Name}}
- +
diff --git a/templates/package/content/maven.tmpl b/templates/package/content/maven.tmpl index d4c991c466..f5da72d711 100644 --- a/templates/package/content/maven.tmpl +++ b/templates/package/content/maven.tmpl @@ -40,7 +40,7 @@
mvn dependency:get -DremoteRepositories= -Dartifact={{.PackageDescriptor.Metadata.GroupID}}:{{.PackageDescriptor.Metadata.ArtifactID}}:{{.PackageDescriptor.Version.Version}}
- +
diff --git a/templates/package/content/npm.tmpl b/templates/package/content/npm.tmpl index e376a45f8b..c648d96162 100644 --- a/templates/package/content/npm.tmpl +++ b/templates/package/content/npm.tmpl @@ -15,7 +15,7 @@
"{{.PackageDescriptor.Package.Name}}": "{{.PackageDescriptor.Version.Version}}"
- +
diff --git a/templates/package/content/nuget.tmpl b/templates/package/content/nuget.tmpl index e95d9176b8..2b3a74316c 100644 --- a/templates/package/content/nuget.tmpl +++ b/templates/package/content/nuget.tmpl @@ -11,7 +11,7 @@
dotnet add package --source {{.PackageDescriptor.Owner.Name}} --version {{.PackageDescriptor.Version.Version}} {{.PackageDescriptor.Package.Name}}
- +
diff --git a/templates/package/content/pub.tmpl b/templates/package/content/pub.tmpl index 840bedbd9d..075d52cf49 100644 --- a/templates/package/content/pub.tmpl +++ b/templates/package/content/pub.tmpl @@ -7,7 +7,7 @@
dart pub add {{.PackageDescriptor.Package.Name}}:{{.PackageDescriptor.Version.Version}} --hosted-url=
- +
diff --git a/templates/package/content/pypi.tmpl b/templates/package/content/pypi.tmpl index 460a06fcd1..1534197219 100644 --- a/templates/package/content/pypi.tmpl +++ b/templates/package/content/pypi.tmpl @@ -7,7 +7,7 @@
pip install --index-url  {{.PackageDescriptor.Package.Name}}
- +
diff --git a/templates/package/content/rpm.tmpl b/templates/package/content/rpm.tmpl index 4da2abff39..fc7af639aa 100644 --- a/templates/package/content/rpm.tmpl +++ b/templates/package/content/rpm.tmpl @@ -21,7 +21,7 @@ zypper install {{$.PackageDescriptor.Package.Name}}
- +
diff --git a/templates/package/content/rubygems.tmpl b/templates/package/content/rubygems.tmpl index 2f618392e4..f94968b094 100644 --- a/templates/package/content/rubygems.tmpl +++ b/templates/package/content/rubygems.tmpl @@ -13,7 +13,7 @@ end
- +
diff --git a/templates/package/content/swift.tmpl b/templates/package/content/swift.tmpl index c227e30c34..92559491a9 100644 --- a/templates/package/content/swift.tmpl +++ b/templates/package/content/swift.tmpl @@ -17,7 +17,7 @@
swift package resolve
- +
diff --git a/templates/package/content/vagrant.tmpl b/templates/package/content/vagrant.tmpl index e23acf58a6..8590b44b37 100644 --- a/templates/package/content/vagrant.tmpl +++ b/templates/package/content/vagrant.tmpl @@ -7,7 +7,7 @@
vagrant box add --box-version {{.PackageDescriptor.Version.Version}} ""
- +
diff --git a/templates/package/shared/cargo.tmpl b/templates/package/shared/cargo.tmpl index 4e26df2909..8831cd8988 100644 --- a/templates/package/shared/cargo.tmpl +++ b/templates/package/shared/cargo.tmpl @@ -18,7 +18,7 @@
- +
diff --git a/templates/package/shared/list.tmpl b/templates/package/shared/list.tmpl index d2370b1208..7cc11b073e 100644 --- a/templates/package/shared/list.tmpl +++ b/templates/package/shared/list.tmpl @@ -43,7 +43,7 @@ {{$packagesUrl := URLJoin .Owner.HomeLink "-" "packages"}}

{{.locale.Tr "packages.empty.repo" $packagesUrl | Safe}}

{{end}} -

{{.locale.Tr "packages.empty.documentation" "https://docs.gitea.io/en-us/usage/packages/overview/" | Safe}}

+

{{.locale.Tr "packages.empty.documentation" "https://docs.gitea.com/usage/packages/overview/" | Safe}}

{{else}}

{{.locale.Tr "packages.filter.no_result"}}

diff --git a/templates/projects/view.tmpl b/templates/projects/view.tmpl index 700b40c39b..ecd07bf5ec 100644 --- a/templates/projects/view.tmpl +++ b/templates/projects/view.tmpl @@ -43,9 +43,7 @@
-
- {{template "repo/issue/label_precolors"}} -
+ {{template "repo/issue/label_precolors"}}
@@ -76,10 +74,10 @@ {{if and $canWriteProject (ne .ID 0)}}