diff --git a/.eslintrc.yaml b/.eslintrc.yaml index 71d8dc38148..8b9123c9884 100644 --- a/.eslintrc.yaml +++ b/.eslintrc.yaml @@ -22,7 +22,7 @@ plugins: - eslint-plugin-wc env: - es2022: true + es2024: true node: true overrides: @@ -156,7 +156,7 @@ rules: import/no-restricted-paths: [0] import/no-self-import: [2] import/no-unassigned-import: [0] - import/no-unresolved: [2, {commonjs: true, ignore: ["\\?.+$"]}] + import/no-unresolved: [2, {commonjs: true, ignore: [\?.+$, ^vitest/]}] import/no-unused-modules: [2, {unusedExports: true}] import/no-useless-path-segments: [2, {commonjs: true}] import/no-webpack-loader-syntax: [2] @@ -693,7 +693,7 @@ rules: unicorn/prefer-dom-node-remove: [2] unicorn/prefer-dom-node-text-content: [2] unicorn/prefer-event-target: [2] - unicorn/prefer-export-from: [2, {ignoreUsedVariables: true}] + unicorn/prefer-export-from: [0] unicorn/prefer-includes: [2] unicorn/prefer-json-parse-buffer: [0] unicorn/prefer-keyboard-event-key: [2] diff --git a/.github/ISSUE_TEMPLATE/feature-request.yaml b/.github/ISSUE_TEMPLATE/feature-request.yaml index 37f57c8f23d..b481e0c2ded 100644 --- a/.github/ISSUE_TEMPLATE/feature-request.yaml +++ b/.github/ISSUE_TEMPLATE/feature-request.yaml @@ -1,6 +1,6 @@ name: Feature Request description: Got an idea for a feature that Gitea doesn't have currently? Submit your idea here! -labels: ["kind/feature", "kind/proposal"] +labels: ["kind/proposal"] body: - type: markdown attributes: diff --git a/.golangci.yml b/.golangci.yml index 22de387facb..7c35bdd2a8d 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -77,16 +77,21 @@ linters-settings: extra-rules: true lang-version: "1.20" depguard: - list-type: denylist - # Check the list against standard lib. - include-go-root: true - packages-with-error-message: - - encoding/json: "use gitea's modules/json instead of encoding/json" - - github.com/unknwon/com: "use gitea's util and replacements" - - io/ioutil: "use os or io instead" - - golang.org/x/exp: "it's experimental and unreliable." - - code.gitea.io/gitea/modules/git/internal: "do not use the internal package, use AddXxx function instead" - - gopkg.in/ini.v1: "do not use the ini package, use gitea's config system instead" + rules: + main: + deny: + - pkg: encoding/json + desc: use gitea's modules/json instead of encoding/json + - pkg: github.com/unknwon/com + desc: use gitea's util and replacements + - pkg: io/ioutil + desc: use os or io instead + - pkg: golang.org/x/exp + desc: it's experimental and unreliable + - pkg: code.gitea.io/gitea/modules/git/internal + desc: do not use the internal package, use AddXxx function instead + - pkg: gopkg.in/ini.v1 + desc: do not use the ini package, use gitea's config system instead issues: max-issues-per-linter: 0 diff --git a/CHANGELOG.md b/CHANGELOG.md index e4e5c9368a1..f3d3aa3a8be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,54 @@ This changelog goes through all the changes that have been made in each release without substantial changes to our git log; to see the highlights of what has been added to each release, please refer to the [blog](https://blog.gitea.io). +## [1.19.4](https://github.com/go-gitea/gitea/releases/tag/v1.19.4) - 2023-07-04 + +* SECURITY + * Fix open redirect check for more cases (#25143) (#25155) +* API + * Return `404` in the API if the requested webhooks were not found (#24823) (#24830) + * Fix `organization` field being `null` in `GET /api/v1/teams/{id}` (#24694) (#24696) +* ENHANCEMENTS + * Set `--font-weight-bold` to 600 (#24840) + * Make mailer SMTP check have timed context (#24751) (#24759) + * Do not select line numbers when selecting text from the action run logs (#24594) (#24596) +* BUGFIXES + * Fix bug when change user name (#25637) (#25645) + * Fix task list checkbox toggle to work with YAML front matter (#25184) (#25236) + * Hide limited users if viewed by anonymous ghost (#25214) (#25224) + * Add `WithPullRequest` for `actionsNotifier` (#25144) (#25196) + * Fix parallelly generating index failure with Mysql (#24567) (#25081) + * GitLab migration: Sanitize response for reaction list (#25054) (#25059) + * Fix users cannot visit issue attachment bug (#25019) (#25027) + * Fix missing reference prefix of commits when sync mirror repository (#24994) + * Only validate changed columns when update user (#24867) (#24903) + * Make DeleteIssue use correct context (#24885) + * Fix topics deleted via API not being deleted in org page (#24825) (#24829) + * Fix Actions being enabled accidentally (#24802) (#24810) + * Fix missed table name on iterate lfs meta objects (#24768) (#24774) + * Fix safari cookie session bug (#24772) + * Respect original content when creating secrets (#24745) (#24746) + * Fix Pull Mirror out-of-sync bugs (#24732) (#24733) + * Fix run list broken when trigger user deleted (#24706) (#24709) + * Fix issues list page multiple selection update milestones (#24660) (#24663) + * Fix: release page for empty or non-existing target (#24659) + * Fix close org projects (#24588) (#24591) + * Refresh the refernce of the closed PR when reopening (#24231) (#24587) + * Fix the permission of team's `Actions` unit issue (#24536) (#24545) + * Bump go.etcd.io/bbolt and blevesearch deps (#23062) (#24519) + * Fix new wiki page mirror (#24518) + * Match unqualified references when syncing pulls as well (#23070) +* DOCS + * Change branch name from master to main in some documents' links (#25126) (#25139) + * Remove unnecessary content on docs (#24976) (#25001) + * Unify doc links to use paths relative to doc folder (#24979) (#25000) + * Fix docs documenting invalid `@every` for `OLDER_THAN` cron settings (#24695) (#24698) +* MISC + * Merge different languages for language stats (#24900) (#24921) + * Hiding Secrets options when Actions feature is disabled (#24792) + * Improve decryption failure message (#24573) (#24575) + * Makefile: Use portable !, not GNUish -not, with find(1). (#24565) (#24572) + ## [1.19.3](https://github.com/go-gitea/gitea/releases/tag/1.19.3) - 2023-05-03 * SECURITY diff --git a/MAINTAINERS b/MAINTAINERS index c3ac7b945f0..ac7fb6d05a5 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -52,3 +52,5 @@ Xinyi Gong (@HesterG) wxiaoguang (@wxiaoguang) Gary Moon (@garymoon) Philip Peterson (@philip-peterson) +Denys Konovalov (@denyskon) +Punit Inani (@puni9869) diff --git a/Makefile b/Makefile index 0c4b42a8c54..7de96f09fdd 100644 --- a/Makefile +++ b/Makefile @@ -25,17 +25,17 @@ COMMA := , XGO_VERSION := go-1.20.x -AIR_PACKAGE ?= github.com/cosmtrek/air@v1.43.0 +AIR_PACKAGE ?= github.com/cosmtrek/air@v1.44.0 EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/cmd/editorconfig-checker@2.7.0 GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.5.0 -GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/cmd/golangci-lint@v1.52.2 +GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/cmd/golangci-lint@v1.53.3 GXZ_PAGAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.11 MISSPELL_PACKAGE ?= github.com/client9/misspell/cmd/misspell@v0.3.4 -SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.30.4 +SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.30.5 XGO_PACKAGE ?= src.techknowlogick.com/xgo@latest GO_LICENSES_PACKAGE ?= github.com/google/go-licenses@v1.6.0 -GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@latest -ACTIONLINT_PACKAGE ?= github.com/rhysd/actionlint/cmd/actionlint@latest +GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@v0.2.0 +ACTIONLINT_PACKAGE ?= github.com/rhysd/actionlint/cmd/actionlint@v1.6.25 DOCKER_IMAGE ?= gitea/gitea DOCKER_TAG ?= latest diff --git a/build/code-batch-process.go b/build/code-batch-process.go index 8556c27ad73..b3ee3994207 100644 --- a/build/code-batch-process.go +++ b/build/code-batch-process.go @@ -25,7 +25,7 @@ import ( var optionLogVerbose bool -func logVerbose(msg string, args ...interface{}) { +func logVerbose(msg string, args ...any) { if optionLogVerbose { log.Printf(msg, args...) } diff --git a/cmd/cert.go b/cmd/cert.go index 816659023ca..897c10c8993 100644 --- a/cmd/cert.go +++ b/cmd/cert.go @@ -63,7 +63,7 @@ Outputs to 'cert.pem' and 'key.pem' and will overwrite existing files.`, }, } -func publicKey(priv interface{}) interface{} { +func publicKey(priv any) any { switch k := priv.(type) { case *rsa.PrivateKey: return &k.PublicKey @@ -74,7 +74,7 @@ func publicKey(priv interface{}) interface{} { } } -func pemBlockForKey(priv interface{}) *pem.Block { +func pemBlockForKey(priv any) *pem.Block { switch k := priv.(type) { case *rsa.PrivateKey: return &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(k)} @@ -94,7 +94,7 @@ func runCert(c *cli.Context) error { return err } - var priv interface{} + var priv any var err error switch c.String("ecdsa-curve") { case "": diff --git a/cmd/dump.go b/cmd/dump.go index 0b7c1d32c5b..b1aed8aef46 100644 --- a/cmd/dump.go +++ b/cmd/dump.go @@ -161,7 +161,7 @@ It can be used for backup and capture Gitea server image to send to maintainer`, }, } -func fatal(format string, args ...interface{}) { +func fatal(format string, args ...any) { fmt.Fprintf(os.Stderr, format+"\n", args...) log.Fatal(format, args...) } @@ -236,7 +236,7 @@ func runDump(ctx *cli.Context) error { return err } - var iface interface{} + var iface any if fileName == "-" { iface, err = archiver.ByExtension(fmt.Sprintf(".%s", outType)) } else { diff --git a/cmd/manager_logging.go b/cmd/manager_logging.go index dd85cc26d80..70d1beb26d0 100644 --- a/cmd/manager_logging.go +++ b/cmd/manager_logging.go @@ -178,7 +178,7 @@ func runAddConnLogger(c *cli.Context) error { defer cancel() setup(ctx, c.Bool("debug")) - vals := map[string]interface{}{} + vals := map[string]any{} mode := "conn" vals["net"] = "tcp" if c.IsSet("protocol") { @@ -208,7 +208,7 @@ func runAddFileLogger(c *cli.Context) error { defer cancel() setup(ctx, c.Bool("debug")) - vals := map[string]interface{}{} + vals := map[string]any{} mode := "file" if c.IsSet("filename") { vals["filename"] = c.String("filename") @@ -236,7 +236,7 @@ func runAddFileLogger(c *cli.Context) error { return commonAddLogger(c, mode, vals) } -func commonAddLogger(c *cli.Context, mode string, vals map[string]interface{}) error { +func commonAddLogger(c *cli.Context, mode string, vals map[string]any) error { if len(c.String("level")) > 0 { vals["level"] = log.LevelFromString(c.String("level")).String() } diff --git a/cmd/serv.go b/cmd/serv.go index 79052e58a8f..484e3bf4048 100644 --- a/cmd/serv.go +++ b/cmd/serv.go @@ -95,7 +95,7 @@ var ( // fail prints message to stdout, it's mainly used for git serv and git hook commands. // The output will be passed to git client and shown to user. -func fail(ctx context.Context, userMessage, logMsgFmt string, args ...interface{}) error { +func fail(ctx context.Context, userMessage, logMsgFmt string, args ...any) error { if userMessage == "" { userMessage = "Internal Server Error (no specific error)" } diff --git a/contrib/environment-to-ini/environment-to-ini.go b/contrib/environment-to-ini/environment-to-ini.go index 230ed58269a..e472384a950 100644 --- a/contrib/environment-to-ini/environment-to-ini.go +++ b/contrib/environment-to-ini/environment-to-ini.go @@ -5,7 +5,6 @@ package main import ( "os" - "strings" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" @@ -13,9 +12,6 @@ import ( "github.com/urfave/cli" ) -// EnvironmentPrefix environment variables prefixed with this represent ini values to write -const EnvironmentPrefix = "GITEA" - func main() { app := cli.NewApp() app.Name = "environment-to-ini" @@ -70,15 +66,6 @@ func main() { Value: "", Usage: "Destination file to write to", }, - cli.BoolFlag{ - Name: "clear", - Usage: "Clears the matched variables from the environment", - }, - cli.StringFlag{ - Name: "prefix, p", - Value: EnvironmentPrefix, - Usage: "Environment prefix to look for - will be suffixed by __ (2 underscores)", - }, } app.Action = runEnvironmentToIni err := app.Run(os.Args) @@ -99,9 +86,7 @@ func runEnvironmentToIni(c *cli.Context) error { log.Fatal("Failed to load custom conf '%s': %v", setting.CustomConf, err) } - prefixGitea := c.String("prefix") + "__" - suffixFile := "__FILE" - changed := setting.EnvironmentToConfig(cfg, prefixGitea, suffixFile, os.Environ()) + changed := setting.EnvironmentToConfig(cfg, os.Environ()) // try to save the config file destination := c.String("out") @@ -116,19 +101,5 @@ func runEnvironmentToIni(c *cli.Context) error { } } - // clear Gitea's specific environment variables if requested - if c.Bool("clear") { - for _, kv := range os.Environ() { - idx := strings.IndexByte(kv, '=') - if idx < 0 { - continue - } - eKey := kv[:idx] - if strings.HasPrefix(eKey, prefixGitea) { - _ = os.Unsetenv(eKey) - } - } - } - return nil } diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini index 42ff0ec6931..b38983e2189 100644 --- a/custom/conf/app.example.ini +++ b/custom/conf/app.example.ini @@ -193,8 +193,8 @@ RUN_USER = ; git ;; Use `ssh-keygen` to parse public SSH keys. The value is passed to the shell. By default, Gitea does the parsing itself. ;SSH_KEYGEN_PATH = ;; -;; Enable SSH Authorized Key Backup when rewriting all keys, default is true -;SSH_AUTHORIZED_KEYS_BACKUP = true +;; Enable SSH Authorized Key Backup when rewriting all keys, default is false +;SSH_AUTHORIZED_KEYS_BACKUP = false ;; ;; Determines which principals to allow ;; - empty: if SSH_TRUSTED_USER_CA_KEYS is empty this will default to off, otherwise will default to email, username. @@ -303,7 +303,10 @@ RUN_USER = ; git ;; ;; ;; LFS authentication secret, change this yourself -LFS_JWT_SECRET = +;LFS_JWT_SECRET = +;; +;; Alternative location to specify LFS authentication secret. You cannot specify both this and LFS_JWT_SECRET, and must pick one +;LFS_JWT_SECRET_URI = file:/etc/gitea/lfs_jwt_secret ;; ;; LFS authentication validity period (in time.Duration), pushes taking longer than this may fail. ;LFS_HTTP_AUTH_EXPIRY = 24h @@ -527,6 +530,9 @@ ENABLE = true ;; This setting is only needed if JWT_SIGNING_ALGORITHM is set to HS256, HS384 or HS512. ;JWT_SECRET = ;; +;; Alternative location to specify OAuth2 authentication secret. You cannot specify both this and JWT_SECRET, and must pick one +;JWT_SECRET_URI = file:/etc/gitea/oauth2_jwt_secret +;; ;; Lifetime of an OAuth2 access token in seconds ;ACCESS_TOKEN_EXPIRATION_TIME = 3600 ;; @@ -2550,8 +2556,8 @@ LEVEL = Info ;; Enable/Disable actions capabilities ;ENABLED = false ;; -;; Default address to get action plugins, e.g. the default value means downloading from "https://gitea.com/actions/checkout" for "uses: actions/checkout@v3" -;DEFAULT_ACTIONS_URL = https://gitea.com +;; Default platform to get action plugins, `github` for `https://github.com`, `self` for the current Gitea instance. +;DEFAULT_ACTIONS_URL = github ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/docker/root/etc/s6/gitea/setup b/docker/root/etc/s6/gitea/setup index d8f6a3b319e..b801ef4e035 100755 --- a/docker/root/etc/s6/gitea/setup +++ b/docker/root/etc/s6/gitea/setup @@ -2,7 +2,15 @@ if [ ! -d /data/git/.ssh ]; then mkdir -p /data/git/.ssh - chmod 700 /data/git/.ssh +fi + +# Set the correct permissions on the .ssh directory and authorized_keys file, +# or sshd will refuse to use them and lead to clone/push/pull failures. +# It could happen when users have copied their data to a new volume and changed the file permission by accident, +# and it would be very hard to troubleshoot unless users know how to check the logs of sshd which is started by s6. +chmod 700 /data/git/.ssh +if [ -f /data/git/.ssh/authorized_keys ]; then + chmod 600 /data/git/.ssh/authorized_keys fi if [ ! -f /data/git/.ssh/environment ]; then diff --git a/docker/root/usr/local/bin/gitea b/docker/root/usr/local/bin/gitea index 24d3f91eb15..3f78d65abd0 100644 --- a/docker/root/usr/local/bin/gitea +++ b/docker/root/usr/local/bin/gitea @@ -8,7 +8,7 @@ # # And place the original in /usr/lib/gitea with working files in /data/gitea GITEA="/app/gitea/gitea" -WORK_DIR="/app/gitea" +WORK_DIR="/data/gitea" CUSTOM_PATH="/data/gitea" # Provide docker defaults diff --git a/docs/content/doc/administration/config-cheat-sheet.en-us.md b/docs/content/doc/administration/config-cheat-sheet.en-us.md index 342b072b244..327e0ae4fa1 100644 --- a/docs/content/doc/administration/config-cheat-sheet.en-us.md +++ b/docs/content/doc/administration/config-cheat-sheet.en-us.md @@ -339,7 +339,7 @@ The following configuration set `Content-Type: application/vnd.android.package-a - `SSH_LISTEN_PORT`: **%(SSH\_PORT)s**: Port for the built-in SSH server. - `SSH_ROOT_PATH`: **~/.ssh**: Root path of SSH directory. - `SSH_CREATE_AUTHORIZED_KEYS_FILE`: **true**: Gitea will create a authorized_keys file by default when it is not using the internal ssh server. If you intend to use the AuthorizedKeysCommand functionality then you should turn this off. -- `SSH_AUTHORIZED_KEYS_BACKUP`: **true**: Enable SSH Authorized Key Backup when rewriting all keys, default is true. +- `SSH_AUTHORIZED_KEYS_BACKUP`: **false**: Enable SSH Authorized Key Backup when rewriting all keys, default is false. - `SSH_TRUSTED_USER_CA_KEYS`: **\**: Specifies the public keys of certificate authorities that are trusted to sign user certificates for authentication. Multiple keys should be comma separated. E.g.`ssh- ` or `ssh- , ssh- `. For more information see `TrustedUserCAKeys` in the sshd config man pages. When empty no file will be created and `SSH_AUTHORIZED_PRINCIPALS_ALLOW` will default to `off`. - `SSH_TRUSTED_USER_CA_KEYS_FILENAME`: **`RUN_USER`/.ssh/gitea-trusted-user-ca-keys.pem**: Absolute path of the `TrustedUserCaKeys` file Gitea will manage. If you're running your own ssh server and you want to use the Gitea managed file you'll also need to modify your sshd_config to point to this file. The official docker image will automatically work without further configuration. - `SSH_AUTHORIZED_PRINCIPALS_ALLOW`: **off** or **username, email**: \[off, username, email, anything\]: Specify the principals values that users are allowed to use as principal. When set to `anything` no checks are done on the principal string. When set to `off` authorized principal are not allowed to be set. @@ -371,6 +371,7 @@ The following configuration set `Content-Type: application/vnd.android.package-a - `LFS_START_SERVER`: **false**: Enables Git LFS support. - `LFS_CONTENT_PATH`: **%(APP_DATA_PATH)s/lfs**: Default LFS content path. (if it is on local storage.) **DEPRECATED** use settings in `[lfs]`. - `LFS_JWT_SECRET`: **\**: LFS authentication secret, change this a unique string. +- `LFS_JWT_SECRET_URI`: **\**: Instead of defining LFS_JWT_SECRET in the configuration, this configuration option can be used to give Gitea a path to a file that contains the secret (example value: `file:/etc/gitea/lfs_jwt_secret`) - `LFS_HTTP_AUTH_EXPIRY`: **24h**: LFS authentication validity period in time.Duration, pushes taking longer than this may fail. - `LFS_MAX_FILE_SIZE`: **0**: Maximum allowed LFS file size in bytes (Set to 0 for no limit). - `LFS_LOCKS_PAGING_NUM`: **50**: Maximum number of LFS Locks returned per page. @@ -1100,6 +1101,7 @@ This section only does "set" config, a removed config key from this section won' - `INVALIDATE_REFRESH_TOKENS`: **false**: Check if refresh token has already been used - `JWT_SIGNING_ALGORITHM`: **RS256**: Algorithm used to sign OAuth2 tokens. Valid values: \[`HS256`, `HS384`, `HS512`, `RS256`, `RS384`, `RS512`, `ES256`, `ES384`, `ES512`\] - `JWT_SECRET`: **\**: OAuth2 authentication secret for access and refresh tokens, change this to a unique string. This setting is only needed if `JWT_SIGNING_ALGORITHM` is set to `HS256`, `HS384` or `HS512`. +- `JWT_SECRET_URI`: **\**: Instead of defining JWT_SECRET in the configuration, this configuration option can be used to give Gitea a path to a file that contains the secret (example value: `file:/etc/gitea/oauth2_jwt_secret`) - `JWT_SIGNING_PRIVATE_KEY_FILE`: **jwt/private.pem**: Private key file path used to sign OAuth2 tokens. The path is relative to `APP_DATA_PATH`. This setting is only needed if `JWT_SIGNING_ALGORITHM` is set to `RS256`, `RS384`, `RS512`, `ES256`, `ES384` or `ES512`. The file must contain a RSA or ECDSA private key in the PKCS8 format. If no key exists a 4096 bit key will be created for you. - `MAX_TOKEN_LENGTH`: **32767**: Maximum length of token/cookie to accept from OAuth2 provider @@ -1379,39 +1381,22 @@ PROXY_HOSTS = *.github.com ## Actions (`actions`) - `ENABLED`: **false**: Enable/Disable actions capabilities -- `DEFAULT_ACTIONS_URL`: **https://gitea.com**: Default address to get action plugins, e.g. the default value means downloading from "" for "uses: actions/checkout@v3" +- `DEFAULT_ACTIONS_URL`: **github**: Default platform to get action plugins, `github` for `https://github.com`, `self` for the current Gitea instance. - `STORAGE_TYPE`: **local**: Storage type for actions logs, `local` for local disk or `minio` for s3 compatible object storage service, default is `local` or other name defined with `[storage.xxx]` - `MINIO_BASE_PATH`: **actions_log/**: Minio base path on the bucket only available when STORAGE_TYPE is `minio` -`DEFAULT_ACTIONS_URL` indicates where should we find the relative path action plugin. i.e. when use an action in a workflow file like +`DEFAULT_ACTIONS_URL` indicates where the Gitea Actions runners should find the actions with relative path. +For example, `uses: actions/checkout@v3` means `https://github.com/actions/checkout@v3` since the value of `DEFAULT_ACTIONS_URL` is `github`. +And it can be changed to `self` to make it `root_url_of_your_gitea/actions/checkout@v3`. -```yaml -name: versions -on: - push: - branches: - - main - - releases/* -jobs: - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 -``` +Please note that using `self` is not recommended for most cases, as it could make names globally ambiguous. +Additionally, it requires you to mirror all the actions you need to your Gitea instance, which may not be worth it. +Therefore, please use `self` only if you understand what you are doing. -Now we need to know how to get actions/checkout, this configuration is the default git server to get it. That means we will get the repository via git clone ${DEFAULT_ACTIONS_URL}/actions/checkout and fetch tag v3. - -To help people who don't want to mirror these actions in their git instances, the default value is https://gitea.com -To help people run actions totally in their network, they can change the value and copy all necessary action repositories into their git server. - -Of course we should support the form in future PRs like - -```yaml -steps: - - uses: gitea.com/actions/checkout@v3 -``` - -although Github don't support this form. +In earlier versions (<= 1.19), `DEFAULT_ACTIONS_URL` cound be set to any custom URLs like `https://gitea.com` or `http://your-git-server,https://gitea.com`, and the default value was `https://gitea.com`. +However, later updates removed those options, and now the only options are `github` and `self`, with the default value being `github`. +However, if you want to use actions from other git server, you can use a complete URL in `uses` field, it's supported by Gitea (but not GitHub). +Like `uses: https://gitea.com/actions/checkout@v3` or `uses: http://your-git-server/actions/checkout@v3`. ## Other (`other`) diff --git a/docs/content/doc/installation/with-docker-rootless.en-us.md b/docs/content/doc/installation/with-docker-rootless.en-us.md index b8c76438f9f..5aa4e46e12c 100644 --- a/docs/content/doc/installation/with-docker-rootless.en-us.md +++ b/docs/content/doc/installation/with-docker-rootless.en-us.md @@ -119,7 +119,7 @@ services: - /etc/localtime:/etc/localtime:ro ports: - "3000:3000" - - "222:22" + - "2222:2222" + depends_on: + - db + @@ -288,7 +288,7 @@ docker-compose up -d In addition to the environment variables above, any settings in `app.ini` can be set or overridden with an environment variable of the form: `GITEA__SECTION_NAME__KEY_NAME`. -These settings are applied each time the docker container starts. +These settings are applied each time the docker container starts, and won't be passed into Gitea's sub-processes. Full information [here](https://github.com/go-gitea/gitea/tree/main/contrib/environment-to-ini). These environment variables can be passed to the docker container in `docker-compose.yml`. diff --git a/docs/content/doc/installation/with-docker.en-us.md b/docs/content/doc/installation/with-docker.en-us.md index e70a6ab1331..a7a575293d3 100644 --- a/docs/content/doc/installation/with-docker.en-us.md +++ b/docs/content/doc/installation/with-docker.en-us.md @@ -289,7 +289,7 @@ docker-compose up -d In addition to the environment variables above, any settings in `app.ini` can be set or overridden with an environment variable of the form: `GITEA__SECTION_NAME__KEY_NAME`. -These settings are applied each time the docker container starts. +These settings are applied each time the docker container starts, and won't be passed into Gitea's sub-processes. Full information [here](https://github.com/go-gitea/gitea/tree/master/contrib/environment-to-ini). These environment variables can be passed to the docker container in `docker-compose.yml`. diff --git a/models/activities/notification.go b/models/activities/notification.go index 75276a04434..e0af2ee8bbf 100644 --- a/models/activities/notification.go +++ b/models/activities/notification.go @@ -343,7 +343,7 @@ func getIssueNotification(ctx context.Context, userID, issueID int64) (*Notifica // NotificationsForUser returns notifications for a given user and status func NotificationsForUser(ctx context.Context, user *user_model.User, statuses []NotificationStatus, page, perPage int) (notifications NotificationList, err error) { if len(statuses) == 0 { - return + return nil, nil } sess := db.GetEngine(ctx). @@ -372,16 +372,16 @@ func CountUnread(ctx context.Context, userID int64) int64 { // LoadAttributes load Repo Issue User and Comment if not loaded func (n *Notification) LoadAttributes(ctx context.Context) (err error) { if err = n.loadRepo(ctx); err != nil { - return + return err } if err = n.loadIssue(ctx); err != nil { - return + return err } if err = n.loadUser(ctx); err != nil { - return + return err } if err = n.loadComment(ctx); err != nil { - return + return err } return err } diff --git a/models/admin/task.go b/models/admin/task.go index fc40b13d026..8aa397ad351 100644 --- a/models/admin/task.go +++ b/models/admin/task.go @@ -44,7 +44,7 @@ func init() { // TranslatableMessage represents JSON struct that can be translated with a Locale type TranslatableMessage struct { Format string - Args []interface{} `json:"omitempty"` + Args []any `json:"omitempty"` } // LoadRepo loads repository of the task diff --git a/models/asymkey/gpg_key_commit_verification.go b/models/asymkey/gpg_key_commit_verification.go index db6e78cad52..65af0bc9453 100644 --- a/models/asymkey/gpg_key_commit_verification.go +++ b/models/asymkey/gpg_key_commit_verification.go @@ -455,9 +455,9 @@ func hashAndVerifyForKeyID(sig *packet.Signature, payload string, committer *use // CalculateTrustStatus will calculate the TrustStatus for a commit verification within a repository // There are several trust models in Gitea -func CalculateTrustStatus(verification *CommitVerification, repoTrustModel repo_model.TrustModelType, isOwnerMemberCollaborator func(*user_model.User) (bool, error), keyMap *map[string]bool) (err error) { +func CalculateTrustStatus(verification *CommitVerification, repoTrustModel repo_model.TrustModelType, isOwnerMemberCollaborator func(*user_model.User) (bool, error), keyMap *map[string]bool) error { if !verification.Verified { - return + return nil } // In the Committer trust model a signature is trusted if it matches the committer @@ -475,7 +475,7 @@ func CalculateTrustStatus(verification *CommitVerification, repoTrustModel repo_ verification.SigningUser.Email == verification.CommittingUser.Email) { verification.TrustStatus = "trusted" } - return + return nil } // Now we drop to the more nuanced trust models... @@ -490,10 +490,11 @@ func CalculateTrustStatus(verification *CommitVerification, repoTrustModel repo_ verification.SigningUser.Email != verification.CommittingUser.Email) { verification.TrustStatus = "untrusted" } - return + return nil } // Check we actually have a GPG SigningKey + var err error if verification.SigningKey != nil { var isMember bool if keyMap != nil { diff --git a/models/asymkey/gpg_key_common.go b/models/asymkey/gpg_key_common.go index 5ceeee9aacf..b02be2851a7 100644 --- a/models/asymkey/gpg_key_common.go +++ b/models/asymkey/gpg_key_common.go @@ -111,7 +111,7 @@ func populateHash(hashFunc crypto.Hash, msg []byte) (hash.Hash, error) { func readArmoredSign(r io.Reader) (body io.Reader, err error) { block, err := armor.Decode(r) if err != nil { - return + return nil, err } if block.Type != openpgp.SignatureType { return nil, fmt.Errorf("expected '" + openpgp.SignatureType + "', got: " + block.Type) diff --git a/models/asymkey/ssh_key_authorized_keys.go b/models/asymkey/ssh_key_authorized_keys.go index e138182d686..77803d6709e 100644 --- a/models/asymkey/ssh_key_authorized_keys.go +++ b/models/asymkey/ssh_key_authorized_keys.go @@ -47,7 +47,7 @@ var sshOpLocker sync.Mutex // AuthorizedStringForKey creates the authorized keys string appropriate for the provided key func AuthorizedStringForKey(key *PublicKey) string { sb := &strings.Builder{} - _ = setting.SSH.AuthorizedKeysCommandTemplateTemplate.Execute(sb, map[string]interface{}{ + _ = setting.SSH.AuthorizedKeysCommandTemplateTemplate.Execute(sb, map[string]any{ "AppPath": util.ShellEscape(setting.AppPath), "AppWorkPath": util.ShellEscape(setting.AppWorkPath), "CustomConf": util.ShellEscape(setting.CustomConf), @@ -175,7 +175,7 @@ func RewriteAllPublicKeys() error { // RegeneratePublicKeys regenerates the authorized_keys file func RegeneratePublicKeys(ctx context.Context, t io.StringWriter) error { - if err := db.GetEngine(ctx).Where("type != ?", KeyTypePrincipal).Iterate(new(PublicKey), func(idx int, bean interface{}) (err error) { + if err := db.GetEngine(ctx).Where("type != ?", KeyTypePrincipal).Iterate(new(PublicKey), func(idx int, bean any) (err error) { _, err = t.WriteString((bean.(*PublicKey)).AuthorizedString()) return err }); err != nil { diff --git a/models/asymkey/ssh_key_authorized_principals.go b/models/asymkey/ssh_key_authorized_principals.go index 092839611f9..592196c255a 100644 --- a/models/asymkey/ssh_key_authorized_principals.go +++ b/models/asymkey/ssh_key_authorized_principals.go @@ -97,7 +97,7 @@ func RewriteAllPrincipalKeys(ctx context.Context) error { } func regeneratePrincipalKeys(ctx context.Context, t io.StringWriter) error { - if err := db.GetEngine(ctx).Where("type = ?", KeyTypePrincipal).Iterate(new(PublicKey), func(idx int, bean interface{}) (err error) { + if err := db.GetEngine(ctx).Where("type = ?", KeyTypePrincipal).Iterate(new(PublicKey), func(idx int, bean any) (err error) { _, err = t.WriteString((bean.(*PublicKey)).AuthorizedString()) return err }); err != nil { diff --git a/models/auth/oauth2.go b/models/auth/oauth2.go index 53a5c28b4a5..0f64b56c163 100644 --- a/models/auth/oauth2.go +++ b/models/auth/oauth2.go @@ -306,9 +306,10 @@ func (code *OAuth2AuthorizationCode) TableName() string { } // GenerateRedirectURI generates a redirect URI for a successful authorization request. State will be used if not empty. -func (code *OAuth2AuthorizationCode) GenerateRedirectURI(state string) (redirect *url.URL, err error) { - if redirect, err = url.Parse(code.RedirectURI); err != nil { - return +func (code *OAuth2AuthorizationCode) GenerateRedirectURI(state string) (*url.URL, error) { + redirect, err := url.Parse(code.RedirectURI) + if err != nil { + return nil, err } q := redirect.Query() if state != "" { diff --git a/models/db/context.go b/models/db/context.go index 59be1e13891..351aea8faf9 100644 --- a/models/db/context.go +++ b/models/db/context.go @@ -52,7 +52,7 @@ func (ctx *Context) Engine() Engine { } // Value shadows Value for context.Context but allows us to get ourselves and an Engined object -func (ctx *Context) Value(key interface{}) interface{} { +func (ctx *Context) Value(key any) any { if key == enginedContextKey { return ctx } @@ -163,28 +163,28 @@ func txWithNoCheck(parentCtx context.Context, f func(ctx context.Context) error) } // Insert inserts records into database -func Insert(ctx context.Context, beans ...interface{}) error { +func Insert(ctx context.Context, beans ...any) error { _, err := GetEngine(ctx).Insert(beans...) return err } // Exec executes a sql with args -func Exec(ctx context.Context, sqlAndArgs ...interface{}) (sql.Result, error) { +func Exec(ctx context.Context, sqlAndArgs ...any) (sql.Result, error) { return GetEngine(ctx).Exec(sqlAndArgs...) } // GetByBean filled empty fields of the bean according non-empty fields to query in database. -func GetByBean(ctx context.Context, bean interface{}) (bool, error) { +func GetByBean(ctx context.Context, bean any) (bool, error) { return GetEngine(ctx).Get(bean) } // DeleteByBean deletes all records according non-empty fields of the bean as conditions. -func DeleteByBean(ctx context.Context, bean interface{}) (int64, error) { +func DeleteByBean(ctx context.Context, bean any) (int64, error) { return GetEngine(ctx).Delete(bean) } // DeleteByID deletes the given bean with the given ID -func DeleteByID(ctx context.Context, id int64, bean interface{}) (int64, error) { +func DeleteByID(ctx context.Context, id int64, bean any) (int64, error) { return GetEngine(ctx).ID(id).NoAutoTime().Delete(bean) } @@ -203,13 +203,13 @@ func FindIDs(ctx context.Context, tableName, idCol string, cond builder.Cond) ([ // DecrByIDs decreases the given column for entities of the "bean" type with one of the given ids by one // Timestamps of the entities won't be updated -func DecrByIDs(ctx context.Context, ids []int64, decrCol string, bean interface{}) error { +func DecrByIDs(ctx context.Context, ids []int64, decrCol string, bean any) error { _, err := GetEngine(ctx).Decr(decrCol).In("id", ids).NoAutoCondition().NoAutoTime().Update(bean) return err } // DeleteBeans deletes all given beans, beans must contain delete conditions. -func DeleteBeans(ctx context.Context, beans ...interface{}) (err error) { +func DeleteBeans(ctx context.Context, beans ...any) (err error) { e := GetEngine(ctx) for i := range beans { if _, err = e.Delete(beans[i]); err != nil { @@ -220,7 +220,7 @@ func DeleteBeans(ctx context.Context, beans ...interface{}) (err error) { } // TruncateBeans deletes all given beans, beans may contain delete conditions. -func TruncateBeans(ctx context.Context, beans ...interface{}) (err error) { +func TruncateBeans(ctx context.Context, beans ...any) (err error) { e := GetEngine(ctx) for i := range beans { if _, err = e.Truncate(beans[i]); err != nil { @@ -231,12 +231,12 @@ func TruncateBeans(ctx context.Context, beans ...interface{}) (err error) { } // CountByBean counts the number of database records according non-empty fields of the bean as conditions. -func CountByBean(ctx context.Context, bean interface{}) (int64, error) { +func CountByBean(ctx context.Context, bean any) (int64, error) { return GetEngine(ctx).Count(bean) } // TableName returns the table name according a bean object -func TableName(bean interface{}) string { +func TableName(bean any) string { return x.TableName(bean) } diff --git a/models/db/engine.go b/models/db/engine.go index 3eb16f8042d..07e4d00270b 100755 --- a/models/db/engine.go +++ b/models/db/engine.go @@ -25,7 +25,7 @@ import ( var ( x *xorm.Engine - tables []interface{} + tables []any initFuncs []func() error // HasEngine specifies if we have a xorm.Engine @@ -34,41 +34,41 @@ var ( // Engine represents a xorm engine or session. type Engine interface { - Table(tableNameOrBean interface{}) *xorm.Session - Count(...interface{}) (int64, error) - Decr(column string, arg ...interface{}) *xorm.Session - Delete(...interface{}) (int64, error) - Truncate(...interface{}) (int64, error) - Exec(...interface{}) (sql.Result, error) - Find(interface{}, ...interface{}) error - Get(beans ...interface{}) (bool, error) - ID(interface{}) *xorm.Session - In(string, ...interface{}) *xorm.Session - Incr(column string, arg ...interface{}) *xorm.Session - Insert(...interface{}) (int64, error) - Iterate(interface{}, xorm.IterFunc) error - Join(joinOperator string, tablename, condition interface{}, args ...interface{}) *xorm.Session - SQL(interface{}, ...interface{}) *xorm.Session - Where(interface{}, ...interface{}) *xorm.Session + Table(tableNameOrBean any) *xorm.Session + Count(...any) (int64, error) + Decr(column string, arg ...any) *xorm.Session + Delete(...any) (int64, error) + Truncate(...any) (int64, error) + Exec(...any) (sql.Result, error) + Find(any, ...any) error + Get(beans ...any) (bool, error) + ID(any) *xorm.Session + In(string, ...any) *xorm.Session + Incr(column string, arg ...any) *xorm.Session + Insert(...any) (int64, error) + Iterate(any, xorm.IterFunc) error + Join(joinOperator string, tablename, condition any, args ...any) *xorm.Session + SQL(any, ...any) *xorm.Session + Where(any, ...any) *xorm.Session Asc(colNames ...string) *xorm.Session Desc(colNames ...string) *xorm.Session Limit(limit int, start ...int) *xorm.Session NoAutoTime() *xorm.Session - SumInt(bean interface{}, columnName string) (res int64, err error) - Sync2(...interface{}) error + SumInt(bean any, columnName string) (res int64, err error) + Sync2(...any) error Select(string) *xorm.Session - NotIn(string, ...interface{}) *xorm.Session - OrderBy(interface{}, ...interface{}) *xorm.Session - Exist(...interface{}) (bool, error) + NotIn(string, ...any) *xorm.Session + OrderBy(any, ...any) *xorm.Session + Exist(...any) (bool, error) Distinct(...string) *xorm.Session - Query(...interface{}) ([]map[string][]byte, error) + Query(...any) ([]map[string][]byte, error) Cols(...string) *xorm.Session Context(ctx context.Context) *xorm.Session Ping() error } // TableInfo returns table's information via an object -func TableInfo(v interface{}) (*schemas.Table, error) { +func TableInfo(v any) (*schemas.Table, error) { return x.TableInfo(v) } @@ -78,7 +78,7 @@ func DumpTables(tables []*schemas.Table, w io.Writer, tp ...schemas.DBType) erro } // RegisterModel registers model, if initfunc provided, it will be invoked after data model sync -func RegisterModel(bean interface{}, initFunc ...func() error) { +func RegisterModel(bean any, initFunc ...func() error) { tables = append(tables, bean) if len(initFuncs) > 0 && initFunc[0] != nil { initFuncs = append(initFuncs, initFunc[0]) @@ -209,14 +209,14 @@ func InitEngineWithMigration(ctx context.Context, migrateFunc func(*xorm.Engine) } // NamesToBean return a list of beans or an error -func NamesToBean(names ...string) ([]interface{}, error) { - beans := []interface{}{} +func NamesToBean(names ...string) ([]any, error) { + beans := []any{} if len(names) == 0 { beans = append(beans, tables...) return beans, nil } // Need to map provided names to beans... - beanMap := make(map[string]interface{}) + beanMap := make(map[string]any) for _, bean := range tables { beanMap[strings.ToLower(reflect.Indirect(reflect.ValueOf(bean)).Type().Name())] = bean @@ -224,7 +224,7 @@ func NamesToBean(names ...string) ([]interface{}, error) { beanMap[strings.ToLower(x.TableName(bean, true))] = bean } - gotBean := make(map[interface{}]bool) + gotBean := make(map[any]bool) for _, name := range names { bean, ok := beanMap[strings.ToLower(strings.TrimSpace(name))] if !ok { @@ -266,7 +266,7 @@ func DumpDatabase(filePath, dbType string) error { } // MaxBatchInsertSize returns the table's max batch insert size -func MaxBatchInsertSize(bean interface{}) int { +func MaxBatchInsertSize(bean any) int { t, err := x.TableInfo(bean) if err != nil { return 50 @@ -286,7 +286,7 @@ func DeleteAllRecords(tableName string) error { } // GetMaxID will return max id of the table -func GetMaxID(beanOrTableName interface{}) (maxID int64, err error) { +func GetMaxID(beanOrTableName any) (maxID int64, err error) { _, err = x.Select("MAX(id)").Table(beanOrTableName).Get(&maxID) return maxID, err } diff --git a/models/db/error.go b/models/db/error.go index edc8e80a9c6..665e970e17c 100644 --- a/models/db/error.go +++ b/models/db/error.go @@ -25,7 +25,7 @@ func (err ErrCancelled) Error() string { } // ErrCancelledf returns an ErrCancelled for the provided format and args -func ErrCancelledf(format string, args ...interface{}) error { +func ErrCancelledf(format string, args ...any) error { return ErrCancelled{ fmt.Sprintf(format, args...), } diff --git a/models/db/log.go b/models/db/log.go index dd95f64ca80..307788ea2e4 100644 --- a/models/db/log.go +++ b/models/db/log.go @@ -28,47 +28,47 @@ func NewXORMLogger(showSQL bool) xormlog.Logger { const stackLevel = 8 // Log a message with defined skip and at logging level -func (l *XORMLogBridge) Log(skip int, level log.Level, format string, v ...interface{}) { +func (l *XORMLogBridge) Log(skip int, level log.Level, format string, v ...any) { l.logger.Log(skip+1, level, format, v...) } // Debug show debug log -func (l *XORMLogBridge) Debug(v ...interface{}) { +func (l *XORMLogBridge) Debug(v ...any) { l.Log(stackLevel, log.DEBUG, "%s", fmt.Sprint(v...)) } // Debugf show debug log -func (l *XORMLogBridge) Debugf(format string, v ...interface{}) { +func (l *XORMLogBridge) Debugf(format string, v ...any) { l.Log(stackLevel, log.DEBUG, format, v...) } // Error show error log -func (l *XORMLogBridge) Error(v ...interface{}) { +func (l *XORMLogBridge) Error(v ...any) { l.Log(stackLevel, log.ERROR, "%s", fmt.Sprint(v...)) } // Errorf show error log -func (l *XORMLogBridge) Errorf(format string, v ...interface{}) { +func (l *XORMLogBridge) Errorf(format string, v ...any) { l.Log(stackLevel, log.ERROR, format, v...) } // Info show information level log -func (l *XORMLogBridge) Info(v ...interface{}) { +func (l *XORMLogBridge) Info(v ...any) { l.Log(stackLevel, log.INFO, "%s", fmt.Sprint(v...)) } // Infof show information level log -func (l *XORMLogBridge) Infof(format string, v ...interface{}) { +func (l *XORMLogBridge) Infof(format string, v ...any) { l.Log(stackLevel, log.INFO, format, v...) } // Warn show warning log -func (l *XORMLogBridge) Warn(v ...interface{}) { +func (l *XORMLogBridge) Warn(v ...any) { l.Log(stackLevel, log.WARN, "%s", fmt.Sprint(v...)) } // Warnf show warnning log -func (l *XORMLogBridge) Warnf(format string, v ...interface{}) { +func (l *XORMLogBridge) Warnf(format string, v ...any) { l.Log(stackLevel, log.WARN, format, v...) } diff --git a/models/fixtures/mirror.yml b/models/fixtures/mirror.yml new file mode 100644 index 00000000000..97bc4ae60dd --- /dev/null +++ b/models/fixtures/mirror.yml @@ -0,0 +1,49 @@ +- + id: 1 + repo_id: 5 + interval: 3600 + enable_prune: false + updated_unix: 0 + next_update_unix: 0 + lfs_enabled: false + lfs_endpoint: "" + +- + id: 2 + repo_id: 25 + interval: 3600 + enable_prune: false + updated_unix: 0 + next_update_unix: 0 + lfs_enabled: false + lfs_endpoint: "" + +- + id: 3 + repo_id: 26 + interval: 3600 + enable_prune: false + updated_unix: 0 + next_update_unix: 0 + lfs_enabled: false + lfs_endpoint: "" + +- + id: 4 + repo_id: 27 + interval: 3600 + enable_prune: false + updated_unix: 0 + next_update_unix: 0 + lfs_enabled: false + lfs_endpoint: "" + +- + id: 5 + repo_id: 28 + interval: 3600 + enable_prune: false + updated_unix: 0 + next_update_unix: 0 + lfs_enabled: false + lfs_endpoint: "" diff --git a/models/fixtures/org_user.yml b/models/fixtures/org_user.yml index d08f6957991..8d58169a32f 100644 --- a/models/fixtures/org_user.yml +++ b/models/fixtures/org_user.yml @@ -81,3 +81,21 @@ uid: 5 org_id: 23 is_public: false + +- + id: 15 + uid: 1 + org_id: 35 + is_public: true + +- + id: 16 + uid: 1 + org_id: 36 + is_public: true + +- + id: 17 + uid: 5 + org_id: 36 + is_public: true diff --git a/models/fixtures/repository.yml b/models/fixtures/repository.yml index ef7730780f7..050a9e2d06f 100644 --- a/models/fixtures/repository.yml +++ b/models/fixtures/repository.yml @@ -141,7 +141,7 @@ num_projects: 0 num_closed_projects: 0 is_private: true - is_empty: true + is_empty: false is_archived: false is_mirror: true status: 0 diff --git a/models/fixtures/team.yml b/models/fixtures/team.yml index aa3b36e6443..65326eedbf4 100644 --- a/models/fixtures/team.yml +++ b/models/fixtures/team.yml @@ -184,3 +184,36 @@ num_members: 1 includes_all_repositories: false can_create_org_repo: true + +- + id: 18 + org_id: 35 + lower_name: owners + name: Owners + authorize: 4 # owner + num_repos: 0 + num_members: 1 + includes_all_repositories: false + can_create_org_repo: true + +- + id: 19 + org_id: 36 + lower_name: owners + name: Owners + authorize: 4 # owner + num_repos: 0 + num_members: 1 + includes_all_repositories: false + can_create_org_repo: true + +- + id: 20 + org_id: 36 + lower_name: team20writepackage + name: team20writepackage + authorize: 1 + num_repos: 0 + num_members: 1 + includes_all_repositories: false + can_create_org_repo: true diff --git a/models/fixtures/team_unit.yml b/models/fixtures/team_unit.yml index 5257d2c3856..5d2ba2fb6cb 100644 --- a/models/fixtures/team_unit.yml +++ b/models/fixtures/team_unit.yml @@ -273,4 +273,10 @@ id: 46 team_id: 17 type: 9 # package - access_mode: 0 + access_mode: 2 + +- + id: 47 + team_id: 20 + type: 9 # package + access_mode: 2 diff --git a/models/fixtures/team_user.yml b/models/fixtures/team_user.yml index b95f76c7237..feace5f2a53 100644 --- a/models/fixtures/team_user.yml +++ b/models/fixtures/team_user.yml @@ -105,3 +105,21 @@ org_id: 23 team_id: 17 uid: 5 + +- + id: 19 + org_id: 35 + team_id: 18 + uid: 1 + +- + id: 20 + org_id: 36 + team_id: 19 + uid: 1 + +- + id: 21 + org_id: 36 + team_id: 20 + uid: 5 diff --git a/models/fixtures/user.yml b/models/fixtures/user.yml index eba33a7c363..26bb7a9f4ba 100644 --- a/models/fixtures/user.yml +++ b/models/fixtures/user.yml @@ -1258,3 +1258,77 @@ repo_admin_change_team_access: false theme: "" keep_activity_private: false + +- + id: 35 + lower_name: private_org35 + name: private_org35 + full_name: Private Org 35 + email: private_org35@example.com + keep_email_private: false + email_notifications_preference: enabled + passwd: ZogKvWdyEx:password + passwd_hash_algo: dummy + must_change_password: false + login_source: 0 + login_name: private_org35 + type: 1 + salt: ZogKvWdyEx + max_repo_creation: -1 + is_active: true + is_admin: false + is_restricted: false + allow_git_hook: false + allow_import_local: false + allow_create_organization: true + prohibit_login: false + avatar: avatar35 + avatar_email: private_org35@example.com + use_custom_avatar: false + num_followers: 0 + num_following: 0 + num_stars: 0 + num_repos: 0 + num_teams: 1 + num_members: 1 + visibility: 2 + repo_admin_change_team_access: false + theme: "" + keep_activity_private: false + +- + id: 36 + lower_name: limited_org36 + name: limited_org36 + full_name: Limited Org 36 + email: limited_org36@example.com + keep_email_private: false + email_notifications_preference: enabled + passwd: ZogKvWdyEx:password + passwd_hash_algo: dummy + must_change_password: false + login_source: 0 + login_name: limited_org36 + type: 1 + salt: ZogKvWdyEx + max_repo_creation: -1 + is_active: true + is_admin: false + is_restricted: false + allow_git_hook: false + allow_import_local: false + allow_create_organization: true + prohibit_login: false + avatar: avatar22 + avatar_email: limited_org36@example.com + use_custom_avatar: false + num_followers: 0 + num_following: 0 + num_stars: 0 + num_repos: 0 + num_teams: 2 + num_members: 2 + visibility: 1 + repo_admin_change_team_access: false + theme: "" + keep_activity_private: false diff --git a/models/git/branch.go b/models/git/branch.go index adf8b0a78d2..d57b72719ce 100644 --- a/models/git/branch.go +++ b/models/git/branch.go @@ -11,9 +11,12 @@ import ( "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/git" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/util" + + "xorm.io/builder" ) // ErrBranchNotExist represents an error that branch with such name does not exist. @@ -100,9 +103,9 @@ func (err ErrBranchesEqual) Unwrap() error { type Branch struct { ID int64 RepoID int64 `xorm:"UNIQUE(s)"` - Name string `xorm:"UNIQUE(s) NOT NULL"` + Name string `xorm:"UNIQUE(s) NOT NULL"` // git's ref-name is case-sensitive internally, however, in some databases (mssql, mysql, by default), it's case-insensitive at the moment CommitID string - CommitMessage string `xorm:"TEXT"` + CommitMessage string `xorm:"TEXT"` // it only stores the message summary (the first line) PusherID int64 Pusher *user_model.User `xorm:"-"` IsDeleted bool `xorm:"index"` @@ -204,14 +207,14 @@ func DeleteBranches(ctx context.Context, repoID, doerID int64, branchIDs []int64 // UpdateBranch updates the branch information in the database. If the branch exist, it will update latest commit of this branch information // If it doest not exist, insert a new record into database -func UpdateBranch(ctx context.Context, repoID int64, branchName, commitID, commitMessage string, pusherID int64, commitTime time.Time) error { +func UpdateBranch(ctx context.Context, repoID, pusherID int64, branchName string, commit *git.Commit) error { cnt, err := db.GetEngine(ctx).Where("repo_id=? AND name=?", repoID, branchName). Cols("commit_id, commit_message, pusher_id, commit_time, is_deleted, updated_unix"). Update(&Branch{ - CommitID: commitID, - CommitMessage: commitMessage, + CommitID: commit.ID.String(), + CommitMessage: commit.Summary(), PusherID: pusherID, - CommitTime: timeutil.TimeStamp(commitTime.Unix()), + CommitTime: timeutil.TimeStamp(commit.Committer.When.Unix()), IsDeleted: false, }) if err != nil { @@ -224,10 +227,10 @@ func UpdateBranch(ctx context.Context, repoID int64, branchName, commitID, commi return db.Insert(ctx, &Branch{ RepoID: repoID, Name: branchName, - CommitID: commitID, - CommitMessage: commitMessage, + CommitID: commit.ID.String(), + CommitMessage: commit.Summary(), PusherID: pusherID, - CommitTime: timeutil.TimeStamp(commitTime.Unix()), + CommitTime: timeutil.TimeStamp(commit.Committer.When.Unix()), }) } @@ -354,7 +357,7 @@ func RenameBranch(ctx context.Context, repo *repo_model.Repository, from, to str // 4. Update all not merged pull request base branch name _, err = sess.Table("pull_request").Where("base_repo_id=? AND base_branch=? AND has_merged=?", repo.ID, from, false). - Update(map[string]interface{}{"base_branch": to}) + Update(map[string]any{"base_branch": to}) if err != nil { return err } @@ -377,3 +380,24 @@ func RenameBranch(ctx context.Context, repo *repo_model.Repository, from, to str return committer.Commit() } + +// FindRecentlyPushedNewBranches return at most 2 new branches pushed by the user in 6 hours which has no opened PRs created +// except the indicate branch +func FindRecentlyPushedNewBranches(ctx context.Context, repoID, userID int64, excludeBranchName string) (BranchList, error) { + branches := make(BranchList, 0, 2) + subQuery := builder.Select("head_branch").From("pull_request"). + InnerJoin("issue", "issue.id = pull_request.issue_id"). + Where(builder.Eq{ + "pull_request.head_repo_id": repoID, + "issue.is_closed": false, + }) + err := db.GetEngine(ctx). + Where("pusher_id=? AND is_deleted=?", userID, false). + And("name <> ?", excludeBranchName). + And("updated_unix >= ?", time.Now().Add(-time.Hour*6).Unix()). + NotIn("name", subQuery). + OrderBy("branch.updated_unix DESC"). + Limit(2). + Find(&branches) + return branches, err +} diff --git a/models/git/branch_list.go b/models/git/branch_list.go index da78248c0bc..131a149782e 100644 --- a/models/git/branch_list.go +++ b/models/git/branch_list.go @@ -64,11 +64,6 @@ func (branches BranchList) LoadPusher(ctx context.Context) error { return nil } -const ( - BranchOrderByNameAsc = "name ASC" - BranchOrderByCommitTimeDesc = "commit_time DESC" -) - type FindBranchOptions struct { db.ListOptions RepoID int64 @@ -102,7 +97,8 @@ func orderByBranches(sess *xorm.Session, opts FindBranchOptions) *xorm.Session { } if opts.OrderBy == "" { - opts.OrderBy = BranchOrderByCommitTimeDesc + // the commit_time might be the same, so add the "name" to make sure the order is stable + opts.OrderBy = "commit_time DESC, name ASC" } return sess.OrderBy(opts.OrderBy) } @@ -130,3 +126,20 @@ func FindBranchNames(ctx context.Context, opts FindBranchOptions) ([]string, err } return branches, nil } + +func FindBranchesByRepoAndBranchName(ctx context.Context, repoBranches map[int64]string) (map[int64]string, error) { + cond := builder.NewCond() + for repoID, branchName := range repoBranches { + cond = cond.Or(builder.And(builder.Eq{"repo_id": repoID}, builder.Eq{"name": branchName})) + } + var branches []*Branch + if err := db.GetEngine(ctx). + Where(cond).Find(&branches); err != nil { + return nil, err + } + branchMap := make(map[int64]string, len(branches)) + for _, branch := range branches { + branchMap[branch.RepoID] = branch.CommitID + } + return branchMap, nil +} diff --git a/models/git/branch_test.go b/models/git/branch_test.go index bb63660d078..ba690269279 100644 --- a/models/git/branch_test.go +++ b/models/git/branch_test.go @@ -11,6 +11,7 @@ import ( issues_model "code.gitea.io/gitea/models/issues" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" + "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/util" "github.com/stretchr/testify/assert" @@ -28,7 +29,15 @@ func TestAddDeletedBranch(t *testing.T) { secondBranch := unittest.AssertExistsAndLoadBean(t, &git_model.Branch{RepoID: repo.ID, Name: "branch2"}) assert.True(t, secondBranch.IsDeleted) - err := git_model.UpdateBranch(db.DefaultContext, repo.ID, secondBranch.Name, secondBranch.CommitID, secondBranch.CommitMessage, secondBranch.PusherID, secondBranch.CommitTime.AsLocalTime()) + commit := &git.Commit{ + ID: git.MustIDFromString(secondBranch.CommitID), + CommitMessage: secondBranch.CommitMessage, + Committer: &git.Signature{ + When: secondBranch.CommitTime.AsLocalTime(), + }, + } + + err := git_model.UpdateBranch(db.DefaultContext, repo.ID, secondBranch.PusherID, secondBranch.Name, commit) assert.NoError(t, err) } diff --git a/models/git/commit_status.go b/models/git/commit_status.go index 49143a87e83..c418cd23eb6 100644 --- a/models/git/commit_status.go +++ b/models/git/commit_status.go @@ -346,6 +346,53 @@ func GetLatestCommitStatusForPairs(ctx context.Context, repoIDsToLatestCommitSHA return repoStatuses, nil } +// GetLatestCommitStatusForRepoCommitIDs returns all statuses with a unique context for a given list of repo-sha pairs +func GetLatestCommitStatusForRepoCommitIDs(ctx context.Context, repoID int64, commitIDs []string) (map[string][]*CommitStatus, error) { + type result struct { + ID int64 + Sha string + } + + results := make([]result, 0, len(commitIDs)) + + sess := db.GetEngine(ctx).Table(&CommitStatus{}) + + // Create a disjunction of conditions for each repoID and SHA pair + conds := make([]builder.Cond, 0, len(commitIDs)) + for _, sha := range commitIDs { + conds = append(conds, builder.Eq{"sha": sha}) + } + sess = sess.Where(builder.Eq{"repo_id": repoID}.And(builder.Or(conds...))). + Select("max( id ) as id, sha"). + GroupBy("context_hash, sha").OrderBy("max( id ) desc") + + err := sess.Find(&results) + if err != nil { + return nil, err + } + + ids := make([]int64, 0, len(results)) + repoStatuses := make(map[string][]*CommitStatus) + for _, result := range results { + ids = append(ids, result.ID) + } + + statuses := make([]*CommitStatus, 0, len(ids)) + if len(ids) > 0 { + err = db.GetEngine(ctx).In("id", ids).Find(&statuses) + if err != nil { + return nil, err + } + + // Group the statuses by repo ID + for _, status := range statuses { + repoStatuses[status.SHA] = append(repoStatuses[status.SHA], status) + } + } + + return repoStatuses, nil +} + // FindRepoRecentCommitStatusContexts returns repository's recent commit status contexts func FindRepoRecentCommitStatusContexts(ctx context.Context, repoID int64, before time.Duration) ([]string, error) { start := timeutil.TimeStampNow().AddDuration(-before) diff --git a/models/git/lfs.go b/models/git/lfs.go index 0f90b292004..7d3da72a941 100644 --- a/models/git/lfs.go +++ b/models/git/lfs.go @@ -264,7 +264,7 @@ func LFSAutoAssociate(ctx context.Context, metas []*LFSMetaObject, user *user_mo sess := db.GetEngine(ctx) - oids := make([]interface{}, len(metas)) + oids := make([]any, len(metas)) oidMap := make(map[string]*LFSMetaObject, len(metas)) for i, meta := range metas { oids[i] = meta.Oid diff --git a/models/issues/comment.go b/models/issues/comment.go index dbe4434ca75..be020b2e1fb 100644 --- a/models/issues/comment.go +++ b/models/issues/comment.go @@ -749,7 +749,7 @@ func (c *Comment) LoadPushCommits(ctx context.Context) (err error) { err = json.Unmarshal([]byte(c.Content), &data) if err != nil { - return + return err } c.IsForcePush = data.IsForcePush @@ -925,7 +925,7 @@ func createIssueDependencyComment(ctx context.Context, doer *user_model.User, is cType = CommentTypeRemoveDependency } if err = issue.LoadRepo(ctx); err != nil { - return + return err } // Make two comments, one in each issue @@ -937,7 +937,7 @@ func createIssueDependencyComment(ctx context.Context, doer *user_model.User, is DependentIssueID: dependentIssue.ID, } if _, err = CreateComment(ctx, opts); err != nil { - return + return err } opts = &CreateCommentOptions{ @@ -1131,7 +1131,7 @@ func DeleteComment(ctx context.Context, comment *Comment) error { } if _, err := e.Table("action"). Where("comment_id = ?", comment.ID). - Update(map[string]interface{}{ + Update(map[string]any{ "is_deleted": true, }); err != nil { return err @@ -1156,7 +1156,7 @@ func UpdateCommentsMigrationsByType(tp structs.GitServiceType, originalAuthorID }), )). And("comment.original_author_id = ?", originalAuthorID). - Update(map[string]interface{}{ + Update(map[string]any{ "poster_id": posterID, "original_author": "", "original_author_id": 0, @@ -1170,11 +1170,11 @@ func CreateAutoMergeComment(ctx context.Context, typ CommentType, pr *PullReques return nil, fmt.Errorf("comment type %d cannot be used to create an auto merge comment", typ) } if err = pr.LoadIssue(ctx); err != nil { - return + return nil, err } if err = pr.LoadBaseRepo(ctx); err != nil { - return + return nil, err } comment, err = CreateComment(ctx, &CreateCommentOptions{ diff --git a/models/issues/comment_list.go b/models/issues/comment_list.go index 477337443dd..e9c8406c3a5 100644 --- a/models/issues/comment_list.go +++ b/models/issues/comment_list.go @@ -468,42 +468,38 @@ func (comments CommentList) loadReviews(ctx context.Context) error { // loadAttributes loads all attributes func (comments CommentList) loadAttributes(ctx context.Context) (err error) { if err = comments.LoadPosters(ctx); err != nil { - return + return err } if err = comments.loadLabels(ctx); err != nil { - return + return err } if err = comments.loadMilestones(ctx); err != nil { - return + return err } if err = comments.loadOldMilestones(ctx); err != nil { - return + return err } if err = comments.loadAssignees(ctx); err != nil { - return + return err } if err = comments.LoadAttachments(ctx); err != nil { - return + return err } if err = comments.loadReviews(ctx); err != nil { - return + return err } if err = comments.LoadIssues(ctx); err != nil { - return + return err } - if err = comments.loadDependentIssues(ctx); err != nil { - return - } - - return nil + return comments.loadDependentIssues(ctx) } // LoadAttributes loads attributes of the comments, except for attachments and diff --git a/models/issues/issue.go b/models/issues/issue.go index eab18f4892c..d0c5ad2bf86 100644 --- a/models/issues/issue.go +++ b/models/issues/issue.go @@ -222,8 +222,7 @@ func (issue *Issue) LoadPoster(ctx context.Context) (err error) { if !user_model.IsErrUserNotExist(err) { return fmt.Errorf("getUserByID.(poster) [%d]: %w", issue.PosterID, err) } - err = nil - return + return nil } } return err @@ -316,27 +315,27 @@ func (issue *Issue) LoadMilestone(ctx context.Context) (err error) { // LoadAttributes loads the attribute of this issue. func (issue *Issue) LoadAttributes(ctx context.Context) (err error) { if err = issue.LoadRepo(ctx); err != nil { - return + return err } if err = issue.LoadPoster(ctx); err != nil { - return + return err } if err = issue.LoadLabels(ctx); err != nil { - return + return err } if err = issue.LoadMilestone(ctx); err != nil { - return + return err } if err = issue.LoadProject(ctx); err != nil { - return + return err } if err = issue.LoadAssignees(ctx); err != nil { - return + return err } if err = issue.LoadPullRequest(ctx); err != nil && !IsErrPullRequestNotExist(err) { @@ -714,7 +713,7 @@ func (issue *Issue) Pin(ctx context.Context, user *user_model.User) error { _, err = db.GetEngine(ctx).Table("issue"). Where("id = ?", issue.ID). - Update(map[string]interface{}{ + Update(map[string]any{ "pin_order": maxPin + 1, }) if err != nil { @@ -750,7 +749,7 @@ func (issue *Issue) Unpin(ctx context.Context, user *user_model.User) error { _, err = db.GetEngine(ctx).Table("issue"). Where("id = ?", issue.ID). - Update(map[string]interface{}{ + Update(map[string]any{ "pin_order": 0, }) if err != nil { @@ -822,7 +821,7 @@ func (issue *Issue) MovePin(ctx context.Context, newPosition int) error { _, err = db.GetEngine(dbctx).Table("issue"). Where("id = ?", issue.ID). - Update(map[string]interface{}{ + Update(map[string]any{ "pin_order": newPosition, }) if err != nil { diff --git a/models/issues/issue_label.go b/models/issues/issue_label.go index f4060b14026..119a13adf22 100644 --- a/models/issues/issue_label.go +++ b/models/issues/issue_label.go @@ -39,7 +39,7 @@ func newIssueLabel(ctx context.Context, issue *Issue, label *Label, doer *user_m } if err = issue.LoadRepo(ctx); err != nil { - return + return err } opts := &CreateCommentOptions{ @@ -168,7 +168,7 @@ func deleteIssueLabel(ctx context.Context, issue *Issue, label *Label, doer *use } if err = issue.LoadRepo(ctx); err != nil { - return + return err } opts := &CreateCommentOptions{ diff --git a/models/issues/issue_update.go b/models/issues/issue_update.go index b6fd720fe5e..9607b21a675 100644 --- a/models/issues/issue_update.go +++ b/models/issues/issue_update.go @@ -511,7 +511,7 @@ func UpdateIssueDeadline(issue *Issue, deadlineUnix timeutil.TimeStamp, doer *us } // DeleteInIssue delete records in beans with external key issue_id = ? -func DeleteInIssue(ctx context.Context, issueID int64, beans ...interface{}) error { +func DeleteInIssue(ctx context.Context, issueID int64, beans ...any) error { e := db.GetEngine(ctx) for _, bean := range beans { if _, err := e.In("issue_id", issueID).Delete(bean); err != nil { @@ -538,10 +538,10 @@ func FindAndUpdateIssueMentions(ctx context.Context, issue *Issue, doer *user_mo // don't have access to reading it. Teams are expanded into their users, but organizations are ignored. func ResolveIssueMentionsByVisibility(ctx context.Context, issue *Issue, doer *user_model.User, mentions []string) (users []*user_model.User, err error) { if len(mentions) == 0 { - return + return nil, nil } if err = issue.LoadRepo(ctx); err != nil { - return + return nil, err } resolved := make(map[string]bool, 10) @@ -635,7 +635,7 @@ func ResolveIssueMentionsByVisibility(ctx context.Context, issue *Issue, doer *u } } if len(mentionUsers) == 0 { - return + return users, err } if users == nil { @@ -673,7 +673,7 @@ func UpdateIssuesMigrationsByType(gitServiceType api.GitServiceType, originalAut _, err := db.GetEngine(db.DefaultContext).Table("issue"). Where("repo_id IN (SELECT id FROM repository WHERE original_service_type = ?)", gitServiceType). And("original_author_id = ?", originalAuthorID). - Update(map[string]interface{}{ + Update(map[string]any{ "poster_id": posterID, "original_author": "", "original_author_id": 0, @@ -686,7 +686,7 @@ func UpdateReactionsMigrationsByType(gitServiceType api.GitServiceType, original _, err := db.GetEngine(db.DefaultContext).Table("reaction"). Where("original_author_id = ?", originalAuthorID). And(migratedIssueCond(gitServiceType)). - Update(map[string]interface{}{ + Update(map[string]any{ "user_id": userID, "original_author": "", "original_author_id": 0, @@ -702,66 +702,66 @@ func DeleteIssuesByRepoID(ctx context.Context, repoID int64) (attachmentPaths [] // Delete content histories if _, err = sess.In("issue_id", deleteCond). Delete(&ContentHistory{}); err != nil { - return + return nil, err } // Delete comments and attachments if _, err = sess.In("issue_id", deleteCond). Delete(&Comment{}); err != nil { - return + return nil, err } // Dependencies for issues in this repository if _, err = sess.In("issue_id", deleteCond). Delete(&IssueDependency{}); err != nil { - return + return nil, err } // Delete dependencies for issues in other repositories if _, err = sess.In("dependency_id", deleteCond). Delete(&IssueDependency{}); err != nil { - return + return nil, err } if _, err = sess.In("issue_id", deleteCond). Delete(&IssueUser{}); err != nil { - return + return nil, err } if _, err = sess.In("issue_id", deleteCond). Delete(&Reaction{}); err != nil { - return + return nil, err } if _, err = sess.In("issue_id", deleteCond). Delete(&IssueWatch{}); err != nil { - return + return nil, err } if _, err = sess.In("issue_id", deleteCond). Delete(&Stopwatch{}); err != nil { - return + return nil, err } if _, err = sess.In("issue_id", deleteCond). Delete(&TrackedTime{}); err != nil { - return + return nil, err } if _, err = sess.In("issue_id", deleteCond). Delete(&project_model.ProjectIssue{}); err != nil { - return + return nil, err } if _, err = sess.In("dependent_issue_id", deleteCond). Delete(&Comment{}); err != nil { - return + return nil, err } var attachments []*repo_model.Attachment if err = sess.In("issue_id", deleteCond). Find(&attachments); err != nil { - return + return nil, err } for j := range attachments { @@ -770,11 +770,11 @@ func DeleteIssuesByRepoID(ctx context.Context, repoID int64) (attachmentPaths [] if _, err = sess.In("issue_id", deleteCond). Delete(&repo_model.Attachment{}); err != nil { - return + return nil, err } if _, err = db.DeleteByBean(ctx, &Issue{RepoID: repoID}); err != nil { - return + return nil, err } return attachmentPaths, err diff --git a/models/issues/review.go b/models/issues/review.go index 3685c65ce58..3ec2c00fe99 100644 --- a/models/issues/review.go +++ b/models/issues/review.go @@ -136,10 +136,10 @@ func init() { // LoadCodeComments loads CodeComments func (r *Review) LoadCodeComments(ctx context.Context) (err error) { if r.CodeComments != nil { - return + return err } if err = r.loadIssue(ctx); err != nil { - return + return err } r.CodeComments, err = fetchCodeCommentsByReview(ctx, r.Issue, nil, r, false) return err @@ -147,7 +147,7 @@ func (r *Review) LoadCodeComments(ctx context.Context) (err error) { func (r *Review) loadIssue(ctx context.Context) (err error) { if r.Issue != nil { - return + return err } r.Issue, err = GetIssueByID(ctx, r.IssueID) return err @@ -156,7 +156,7 @@ func (r *Review) loadIssue(ctx context.Context) (err error) { // LoadReviewer loads reviewer func (r *Review) LoadReviewer(ctx context.Context) (err error) { if r.ReviewerID == 0 || r.Reviewer != nil { - return + return err } r.Reviewer, err = user_model.GetPossibleUserByID(ctx, r.ReviewerID) return err @@ -186,7 +186,7 @@ func LoadReviewers(ctx context.Context, reviews []*Review) (err error) { // LoadReviewerTeam loads reviewer team func (r *Review) LoadReviewerTeam(ctx context.Context) (err error) { if r.ReviewerTeamID == 0 || r.ReviewerTeam != nil { - return + return nil } r.ReviewerTeam, err = organization.GetTeamByID(ctx, r.ReviewerTeamID) @@ -196,16 +196,16 @@ func (r *Review) LoadReviewerTeam(ctx context.Context) (err error) { // LoadAttributes loads all attributes except CodeComments func (r *Review) LoadAttributes(ctx context.Context) (err error) { if err = r.loadIssue(ctx); err != nil { - return + return err } if err = r.LoadCodeComments(ctx); err != nil { - return + return err } if err = r.LoadReviewer(ctx); err != nil { - return + return err } if err = r.LoadReviewerTeam(ctx); err != nil { - return + return err } return err } @@ -1111,7 +1111,7 @@ func UpdateReviewsMigrationsByType(tp structs.GitServiceType, originalAuthorID s _, err := db.GetEngine(db.DefaultContext).Table("review"). Where("original_author_id = ?", originalAuthorID). And(migratedIssueCond(tp)). - Update(map[string]interface{}{ + Update(map[string]any{ "reviewer_id": posterID, "original_author": "", "original_author_id": 0, diff --git a/models/issues/tracked_time.go b/models/issues/tracked_time.go index 1d7592926bc..d117b74bc03 100644 --- a/models/issues/tracked_time.go +++ b/models/issues/tracked_time.go @@ -302,7 +302,7 @@ func DeleteTime(t *TrackedTime) error { func deleteTimes(ctx context.Context, opts FindTrackedTimesOptions) (removedTime int64, err error) { removedTime, err = GetTrackedSeconds(ctx, opts) if err != nil || removedTime == 0 { - return + return removedTime, err } _, err = opts.toSession(db.GetEngine(ctx)).Table("tracked_time").Cols("deleted").Update(&TrackedTime{Deleted: true}) diff --git a/models/migrations/base/db.go b/models/migrations/base/db.go index b038ad73372..51351cc7d32 100644 --- a/models/migrations/base/db.go +++ b/models/migrations/base/db.go @@ -27,7 +27,7 @@ import ( // RecreateTables will recreate the tables for the provided beans using the newly provided bean definition and move all data to that new table // WARNING: YOU MUST PROVIDE THE FULL BEAN DEFINITION -func RecreateTables(beans ...interface{}) func(*xorm.Engine) error { +func RecreateTables(beans ...any) func(*xorm.Engine) error { return func(x *xorm.Engine) error { sess := x.NewSession() defer sess.Close() @@ -48,7 +48,7 @@ func RecreateTables(beans ...interface{}) func(*xorm.Engine) error { // RecreateTable will recreate the table using the newly provided bean definition and move all data to that new table // WARNING: YOU MUST PROVIDE THE FULL BEAN DEFINITION // WARNING: YOU MUST COMMIT THE SESSION AT THE END -func RecreateTable(sess *xorm.Session, bean interface{}) error { +func RecreateTable(sess *xorm.Session, bean any) error { // TODO: This will not work if there are foreign keys tableName := sess.Engine().TableName(bean) diff --git a/models/migrations/base/tests.go b/models/migrations/base/tests.go index c3100ba6659..e7ff5241447 100644 --- a/models/migrations/base/tests.go +++ b/models/migrations/base/tests.go @@ -30,7 +30,7 @@ import ( // Provide models to be sync'd with the database - in particular any models you expect fixtures to be loaded from. // // fixtures in `models/migrations/fixtures/` will be loaded automatically -func PrepareTestEnv(t *testing.T, skip int, syncModels ...interface{}) (*xorm.Engine, func()) { +func PrepareTestEnv(t *testing.T, skip int, syncModels ...any) (*xorm.Engine, func()) { t.Helper() ourSkip := 2 ourSkip += skip diff --git a/models/migrations/v1_10/v100.go b/models/migrations/v1_10/v100.go index bd11790b988..e94024f4df4 100644 --- a/models/migrations/v1_10/v100.go +++ b/models/migrations/v1_10/v100.go @@ -59,11 +59,11 @@ func UpdateMigrationServiceTypes(x *xorm.Engine) error { } type ExternalLoginUser struct { - ExternalID string `xorm:"pk NOT NULL"` - UserID int64 `xorm:"INDEX NOT NULL"` - LoginSourceID int64 `xorm:"pk NOT NULL"` - RawData map[string]interface{} `xorm:"TEXT JSON"` - Provider string `xorm:"index VARCHAR(25)"` + ExternalID string `xorm:"pk NOT NULL"` + UserID int64 `xorm:"INDEX NOT NULL"` + LoginSourceID int64 `xorm:"pk NOT NULL"` + RawData map[string]any `xorm:"TEXT JSON"` + Provider string `xorm:"index VARCHAR(25)"` Email string Name string FirstName string diff --git a/models/migrations/v1_11/v106.go b/models/migrations/v1_11/v106.go index 3e06309a8d8..18d436ae20c 100644 --- a/models/migrations/v1_11/v106.go +++ b/models/migrations/v1_11/v106.go @@ -16,10 +16,10 @@ type Watch struct { Mode RepoWatchMode `xorm:"SMALLINT NOT NULL DEFAULT 1"` } -func AddModeColumnToWatch(x *xorm.Engine) (err error) { - if err = x.Sync2(new(Watch)); err != nil { - return +func AddModeColumnToWatch(x *xorm.Engine) error { + if err := x.Sync2(new(Watch)); err != nil { + return err } - _, err = x.Exec("UPDATE `watch` SET `mode` = 1") + _, err := x.Exec("UPDATE `watch` SET `mode` = 1") return err } diff --git a/models/migrations/v1_13/v143.go b/models/migrations/v1_13/v143.go index ad1a8c66a5d..885768dff37 100644 --- a/models/migrations/v1_13/v143.go +++ b/models/migrations/v1_13/v143.go @@ -23,25 +23,25 @@ func RecalculateStars(x *xorm.Engine) (err error) { for start := 0; ; start += batchSize { users := make([]User, 0, batchSize) - if err = sess.Limit(batchSize, start).Where("type = ?", 0).Cols("id").Find(&users); err != nil { - return + if err := sess.Limit(batchSize, start).Where("type = ?", 0).Cols("id").Find(&users); err != nil { + return err } if len(users) == 0 { break } - if err = sess.Begin(); err != nil { - return + if err := sess.Begin(); err != nil { + return err } for _, user := range users { - if _, err = sess.Exec("UPDATE `user` SET num_stars=(SELECT COUNT(*) FROM `star` WHERE uid=?) WHERE id=?", user.ID, user.ID); err != nil { - return + if _, err := sess.Exec("UPDATE `user` SET num_stars=(SELECT COUNT(*) FROM `star` WHERE uid=?) WHERE id=?", user.ID, user.ID); err != nil { + return err } } - if err = sess.Commit(); err != nil { - return + if err := sess.Commit(); err != nil { + return err } } diff --git a/models/migrations/v1_14/v166.go b/models/migrations/v1_14/v166.go index de7626076a9..78f33e8f9bd 100644 --- a/models/migrations/v1_14/v166.go +++ b/models/migrations/v1_14/v166.go @@ -78,14 +78,14 @@ func RecalculateUserEmptyPWD(x *xorm.Engine) (err error) { for start := 0; ; start += batchSize { users := make([]*User, 0, batchSize) if err = sess.Limit(batchSize, start).Where(builder.Neq{"passwd": ""}, 0).Find(&users); err != nil { - return + return err } if len(users) == 0 { break } if err = sess.Begin(); err != nil { - return + return err } for _, user := range users { @@ -100,7 +100,7 @@ func RecalculateUserEmptyPWD(x *xorm.Engine) (err error) { } if err = sess.Commit(); err != nil { - return + return err } } diff --git a/models/migrations/v1_15/v180.go b/models/migrations/v1_15/v180.go index 17163ee2c68..c71e7718617 100644 --- a/models/migrations/v1_15/v180.go +++ b/models/migrations/v1_15/v180.go @@ -44,25 +44,25 @@ func DeleteMigrationCredentials(x *xorm.Engine) (err error) { for start := 0; ; start += batchSize { tasks := make([]*Task, 0, batchSize) - if err = sess.Limit(batchSize, start).Where(cond, 0).Find(&tasks); err != nil { - return + if err := sess.Limit(batchSize, start).Where(cond, 0).Find(&tasks); err != nil { + return err } if len(tasks) == 0 { break } - if err = sess.Begin(); err != nil { - return + if err := sess.Begin(); err != nil { + return err } for _, t := range tasks { if t.PayloadContent, err = removeCredentials(t.PayloadContent); err != nil { - return + return err } - if _, err = sess.ID(t.ID).Cols("payload_content").Update(t); err != nil { - return + if _, err := sess.ID(t.ID).Cols("payload_content").Update(t); err != nil { + return err } } - if err = sess.Commit(); err != nil { - return + if err := sess.Commit(); err != nil { + return err } } return err diff --git a/models/migrations/v1_15/v181.go b/models/migrations/v1_15/v181.go index e2bb3208c41..f4dd1d6016c 100644 --- a/models/migrations/v1_15/v181.go +++ b/models/migrations/v1_15/v181.go @@ -9,7 +9,7 @@ import ( "xorm.io/xorm" ) -func AddPrimaryEmail2EmailAddress(x *xorm.Engine) (err error) { +func AddPrimaryEmail2EmailAddress(x *xorm.Engine) error { type User struct { ID int64 `xorm:"pk autoincr"` Email string `xorm:"NOT NULL"` @@ -26,12 +26,12 @@ func AddPrimaryEmail2EmailAddress(x *xorm.Engine) (err error) { } // Add lower_email and is_primary columns - if err = x.Table("email_address").Sync2(new(EmailAddress1)); err != nil { - return + if err := x.Table("email_address").Sync2(new(EmailAddress1)); err != nil { + return err } - if _, err = x.Exec("UPDATE email_address SET lower_email=LOWER(email), is_primary=?", false); err != nil { - return + if _, err := x.Exec("UPDATE email_address SET lower_email=LOWER(email), is_primary=?", false); err != nil { + return err } type EmailAddress struct { @@ -44,8 +44,8 @@ func AddPrimaryEmail2EmailAddress(x *xorm.Engine) (err error) { } // change lower_email as unique - if err = x.Sync2(new(EmailAddress)); err != nil { - return + if err := x.Sync2(new(EmailAddress)); err != nil { + return err } sess := x.NewSession() @@ -55,34 +55,33 @@ func AddPrimaryEmail2EmailAddress(x *xorm.Engine) (err error) { for start := 0; ; start += batchSize { users := make([]*User, 0, batchSize) - if err = sess.Limit(batchSize, start).Find(&users); err != nil { - return + if err := sess.Limit(batchSize, start).Find(&users); err != nil { + return err } if len(users) == 0 { break } for _, user := range users { - var exist bool - exist, err = sess.Where("email=?", user.Email).Table("email_address").Exist() + exist, err := sess.Where("email=?", user.Email).Table("email_address").Exist() if err != nil { - return + return err } if !exist { - if _, err = sess.Insert(&EmailAddress{ + if _, err := sess.Insert(&EmailAddress{ UID: user.ID, Email: user.Email, LowerEmail: strings.ToLower(user.Email), IsActivated: user.IsActive, IsPrimary: true, }); err != nil { - return + return err } } else { - if _, err = sess.Where("email=?", user.Email).Cols("is_primary").Update(&EmailAddress{ + if _, err := sess.Where("email=?", user.Email).Cols("is_primary").Update(&EmailAddress{ IsPrimary: true, }); err != nil { - return + return err } } } diff --git a/models/migrations/v1_16/v189.go b/models/migrations/v1_16/v189.go index 32e3899a3a1..79e3289ba7f 100644 --- a/models/migrations/v1_16/v189.go +++ b/models/migrations/v1_16/v189.go @@ -14,7 +14,7 @@ import ( ) func UnwrapLDAPSourceCfg(x *xorm.Engine) error { - jsonUnmarshalHandleDoubleEncode := func(bs []byte, v interface{}) error { + jsonUnmarshalHandleDoubleEncode := func(bs []byte, v any) error { err := json.Unmarshal(bs, v) if err != nil { ok := true @@ -54,7 +54,7 @@ func UnwrapLDAPSourceCfg(x *xorm.Engine) error { const dldapType = 5 type WrappedSource struct { - Source map[string]interface{} + Source map[string]any } // change lower_email as unique @@ -77,7 +77,7 @@ func UnwrapLDAPSourceCfg(x *xorm.Engine) error { for _, source := range sources { wrapped := &WrappedSource{ - Source: map[string]interface{}{}, + Source: map[string]any{}, } err := jsonUnmarshalHandleDoubleEncode([]byte(source.Cfg), &wrapped) if err != nil { diff --git a/models/migrations/v1_16/v189_test.go b/models/migrations/v1_16/v189_test.go index 96cb97c3283..32ef821d278 100644 --- a/models/migrations/v1_16/v189_test.go +++ b/models/migrations/v1_16/v189_test.go @@ -62,8 +62,8 @@ func Test_UnwrapLDAPSourceCfg(t *testing.T) { } for _, source := range sources { - converted := map[string]interface{}{} - expected := map[string]interface{}{} + converted := map[string]any{} + expected := map[string]any{} if err := json.Unmarshal([]byte(source.Cfg), &converted); err != nil { assert.NoError(t, err) diff --git a/models/migrations/v1_19/v233_test.go b/models/migrations/v1_19/v233_test.go index 83558da334e..32c10ab0f45 100644 --- a/models/migrations/v1_19/v233_test.go +++ b/models/migrations/v1_19/v233_test.go @@ -79,7 +79,7 @@ func Test_AddHeaderAuthorizationEncryptedColWebhook(t *testing.T) { return } for _, h := range hookTasks { - var m map[string]interface{} + var m map[string]any err := json.Unmarshal([]byte(h.PayloadContent), &m) assert.NoError(t, err) assert.Nil(t, m["access_token"]) diff --git a/models/migrations/v1_6/v70.go b/models/migrations/v1_6/v70.go index fec88266ba6..74434a84a14 100644 --- a/models/migrations/v1_6/v70.go +++ b/models/migrations/v1_6/v70.go @@ -81,11 +81,11 @@ func AddIssueDependencies(x *xorm.Engine) (err error) { // RepoUnit describes all units of a repository type RepoUnit struct { ID int64 - RepoID int64 `xorm:"INDEX(s)"` - Type int `xorm:"INDEX(s)"` - Config map[string]interface{} `xorm:"JSON"` - CreatedUnix int64 `xorm:"INDEX CREATED"` - Created time.Time `xorm:"-"` + RepoID int64 `xorm:"INDEX(s)"` + Type int `xorm:"INDEX(s)"` + Config map[string]any `xorm:"JSON"` + CreatedUnix int64 `xorm:"INDEX CREATED"` + Created time.Time `xorm:"-"` } // Updating existing issue units @@ -96,7 +96,7 @@ func AddIssueDependencies(x *xorm.Engine) (err error) { } for _, unit := range units { if unit.Config == nil { - unit.Config = make(map[string]interface{}) + unit.Config = make(map[string]any) } if _, ok := unit.Config["EnableDependencies"]; !ok { unit.Config["EnableDependencies"] = setting.Service.DefaultEnableDependencies diff --git a/models/migrations/v1_8/v76.go b/models/migrations/v1_8/v76.go index f35856cc606..d3fbd94deb1 100644 --- a/models/migrations/v1_8/v76.go +++ b/models/migrations/v1_8/v76.go @@ -15,10 +15,10 @@ func AddPullRequestRebaseWithMerge(x *xorm.Engine) error { // RepoUnit describes all units of a repository type RepoUnit struct { ID int64 - RepoID int64 `xorm:"INDEX(s)"` - Type int `xorm:"INDEX(s)"` - Config map[string]interface{} `xorm:"JSON"` - CreatedUnix timeutil.TimeStamp `xorm:"INDEX CREATED"` + RepoID int64 `xorm:"INDEX(s)"` + Type int `xorm:"INDEX(s)"` + Config map[string]any `xorm:"JSON"` + CreatedUnix timeutil.TimeStamp `xorm:"INDEX CREATED"` } const ( @@ -46,7 +46,7 @@ func AddPullRequestRebaseWithMerge(x *xorm.Engine) error { } for _, unit := range units { if unit.Config == nil { - unit.Config = make(map[string]interface{}) + unit.Config = make(map[string]any) } // Allow the new merge style if all other merge styles are allowed allowMergeRebase := true diff --git a/models/organization/org.go b/models/organization/org.go index 05db6ba15f8..8fd4ad076bd 100644 --- a/models/organization/org.go +++ b/models/organization/org.go @@ -532,27 +532,6 @@ func GetOrgsCanCreateRepoByUserID(userID int64) ([]*Organization, error) { Find(&orgs) } -// GetOrgUsersByUserID returns all organization-user relations by user ID. -func GetOrgUsersByUserID(uid int64, opts *SearchOrganizationsOptions) ([]*OrgUser, error) { - ous := make([]*OrgUser, 0, 10) - sess := db.GetEngine(db.DefaultContext). - Join("LEFT", "`user`", "`org_user`.org_id=`user`.id"). - Where("`org_user`.uid=?", uid) - if !opts.All { - // Only show public organizations - sess.And("is_public=?", true) - } - - if opts.PageSize != 0 { - sess = db.SetSessionPagination(sess, opts) - } - - err := sess. - Asc("`user`.name"). - Find(&ous) - return ous, err -} - // GetOrgUsersByOrgID returns all organization-user relations by organization ID. func GetOrgUsersByOrgID(ctx context.Context, opts *FindOrgMembersOpts) ([]*OrgUser, error) { sess := db.GetEngine(ctx).Where("org_id=?", opts.OrgID) diff --git a/models/organization/org_test.go b/models/organization/org_test.go index 27a173d497c..226807232c6 100644 --- a/models/organization/org_test.go +++ b/models/organization/org_test.go @@ -207,42 +207,6 @@ func TestFindOrgs(t *testing.T) { assert.EqualValues(t, 1, total) } -func TestGetOrgUsersByUserID(t *testing.T) { - assert.NoError(t, unittest.PrepareTestDatabase()) - - orgUsers, err := organization.GetOrgUsersByUserID(5, &organization.SearchOrganizationsOptions{All: true}) - assert.NoError(t, err) - if assert.Len(t, orgUsers, 3) { - assert.Equal(t, organization.OrgUser{ - ID: orgUsers[0].ID, - OrgID: 23, - UID: 5, - IsPublic: false, - }, *orgUsers[0]) - assert.Equal(t, organization.OrgUser{ - ID: orgUsers[1].ID, - OrgID: 6, - UID: 5, - IsPublic: true, - }, *orgUsers[1]) - assert.Equal(t, organization.OrgUser{ - ID: orgUsers[2].ID, - OrgID: 7, - UID: 5, - IsPublic: false, - }, *orgUsers[2]) - } - - publicOrgUsers, err := organization.GetOrgUsersByUserID(5, &organization.SearchOrganizationsOptions{All: false}) - assert.NoError(t, err) - assert.Len(t, publicOrgUsers, 1) - assert.Equal(t, *orgUsers[1], *publicOrgUsers[0]) - - orgUsers, err = organization.GetOrgUsersByUserID(1, &organization.SearchOrganizationsOptions{All: true}) - assert.NoError(t, err) - assert.Len(t, orgUsers, 0) -} - func TestGetOrgUsersByOrgID(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) diff --git a/models/packages/descriptor.go b/models/packages/descriptor.go index ee35ffe0f2a..f849ab5c041 100644 --- a/models/packages/descriptor.go +++ b/models/packages/descriptor.go @@ -59,7 +59,7 @@ type PackageDescriptor struct { Creator *user_model.User PackageProperties PackagePropertyList VersionProperties PackagePropertyList - Metadata interface{} + Metadata any Files []*PackageFileDescriptor } @@ -136,7 +136,7 @@ func GetPackageDescriptor(ctx context.Context, pv *PackageVersion) (*PackageDesc return nil, err } - var metadata interface{} + var metadata any switch p.Type { case TypeAlpine: metadata = &alpine.VersionMetadata{} diff --git a/models/packages/package_blob.go b/models/packages/package_blob.go index a55109af964..d1f470d5205 100644 --- a/models/packages/package_blob.go +++ b/models/packages/package_blob.go @@ -5,11 +5,18 @@ package packages import ( "context" + "strconv" "time" "code.gitea.io/gitea/models/db" + "code.gitea.io/gitea/models/perm" + "code.gitea.io/gitea/models/unit" + user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/util" + + "xorm.io/builder" ) // ErrPackageBlobNotExist indicates a package blob not exist error @@ -98,3 +105,42 @@ func GetTotalUnreferencedBlobSize(ctx context.Context) (int64, error) { Where("package_file.id IS NULL"). SumInt(&PackageBlob{}, "size") } + +// IsBlobAccessibleForUser tests if the user has access to the blob +func IsBlobAccessibleForUser(ctx context.Context, blobID int64, user *user_model.User) (bool, error) { + if user.IsAdmin { + return true, nil + } + + maxTeamAuthorize := builder. + Select("max(team.authorize)"). + From("team"). + InnerJoin("team_user", "team_user.team_id = team.id"). + Where(builder.Eq{"team_user.uid": user.ID}.And(builder.Expr("team_user.org_id = `user`.id"))) + + maxTeamUnitAccessMode := builder. + Select("max(team_unit.access_mode)"). + From("team"). + InnerJoin("team_user", "team_user.team_id = team.id"). + InnerJoin("team_unit", "team_unit.team_id = team.id"). + Where(builder.Eq{"team_user.uid": user.ID, "team_unit.type": unit.TypePackages}.And(builder.Expr("team_user.org_id = `user`.id"))) + + cond := builder.Eq{"package_blob.id": blobID}.And( + // owner = user + builder.Eq{"`user`.id": user.ID}. + // user can see owner + Or(builder.Eq{"`user`.visibility": structs.VisibleTypePublic}.Or(builder.Eq{"`user`.visibility": structs.VisibleTypeLimited})). + // owner is an organization and user has access to it + Or(builder.Eq{"`user`.type": user_model.UserTypeOrganization}. + And(builder.Lte{strconv.Itoa(int(perm.AccessModeRead)): maxTeamAuthorize}.Or(builder.Lte{strconv.Itoa(int(perm.AccessModeRead)): maxTeamUnitAccessMode}))), + ) + + return db.GetEngine(ctx). + Table("package_blob"). + Join("INNER", "package_file", "package_file.blob_id = package_blob.id"). + Join("INNER", "package_version", "package_version.id = package_file.version_id"). + Join("INNER", "package", "package.id = package_version.package_id"). + Join("INNER", "user", "`user`.id = package.owner_id"). + Where(cond). + Exist(&PackageBlob{}) +} diff --git a/models/perm/access/repo_permission.go b/models/perm/access/repo_permission.go index 64df5355bba..2027b87ecb4 100644 --- a/models/perm/access/repo_permission.go +++ b/models/perm/access/repo_permission.go @@ -127,7 +127,8 @@ func (p *Permission) LogString() string { } // GetUserRepoPermission returns the user permissions to the repository -func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, user *user_model.User) (perm Permission, err error) { +func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, user *user_model.User) (Permission, error) { + var perm Permission if log.IsTrace() { defer func() { if user == nil { @@ -147,30 +148,31 @@ func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, use // TODO: anonymous user visit public unit of private repo??? if user == nil && repo.IsPrivate { perm.AccessMode = perm_model.AccessModeNone - return + return perm, nil } - var is bool + var isCollaborator bool + var err error if user != nil { - is, err = repo_model.IsCollaborator(ctx, repo.ID, user.ID) + isCollaborator, err = repo_model.IsCollaborator(ctx, repo.ID, user.ID) if err != nil { return perm, err } } - if err = repo.LoadOwner(ctx); err != nil { - return + if err := repo.LoadOwner(ctx); err != nil { + return perm, err } // Prevent strangers from checking out public repo of private organization/users // Allow user if they are collaborator of a repo within a private user or a private organization but not a member of the organization itself - if !organization.HasOrgOrUserVisible(ctx, repo.Owner, user) && !is { + if !organization.HasOrgOrUserVisible(ctx, repo.Owner, user) && !isCollaborator { perm.AccessMode = perm_model.AccessModeNone - return + return perm, nil } - if err = repo.LoadUnits(ctx); err != nil { - return + if err := repo.LoadUnits(ctx); err != nil { + return perm, err } perm.Units = repo.Units @@ -178,32 +180,32 @@ func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, use // anonymous visit public repo if user == nil { perm.AccessMode = perm_model.AccessModeRead - return + return perm, nil } // Admin or the owner has super access to the repository if user.IsAdmin || user.ID == repo.OwnerID { perm.AccessMode = perm_model.AccessModeOwner - return + return perm, nil } // plain user perm.AccessMode, err = accessLevel(ctx, user, repo) if err != nil { - return + return perm, err } - if err = repo.LoadOwner(ctx); err != nil { - return + if err := repo.LoadOwner(ctx); err != nil { + return perm, err } if !repo.Owner.IsOrganization() { - return + return perm, nil } perm.UnitsMode = make(map[unit.Type]perm_model.AccessMode) // Collaborators on organization - if is { + if isCollaborator { for _, u := range repo.Units { perm.UnitsMode[u.Type] = perm.AccessMode } @@ -212,7 +214,7 @@ func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, use // get units mode from teams teams, err := organization.GetUserRepoTeams(ctx, repo.OwnerID, user.ID, repo.ID) if err != nil { - return + return perm, err } // if user in an owner team @@ -220,7 +222,7 @@ func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, use if team.AccessMode >= perm_model.AccessModeAdmin { perm.AccessMode = perm_model.AccessModeOwner perm.UnitsMode = nil - return + return perm, nil } } diff --git a/models/repo.go b/models/repo.go index 933f7e56a3a..7579d2ad734 100644 --- a/models/repo.go +++ b/models/repo.go @@ -456,7 +456,7 @@ func repoStatsCorrectNumClosedPulls(ctx context.Context, id int64) error { return repo_model.UpdateRepoIssueNumbers(ctx, id, true, true) } -func statsQuery(args ...interface{}) func(context.Context) ([]map[string][]byte, error) { +func statsQuery(args ...any) func(context.Context) ([]map[string][]byte, error) { return func(ctx context.Context) ([]map[string][]byte, error) { return db.GetEngine(ctx).Query(args...) } @@ -628,14 +628,14 @@ func DoctorUserStarNum() (err error) { for start := 0; ; start += batchSize { users := make([]user_model.User, 0, batchSize) if err = db.GetEngine(db.DefaultContext).Limit(batchSize, start).Where("type = ?", 0).Cols("id").Find(&users); err != nil { - return + return err } if len(users) == 0 { break } if err = updateUserStarNumbers(users); err != nil { - return + return err } } diff --git a/models/repo/attachment.go b/models/repo/attachment.go index 93b83aae8a8..df3b9cd2137 100644 --- a/models/repo/attachment.go +++ b/models/repo/attachment.go @@ -65,13 +65,6 @@ func (a *Attachment) DownloadURL() string { return setting.AppURL + "attachments/" + url.PathEscape(a.UUID) } -// _____ __ __ .__ __ -// / _ \_/ |__/ |______ ____ | |__ _____ ____ _____/ |_ -// / /_\ \ __\ __\__ \ _/ ___\| | \ / \_/ __ \ / \ __\ -// / | \ | | | / __ \\ \___| Y \ Y Y \ ___/| | \ | -// \____|__ /__| |__| (____ /\___ >___| /__|_| /\___ >___| /__| -// \/ \/ \/ \/ \/ \/ \/ - // ErrAttachmentNotExist represents a "AttachmentNotExist" kind of error. type ErrAttachmentNotExist struct { ID int64 diff --git a/models/repo/collaboration_test.go b/models/repo/collaboration_test.go index 0a6444de858..a29dfe99a50 100644 --- a/models/repo/collaboration_test.go +++ b/models/repo/collaboration_test.go @@ -33,6 +33,19 @@ func TestRepository_GetCollaborators(t *testing.T) { test(2) test(3) test(4) + + // Test db.ListOptions + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 22}) + + collaborators1, err := repo_model.GetCollaborators(db.DefaultContext, repo.ID, db.ListOptions{PageSize: 1, Page: 1}) + assert.NoError(t, err) + assert.Len(t, collaborators1, 1) + + collaborators2, err := repo_model.GetCollaborators(db.DefaultContext, repo.ID, db.ListOptions{PageSize: 1, Page: 2}) + assert.NoError(t, err) + assert.Len(t, collaborators2, 1) + + assert.NotEqualValues(t, collaborators1[0].ID, collaborators2[0].ID) } func TestRepository_IsCollaborator(t *testing.T) { @@ -66,5 +79,80 @@ func TestRepository_ChangeCollaborationAccessMode(t *testing.T) { assert.NoError(t, repo_model.ChangeCollaborationAccessMode(db.DefaultContext, repo, unittest.NonexistentID, perm.AccessModeAdmin)) + // Disvard invalid input. + assert.NoError(t, repo_model.ChangeCollaborationAccessMode(db.DefaultContext, repo, 4, perm.AccessMode(unittest.NonexistentID))) + unittest.CheckConsistencyFor(t, &repo_model.Repository{ID: repo.ID}) } + +func TestRepository_CountCollaborators(t *testing.T) { + assert.NoError(t, unittest.PrepareTestDatabase()) + + repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4}) + count, err := repo_model.CountCollaborators(repo1.ID) + assert.NoError(t, err) + assert.EqualValues(t, 2, count) + + repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 22}) + count, err = repo_model.CountCollaborators(repo2.ID) + assert.NoError(t, err) + assert.EqualValues(t, 2, count) + + // Non-existent repository. + count, err = repo_model.CountCollaborators(unittest.NonexistentID) + assert.NoError(t, err) + assert.EqualValues(t, 0, count) +} + +func TestRepository_IsOwnerMemberCollaborator(t *testing.T) { + assert.NoError(t, unittest.PrepareTestDatabase()) + + repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3}) + + // Organisation owner. + actual, err := repo_model.IsOwnerMemberCollaborator(repo1, 2) + assert.NoError(t, err) + assert.True(t, actual) + + // Team member. + actual, err = repo_model.IsOwnerMemberCollaborator(repo1, 4) + assert.NoError(t, err) + assert.True(t, actual) + + // Normal user. + actual, err = repo_model.IsOwnerMemberCollaborator(repo1, 1) + assert.NoError(t, err) + assert.False(t, actual) + + repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4}) + + // Collaborator. + actual, err = repo_model.IsOwnerMemberCollaborator(repo2, 4) + assert.NoError(t, err) + assert.True(t, actual) + + repo3 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 15}) + + // Repository owner. + actual, err = repo_model.IsOwnerMemberCollaborator(repo3, 2) + assert.NoError(t, err) + assert.True(t, actual) +} + +func TestRepo_GetCollaboration(t *testing.T) { + assert.NoError(t, unittest.PrepareTestDatabase()) + + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4}) + + // Existing collaboration. + collab, err := repo_model.GetCollaboration(db.DefaultContext, repo.ID, 4) + assert.NoError(t, err) + assert.NotNil(t, collab) + assert.EqualValues(t, 4, collab.UserID) + assert.EqualValues(t, 4, collab.RepoID) + + // Non-existing collaboration. + collab, err = repo_model.GetCollaboration(db.DefaultContext, repo.ID, 1) + assert.NoError(t, err) + assert.Nil(t, collab) +} diff --git a/models/repo/git.go b/models/repo/git.go index 7ae88058dc8..c1af7ee9608 100644 --- a/models/repo/git.go +++ b/models/repo/git.go @@ -11,7 +11,7 @@ type MergeStyle string const ( // MergeStyleMerge create merge commit MergeStyleMerge MergeStyle = "merge" - // MergeStyleRebase rebase before merging + // MergeStyleRebase rebase before merging, and fast-forward MergeStyleRebase MergeStyle = "rebase" // MergeStyleRebaseMerge rebase before merging with merge commit (--no-ff) MergeStyleRebaseMerge MergeStyle = "rebase-merge" diff --git a/models/repo/mirror.go b/models/repo/mirror.go index c1d24a48863..39482037b20 100644 --- a/models/repo/mirror.go +++ b/models/repo/mirror.go @@ -105,7 +105,7 @@ func DeleteMirrorByRepoID(repoID int64) error { } // MirrorsIterate iterates all mirror repositories. -func MirrorsIterate(limit int, f func(idx int, bean interface{}) error) error { +func MirrorsIterate(limit int, f func(idx int, bean any) error) error { sess := db.GetEngine(db.DefaultContext). Where("next_update_unix<=?", time.Now().Unix()). And("next_update_unix!=0"). diff --git a/models/repo/pushmirror.go b/models/repo/pushmirror.go index 642020bb5e6..f34484f6387 100644 --- a/models/repo/pushmirror.go +++ b/models/repo/pushmirror.go @@ -127,7 +127,7 @@ func GetPushMirrorsSyncedOnCommit(ctx context.Context, repoID int64) ([]*PushMir } // PushMirrorsIterate iterates all push-mirror repositories. -func PushMirrorsIterate(ctx context.Context, limit int, f func(idx int, bean interface{}) error) error { +func PushMirrorsIterate(ctx context.Context, limit int, f func(idx int, bean any) error) error { sess := db.GetEngine(ctx). Where("last_update + (`interval` / ?) <= ?", time.Second, time.Now().Unix()). And("`interval` != 0"). diff --git a/models/repo/pushmirror_test.go b/models/repo/pushmirror_test.go index 2b3c5be292c..9ab70235913 100644 --- a/models/repo/pushmirror_test.go +++ b/models/repo/pushmirror_test.go @@ -41,7 +41,7 @@ func TestPushMirrorsIterate(t *testing.T) { time.Sleep(1 * time.Millisecond) - repo_model.PushMirrorsIterate(db.DefaultContext, 1, func(idx int, bean interface{}) error { + repo_model.PushMirrorsIterate(db.DefaultContext, 1, func(idx int, bean any) error { m, ok := bean.(*repo_model.PushMirror) assert.True(t, ok) assert.Equal(t, "test-1", m.RemoteName) diff --git a/models/repo/release.go b/models/repo/release.go index b77490584f9..c63b3244576 100644 --- a/models/repo/release.go +++ b/models/repo/release.go @@ -339,7 +339,7 @@ func (s releaseMetaSearch) Less(i, j int) bool { // GetReleaseAttachments retrieves the attachments for releases func GetReleaseAttachments(ctx context.Context, rels ...*Release) (err error) { if len(rels) == 0 { - return + return nil } // To keep this efficient as possible sort all releases by id, @@ -442,7 +442,7 @@ func UpdateReleasesMigrationsByType(gitServiceType structs.GitServiceType, origi _, err := db.GetEngine(db.DefaultContext).Table("release"). Where("repo_id IN (SELECT id FROM repository WHERE original_service_type = ?)", gitServiceType). And("original_author_id = ?", originalAuthorID). - Update(map[string]interface{}{ + Update(map[string]any{ "publisher_id": posterID, "original_author": "", "original_author_id": 0, diff --git a/models/repo/repo.go b/models/repo/repo.go index a503f6e16c1..73e9870953e 100644 --- a/models/repo/repo.go +++ b/models/repo/repo.go @@ -530,6 +530,18 @@ func (repo *Repository) ComposeCompareURL(oldCommitID, newCommitID string) strin return fmt.Sprintf("%s/%s/compare/%s...%s", url.PathEscape(repo.OwnerName), url.PathEscape(repo.Name), util.PathEscapeSegments(oldCommitID), util.PathEscapeSegments(newCommitID)) } +func (repo *Repository) ComposeBranchCompareURL(baseRepo *Repository, branchName string) string { + if baseRepo == nil { + baseRepo = repo + } + var cmpBranchEscaped string + if repo.ID != baseRepo.ID { + cmpBranchEscaped = fmt.Sprintf("%s/%s:", url.PathEscape(repo.OwnerName), url.PathEscape(repo.Name)) + } + cmpBranchEscaped = fmt.Sprintf("%s%s", cmpBranchEscaped, util.PathEscapeSegments(branchName)) + return fmt.Sprintf("%s/compare/%s...%s", baseRepo.Link(), util.PathEscapeSegments(baseRepo.DefaultBranch), cmpBranchEscaped) +} + // IsOwnedBy returns true when user owns this repository func (repo *Repository) IsOwnedBy(userID int64) bool { return repo.OwnerID == userID diff --git a/models/repo/repo_list.go b/models/repo/repo_list.go index 92b9c15b4bd..83ba02e3165 100644 --- a/models/repo/repo_list.go +++ b/models/repo/repo_list.go @@ -560,7 +560,7 @@ func searchRepositoryByCondition(ctx context.Context, opts *SearchRepoOptions, c opts.OrderBy = db.SearchOrderByAlphabetically } - args := make([]interface{}, 0) + args := make([]any, 0) if opts.PriorityOwnerID > 0 { opts.OrderBy = db.SearchOrderBy(fmt.Sprintf("CASE WHEN owner_id = ? THEN 0 ELSE owner_id END, %s", opts.OrderBy)) args = append(args, opts.PriorityOwnerID) diff --git a/models/system/notice.go b/models/system/notice.go index e598abe2225..784ad74375b 100644 --- a/models/system/notice.go +++ b/models/system/notice.go @@ -43,7 +43,7 @@ func (n *Notice) TrStr() string { } // CreateNotice creates new system notice. -func CreateNotice(ctx context.Context, tp NoticeType, desc string, args ...interface{}) error { +func CreateNotice(ctx context.Context, tp NoticeType, desc string, args ...any) error { if len(args) > 0 { desc = fmt.Sprintf(desc, args...) } @@ -55,7 +55,7 @@ func CreateNotice(ctx context.Context, tp NoticeType, desc string, args ...inter } // CreateRepositoryNotice creates new system notice with type NoticeRepository. -func CreateRepositoryNotice(desc string, args ...interface{}) error { +func CreateRepositoryNotice(desc string, args ...any) error { // Note we use the db.DefaultContext here rather than passing in a context as the context may be cancelled return CreateNotice(db.DefaultContext, NoticeRepository, desc, args...) } diff --git a/models/unittest/consistency.go b/models/unittest/consistency.go index 41798c62536..faa02589aab 100644 --- a/models/unittest/consistency.go +++ b/models/unittest/consistency.go @@ -21,10 +21,10 @@ const ( modelsCommentTypeComment = 0 ) -var consistencyCheckMap = make(map[string]func(t assert.TestingT, bean interface{})) +var consistencyCheckMap = make(map[string]func(t assert.TestingT, bean any)) // CheckConsistencyFor test that all matching database entries are consistent -func CheckConsistencyFor(t assert.TestingT, beansToCheck ...interface{}) { +func CheckConsistencyFor(t assert.TestingT, beansToCheck ...any) { for _, bean := range beansToCheck { sliceType := reflect.SliceOf(reflect.TypeOf(bean)) sliceValue := reflect.MakeSlice(sliceType, 0, 10) @@ -42,7 +42,7 @@ func CheckConsistencyFor(t assert.TestingT, beansToCheck ...interface{}) { } } -func checkForConsistency(t assert.TestingT, bean interface{}) { +func checkForConsistency(t assert.TestingT, bean any) { tb, err := db.TableInfo(bean) assert.NoError(t, err) f := consistencyCheckMap[tb.Name] @@ -63,7 +63,7 @@ func init() { return i } - checkForUserConsistency := func(t assert.TestingT, bean interface{}) { + checkForUserConsistency := func(t assert.TestingT, bean any) { user := reflectionWrap(bean) AssertCountByCond(t, "repository", builder.Eq{"owner_id": user.int("ID")}, user.int("NumRepos")) AssertCountByCond(t, "star", builder.Eq{"uid": user.int("ID")}, user.int("NumStars")) @@ -77,7 +77,7 @@ func init() { } } - checkForRepoConsistency := func(t assert.TestingT, bean interface{}) { + checkForRepoConsistency := func(t assert.TestingT, bean any) { repo := reflectionWrap(bean) assert.Equal(t, repo.str("LowerName"), strings.ToLower(repo.str("Name")), "repo: %+v", repo) AssertCountByCond(t, "star", builder.Eq{"repo_id": repo.int("ID")}, repo.int("NumStars")) @@ -113,7 +113,7 @@ func init() { "Unexpected number of closed milestones for repo id: %d", repo.int("ID")) } - checkForIssueConsistency := func(t assert.TestingT, bean interface{}) { + checkForIssueConsistency := func(t assert.TestingT, bean any) { issue := reflectionWrap(bean) typeComment := modelsCommentTypeComment actual := GetCountByCond(t, "comment", builder.Eq{"`type`": typeComment, "issue_id": issue.int("ID")}) @@ -124,14 +124,14 @@ func init() { } } - checkForPullRequestConsistency := func(t assert.TestingT, bean interface{}) { + checkForPullRequestConsistency := func(t assert.TestingT, bean any) { pr := reflectionWrap(bean) issueRow := AssertExistsAndLoadMap(t, "issue", builder.Eq{"id": pr.int("IssueID")}) assert.True(t, parseBool(issueRow["is_pull"])) assert.EqualValues(t, parseInt(issueRow["index"]), pr.int("Index"), "Unexpected index for pull request id: %d", pr.int("ID")) } - checkForMilestoneConsistency := func(t assert.TestingT, bean interface{}) { + checkForMilestoneConsistency := func(t assert.TestingT, bean any) { milestone := reflectionWrap(bean) AssertCountByCond(t, "issue", builder.Eq{"milestone_id": milestone.int("ID")}, milestone.int("NumIssues")) @@ -145,7 +145,7 @@ func init() { assert.Equal(t, completeness, milestone.int("Completeness")) } - checkForLabelConsistency := func(t assert.TestingT, bean interface{}) { + checkForLabelConsistency := func(t assert.TestingT, bean any) { label := reflectionWrap(bean) issueLabels, err := db.GetEngine(db.DefaultContext).Table("issue_label"). Where(builder.Eq{"label_id": label.int("ID")}). @@ -166,13 +166,13 @@ func init() { assert.EqualValues(t, expected, label.int("NumClosedIssues"), "Unexpected number of closed issues for label id: %d", label.int("ID")) } - checkForTeamConsistency := func(t assert.TestingT, bean interface{}) { + checkForTeamConsistency := func(t assert.TestingT, bean any) { team := reflectionWrap(bean) AssertCountByCond(t, "team_user", builder.Eq{"team_id": team.int("ID")}, team.int("NumMembers")) AssertCountByCond(t, "team_repo", builder.Eq{"team_id": team.int("ID")}, team.int("NumRepos")) } - checkForActionConsistency := func(t assert.TestingT, bean interface{}) { + checkForActionConsistency := func(t assert.TestingT, bean any) { action := reflectionWrap(bean) if action.int("RepoID") != 1700 { // dangling intentional repoRow := AssertExistsAndLoadMap(t, "repository", builder.Eq{"id": action.int("RepoID")}) diff --git a/models/unittest/reflection.go b/models/unittest/reflection.go index 1b149b19fe0..141fc66b998 100644 --- a/models/unittest/reflection.go +++ b/models/unittest/reflection.go @@ -23,7 +23,7 @@ type reflectionValue struct { v reflect.Value } -func reflectionWrap(v interface{}) *reflectionValue { +func reflectionWrap(v any) *reflectionValue { return &reflectionValue{v: reflect.ValueOf(v)} } diff --git a/models/unittest/testdb.go b/models/unittest/testdb.go index f926a65538e..1ff0fdc25bd 100644 --- a/models/unittest/testdb.go +++ b/models/unittest/testdb.go @@ -37,7 +37,7 @@ func FixturesDir() string { return fixturesDir } -func fatalTestError(fmtStr string, args ...interface{}) { +func fatalTestError(fmtStr string, args ...any) { _, _ = fmt.Fprintf(os.Stderr, fmtStr, args...) os.Exit(1) } diff --git a/models/user/external_login_user.go b/models/user/external_login_user.go index f70f3effcc5..3a56240435f 100644 --- a/models/user/external_login_user.go +++ b/models/user/external_login_user.go @@ -57,11 +57,11 @@ func (err ErrExternalLoginUserNotExist) Unwrap() error { // ExternalLoginUser makes the connecting between some existing user and additional external login sources type ExternalLoginUser struct { - ExternalID string `xorm:"pk NOT NULL"` - UserID int64 `xorm:"INDEX NOT NULL"` - LoginSourceID int64 `xorm:"pk NOT NULL"` - RawData map[string]interface{} `xorm:"TEXT JSON"` - Provider string `xorm:"index VARCHAR(25)"` + ExternalID string `xorm:"pk NOT NULL"` + UserID int64 `xorm:"INDEX NOT NULL"` + LoginSourceID int64 `xorm:"pk NOT NULL"` + RawData map[string]any `xorm:"TEXT JSON"` + Provider string `xorm:"index VARCHAR(25)"` Email string Name string FirstName string diff --git a/models/user/redirect.go b/models/user/redirect.go index 9d8903c0599..42e991888a6 100644 --- a/models/user/redirect.go +++ b/models/user/redirect.go @@ -64,6 +64,10 @@ func NewUserRedirect(ctx context.Context, ID int64, oldUserName, newUserName str oldUserName = strings.ToLower(oldUserName) newUserName = strings.ToLower(newUserName) + if err := DeleteUserRedirect(ctx, oldUserName); err != nil { + return err + } + if err := DeleteUserRedirect(ctx, newUserName); err != nil { return err } diff --git a/models/user/user.go b/models/user/user.go index 6f9c2f5b35a..4b19eda67b6 100644 --- a/models/user/user.go +++ b/models/user/user.go @@ -336,7 +336,7 @@ func GetUserFollowers(ctx context.Context, u, viewer *User, listOptions db.ListO // GetUserFollowing returns range of user's following. func GetUserFollowing(ctx context.Context, u, viewer *User, listOptions db.ListOptions) ([]*User, int64, error) { - sess := db.GetEngine(db.DefaultContext). + sess := db.GetEngine(ctx). Select("`user`.*"). Join("LEFT", "follow", "`user`.id=follow.follow_id"). Where("follow.user_id=?", u.ID). diff --git a/models/webhook/hooktask.go b/models/webhook/hooktask.go index f9fc8868263..3ece5f062d9 100644 --- a/models/webhook/hooktask.go +++ b/models/webhook/hooktask.go @@ -92,7 +92,7 @@ func (t *HookTask) AfterLoad() { } } -func (t *HookTask) simpleMarshalJSON(v interface{}) string { +func (t *HookTask) simpleMarshalJSON(v any) string { p, err := json.Marshal(v) if err != nil { log.Error("Marshal [%d]: %v", t.ID, err) diff --git a/modules/actions/workflows.go b/modules/actions/workflows.go index 3786f2a274c..2c7cec5591d 100644 --- a/modules/actions/workflows.go +++ b/modules/actions/workflows.go @@ -23,13 +23,11 @@ import ( type DetectedWorkflow struct { EntryName string TriggerEvent string - Commit *git.Commit - Ref string Content []byte } func init() { - model.OnDecodeNodeError = func(node yaml.Node, out interface{}, err error) { + model.OnDecodeNodeError = func(node yaml.Node, out any, err error) { // Log the error instead of panic or fatal. // It will be a big job to refactor act/pkg/model to return decode error, // so we just log the error and return empty value, and improve it later. @@ -120,7 +118,6 @@ func DetectWorkflows(commit *git.Commit, triggedEvent webhook_module.HookEventTy dwf := &DetectedWorkflow{ EntryName: entry.Name(), TriggerEvent: evt.Name, - Commit: commit, Content: content, } workflows = append(workflows, dwf) @@ -335,44 +332,47 @@ func matchIssuesEvent(commit *git.Commit, issuePayload *api.IssuePayload, evt *j } func matchPullRequestEvent(commit *git.Commit, prPayload *api.PullRequestPayload, evt *jobparser.Event) bool { - // with no special filter parameters - if len(evt.Acts()) == 0 { + acts := evt.Acts() + activityTypeMatched := false + matchTimes := 0 + + if vals, ok := acts["types"]; !ok { // defaultly, only pull request `opened`, `reopened` and `synchronized` will trigger workflow // See https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request - return prPayload.Action == api.HookIssueSynchronized || prPayload.Action == api.HookIssueOpened || prPayload.Action == api.HookIssueReOpened + activityTypeMatched = prPayload.Action == api.HookIssueSynchronized || prPayload.Action == api.HookIssueOpened || prPayload.Action == api.HookIssueReOpened + } else { + // See https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request + // Actions with the same name: + // opened, edited, closed, reopened, assigned, unassigned + // Actions need to be converted: + // synchronized -> synchronize + // label_updated -> labeled + // label_cleared -> unlabeled + // Unsupported activity types: + // converted_to_draft, ready_for_review, locked, unlocked, review_requested, review_request_removed, auto_merge_enabled, auto_merge_disabled + + action := prPayload.Action + switch action { + case api.HookIssueSynchronized: + action = "synchronize" + case api.HookIssueLabelUpdated: + action = "labeled" + case api.HookIssueLabelCleared: + action = "unlabeled" + } + log.Trace("matching pull_request %s with %v", action, vals) + for _, val := range vals { + if glob.MustCompile(val, '/').Match(string(action)) { + activityTypeMatched = true + matchTimes++ + break + } + } } - matchTimes := 0 // all acts conditions should be satisfied - for cond, vals := range evt.Acts() { + for cond, vals := range acts { switch cond { - case "types": - // See https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request - // Actions with the same name: - // opened, edited, closed, reopened, assigned, unassigned - // Actions need to be converted: - // synchronized -> synchronize - // label_updated -> labeled - // label_cleared -> unlabeled - // Unsupported activity types: - // converted_to_draft, ready_for_review, locked, unlocked, review_requested, review_request_removed, auto_merge_enabled, auto_merge_disabled - - action := prPayload.Action - switch action { - case api.HookIssueSynchronized: - action = "synchronize" - case api.HookIssueLabelUpdated: - action = "labeled" - case api.HookIssueLabelCleared: - action = "unlabeled" - } - log.Trace("matching pull_request %s with %v", action, vals) - for _, val := range vals { - if glob.MustCompile(val, '/').Match(string(action)) { - matchTimes++ - break - } - } case "branches": refName := git.RefName(prPayload.PullRequest.Base.Ref) patterns, err := workflowpattern.CompilePatterns(vals...) @@ -421,7 +421,7 @@ func matchPullRequestEvent(commit *git.Commit, prPayload *api.PullRequestPayload log.Warn("pull request event unsupported condition %q", cond) } } - return matchTimes == len(evt.Acts()) + return activityTypeMatched && matchTimes == len(evt.Acts()) } func matchIssueCommentEvent(commit *git.Commit, issueCommentPayload *api.IssueCommentPayload, evt *jobparser.Event) bool { diff --git a/modules/actions/workflows_test.go b/modules/actions/workflows_test.go index 2c374d2c0d0..ef553c4a572 100644 --- a/modules/actions/workflows_test.go +++ b/modules/actions/workflows_test.go @@ -57,6 +57,25 @@ func TestDetectMatched(t *testing.T) { yamlOn: "on: pull_request", expected: false, }, + { + desc: "HookEventPullRequest(pull_request) `closed` action doesn't match GithubEventPullRequest(pull_request) with no activity type", + triggedEvent: webhook_module.HookEventPullRequest, + payload: &api.PullRequestPayload{Action: api.HookIssueClosed}, + yamlOn: "on: pull_request", + expected: false, + }, + { + desc: "HookEventPullRequest(pull_request) `closed` action doesn't match GithubEventPullRequest(pull_request) with branches", + triggedEvent: webhook_module.HookEventPullRequest, + payload: &api.PullRequestPayload{ + Action: api.HookIssueClosed, + PullRequest: &api.PullRequest{ + Base: &api.PRBranchInfo{}, + }, + }, + yamlOn: "on:\n pull_request:\n branches: [main]", + expected: false, + }, { desc: "HookEventPullRequest(pull_request) `label_updated` action matches GithubEventPullRequest(pull_request) with `label` activity type", triggedEvent: webhook_module.HookEventPullRequest, diff --git a/modules/activitypub/client.go b/modules/activitypub/client.go index ed5c9990d65..fa1b57638f3 100644 --- a/modules/activitypub/client.go +++ b/modules/activitypub/client.go @@ -63,19 +63,19 @@ type Client struct { // NewClient function func NewClient(user *user_model.User, pubID string) (c *Client, err error) { if err = containsRequiredHTTPHeaders(http.MethodGet, setting.Federation.GetHeaders); err != nil { - return + return nil, err } else if err = containsRequiredHTTPHeaders(http.MethodPost, setting.Federation.PostHeaders); err != nil { - return + return nil, err } priv, err := GetPrivateKey(user) if err != nil { - return + return nil, err } privPem, _ := pem.Decode([]byte(priv)) privParsed, err := x509.ParsePKCS1PrivateKey(privPem.Bytes) if err != nil { - return + return nil, err } c = &Client{ @@ -99,14 +99,14 @@ func (c *Client) NewRequest(b []byte, to string) (req *http.Request, err error) buf := bytes.NewBuffer(b) req, err = http.NewRequest(http.MethodPost, to, buf) if err != nil { - return + return nil, err } req.Header.Add("Content-Type", ActivityStreamsContentType) req.Header.Add("Date", CurrentTime()) req.Header.Add("User-Agent", "Gitea/"+setting.AppVer) signer, _, err := httpsig.NewSigner(c.algs, c.digestAlg, c.postHeaders, httpsig.Signature, httpsigExpirationTime) if err != nil { - return + return nil, err } err = signer.SignRequest(c.priv, c.pubID, req, b) return req, err @@ -116,7 +116,7 @@ func (c *Client) NewRequest(b []byte, to string) (req *http.Request, err error) func (c *Client) Post(b []byte, to string) (resp *http.Response, err error) { var req *http.Request if req, err = c.NewRequest(b, to); err != nil { - return + return nil, err } resp, err = c.client.Do(req) return resp, err diff --git a/modules/activitypub/user_settings.go b/modules/activitypub/user_settings.go index 2d156c17e65..20b3d759c2f 100644 --- a/modules/activitypub/user_settings.go +++ b/modules/activitypub/user_settings.go @@ -15,22 +15,22 @@ func GetKeyPair(user *user_model.User) (pub, priv string, err error) { var settings map[string]*user_model.Setting settings, err = user_model.GetSettings(user.ID, []string{user_model.UserActivityPubPrivPem, user_model.UserActivityPubPubPem}) if err != nil { - return + return pub, priv, err } else if len(settings) == 0 { if priv, pub, err = util.GenerateKeyPair(rsaBits); err != nil { - return + return pub, priv, err } if err = user_model.SetUserSetting(user.ID, user_model.UserActivityPubPrivPem, priv); err != nil { - return + return pub, priv, err } if err = user_model.SetUserSetting(user.ID, user_model.UserActivityPubPubPem, pub); err != nil { - return + return pub, priv, err } - return + return pub, priv, err } else { priv = settings[user_model.UserActivityPubPrivPem].SettingValue pub = settings[user_model.UserActivityPubPubPem].SettingValue - return + return pub, priv, err } } diff --git a/modules/assetfs/layered.go b/modules/assetfs/layered.go index d032160a6fe..e18a13e4aaf 100644 --- a/modules/assetfs/layered.go +++ b/modules/assetfs/layered.go @@ -215,6 +215,7 @@ func (l *LayeredFS) WatchLocalChanges(ctx context.Context, callback func()) { log.Error("Unable to list directories for asset local file-system %q: %v", layer.localPath, err) continue } + layerDirs = append(layerDirs, ".") for _, dir := range layerDirs { if err = watcher.Add(util.FilePathJoinAbs(layer.localPath, dir)); err != nil { log.Error("Unable to watch directory %s: %v", dir, err) diff --git a/modules/avatar/hash_test.go b/modules/avatar/hash_test.go new file mode 100644 index 00000000000..1b8249c696c --- /dev/null +++ b/modules/avatar/hash_test.go @@ -0,0 +1,26 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package avatar_test + +import ( + "bytes" + "image" + "image/png" + "testing" + + "code.gitea.io/gitea/modules/avatar" + + "github.com/stretchr/testify/assert" +) + +func Test_HashAvatar(t *testing.T) { + myImage := image.NewRGBA(image.Rect(0, 0, 32, 32)) + var buff bytes.Buffer + png.Encode(&buff, myImage) + + assert.EqualValues(t, "9ddb5bac41d57e72aa876321d0c09d71090c05f94bc625303801be2f3240d2cb", avatar.HashAvatar(1, buff.Bytes())) + assert.EqualValues(t, "9a5d44e5d637b9582a976676e8f3de1dccd877c2fe3e66ca3fab1629f2f47609", avatar.HashAvatar(8, buff.Bytes())) + assert.EqualValues(t, "ed7399158672088770de6f5211ce15528ebd675e92fc4fc060c025f4b2794ccb", avatar.HashAvatar(1024, buff.Bytes())) + assert.EqualValues(t, "161178642c7d59eb25a61dddced5e6b66eae1c70880d5f148b1b497b767e72d9", avatar.HashAvatar(1024, []byte{})) +} diff --git a/modules/base/tool.go b/modules/base/tool.go index 7fe7ffcddcd..328992684b6 100644 --- a/modules/base/tool.go +++ b/modules/base/tool.go @@ -107,7 +107,7 @@ const TimeLimitCodeLength = 12 + 6 + 40 // CreateTimeLimitCode create a time limit code // code format: 12 length date time string + 6 minutes string + 40 sha1 encoded string -func CreateTimeLimitCode(data string, minutes int, startInf interface{}) string { +func CreateTimeLimitCode(data string, minutes int, startInf any) string { format := "200601021504" var start, end time.Time @@ -252,7 +252,7 @@ func SetupGiteaRoot() string { } // FormatNumberSI format a number -func FormatNumberSI(data interface{}) string { +func FormatNumberSI(data any) string { var num int64 if num1, ok := data.(int64); ok { num = num1 diff --git a/modules/cache/cache_redis.go b/modules/cache/cache_redis.go index f22482de48a..6c358b0a78a 100644 --- a/modules/cache/cache_redis.go +++ b/modules/cache/cache_redis.go @@ -24,7 +24,7 @@ type RedisCacher struct { } // toStr convert string/int/int64 interface to string. it's only used by the RedisCacher.Put internally -func toStr(v interface{}) string { +func toStr(v any) string { if v == nil { return "" } @@ -44,7 +44,7 @@ func toStr(v interface{}) string { // Put puts value (string type) into cache with key and expire time. // If expired is 0, it lives forever. -func (c *RedisCacher) Put(key string, val interface{}, expire int64) error { +func (c *RedisCacher) Put(key string, val any, expire int64) error { // this function is not well-designed, it only puts string values into cache key = c.prefix + key if expire == 0 { @@ -65,7 +65,7 @@ func (c *RedisCacher) Put(key string, val interface{}, expire int64) error { } // Get gets cached value by given key. -func (c *RedisCacher) Get(key string) interface{} { +func (c *RedisCacher) Get(key string) any { val, err := c.c.Get(graceful.GetManager().HammerContext(), c.prefix+key).Result() if err != nil { return nil diff --git a/modules/cache/cache_twoqueue.go b/modules/cache/cache_twoqueue.go index 2e2efd00f24..184db255736 100644 --- a/modules/cache/cache_twoqueue.go +++ b/modules/cache/cache_twoqueue.go @@ -30,7 +30,7 @@ type TwoQueueCacheConfig struct { // MemoryItem represents a memory cache item. type MemoryItem struct { - Val interface{} + Val any Created int64 Timeout int64 } @@ -43,7 +43,7 @@ func (item *MemoryItem) hasExpired() bool { var _ mc.Cache = &TwoQueueCache{} // Put puts value into cache with key and expire time. -func (c *TwoQueueCache) Put(key string, val interface{}, timeout int64) error { +func (c *TwoQueueCache) Put(key string, val any, timeout int64) error { item := &MemoryItem{ Val: val, Created: time.Now().Unix(), @@ -56,7 +56,7 @@ func (c *TwoQueueCache) Put(key string, val interface{}, timeout int64) error { } // Get gets cached value by given key. -func (c *TwoQueueCache) Get(key string) interface{} { +func (c *TwoQueueCache) Get(key string) any { c.lock.Lock() defer c.lock.Unlock() cached, ok := c.cache.Get(key) @@ -146,7 +146,7 @@ func (c *TwoQueueCache) Flush() error { return nil } -func (c *TwoQueueCache) checkAndInvalidate(key interface{}) { +func (c *TwoQueueCache) checkAndInvalidate(key any) { c.lock.Lock() defer c.lock.Unlock() cached, ok := c.cache.Peek(key) diff --git a/modules/charset/ambiguous/generate.go b/modules/charset/ambiguous/generate.go index b0c25310082..e3fda5be982 100644 --- a/modules/charset/ambiguous/generate.go +++ b/modules/charset/ambiguous/generate.go @@ -90,7 +90,7 @@ Usage: %[1]s [-v] [-o output.go] ambiguous.json sort.Slice(tables, func(i, j int) bool { return tables[i].Locale < tables[j].Locale }) - data := map[string]interface{}{ + data := map[string]any{ "Tables": tables, } @@ -99,7 +99,7 @@ Usage: %[1]s [-v] [-o output.go] ambiguous.json } } -func runTemplate(t *template.Template, filename string, data interface{}) error { +func runTemplate(t *template.Template, filename string, data any) error { buf := bytes.NewBuffer(nil) if err := t.Execute(buf, data); err != nil { return fmt.Errorf("unable to execute template: %w", err) @@ -172,17 +172,17 @@ var AmbiguousCharacters = map[string]*AmbiguousTable{ `)) -func logf(format string, args ...interface{}) { +func logf(format string, args ...any) { fmt.Fprintf(os.Stderr, format+"\n", args...) } -func verbosef(format string, args ...interface{}) { +func verbosef(format string, args ...any) { if verbose { logf(format, args...) } } -func fatalf(format string, args ...interface{}) { +func fatalf(format string, args ...any) { logf("fatal: "+format+"\n", args...) os.Exit(1) } diff --git a/modules/charset/invisible/generate.go b/modules/charset/invisible/generate.go index 9dfc2533335..bd57dd6c4dd 100644 --- a/modules/charset/invisible/generate.go +++ b/modules/charset/invisible/generate.go @@ -52,7 +52,7 @@ Usage: %[1]s [-v] [-o output.go] } } -func runTemplate(t *template.Template, filename string, data interface{}) error { +func runTemplate(t *template.Template, filename string, data any) error { buf := bytes.NewBuffer(nil) if err := t.Execute(buf, data); err != nil { return fmt.Errorf("unable to execute template: %w", err) @@ -105,17 +105,17 @@ var InvisibleRanges = &unicode.RangeTable{ } `)) -func logf(format string, args ...interface{}) { +func logf(format string, args ...any) { fmt.Fprintf(os.Stderr, format+"\n", args...) } -func verbosef(format string, args ...interface{}) { +func verbosef(format string, args ...any) { if verbose { logf(format, args...) } } -func fatalf(format string, args ...interface{}) { +func fatalf(format string, args ...any) { logf("fatal: "+format+"\n", args...) os.Exit(1) } diff --git a/modules/context/access_log.go b/modules/context/access_log.go index 373574ba148..0926748ac54 100644 --- a/modules/context/access_log.go +++ b/modules/context/access_log.go @@ -23,7 +23,7 @@ type routerLoggerOptions struct { Identity *string Start *time.Time ResponseWriter http.ResponseWriter - Ctx map[string]interface{} + Ctx map[string]any RequestID *string } @@ -84,7 +84,7 @@ func AccessLogger() func(http.Handler) http.Handler { Identity: &identity, Start: &start, ResponseWriter: rw, - Ctx: map[string]interface{}{ + Ctx: map[string]any{ "RemoteAddr": req.RemoteAddr, "RemoteHost": reqHost, "Req": req, diff --git a/modules/context/api.go b/modules/context/api.go index 3c4d0204133..a367597e8a4 100644 --- a/modules/context/api.go +++ b/modules/context/api.go @@ -108,7 +108,7 @@ func (ctx *APIContext) ServerError(title string, err error) { // Error responds with an error message to client with given obj as the message. // If status is 500, also it prints error to log. -func (ctx *APIContext) Error(status int, title string, obj interface{}) { +func (ctx *APIContext) Error(status int, title string, obj any) { var message string if err, ok := obj.(error); ok { message = err.Error() @@ -265,7 +265,7 @@ func APIContexter() func(http.Handler) http.Handler { // NotFound handles 404s for APIContext // String will replace message, errors will be added to a slice -func (ctx *APIContext) NotFound(objs ...interface{}) { +func (ctx *APIContext) NotFound(objs ...any) { message := ctx.Tr("error.not_found") var errors []string for _, obj := range objs { @@ -281,7 +281,7 @@ func (ctx *APIContext) NotFound(objs ...interface{}) { } } - ctx.JSON(http.StatusNotFound, map[string]interface{}{ + ctx.JSON(http.StatusNotFound, map[string]any{ "message": message, "url": setting.API.SwaggerURL, "errors": errors, @@ -294,7 +294,7 @@ func ReferencesGitRepo(allowEmpty ...bool) func(ctx *APIContext) (cancel context return func(ctx *APIContext) (cancel context.CancelFunc) { // Empty repository does not have reference information. if ctx.Repo.Repository.IsEmpty && !(len(allowEmpty) != 0 && allowEmpty[0]) { - return + return nil } // For API calls. @@ -303,7 +303,7 @@ func ReferencesGitRepo(allowEmpty ...bool) func(ctx *APIContext) (cancel context gitRepo, err := git.OpenRepository(ctx, repoPath) if err != nil { ctx.Error(http.StatusInternalServerError, "RepoRef Invalid repo "+repoPath, err) - return + return cancel } ctx.Repo.GitRepo = gitRepo // We opened it, we should close it diff --git a/modules/context/base.go b/modules/context/base.go index 839f3e10df4..8566ef7861b 100644 --- a/modules/context/base.go +++ b/modules/context/base.go @@ -128,7 +128,7 @@ func (b *Base) Error(status int, contents ...string) { } // JSON render content as JSON -func (b *Base) JSON(status int, content interface{}) { +func (b *Base) JSON(status int, content any) { b.Resp.Header().Set("Content-Type", "application/json;charset=utf-8") b.Resp.WriteHeader(status) if err := json.NewEncoder(b.Resp).Encode(content); err != nil { diff --git a/modules/context/captcha.go b/modules/context/captcha.go index 07232e93906..a1999900c94 100644 --- a/modules/context/captcha.go +++ b/modules/context/captcha.go @@ -60,7 +60,7 @@ const ( // VerifyCaptcha verifies Captcha data // No-op if captchas are not enabled -func VerifyCaptcha(ctx *Context, tpl base.TplName, form interface{}) { +func VerifyCaptcha(ctx *Context, tpl base.TplName, form any) { if !setting.Service.EnableCaptcha { return } diff --git a/modules/context/context.go b/modules/context/context.go index 93d448fca55..47a04c989c8 100644 --- a/modules/context/context.go +++ b/modules/context/context.go @@ -32,7 +32,7 @@ import ( // Render represents a template render type Render interface { TemplateLookup(tmpl string) (templates.TemplateExecutor, error) - HTML(w io.Writer, status int, name string, data interface{}) error + HTML(w io.Writer, status int, name string, data any) error } // Context represents context of a request. @@ -69,7 +69,7 @@ func init() { // TrHTMLEscapeArgs runs ".Locale.Tr()" but pre-escapes all arguments with html.EscapeString. // This is useful if the locale message is intended to only produce HTML content. func (ctx *Context) TrHTMLEscapeArgs(msg string, args ...string) string { - trArgs := make([]interface{}, len(args)) + trArgs := make([]any, len(args)) for i, arg := range args { trArgs[i] = html.EscapeString(arg) } diff --git a/modules/context/context_response.go b/modules/context/context_response.go index 88e375986cd..bb3ccf69ce4 100644 --- a/modules/context/context_response.go +++ b/modules/context/context_response.go @@ -91,14 +91,14 @@ func (ctx *Context) HTML(status int, name base.TplName) { } // RenderToString renders the template content to a string -func (ctx *Context) RenderToString(name base.TplName, data map[string]interface{}) (string, error) { +func (ctx *Context) RenderToString(name base.TplName, data map[string]any) (string, error) { var buf strings.Builder err := ctx.Render.HTML(&buf, http.StatusOK, string(name), data) return buf.String(), err } // RenderWithErr used for page has form validation but need to prompt error to users. -func (ctx *Context) RenderWithErr(msg string, tpl base.TplName, form interface{}) { +func (ctx *Context) RenderWithErr(msg string, tpl base.TplName, form any) { if form != nil { middleware.AssignForm(form, ctx.Data) } diff --git a/modules/context/org.go b/modules/context/org.go index 355ba0ebd01..835c761372f 100644 --- a/modules/context/org.go +++ b/modules/context/org.go @@ -161,7 +161,6 @@ func HandleOrgAssignment(ctx *Context, args ...bool) { } ctx.Data["IsOrganizationOwner"] = ctx.Org.IsOwner ctx.Data["IsOrganizationMember"] = ctx.Org.IsMember - ctx.Data["IsProjectEnabled"] = true ctx.Data["IsPackageEnabled"] = setting.Packages.Enabled ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled ctx.Data["IsPublicMember"] = func(uid int64) bool { diff --git a/modules/context/package.go b/modules/context/package.go index 80520327871..be50e0a991e 100644 --- a/modules/context/package.go +++ b/modules/context/package.go @@ -33,7 +33,7 @@ type packageAssignmentCtx struct { // PackageAssignment returns a middleware to handle Context.Package assignment func PackageAssignment() func(ctx *Context) { return func(ctx *Context) { - errorFn := func(status int, title string, obj interface{}) { + errorFn := func(status int, title string, obj any) { err, ok := obj.(error) if !ok { err = fmt.Errorf("%s", obj) @@ -57,7 +57,7 @@ func PackageAssignmentAPI() func(ctx *APIContext) { } } -func packageAssignment(ctx *packageAssignmentCtx, errCb func(int, string, interface{})) *Package { +func packageAssignment(ctx *packageAssignmentCtx, errCb func(int, string, any)) *Package { pkg := &Package{ Owner: ctx.ContextUser, } @@ -108,18 +108,28 @@ func determineAccessMode(ctx *Base, pkg *Package, doer *user_model.User) (perm.A if doer != nil && !doer.IsGhost() { // 1. If user is logged in, check all team packages permissions - teams, err := organization.GetUserOrgTeams(ctx, org.ID, doer.ID) + var err error + accessMode, err = org.GetOrgUserMaxAuthorizeLevel(doer.ID) if err != nil { return accessMode, err } - for _, t := range teams { - perm := t.UnitAccessMode(ctx, unit.TypePackages) - if accessMode < perm { - accessMode = perm + // If access mode is less than write check every team for more permissions + // The minimum possible access mode is read for org members + if accessMode < perm.AccessModeWrite { + teams, err := organization.GetUserOrgTeams(ctx, org.ID, doer.ID) + if err != nil { + return accessMode, err + } + for _, t := range teams { + perm := t.UnitAccessMode(ctx, unit.TypePackages) + if accessMode < perm { + accessMode = perm + } } } - } else if organization.HasOrgOrUserVisible(ctx, pkg.Owner, doer) { - // 2. If user is non-login, check if org is visible to non-login user + } + if accessMode == perm.AccessModeNone && organization.HasOrgOrUserVisible(ctx, pkg.Owner, doer) { + // 2. If user is unauthorized or no org member, check if org is visible accessMode = perm.AccessModeRead } } else { diff --git a/modules/context/pagination.go b/modules/context/pagination.go index 5a88c92053a..68237c630c0 100644 --- a/modules/context/pagination.go +++ b/modules/context/pagination.go @@ -32,7 +32,7 @@ func (p *Pagination) AddParam(ctx *Context, paramKey, ctxKey string) { if !exists { return } - paramData := fmt.Sprintf("%v", ctx.Data[ctxKey]) // cast interface{} to string + paramData := fmt.Sprintf("%v", ctx.Data[ctxKey]) // cast any to string urlParam := fmt.Sprintf("%s=%v", url.QueryEscape(paramKey), url.QueryEscape(paramData)) p.urlParams = append(p.urlParams, urlParam) } diff --git a/modules/context/permission.go b/modules/context/permission.go index 0f72b8e244d..09343b8b50b 100644 --- a/modules/context/permission.go +++ b/modules/context/permission.go @@ -90,7 +90,7 @@ func RequireRepoReaderOr(unitTypes ...unit.Type) func(ctx *Context) { } if log.IsTrace() { var format string - var args []interface{} + var args []any if ctx.IsSigned { format = "Permission Denied: User %-v cannot read [" args = append(args, ctx.Doer) diff --git a/modules/context/private.go b/modules/context/private.go index 2e9b31140ab..8b41949f604 100644 --- a/modules/context/private.go +++ b/modules/context/private.go @@ -53,7 +53,7 @@ func (ctx *PrivateContext) Err() error { return ctx.Base.Err() } -var privateContextKey interface{} = "default_private_context" +var privateContextKey any = "default_private_context" // GetPrivateContext returns a context for Private routes func GetPrivateContext(req *http.Request) *PrivateContext { diff --git a/modules/context/repo.go b/modules/context/repo.go index e9990852512..eae71cfb7be 100644 --- a/modules/context/repo.go +++ b/modules/context/repo.go @@ -422,10 +422,10 @@ func RepoIDAssignment() func(ctx *Context) { } // RepoAssignment returns a middleware to handle repository assignment -func RepoAssignment(ctx *Context) (cancel context.CancelFunc) { +func RepoAssignment(ctx *Context) context.CancelFunc { if _, repoAssignmentOnce := ctx.Data["repoAssignmentExecuted"]; repoAssignmentOnce { log.Trace("RepoAssignment was exec already, skipping second call ...") - return + return nil } ctx.Data["repoAssignmentExecuted"] = true @@ -453,7 +453,7 @@ func RepoAssignment(ctx *Context) (cancel context.CancelFunc) { // https://github.com/golang/go/issues/19760 if ctx.FormString("go-get") == "1" { EarlyResponseForGoGetMeta(ctx) - return + return nil } if redirectUserID, err := user_model.LookupUserRedirect(userName); err == nil { @@ -466,7 +466,7 @@ func RepoAssignment(ctx *Context) (cancel context.CancelFunc) { } else { ctx.ServerError("GetUserByName", err) } - return + return nil } } ctx.Repo.Owner = owner @@ -490,7 +490,7 @@ func RepoAssignment(ctx *Context) (cancel context.CancelFunc) { redirectPath += "?" + ctx.Req.URL.RawQuery } ctx.Redirect(path.Join(setting.AppSubURL, redirectPath)) - return + return nil } // Get repository. @@ -503,7 +503,7 @@ func RepoAssignment(ctx *Context) (cancel context.CancelFunc) { } else if repo_model.IsErrRedirectNotExist(err) { if ctx.FormString("go-get") == "1" { EarlyResponseForGoGetMeta(ctx) - return + return nil } ctx.NotFound("GetRepositoryByName", nil) } else { @@ -512,13 +512,13 @@ func RepoAssignment(ctx *Context) (cancel context.CancelFunc) { } else { ctx.ServerError("GetRepositoryByName", err) } - return + return nil } repo.Owner = owner repoAssignment(ctx, repo) if ctx.Written() { - return + return nil } ctx.Repo.RepoLink = repo.Link() @@ -542,12 +542,12 @@ func RepoAssignment(ctx *Context) (cancel context.CancelFunc) { }) if err != nil { ctx.ServerError("GetReleaseCountByRepoID", err) - return + return nil } ctx.Data["NumReleases"], err = repo_model.GetReleaseCountByRepoID(ctx, ctx.Repo.Repository.ID, repo_model.FindReleasesOptions{}) if err != nil { ctx.ServerError("GetReleaseCountByRepoID", err) - return + return nil } ctx.Data["Title"] = owner.Name + "/" + repo.Name @@ -563,14 +563,14 @@ func RepoAssignment(ctx *Context) (cancel context.CancelFunc) { canSignedUserFork, err := repo_module.CanUserForkRepo(ctx.Doer, ctx.Repo.Repository) if err != nil { ctx.ServerError("CanUserForkRepo", err) - return + return nil } ctx.Data["CanSignedUserFork"] = canSignedUserFork userAndOrgForks, err := repo_model.GetForksByUserAndOrgs(ctx, ctx.Doer, ctx.Repo.Repository) if err != nil { ctx.ServerError("GetForksByUserAndOrgs", err) - return + return nil } ctx.Data["UserAndOrgForks"] = userAndOrgForks @@ -604,14 +604,14 @@ func RepoAssignment(ctx *Context) (cancel context.CancelFunc) { if repo.IsFork { RetrieveBaseRepo(ctx, repo) if ctx.Written() { - return + return nil } } if repo.IsGenerated() { RetrieveTemplateRepo(ctx, repo) if ctx.Written() { - return + return nil } } @@ -623,7 +623,7 @@ func RepoAssignment(ctx *Context) (cancel context.CancelFunc) { if !isHomeOrSettings { ctx.Redirect(ctx.Repo.RepoLink) } - return + return nil } gitRepo, err := git.OpenRepository(ctx, repo_model.RepoPath(userName, repoName)) @@ -636,10 +636,10 @@ func RepoAssignment(ctx *Context) (cancel context.CancelFunc) { if !isHomeOrSettings { ctx.Redirect(ctx.Repo.RepoLink) } - return + return nil } ctx.ServerError("RepoAssignment Invalid repo "+repo_model.RepoPath(userName, repoName), err) - return + return nil } if ctx.Repo.GitRepo != nil { ctx.Repo.GitRepo.Close() @@ -647,7 +647,7 @@ func RepoAssignment(ctx *Context) (cancel context.CancelFunc) { ctx.Repo.GitRepo = gitRepo // We opened it, we should close it - cancel = func() { + cancel := func() { // If it's been set to nil then assume someone else has closed it. if ctx.Repo.GitRepo != nil { ctx.Repo.GitRepo.Close() @@ -657,13 +657,13 @@ func RepoAssignment(ctx *Context) (cancel context.CancelFunc) { // Stop at this point when the repo is empty. if ctx.Repo.Repository.IsEmpty { ctx.Data["BranchName"] = ctx.Repo.Repository.DefaultBranch - return + return cancel } tags, err := repo_model.GetTagNamesByRepoID(ctx, ctx.Repo.Repository.ID) if err != nil { ctx.ServerError("GetTagNamesByRepoID", err) - return + return cancel } ctx.Data["Tags"] = tags @@ -677,7 +677,7 @@ func RepoAssignment(ctx *Context) (cancel context.CancelFunc) { branchesTotal, err := git_model.CountBranches(ctx, branchOpts) if err != nil { ctx.ServerError("CountBranches", err) - return + return cancel } // non empty repo should have at least 1 branch, so this repository's branches haven't been synced yet @@ -685,7 +685,7 @@ func RepoAssignment(ctx *Context) (cancel context.CancelFunc) { branchesTotal, err = repo_module.SyncRepoBranches(ctx, ctx.Repo.Repository.ID, 0) if err != nil { ctx.ServerError("SyncRepoBranches", err) - return + return cancel } } @@ -694,7 +694,7 @@ func RepoAssignment(ctx *Context) (cancel context.CancelFunc) { brs, err := git_model.FindBranchNames(ctx, branchOpts) if err != nil { ctx.ServerError("GetBranches", err) - return + return cancel } // always put default branch on the top ctx.Data["Branches"] = append(branchOpts.ExcludeBranchNames, brs...) @@ -741,12 +741,12 @@ func RepoAssignment(ctx *Context) (cancel context.CancelFunc) { repoTransfer, err := models.GetPendingRepositoryTransfer(ctx, ctx.Repo.Repository) if err != nil { ctx.ServerError("GetPendingRepositoryTransfer", err) - return + return cancel } if err := repoTransfer.LoadAttributes(ctx); err != nil { ctx.ServerError("LoadRecipient", err) - return + return cancel } ctx.Data["RepoTransfer"] = repoTransfer @@ -894,7 +894,7 @@ func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context ctx.Repo.IsViewBranch = true ctx.Repo.BranchName = ctx.Repo.Repository.DefaultBranch ctx.Data["TreePath"] = "" - return + return nil } var ( @@ -907,7 +907,7 @@ func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context ctx.Repo.GitRepo, err = git.OpenRepository(ctx, repoPath) if err != nil { ctx.ServerError("RepoRef Invalid repo "+repoPath, err) - return + return nil } // We opened it, we should close it cancel = func() { @@ -944,7 +944,7 @@ func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context ctx.Repo.Repository.MarkAsBrokenEmpty() } else { ctx.ServerError("GetBranchCommit", err) - return + return cancel } ctx.Repo.IsViewBranch = true } else { @@ -956,7 +956,7 @@ func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context ctx.Flash.Info(ctx.Tr("repo.branch.renamed", refName, renamedBranchName)) link := setting.AppSubURL + strings.Replace(ctx.Req.URL.EscapedPath(), util.PathEscapeSegments(refName), util.PathEscapeSegments(renamedBranchName), 1) ctx.Redirect(link) - return + return cancel } if refType.RefTypeIncludesBranches() && ctx.Repo.GitRepo.IsBranchExist(refName) { @@ -966,7 +966,7 @@ func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(refName) if err != nil { ctx.ServerError("GetBranchCommit", err) - return + return cancel } ctx.Repo.CommitID = ctx.Repo.Commit.ID.String() @@ -978,10 +978,10 @@ func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context if err != nil { if git.IsErrNotExist(err) { ctx.NotFound("GetTagCommit", err) - return + return cancel } ctx.ServerError("GetTagCommit", err) - return + return cancel } ctx.Repo.CommitID = ctx.Repo.Commit.ID.String() } else if len(refName) >= 7 && len(refName) <= git.SHAFullLength { @@ -991,7 +991,7 @@ func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetCommit(refName) if err != nil { ctx.NotFound("GetCommit", err) - return + return cancel } // If short commit ID add canonical link header if len(refName) < git.SHAFullLength { @@ -1000,10 +1000,10 @@ func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context } } else { if len(ignoreNotExistErr) > 0 && ignoreNotExistErr[0] { - return + return cancel } ctx.NotFound("RepoRef invalid repo", fmt.Errorf("branch or tag not exist: %s", refName)) - return + return cancel } if refType == RepoRefLegacy { @@ -1015,7 +1015,7 @@ func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context util.PathEscapeSegments(prefix), ctx.Repo.BranchNameSubURL(), util.PathEscapeSegments(ctx.Repo.TreePath))) - return + return cancel } } @@ -1033,7 +1033,7 @@ func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context ctx.Repo.CommitsCount, err = ctx.Repo.GetCommitsCount() if err != nil { ctx.ServerError("GetCommitsCount", err) - return + return cancel } ctx.Data["CommitsCount"] = ctx.Repo.CommitsCount ctx.Repo.GitRepo.LastCommitCache = git.NewLastCommitCache(ctx.Repo.CommitsCount, ctx.Repo.Repository.FullName(), ctx.Repo.GitRepo, cache.GetCache()) diff --git a/modules/doctor/fix16961.go b/modules/doctor/fix16961.go index ea14a9b2c41..562c78dd765 100644 --- a/modules/doctor/fix16961.go +++ b/modules/doctor/fix16961.go @@ -6,6 +6,7 @@ package doctor import ( "bytes" "context" + "errors" "fmt" "code.gitea.io/gitea/models/db" @@ -40,12 +41,12 @@ func parseBool16961(bs []byte) (bool, error) { func fixUnitConfig16961(bs []byte, cfg *repo_model.UnitConfig) (fixed bool, err error) { err = json.UnmarshalHandleDoubleEncode(bs, &cfg) if err == nil { - return + return false, nil } // Handle #16961 if string(bs) != "&{}" && len(bs) != 0 { - return + return false, err } return true, nil @@ -54,14 +55,14 @@ func fixUnitConfig16961(bs []byte, cfg *repo_model.UnitConfig) (fixed bool, err func fixExternalWikiConfig16961(bs []byte, cfg *repo_model.ExternalWikiConfig) (fixed bool, err error) { err = json.UnmarshalHandleDoubleEncode(bs, &cfg) if err == nil { - return + return false, nil } if len(bs) < 3 { - return + return false, err } if bs[0] != '&' || bs[1] != '{' || bs[len(bs)-1] != '}' { - return + return false, err } cfg.ExternalWikiURL = string(bs[2 : len(bs)-1]) return true, nil @@ -70,20 +71,20 @@ func fixExternalWikiConfig16961(bs []byte, cfg *repo_model.ExternalWikiConfig) ( func fixExternalTrackerConfig16961(bs []byte, cfg *repo_model.ExternalTrackerConfig) (fixed bool, err error) { err = json.UnmarshalHandleDoubleEncode(bs, &cfg) if err == nil { - return + return false, nil } // Handle #16961 if len(bs) < 3 { - return + return false, err } if bs[0] != '&' || bs[1] != '{' || bs[len(bs)-1] != '}' { - return + return false, err } parts := bytes.Split(bs[2:len(bs)-1], []byte{' '}) if len(parts) != 3 { - return + return false, err } cfg.ExternalTrackerURL = string(bytes.Join(parts[:len(parts)-2], []byte{' '})) @@ -95,16 +96,16 @@ func fixExternalTrackerConfig16961(bs []byte, cfg *repo_model.ExternalTrackerCon func fixPullRequestsConfig16961(bs []byte, cfg *repo_model.PullRequestsConfig) (fixed bool, err error) { err = json.UnmarshalHandleDoubleEncode(bs, &cfg) if err == nil { - return + return false, nil } // Handle #16961 if len(bs) < 3 { - return + return false, err } if bs[0] != '&' || bs[1] != '{' || bs[len(bs)-1] != '}' { - return + return false, err } // PullRequestsConfig was the following in 1.14 @@ -123,37 +124,37 @@ func fixPullRequestsConfig16961(bs []byte, cfg *repo_model.PullRequestsConfig) ( // DefaultMergeStyle MergeStyle parts := bytes.Split(bs[2:len(bs)-1], []byte{' '}) if len(parts) < 7 { - return + return false, err } var parseErr error cfg.IgnoreWhitespaceConflicts, parseErr = parseBool16961(parts[0]) if parseErr != nil { - return + return false, errors.Join(err, parseErr) } cfg.AllowMerge, parseErr = parseBool16961(parts[1]) if parseErr != nil { - return + return false, errors.Join(err, parseErr) } cfg.AllowRebase, parseErr = parseBool16961(parts[2]) if parseErr != nil { - return + return false, errors.Join(err, parseErr) } cfg.AllowRebaseMerge, parseErr = parseBool16961(parts[3]) if parseErr != nil { - return + return false, errors.Join(err, parseErr) } cfg.AllowSquash, parseErr = parseBool16961(parts[4]) if parseErr != nil { - return + return false, errors.Join(err, parseErr) } cfg.AllowManualMerge, parseErr = parseBool16961(parts[5]) if parseErr != nil { - return + return false, errors.Join(err, parseErr) } cfg.AutodetectManualMerge, parseErr = parseBool16961(parts[6]) if parseErr != nil { - return + return false, errors.Join(err, parseErr) } // 1.14 unit @@ -162,12 +163,12 @@ func fixPullRequestsConfig16961(bs []byte, cfg *repo_model.PullRequestsConfig) ( } if len(parts) < 9 { - return + return false, err } cfg.DefaultDeleteBranchAfterMerge, parseErr = parseBool16961(parts[7]) if parseErr != nil { - return + return false, errors.Join(err, parseErr) } cfg.DefaultMergeStyle = repo_model.MergeStyle(string(bytes.Join(parts[8:], []byte{' '}))) @@ -177,34 +178,34 @@ func fixPullRequestsConfig16961(bs []byte, cfg *repo_model.PullRequestsConfig) ( func fixIssuesConfig16961(bs []byte, cfg *repo_model.IssuesConfig) (fixed bool, err error) { err = json.UnmarshalHandleDoubleEncode(bs, &cfg) if err == nil { - return + return false, nil } // Handle #16961 if len(bs) < 3 { - return + return false, err } if bs[0] != '&' || bs[1] != '{' || bs[len(bs)-1] != '}' { - return + return false, err } parts := bytes.Split(bs[2:len(bs)-1], []byte{' '}) if len(parts) != 3 { - return + return false, err } var parseErr error cfg.EnableTimetracker, parseErr = parseBool16961(parts[0]) if parseErr != nil { - return + return false, errors.Join(err, parseErr) } cfg.AllowOnlyContributorsToTrackTime, parseErr = parseBool16961(parts[1]) if parseErr != nil { - return + return false, errors.Join(err, parseErr) } cfg.EnableDependencies, parseErr = parseBool16961(parts[2]) if parseErr != nil { - return + return false, errors.Join(err, parseErr) } return true, nil } diff --git a/modules/doctor/lfs.go b/modules/doctor/lfs.go index 64ee4c40bfe..5f110b8f97d 100644 --- a/modules/doctor/lfs.go +++ b/modules/doctor/lfs.go @@ -31,8 +31,8 @@ func garbageCollectLFSCheck(ctx context.Context, logger log.Logger, autofix bool } if err := repository.GarbageCollectLFSMetaObjects(ctx, repository.GarbageCollectLFSMetaObjectsOptions{ - Logger: logger, - AutoFix: autofix, + LogDetail: logger.Info, + AutoFix: autofix, // Only attempt to garbage collect lfs meta objects older than a week as the order of git lfs upload // and git object upload is not necessarily guaranteed. It's possible to imagine a situation whereby // an LFS object is uploaded but the git branch is not uploaded immediately, or there are some rapid diff --git a/modules/eventsource/event.go b/modules/eventsource/event.go index d5d2f323afe..ebcca509034 100644 --- a/modules/eventsource/event.go +++ b/modules/eventsource/event.go @@ -15,7 +15,7 @@ import ( func wrapNewlines(w io.Writer, prefix, value []byte) (sum int64, err error) { if len(value) == 0 { - return + return 0, nil } var n int last := 0 @@ -23,24 +23,24 @@ func wrapNewlines(w io.Writer, prefix, value []byte) (sum int64, err error) { n, err = w.Write(prefix) sum += int64(n) if err != nil { - return + return sum, err } n, err = w.Write(value[last : last+j+1]) sum += int64(n) if err != nil { - return + return sum, err } last += j + 1 } n, err = w.Write(prefix) sum += int64(n) if err != nil { - return + return sum, err } n, err = w.Write(value[last:]) sum += int64(n) if err != nil { - return + return sum, err } n, err = w.Write([]byte("\n")) sum += int64(n) @@ -51,8 +51,8 @@ func wrapNewlines(w io.Writer, prefix, value []byte) (sum int64, err error) { type Event struct { // Name represents the value of the event: tag in the stream Name string - // Data is either JSONified []byte or interface{} that can be JSONd - Data interface{} + // Data is either JSONified []byte or any that can be JSONd + Data any // ID represents the ID of an event ID string // Retry tells the receiver only to attempt to reconnect to the source after this time diff --git a/modules/git/batch_reader.go b/modules/git/batch_reader.go index 75539c0d0a9..891e8a2384c 100644 --- a/modules/git/batch_reader.go +++ b/modules/git/batch_reader.go @@ -148,27 +148,25 @@ func CatFileBatch(ctx context.Context, repoPath string) (WriteCloserError, *bufi func ReadBatchLine(rd *bufio.Reader) (sha []byte, typ string, size int64, err error) { typ, err = rd.ReadString('\n') if err != nil { - return + return sha, typ, size, err } if len(typ) == 1 { typ, err = rd.ReadString('\n') if err != nil { - return + return sha, typ, size, err } } idx := strings.IndexByte(typ, ' ') if idx < 0 { log.Debug("missing space typ: %s", typ) - err = ErrNotExist{ID: string(sha)} - return + return sha, typ, size, ErrNotExist{ID: string(sha)} } sha = []byte(typ[:idx]) typ = typ[idx+1:] idx = strings.IndexByte(typ, ' ') if idx < 0 { - err = ErrNotExist{ID: string(sha)} - return + return sha, typ, size, ErrNotExist{ID: string(sha)} } sizeStr := typ[idx+1 : len(typ)-1] @@ -285,14 +283,12 @@ func ParseTreeLine(rd *bufio.Reader, modeBuf, fnameBuf, shaBuf []byte) (mode, fn // Read the Mode & fname readBytes, err = rd.ReadSlice('\x00') if err != nil { - return + return mode, fname, sha, n, err } idx := bytes.IndexByte(readBytes, ' ') if idx < 0 { log.Debug("missing space in readBytes ParseTreeLine: %s", readBytes) - - err = &ErrNotExist{} - return + return mode, fname, sha, n, &ErrNotExist{} } n += idx + 1 @@ -319,7 +315,7 @@ func ParseTreeLine(rd *bufio.Reader, modeBuf, fnameBuf, shaBuf []byte) (mode, fn } n += len(fnameBuf) if err != nil { - return + return mode, fname, sha, n, err } fnameBuf = fnameBuf[:len(fnameBuf)-1] fname = fnameBuf @@ -331,7 +327,7 @@ func ParseTreeLine(rd *bufio.Reader, modeBuf, fnameBuf, shaBuf []byte) (mode, fn read, err = rd.Read(shaBuf[idx:20]) n += read if err != nil { - return + return mode, fname, sha, n, err } idx += read } diff --git a/modules/git/command.go b/modules/git/command.go index ac013d4ea1c..c38fd046966 100644 --- a/modules/git/command.go +++ b/modules/git/command.go @@ -301,6 +301,8 @@ func (c *Command) Run(opts *RunOpts) error { } defer finished() + startTime := time.Now() + cmd := exec.CommandContext(ctx, c.prog, c.args...) if opts.Env == nil { cmd.Env = os.Environ() @@ -327,7 +329,13 @@ func (c *Command) Run(opts *RunOpts) error { } } - if err := cmd.Wait(); err != nil && ctx.Err() != context.DeadlineExceeded { + err := cmd.Wait() + elapsed := time.Since(startTime) + if elapsed > time.Second { + log.Debug("slow git.Command.Run: %s (%s)", c, elapsed) + } + + if err != nil && ctx.Err() != context.DeadlineExceeded { return err } diff --git a/modules/git/commit.go b/modules/git/commit.go index ff654f394d2..729e3b4672a 100644 --- a/modules/git/commit.go +++ b/modules/git/commit.go @@ -435,7 +435,7 @@ func (c *Commit) GetBranchName() (string, error) { // LoadBranchName load branch name for commit func (c *Commit) LoadBranchName() (err error) { if len(c.Branch) != 0 { - return + return nil } c.Branch, err = c.GetBranchName() diff --git a/modules/git/commit_info_gogit.go b/modules/git/commit_info_gogit.go index 20db5691ebe..c61d27993cd 100644 --- a/modules/git/commit_info_gogit.go +++ b/modules/git/commit_info_gogit.go @@ -177,7 +177,7 @@ func GetLastCommitForPaths(ctx context.Context, cache *LastCommitCache, c cgobje refSha := c.ID().String() // We do a tree traversal with nodes sorted by commit time - heap := binaryheap.NewWith(func(a, b interface{}) int { + heap := binaryheap.NewWith(func(a, b any) int { if a.(*commitAndPaths).commit.CommitTime().Before(b.(*commitAndPaths).commit.CommitTime()) { return 1 } diff --git a/modules/git/foreachref/parser_test.go b/modules/git/foreachref/parser_test.go index 5468318ca80..7a37ced3565 100644 --- a/modules/git/foreachref/parser_test.go +++ b/modules/git/foreachref/parser_test.go @@ -217,7 +217,7 @@ func TestParser(t *testing.T) { } } -func pretty(v interface{}) string { +func pretty(v any) string { data, err := json.MarshalIndent(v, "", " ") if err != nil { // shouldn't happen diff --git a/modules/git/git.go b/modules/git/git.go index f9c0ed669f4..12d2f94e514 100644 --- a/modules/git/git.go +++ b/modules/git/git.go @@ -114,7 +114,7 @@ func VersionInfo() string { return "(git not found)" } format := "%s" - args := []interface{}{gitVersion.Original()} + args := []any{gitVersion.Original()} // Since git wire protocol has been released from git v2.18 if setting.Git.EnableAutoGitWireProtocol && CheckGitVersionAtLeast("2.18") == nil { format += ", Wire Protocol %s Enabled" @@ -171,7 +171,7 @@ func InitFull(ctx context.Context) (err error) { } if err = InitSimple(ctx); err != nil { - return + return err } // when git works with gnupg (commit signing), there should be a stable home for gnupg commands @@ -188,7 +188,6 @@ func InitFull(ctx context.Context) (err error) { if CheckGitVersionAtLeast("2.9") == nil { globalCommandArgs = append(globalCommandArgs, "-c", "credential.helper=") } - SupportProcReceive = CheckGitVersionAtLeast("2.29") == nil if setting.LFS.StartServer { diff --git a/modules/git/last_commit_cache.go b/modules/git/last_commit_cache.go index 984561b2c67..20bc7960855 100644 --- a/modules/git/last_commit_cache.go +++ b/modules/git/last_commit_cache.go @@ -15,9 +15,9 @@ import ( // Cache represents a caching interface type Cache interface { // Put puts value into cache with key and expire time. - Put(key string, val interface{}, timeout int64) error + Put(key string, val any, timeout int64) error // Get gets cached value by given key. - Get(key string) interface{} + Get(key string) any } func getCacheKey(repoPath, commitID, entryPath string) string { diff --git a/modules/git/repo_base_nogogit.go b/modules/git/repo_base_nogogit.go index e0f2d563b3e..414e4eb1a83 100644 --- a/modules/git/repo_base_nogogit.go +++ b/modules/git/repo_base_nogogit.go @@ -87,7 +87,7 @@ func (repo *Repository) CatFileBatchCheck(ctx context.Context) (WriteCloserError // Close this repository, in particular close the underlying gogitStorage if this is not nil func (repo *Repository) Close() (err error) { if repo == nil { - return + return nil } if repo.batchCancel != nil { repo.batchCancel() diff --git a/modules/git/repo_index.go b/modules/git/repo_index.go index 5ff2a2e4fc9..34dd1e01291 100644 --- a/modules/git/repo_index.go +++ b/modules/git/repo_index.go @@ -48,7 +48,7 @@ func (repo *Repository) readTreeToIndex(id SHA1, indexFilename ...string) error func (repo *Repository) ReadTreeToTemporaryIndex(treeish string) (filename, tmpDir string, cancel context.CancelFunc, err error) { tmpDir, err = os.MkdirTemp("", "index") if err != nil { - return + return filename, tmpDir, cancel, err } filename = filepath.Join(tmpDir, ".tmp-index") diff --git a/modules/git/signature_nogogit.go b/modules/git/signature_nogogit.go index a203d5ce6d3..25277f99d52 100644 --- a/modules/git/signature_nogogit.go +++ b/modules/git/signature_nogogit.go @@ -43,12 +43,13 @@ func (s *Signature) Decode(b []byte) { // // but without the "author " at the beginning (this method should) // be used for author and committer. +// FIXME: there are a lot of "return sig, err" (but the err is also nil), that's the old behavior, to avoid breaking func newSignatureFromCommitline(line []byte) (sig *Signature, err error) { sig = new(Signature) emailStart := bytes.LastIndexByte(line, '<') emailEnd := bytes.LastIndexByte(line, '>') if emailStart == -1 || emailEnd == -1 || emailEnd < emailStart { - return + return sig, err } if emailStart > 0 { // Empty name has already occurred, even if it shouldn't @@ -58,7 +59,7 @@ func newSignatureFromCommitline(line []byte) (sig *Signature, err error) { hasTime := emailEnd+2 < len(line) if !hasTime { - return + return sig, err } // Check date format. @@ -66,7 +67,7 @@ func newSignatureFromCommitline(line []byte) (sig *Signature, err error) { if firstChar >= 48 && firstChar <= 57 { idx := bytes.IndexByte(line[emailEnd+2:], ' ') if idx < 0 { - return + return sig, err } timestring := string(line[emailEnd+2 : emailEnd+2+idx]) @@ -75,14 +76,14 @@ func newSignatureFromCommitline(line []byte) (sig *Signature, err error) { idx += emailEnd + 3 if idx >= len(line) || idx+5 > len(line) { - return + return sig, err } timezone := string(line[idx : idx+5]) tzhours, err1 := strconv.ParseInt(timezone[0:3], 10, 64) tzmins, err2 := strconv.ParseInt(timezone[3:], 10, 64) if err1 != nil || err2 != nil { - return + return sig, err } if tzhours < 0 { tzmins *= -1 @@ -92,7 +93,7 @@ func newSignatureFromCommitline(line []byte) (sig *Signature, err error) { } else { sig.When, err = time.Parse(GitTimeLayout, string(line[emailEnd+2:])) if err != nil { - return + return sig, err } } return sig, err diff --git a/modules/git/utils.go b/modules/git/utils.go index b44363820dc..0d67412707e 100644 --- a/modules/git/utils.go +++ b/modules/git/utils.go @@ -15,17 +15,17 @@ import ( // ObjectCache provides thread-safe cache operations. type ObjectCache struct { lock sync.RWMutex - cache map[string]interface{} + cache map[string]any } func newObjectCache() *ObjectCache { return &ObjectCache{ - cache: make(map[string]interface{}, 10), + cache: make(map[string]any, 10), } } // Set add obj to cache -func (oc *ObjectCache) Set(id string, obj interface{}) { +func (oc *ObjectCache) Set(id string, obj any) { oc.lock.Lock() defer oc.lock.Unlock() @@ -33,7 +33,7 @@ func (oc *ObjectCache) Set(id string, obj interface{}) { } // Get get cached obj by id -func (oc *ObjectCache) Get(id string) (interface{}, bool) { +func (oc *ObjectCache) Get(id string) (any, bool) { oc.lock.RLock() defer oc.lock.RUnlock() diff --git a/modules/graceful/manager.go b/modules/graceful/manager.go index 3604c0a3f58..068de21076f 100644 --- a/modules/graceful/manager.go +++ b/modules/graceful/manager.go @@ -283,7 +283,7 @@ func (g *Manager) Err() error { } // Value allows the manager to be viewed as a context.Context done at Terminate -func (g *Manager) Value(key interface{}) interface{} { +func (g *Manager) Value(key any) any { return g.managerCtx.Value(key) } diff --git a/modules/html/html.go b/modules/html/html.go index 3219b939f40..6cb6b847ef1 100644 --- a/modules/html/html.go +++ b/modules/html/html.go @@ -5,7 +5,7 @@ package html // ParseSizeAndClass get size and class from string with default values // If present, "others" expects the new size first and then the classes to use -func ParseSizeAndClass(defaultSize int, defaultClass string, others ...interface{}) (int, string) { +func ParseSizeAndClass(defaultSize int, defaultClass string, others ...any) (int, string) { if len(others) == 0 { return defaultSize, defaultClass } diff --git a/modules/httpcache/httpcache.go b/modules/httpcache/httpcache.go index 001ac064153..b57b3218322 100644 --- a/modules/httpcache/httpcache.go +++ b/modules/httpcache/httpcache.go @@ -70,11 +70,11 @@ func checkIfNoneMatchIsValid(req *http.Request, etag string) bool { // HandleGenericETagTimeCache handles ETag-based caching with Last-Modified caching for a HTTP request. // It returns true if the request was handled. -func HandleGenericETagTimeCache(req *http.Request, w http.ResponseWriter, etag string, lastModified time.Time) (handled bool) { +func HandleGenericETagTimeCache(req *http.Request, w http.ResponseWriter, etag string, lastModified *time.Time) (handled bool) { if len(etag) > 0 { w.Header().Set("Etag", etag) } - if !lastModified.IsZero() { + if lastModified != nil && !lastModified.IsZero() { w.Header().Set("Last-Modified", lastModified.Format(http.TimeFormat)) } @@ -84,7 +84,7 @@ func HandleGenericETagTimeCache(req *http.Request, w http.ResponseWriter, etag s return true } } - if !lastModified.IsZero() { + if lastModified != nil && !lastModified.IsZero() { ifModifiedSince := req.Header.Get("If-Modified-Since") if ifModifiedSince != "" { t, err := time.Parse(http.TimeFormat, ifModifiedSince) diff --git a/modules/httplib/request.go b/modules/httplib/request.go index e904d77e149..880d7ad3cbe 100644 --- a/modules/httplib/request.go +++ b/modules/httplib/request.go @@ -101,7 +101,7 @@ func (r *Request) Param(key, value string) *Request { // Body adds request raw body. // it supports string and []byte. -func (r *Request) Body(data interface{}) *Request { +func (r *Request) Body(data any) *Request { switch t := data.(type) { case string: bf := bytes.NewBufferString(t) diff --git a/modules/httplib/serve.go b/modules/httplib/serve.go index 12d68c2d656..a193ed901cf 100644 --- a/modules/httplib/serve.go +++ b/modules/httplib/serve.go @@ -206,7 +206,7 @@ func ServeContentByReader(r *http.Request, w http.ResponseWriter, filePath strin _, _ = io.CopyN(w, reader, partialLength) // just like http.ServeContent, not necessary to handle the error } -func ServeContentByReadSeeker(r *http.Request, w http.ResponseWriter, filePath string, modTime time.Time, reader io.ReadSeeker) { +func ServeContentByReadSeeker(r *http.Request, w http.ResponseWriter, filePath string, modTime *time.Time, reader io.ReadSeeker) { buf := make([]byte, mimeDetectionBufferLen) n, err := util.ReadAtMost(reader, buf) if err != nil { @@ -221,5 +221,8 @@ func ServeContentByReadSeeker(r *http.Request, w http.ResponseWriter, filePath s buf = buf[:n] } setServeHeadersByFile(r, w, filePath, buf) - http.ServeContent(w, r, path.Base(filePath), modTime, reader) + if modTime == nil { + modTime = &time.Time{} + } + http.ServeContent(w, r, path.Base(filePath), *modTime, reader) } diff --git a/modules/httplib/serve_test.go b/modules/httplib/serve_test.go index fed4611d217..c2229dffe96 100644 --- a/modules/httplib/serve_test.go +++ b/modules/httplib/serve_test.go @@ -11,7 +11,6 @@ import ( "os" "strings" "testing" - "time" "github.com/stretchr/testify/assert" ) @@ -78,7 +77,7 @@ func TestServeContentByReadSeeker(t *testing.T) { defer seekReader.Close() w := httptest.NewRecorder() - ServeContentByReadSeeker(r, w, "test", time.Time{}, seekReader) + ServeContentByReadSeeker(r, w, "test", nil, seekReader) assert.Equal(t, expectedStatusCode, w.Code) if expectedStatusCode == http.StatusPartialContent || expectedStatusCode == http.StatusOK { assert.Equal(t, fmt.Sprint(len(expectedContent)), w.Header().Get("Content-Length")) diff --git a/modules/indexer/code/bleve/bleve.go b/modules/indexer/code/bleve/bleve.go index 33cc4e02b51..1e34226e8df 100644 --- a/modules/indexer/code/bleve/bleve.go +++ b/modules/indexer/code/bleve/bleve.go @@ -51,7 +51,7 @@ func numericEqualityQuery(value int64, field string) *query.NumericRangeQuery { } func addUnicodeNormalizeTokenFilter(m *mapping.IndexMappingImpl) error { - return m.AddCustomTokenFilter(unicodeNormalizeName, map[string]interface{}{ + return m.AddCustomTokenFilter(unicodeNormalizeName, map[string]any{ "type": unicodenorm.Name, "form": unicodenorm.NFC, }) @@ -101,7 +101,7 @@ func generateBleveIndexMapping() (mapping.IndexMapping, error) { mapping := bleve.NewIndexMapping() if err := addUnicodeNormalizeTokenFilter(mapping); err != nil { return nil, err - } else if err := mapping.AddCustomAnalyzer(repoIndexerAnalyzer, map[string]interface{}{ + } else if err := mapping.AddCustomAnalyzer(repoIndexerAnalyzer, map[string]any{ "type": analyzer_custom.Name, "char_filters": []string{}, "tokenizer": unicode.Name, diff --git a/modules/indexer/code/elasticsearch/elasticsearch.go b/modules/indexer/code/elasticsearch/elasticsearch.go index 88054585cd2..e7e3429a39a 100644 --- a/modules/indexer/code/elasticsearch/elasticsearch.go +++ b/modules/indexer/code/elasticsearch/elasticsearch.go @@ -133,7 +133,7 @@ func (b *Indexer) addUpdate(ctx context.Context, batchWriter git.WriteCloserErro elastic.NewBulkIndexRequest(). Index(b.inner.VersionedIndexName()). Id(id). - Doc(map[string]interface{}{ + Doc(map[string]any{ "repo_id": repo.ID, "content": string(charset.ToUTF8DropErrors(fileContents)), "commit_id": sha, @@ -234,7 +234,7 @@ func convertResult(searchResult *elastic.SearchResult, kw string, pageSize int) } repoID, fileName := internal.ParseIndexerID(hit.Id) - res := make(map[string]interface{}) + res := make(map[string]any) if err := json.Unmarshal(hit.Source, &res); err != nil { return 0, nil, nil, err } @@ -285,7 +285,7 @@ func (b *Indexer) Search(ctx context.Context, repoIDs []int64, language, keyword query := elastic.NewBoolQuery() query = query.Must(kwQuery) if len(repoIDs) > 0 { - repoStrs := make([]interface{}, 0, len(repoIDs)) + repoStrs := make([]any, 0, len(repoIDs)) for _, repoID := range repoIDs { repoStrs = append(repoStrs, repoID) } diff --git a/modules/indexer/internal/bleve/batch.go b/modules/indexer/internal/bleve/batch.go index 77675147b2c..ed5ef0773c8 100644 --- a/modules/indexer/internal/bleve/batch.go +++ b/modules/indexer/internal/bleve/batch.go @@ -27,7 +27,7 @@ func NewFlushingBatch(index bleve.Index, maxBatchSize int) *FlushingBatch { } // Index add a new index to batch -func (b *FlushingBatch) Index(id string, data interface{}) error { +func (b *FlushingBatch) Index(id string, data any) error { if err := b.batch.Index(id, data); err != nil { return err } diff --git a/modules/indexer/issues/bleve/bleve.go b/modules/indexer/issues/bleve/bleve.go index bb0bc4b04a4..c368a67ab58 100644 --- a/modules/indexer/issues/bleve/bleve.go +++ b/modules/indexer/issues/bleve/bleve.go @@ -23,7 +23,7 @@ import ( const ( issueIndexerAnalyzer = "issueIndexer" issueIndexerDocType = "issueIndexerDocType" - issueIndexerLatestVersion = 2 + issueIndexerLatestVersion = 3 ) // numericEqualityQuery a numeric equality query for the given value and field @@ -45,7 +45,7 @@ func newMatchPhraseQuery(matchPhrase, field, analyzer string) *query.MatchPhrase const unicodeNormalizeName = "unicodeNormalize" func addUnicodeNormalizeTokenFilter(m *mapping.IndexMappingImpl) error { - return m.AddCustomTokenFilter(unicodeNormalizeName, map[string]interface{}{ + return m.AddCustomTokenFilter(unicodeNormalizeName, map[string]any{ "type": unicodenorm.Name, "form": unicodenorm.NFC, }) @@ -67,19 +67,20 @@ func generateIssueIndexMapping() (mapping.IndexMapping, error) { docMapping := bleve.NewDocumentMapping() numericFieldMapping := bleve.NewNumericFieldMapping() + numericFieldMapping.Store = false numericFieldMapping.IncludeInAll = false - docMapping.AddFieldMappingsAt("RepoID", numericFieldMapping) + docMapping.AddFieldMappingsAt("repo_id", numericFieldMapping) textFieldMapping := bleve.NewTextFieldMapping() textFieldMapping.Store = false textFieldMapping.IncludeInAll = false - docMapping.AddFieldMappingsAt("Title", textFieldMapping) - docMapping.AddFieldMappingsAt("Content", textFieldMapping) - docMapping.AddFieldMappingsAt("Comments", textFieldMapping) + docMapping.AddFieldMappingsAt("title", textFieldMapping) + docMapping.AddFieldMappingsAt("content", textFieldMapping) + docMapping.AddFieldMappingsAt("comments", textFieldMapping) if err := addUnicodeNormalizeTokenFilter(mapping); err != nil { return nil, err - } else if err = mapping.AddCustomAnalyzer(issueIndexerAnalyzer, map[string]interface{}{ + } else if err = mapping.AddCustomAnalyzer(issueIndexerAnalyzer, map[string]any{ "type": custom.Name, "char_filters": []string{}, "tokenizer": unicode.Name, @@ -91,6 +92,7 @@ func generateIssueIndexMapping() (mapping.IndexMapping, error) { mapping.DefaultAnalyzer = issueIndexerAnalyzer mapping.AddDocumentMapping(issueIndexerDocType, docMapping) mapping.AddDocumentMapping("_all", bleve.NewDocumentDisabledMapping()) + mapping.DefaultMapping = bleve.NewDocumentDisabledMapping() // disable default mapping, avoid indexing unexpected structs return mapping, nil } @@ -116,17 +118,7 @@ func NewIndexer(indexDir string) *Indexer { func (b *Indexer) Index(_ context.Context, issues []*internal.IndexerData) error { batch := inner_bleve.NewFlushingBatch(b.inner.Indexer, maxBatchSize) for _, issue := range issues { - if err := batch.Index(indexer_internal.Base36(issue.ID), struct { - RepoID int64 - Title string - Content string - Comments []string - }{ - RepoID: issue.RepoID, - Title: issue.Title, - Content: issue.Content, - Comments: issue.Comments, - }); err != nil { + if err := batch.Index(indexer_internal.Base36(issue.ID), (*IndexerData)(issue)); err != nil { return err } } @@ -146,10 +138,10 @@ func (b *Indexer) Delete(_ context.Context, ids ...int64) error { // Search searches for issues by given conditions. // Returns the matching issue IDs -func (b *Indexer) Search(ctx context.Context, keyword string, repoIDs []int64, limit, start int) (*internal.SearchResult, error) { +func (b *Indexer) Search(ctx context.Context, keyword string, repoIDs []int64, limit, start int, state string) (*internal.SearchResult, error) { var repoQueriesP []*query.NumericRangeQuery for _, repoID := range repoIDs { - repoQueriesP = append(repoQueriesP, numericEqualityQuery(repoID, "RepoID")) + repoQueriesP = append(repoQueriesP, numericEqualityQuery(repoID, "repo_id")) } repoQueries := make([]query.Query, len(repoQueriesP)) for i, v := range repoQueriesP { @@ -159,9 +151,9 @@ func (b *Indexer) Search(ctx context.Context, keyword string, repoIDs []int64, l indexerQuery := bleve.NewConjunctionQuery( bleve.NewDisjunctionQuery(repoQueries...), bleve.NewDisjunctionQuery( - newMatchPhraseQuery(keyword, "Title", issueIndexerAnalyzer), - newMatchPhraseQuery(keyword, "Content", issueIndexerAnalyzer), - newMatchPhraseQuery(keyword, "Comments", issueIndexerAnalyzer), + newMatchPhraseQuery(keyword, "title", issueIndexerAnalyzer), + newMatchPhraseQuery(keyword, "content", issueIndexerAnalyzer), + newMatchPhraseQuery(keyword, "comments", issueIndexerAnalyzer), )) search := bleve.NewSearchRequestOptions(indexerQuery, limit, start, false) search.SortBy([]string{"-_score"}) diff --git a/modules/indexer/issues/bleve/bleve_test.go b/modules/indexer/issues/bleve/bleve_test.go index f890f8eb488..0eb136d22b2 100644 --- a/modules/indexer/issues/bleve/bleve_test.go +++ b/modules/indexer/issues/bleve/bleve_test.go @@ -77,7 +77,7 @@ func TestBleveIndexAndSearch(t *testing.T) { } for _, kw := range keywords { - res, err := indexer.Search(context.TODO(), kw.Keyword, []int64{2}, 10, 0) + res, err := indexer.Search(context.TODO(), kw.Keyword, []int64{2}, 10, 0, "") assert.NoError(t, err) ids := make([]int64, 0, len(res.Hits)) diff --git a/modules/indexer/issues/db/db.go b/modules/indexer/issues/db/db.go index 17ed426b384..b054b9d800e 100644 --- a/modules/indexer/issues/db/db.go +++ b/modules/indexer/issues/db/db.go @@ -36,7 +36,7 @@ func (i *Indexer) Delete(_ context.Context, _ ...int64) error { } // Search searches for issues -func (i *Indexer) Search(ctx context.Context, kw string, repoIDs []int64, limit, start int) (*internal.SearchResult, error) { +func (i *Indexer) Search(ctx context.Context, kw string, repoIDs []int64, limit, start int, state string) (*internal.SearchResult, error) { total, ids, err := issues_model.SearchIssueIDsByKeyword(ctx, kw, repoIDs, limit, start) if err != nil { return nil, err diff --git a/modules/indexer/issues/elasticsearch/elasticsearch.go b/modules/indexer/issues/elasticsearch/elasticsearch.go index 33a7dfc21e0..cfd3628c185 100644 --- a/modules/indexer/issues/elasticsearch/elasticsearch.go +++ b/modules/indexer/issues/elasticsearch/elasticsearch.go @@ -76,7 +76,7 @@ func (b *Indexer) Index(ctx context.Context, issues []*internal.IndexerData) err _, err := b.inner.Client.Index(). Index(b.inner.VersionedIndexName()). Id(fmt.Sprintf("%d", issue.ID)). - BodyJson(map[string]interface{}{ + BodyJson(map[string]any{ "id": issue.ID, "repo_id": issue.RepoID, "title": issue.Title, @@ -93,7 +93,7 @@ func (b *Indexer) Index(ctx context.Context, issues []*internal.IndexerData) err elastic.NewBulkIndexRequest(). Index(b.inner.VersionedIndexName()). Id(fmt.Sprintf("%d", issue.ID)). - Doc(map[string]interface{}{ + Doc(map[string]any{ "id": issue.ID, "repo_id": issue.RepoID, "title": issue.Title, @@ -140,12 +140,12 @@ func (b *Indexer) Delete(ctx context.Context, ids ...int64) error { // Search searches for issues by given conditions. // Returns the matching issue IDs -func (b *Indexer) Search(ctx context.Context, keyword string, repoIDs []int64, limit, start int) (*internal.SearchResult, error) { +func (b *Indexer) Search(ctx context.Context, keyword string, repoIDs []int64, limit, start int, state string) (*internal.SearchResult, error) { kwQuery := elastic.NewMultiMatchQuery(keyword, "title", "content", "comments") query := elastic.NewBoolQuery() query = query.Must(kwQuery) if len(repoIDs) > 0 { - repoStrs := make([]interface{}, 0, len(repoIDs)) + repoStrs := make([]any, 0, len(repoIDs)) for _, repoID := range repoIDs { repoStrs = append(repoStrs, repoID) } diff --git a/modules/indexer/issues/indexer.go b/modules/indexer/issues/indexer.go index 9e2f13371e4..fe5c5d8f26d 100644 --- a/modules/indexer/issues/indexer.go +++ b/modules/indexer/issues/indexer.go @@ -242,12 +242,18 @@ func UpdateIssueIndexer(issue *issues_model.Issue) { comments = append(comments, comment.Content) } } + issueType := "issue" + if issue.IsPull { + issueType = "pull" + } indexerData := &internal.IndexerData{ - ID: issue.ID, - RepoID: issue.RepoID, - Title: issue.Title, - Content: issue.Content, - Comments: comments, + ID: issue.ID, + RepoID: issue.RepoID, + State: string(issue.State()), + IssueType: issueType, + Title: issue.Title, + Content: issue.Content, + Comments: comments, } log.Debug("Adding to channel: %v", indexerData) if err := issueIndexerQueue.Push(indexerData); err != nil { @@ -278,10 +284,10 @@ func DeleteRepoIssueIndexer(ctx context.Context, repo *repo_model.Repository) { // SearchIssuesByKeyword search issue ids by keywords and repo id // WARNNING: You have to ensure user have permission to visit repoIDs' issues -func SearchIssuesByKeyword(ctx context.Context, repoIDs []int64, keyword string) ([]int64, error) { +func SearchIssuesByKeyword(ctx context.Context, repoIDs []int64, keyword, state string) ([]int64, error) { var issueIDs []int64 indexer := *globalIndexer.Load() - res, err := indexer.Search(ctx, keyword, repoIDs, 50, 0) + res, err := indexer.Search(ctx, keyword, repoIDs, 50, 0, state) if err != nil { return nil, err } diff --git a/modules/indexer/issues/indexer_test.go b/modules/indexer/issues/indexer_test.go index 5962a4ee9cb..757eb2f3d93 100644 --- a/modules/indexer/issues/indexer_test.go +++ b/modules/indexer/issues/indexer_test.go @@ -50,19 +50,19 @@ func TestBleveSearchIssues(t *testing.T) { time.Sleep(5 * time.Second) - ids, err := SearchIssuesByKeyword(context.TODO(), []int64{1}, "issue2") + ids, err := SearchIssuesByKeyword(context.TODO(), []int64{1}, "issue2", "") assert.NoError(t, err) assert.EqualValues(t, []int64{2}, ids) - ids, err = SearchIssuesByKeyword(context.TODO(), []int64{1}, "first") + ids, err = SearchIssuesByKeyword(context.TODO(), []int64{1}, "first", "") assert.NoError(t, err) assert.EqualValues(t, []int64{1}, ids) - ids, err = SearchIssuesByKeyword(context.TODO(), []int64{1}, "for") + ids, err = SearchIssuesByKeyword(context.TODO(), []int64{1}, "for", "") assert.NoError(t, err) assert.ElementsMatch(t, []int64{1, 2, 3, 5, 11}, ids) - ids, err = SearchIssuesByKeyword(context.TODO(), []int64{1}, "good") + ids, err = SearchIssuesByKeyword(context.TODO(), []int64{1}, "good", "") assert.NoError(t, err) assert.EqualValues(t, []int64{1}, ids) } @@ -73,19 +73,19 @@ func TestDBSearchIssues(t *testing.T) { setting.Indexer.IssueType = "db" InitIssueIndexer(true) - ids, err := SearchIssuesByKeyword(context.TODO(), []int64{1}, "issue2") + ids, err := SearchIssuesByKeyword(context.TODO(), []int64{1}, "issue2", "") assert.NoError(t, err) assert.EqualValues(t, []int64{2}, ids) - ids, err = SearchIssuesByKeyword(context.TODO(), []int64{1}, "first") + ids, err = SearchIssuesByKeyword(context.TODO(), []int64{1}, "first", "") assert.NoError(t, err) assert.EqualValues(t, []int64{1}, ids) - ids, err = SearchIssuesByKeyword(context.TODO(), []int64{1}, "for") + ids, err = SearchIssuesByKeyword(context.TODO(), []int64{1}, "for", "") assert.NoError(t, err) assert.ElementsMatch(t, []int64{1, 2, 3, 5, 11}, ids) - ids, err = SearchIssuesByKeyword(context.TODO(), []int64{1}, "good") + ids, err = SearchIssuesByKeyword(context.TODO(), []int64{1}, "good", "") assert.NoError(t, err) assert.EqualValues(t, []int64{1}, ids) } diff --git a/modules/indexer/issues/internal/indexer.go b/modules/indexer/issues/internal/indexer.go index 553c8a573cd..b96517bb80d 100644 --- a/modules/indexer/issues/internal/indexer.go +++ b/modules/indexer/issues/internal/indexer.go @@ -15,7 +15,7 @@ type Indexer interface { internal.Indexer Index(ctx context.Context, issue []*IndexerData) error Delete(ctx context.Context, ids ...int64) error - Search(ctx context.Context, kw string, repoIDs []int64, limit, start int) (*SearchResult, error) + Search(ctx context.Context, kw string, repoIDs []int64, limit, start int, state string) (*SearchResult, error) } // NewDummyIndexer returns a dummy indexer @@ -37,6 +37,6 @@ func (d *dummyIndexer) Delete(ctx context.Context, ids ...int64) error { return fmt.Errorf("indexer is not ready") } -func (d *dummyIndexer) Search(ctx context.Context, kw string, repoIDs []int64, limit, start int) (*SearchResult, error) { +func (d *dummyIndexer) Search(ctx context.Context, kw string, repoIDs []int64, limit, start int, state string) (*SearchResult, error) { return nil, fmt.Errorf("indexer is not ready") } diff --git a/modules/indexer/issues/internal/model.go b/modules/indexer/issues/internal/model.go index 8c206fc1cfc..2b52d32302a 100644 --- a/modules/indexer/issues/internal/model.go +++ b/modules/indexer/issues/internal/model.go @@ -5,13 +5,15 @@ package internal // IndexerData data stored in the issue indexer type IndexerData struct { - ID int64 `json:"id"` - RepoID int64 `json:"repo_id"` - Title string `json:"title"` - Content string `json:"content"` - Comments []string `json:"comments"` - IsDelete bool `json:"is_delete"` - IDs []int64 `json:"ids"` + ID int64 `json:"id"` + RepoID int64 `json:"repo_id"` + State string `json:"state"` // open, closed, all + IssueType string `json:"type"` // issue or pull + Title string `json:"title"` + Content string `json:"content"` + Comments []string `json:"comments"` + IsDelete bool `json:"is_delete"` + IDs []int64 `json:"ids"` } // Match represents on search result diff --git a/modules/indexer/issues/meilisearch/meilisearch.go b/modules/indexer/issues/meilisearch/meilisearch.go index 877c04f1dcb..2ea06b576c0 100644 --- a/modules/indexer/issues/meilisearch/meilisearch.go +++ b/modules/indexer/issues/meilisearch/meilisearch.go @@ -16,7 +16,7 @@ import ( ) const ( - issueIndexerLatestVersion = 0 + issueIndexerLatestVersion = 1 ) var _ internal.Indexer = &Indexer{} @@ -70,12 +70,19 @@ func (b *Indexer) Delete(_ context.Context, ids ...int64) error { // Search searches for issues by given conditions. // Returns the matching issue IDs -func (b *Indexer) Search(ctx context.Context, keyword string, repoIDs []int64, limit, start int) (*internal.SearchResult, error) { +func (b *Indexer) Search(ctx context.Context, keyword string, repoIDs []int64, limit, start int, state string) (*internal.SearchResult, error) { repoFilters := make([]string, 0, len(repoIDs)) for _, repoID := range repoIDs { repoFilters = append(repoFilters, "repo_id = "+strconv.FormatInt(repoID, 10)) } filter := strings.Join(repoFilters, " OR ") + if state == "open" || state == "closed" { + if filter != "" { + filter = "(" + filter + ") AND state = " + state + } else { + filter = "state = " + state + } + } searchRes, err := b.inner.Client.Index(b.inner.VersionedIndexName()).Search(keyword, &meilisearch.SearchRequest{ Filter: filter, Limit: int64(limit), @@ -88,7 +95,7 @@ func (b *Indexer) Search(ctx context.Context, keyword string, repoIDs []int64, l hits := make([]internal.Match, 0, len(searchRes.Hits)) for _, hit := range searchRes.Hits { hits = append(hits, internal.Match{ - ID: int64(hit.(map[string]interface{})["id"].(float64)), + ID: int64(hit.(map[string]any)["id"].(float64)), }) } return &internal.SearchResult{ diff --git a/modules/issue/template/template.go b/modules/issue/template/template.go index 0f19d87e8d5..4e813fc91f7 100644 --- a/modules/issue/template/template.go +++ b/modules/issue/template/template.go @@ -151,7 +151,7 @@ func validateOptions(field *api.IssueFormField, idx int) error { } position := newErrorPosition(idx, field.Type) - options, ok := field.Attributes["options"].([]interface{}) + options, ok := field.Attributes["options"].([]any) if !ok || len(options) == 0 { return position.Errorf("'options' is required and should be a array") } @@ -164,7 +164,7 @@ func validateOptions(field *api.IssueFormField, idx int) error { return position.Errorf("should be a string") } case api.IssueFormFieldTypeCheckboxes: - opt, ok := option.(map[string]interface{}) + opt, ok := option.(map[string]any) if !ok { return position.Errorf("should be a dictionary") } @@ -182,7 +182,7 @@ func validateOptions(field *api.IssueFormField, idx int) error { return nil } -func validateStringItem(position errorPosition, m map[string]interface{}, required bool, names ...string) error { +func validateStringItem(position errorPosition, m map[string]any, required bool, names ...string) error { for _, name := range names { v, ok := m[name] if !ok { @@ -202,7 +202,7 @@ func validateStringItem(position errorPosition, m map[string]interface{}, requir return nil } -func validateBoolItem(position errorPosition, m map[string]interface{}, names ...string) error { +func validateBoolItem(position errorPosition, m map[string]any, names ...string) error { for _, name := range names { v, ok := m[name] if !ok { @@ -217,7 +217,7 @@ func validateBoolItem(position errorPosition, m map[string]interface{}, names .. type errorPosition string -func (p errorPosition) Errorf(format string, a ...interface{}) error { +func (p errorPosition) Errorf(format string, a ...any) error { return fmt.Errorf(string(p)+": "+format, a...) } @@ -332,7 +332,7 @@ func (f *valuedField) Value() string { } func (f *valuedField) Options() []*valuedOption { - if options, ok := f.Attributes["options"].([]interface{}); ok { + if options, ok := f.Attributes["options"].([]any); ok { ret := make([]*valuedOption, 0, len(options)) for i, option := range options { ret = append(ret, &valuedOption{ @@ -348,7 +348,7 @@ func (f *valuedField) Options() []*valuedOption { type valuedOption struct { index int - data interface{} + data any field *valuedField } @@ -359,7 +359,7 @@ func (o *valuedOption) Label() string { return label } case api.IssueFormFieldTypeCheckboxes: - if vs, ok := o.data.(map[string]interface{}); ok { + if vs, ok := o.data.(map[string]any); ok { if v, ok := vs["label"].(string); ok { return v } diff --git a/modules/issue/template/template_test.go b/modules/issue/template/template_test.go index 0cdddd0c85f..06e6b70d35f 100644 --- a/modules/issue/template/template_test.go +++ b/modules/issue/template/template_test.go @@ -387,34 +387,34 @@ body: { Type: "markdown", ID: "id1", - Attributes: map[string]interface{}{ + Attributes: map[string]any{ "value": "Value of the markdown", }, }, { Type: "textarea", ID: "id2", - Attributes: map[string]interface{}{ + Attributes: map[string]any{ "label": "Label of textarea", "description": "Description of textarea", "placeholder": "Placeholder of textarea", "value": "Value of textarea", "render": "bash", }, - Validations: map[string]interface{}{ + Validations: map[string]any{ "required": true, }, }, { Type: "input", ID: "id3", - Attributes: map[string]interface{}{ + Attributes: map[string]any{ "label": "Label of input", "description": "Description of input", "placeholder": "Placeholder of input", "value": "Value of input", }, - Validations: map[string]interface{}{ + Validations: map[string]any{ "required": true, "is_number": true, "regex": "[a-zA-Z0-9]+", @@ -423,30 +423,30 @@ body: { Type: "dropdown", ID: "id4", - Attributes: map[string]interface{}{ + Attributes: map[string]any{ "label": "Label of dropdown", "description": "Description of dropdown", "multiple": true, - "options": []interface{}{ + "options": []any{ "Option 1 of dropdown", "Option 2 of dropdown", "Option 3 of dropdown", }, }, - Validations: map[string]interface{}{ + Validations: map[string]any{ "required": true, }, }, { Type: "checkboxes", ID: "id5", - Attributes: map[string]interface{}{ + Attributes: map[string]any{ "label": "Label of checkboxes", "description": "Description of checkboxes", - "options": []interface{}{ - map[string]interface{}{"label": "Option 1 of checkboxes", "required": true}, - map[string]interface{}{"label": "Option 2 of checkboxes", "required": false}, - map[string]interface{}{"label": "Option 3 of checkboxes", "required": true}, + "options": []any{ + map[string]any{"label": "Option 1 of checkboxes", "required": true}, + map[string]any{"label": "Option 2 of checkboxes", "required": false}, + map[string]any{"label": "Option 3 of checkboxes", "required": true}, }, }, }, @@ -479,7 +479,7 @@ body: { Type: "markdown", ID: "id1", - Attributes: map[string]interface{}{ + Attributes: map[string]any{ "value": "Value of the markdown", }, }, @@ -512,7 +512,7 @@ body: { Type: "markdown", ID: "id1", - Attributes: map[string]interface{}{ + Attributes: map[string]any{ "value": "Value of the markdown", }, }, @@ -545,7 +545,7 @@ body: { Type: "markdown", ID: "id1", - Attributes: map[string]interface{}{ + Attributes: map[string]any{ "value": "Value of the markdown", }, }, diff --git a/modules/json/json.go b/modules/json/json.go index 5f8c474e052..34568c75c6e 100644 --- a/modules/json/json.go +++ b/modules/json/json.go @@ -15,18 +15,18 @@ import ( // Encoder represents an encoder for json type Encoder interface { - Encode(v interface{}) error + Encode(v any) error } // Decoder represents a decoder for json type Decoder interface { - Decode(v interface{}) error + Decode(v any) error } // Interface represents an interface to handle json data type Interface interface { - Marshal(v interface{}) ([]byte, error) - Unmarshal(data []byte, v interface{}) error + Marshal(v any) ([]byte, error) + Unmarshal(data []byte, v any) error NewEncoder(writer io.Writer) Encoder NewDecoder(reader io.Reader) Decoder Indent(dst *bytes.Buffer, src []byte, prefix, indent string) error @@ -44,12 +44,12 @@ var ( type StdJSON struct{} // Marshal implements Interface -func (StdJSON) Marshal(v interface{}) ([]byte, error) { +func (StdJSON) Marshal(v any) ([]byte, error) { return json.Marshal(v) } // Unmarshal implements Interface -func (StdJSON) Unmarshal(data []byte, v interface{}) error { +func (StdJSON) Unmarshal(data []byte, v any) error { return json.Unmarshal(data, v) } @@ -74,12 +74,12 @@ type JSONiter struct { } // Marshal implements Interface -func (j JSONiter) Marshal(v interface{}) ([]byte, error) { +func (j JSONiter) Marshal(v any) ([]byte, error) { return j.API.Marshal(v) } // Unmarshal implements Interface -func (j JSONiter) Unmarshal(data []byte, v interface{}) error { +func (j JSONiter) Unmarshal(data []byte, v any) error { return j.API.Unmarshal(data, v) } @@ -99,12 +99,12 @@ func (j JSONiter) Indent(dst *bytes.Buffer, src []byte, prefix, indent string) e } // Marshal converts object as bytes -func Marshal(v interface{}) ([]byte, error) { +func Marshal(v any) ([]byte, error) { return DefaultJSONHandler.Marshal(v) } // Unmarshal decodes object from bytes -func Unmarshal(data []byte, v interface{}) error { +func Unmarshal(data []byte, v any) error { return DefaultJSONHandler.Unmarshal(data, v) } @@ -124,7 +124,7 @@ func Indent(dst *bytes.Buffer, src []byte, prefix, indent string) error { } // MarshalIndent copied from encoding/json -func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) { +func MarshalIndent(v any, prefix, indent string) ([]byte, error) { b, err := Marshal(v) if err != nil { return nil, err @@ -144,7 +144,7 @@ func Valid(data []byte) bool { // UnmarshalHandleDoubleEncode - due to a bug in xorm (see https://gitea.com/xorm/xorm/pulls/1957) - it's // possible that a Blob may be double encoded or gain an unwanted prefix of 0xff 0xfe. -func UnmarshalHandleDoubleEncode(bs []byte, v interface{}) error { +func UnmarshalHandleDoubleEncode(bs []byte, v any) error { err := json.Unmarshal(bs, v) if err != nil { ok := true diff --git a/modules/markup/html_internal_test.go b/modules/markup/html_internal_test.go index 7e04b03531d..00ffe45c285 100644 --- a/modules/markup/html_internal_test.go +++ b/modules/markup/html_internal_test.go @@ -120,7 +120,7 @@ func TestRender_IssueIndexPattern2(t *testing.T) { isExternal = true } - links := make([]interface{}, len(indices)) + links := make([]any, len(indices)) for i, index := range indices { links[i] = numericIssueLink(util.URLJoin(TestRepoURL, path), "ref-issue", index, marker) } @@ -204,7 +204,7 @@ func TestRender_IssueIndexPattern4(t *testing.T) { // alphanumeric: render inputs with valid mentions test := func(s, expectedFmt string, names ...string) { - links := make([]interface{}, len(names)) + links := make([]any, len(names)) for i, name := range names { links[i] = externalIssueLink("https://someurl.com/someUser/someRepo/", "ref-issue ref-external-issue", name) } @@ -226,7 +226,7 @@ func TestRender_IssueIndexPattern5(t *testing.T) { test := func(s, expectedFmt, pattern string, ids, names []string) { metas := regexpMetas metas["regexp"] = pattern - links := make([]interface{}, len(ids)) + links := make([]any, len(ids)) for i, id := range ids { links[i] = link(util.URLJoin("https://someurl.com/someUser/someRepo/", id), "ref-issue ref-external-issue", names[i]) } diff --git a/modules/markup/markdown/meta.go b/modules/markup/markdown/meta.go index bbefbd380c2..e76b253ecd2 100644 --- a/modules/markup/markdown/meta.go +++ b/modules/markup/markdown/meta.go @@ -55,14 +55,14 @@ func isYAMLSeparator(line []byte) bool { // ExtractMetadata consumes a markdown file, parses YAML frontmatter, // and returns the frontmatter metadata separated from the markdown content -func ExtractMetadata(contents string, out interface{}) (string, error) { +func ExtractMetadata(contents string, out any) (string, error) { body, err := ExtractMetadataBytes([]byte(contents), out) return string(body), err } // ExtractMetadata consumes a markdown file, parses YAML frontmatter, // and returns the frontmatter metadata separated from the markdown content -func ExtractMetadataBytes(contents []byte, out interface{}) ([]byte, error) { +func ExtractMetadataBytes(contents []byte, out any) ([]byte, error) { var front, body []byte start, end := 0, len(contents) diff --git a/modules/migration/comment.go b/modules/migration/comment.go index 92ce30e3020..e041758467d 100644 --- a/modules/migration/comment.go +++ b/modules/migration/comment.go @@ -24,7 +24,7 @@ type Comment struct { Updated time.Time Content string Reactions []*Reaction - Meta map[string]interface{} `yaml:"meta,omitempty"` // see models/issues/comment.go for fields in Comment struct + Meta map[string]any `yaml:"meta,omitempty"` // see models/issues/comment.go for fields in Comment struct } // GetExternalName ExternalUserMigrated interface diff --git a/modules/migration/downloader.go b/modules/migration/downloader.go index ebd3672d636..08dbbc29a99 100644 --- a/modules/migration/downloader.go +++ b/modules/migration/downloader.go @@ -34,4 +34,4 @@ type DownloaderFactory interface { } // DownloaderContext has opaque information only relevant to a given downloader -type DownloaderContext interface{} +type DownloaderContext any diff --git a/modules/migration/file_format.go b/modules/migration/file_format.go index 04e5d769813..e8b6891ca11 100644 --- a/modules/migration/file_format.go +++ b/modules/migration/file_format.go @@ -17,7 +17,7 @@ import ( ) // Load project data from file, with optional validation -func Load(filename string, data interface{}, validation bool) error { +func Load(filename string, data any, validation bool) error { isJSON := strings.HasSuffix(filename, ".json") bs, err := os.ReadFile(filename) @@ -34,7 +34,7 @@ func Load(filename string, data interface{}, validation bool) error { return unmarshal(bs, data, isJSON) } -func unmarshal(bs []byte, data interface{}, isJSON bool) error { +func unmarshal(bs []byte, data any, isJSON bool) error { if isJSON { return json.Unmarshal(bs, data) } @@ -47,8 +47,8 @@ func getSchema(filename string) (*jsonschema.Schema, error) { return c.Compile(filename) } -func validate(bs []byte, datatype interface{}, isJSON bool) error { - var v interface{} +func validate(bs []byte, datatype any, isJSON bool) error { + var v any err := unmarshal(bs, &v, isJSON) if err != nil { return err @@ -81,11 +81,11 @@ func validate(bs []byte, datatype interface{}, isJSON bool) error { return err } -func toStringKeys(val interface{}) (interface{}, error) { +func toStringKeys(val any) (any, error) { var err error switch val := val.(type) { - case map[string]interface{}: - m := make(map[string]interface{}) + case map[string]any: + m := make(map[string]any) for k, v := range val { m[k], err = toStringKeys(v) if err != nil { @@ -93,8 +93,8 @@ func toStringKeys(val interface{}) (interface{}, error) { } } return m, nil - case []interface{}: - l := make([]interface{}, len(val)) + case []any: + l := make([]any, len(val)) for i, v := range val { l[i], err = toStringKeys(v) if err != nil { diff --git a/modules/migration/messenger.go b/modules/migration/messenger.go index 1fd5456259e..924aac97694 100644 --- a/modules/migration/messenger.go +++ b/modules/migration/messenger.go @@ -4,7 +4,7 @@ package migration // Messenger is a formatting function similar to i18n.Tr -type Messenger func(key string, args ...interface{}) +type Messenger func(key string, args ...any) // NilMessenger represents an empty formatting function -func NilMessenger(string, ...interface{}) {} +func NilMessenger(string, ...any) {} diff --git a/modules/nosql/manager.go b/modules/nosql/manager.go index 31e43297dcd..375c2b5d003 100644 --- a/modules/nosql/manager.go +++ b/modules/nosql/manager.go @@ -71,7 +71,7 @@ func valToTimeDuration(vs []string) (result time.Duration) { result = time.Duration(val) } if err == nil { - return + return result } } return result diff --git a/modules/nosql/manager_leveldb.go b/modules/nosql/manager_leveldb.go index 078a8028116..4d2c90debc6 100644 --- a/modules/nosql/manager_leveldb.go +++ b/modules/nosql/manager_leveldb.go @@ -54,7 +54,7 @@ func (m *Manager) GetLevelDB(connection string) (db *leveldb.DB, err error) { // Because we want associate any goroutines created by this call to the main nosqldb context we need to // wrap this in a goroutine labelled with the nosqldb context done := make(chan struct{}) - var recovered interface{} + var recovered any go func() { defer func() { recovered = recover() diff --git a/modules/nosql/manager_redis.go b/modules/nosql/manager_redis.go index 7066863b89f..3c82651541f 100644 --- a/modules/nosql/manager_redis.go +++ b/modules/nosql/manager_redis.go @@ -47,7 +47,7 @@ func (m *Manager) GetRedisClient(connection string) (client redis.UniversalClien // Because we want associate any goroutines created by this call to the main nosqldb context we need to // wrap this in a goroutine labelled with the nosqldb context done := make(chan struct{}) - var recovered interface{} + var recovered any go func() { defer func() { recovered = recover() diff --git a/modules/packages/composer/metadata.go b/modules/packages/composer/metadata.go index 36b0b8e4219..1d0f0256488 100644 --- a/modules/packages/composer/metadata.go +++ b/modules/packages/composer/metadata.go @@ -38,18 +38,18 @@ type Package struct { // Metadata represents the metadata of a Composer package type Metadata struct { - Description string `json:"description,omitempty"` - Keywords []string `json:"keywords,omitempty"` - Homepage string `json:"homepage,omitempty"` - License Licenses `json:"license,omitempty"` - Authors []Author `json:"authors,omitempty"` - Autoload map[string]interface{} `json:"autoload,omitempty"` - AutoloadDev map[string]interface{} `json:"autoload-dev,omitempty"` - Extra map[string]interface{} `json:"extra,omitempty"` - Require map[string]string `json:"require,omitempty"` - RequireDev map[string]string `json:"require-dev,omitempty"` - Suggest map[string]string `json:"suggest,omitempty"` - Provide map[string]string `json:"provide,omitempty"` + Description string `json:"description,omitempty"` + Keywords []string `json:"keywords,omitempty"` + Homepage string `json:"homepage,omitempty"` + License Licenses `json:"license,omitempty"` + Authors []Author `json:"authors,omitempty"` + Autoload map[string]any `json:"autoload,omitempty"` + AutoloadDev map[string]any `json:"autoload-dev,omitempty"` + Extra map[string]any `json:"extra,omitempty"` + Require map[string]string `json:"require,omitempty"` + RequireDev map[string]string `json:"require-dev,omitempty"` + Suggest map[string]string `json:"suggest,omitempty"` + Provide map[string]string `json:"provide,omitempty"` } // Licenses represents the licenses of a Composer package diff --git a/modules/packages/content_store.go b/modules/packages/content_store.go index 1181fa4d528..da93e6cf6bc 100644 --- a/modules/packages/content_store.go +++ b/modules/packages/content_store.go @@ -5,9 +5,11 @@ package packages import ( "io" + "net/url" "path" "strings" + "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/storage" "code.gitea.io/gitea/modules/util" ) @@ -31,6 +33,14 @@ func (s *ContentStore) Get(key BlobHash256Key) (storage.Object, error) { return s.store.Open(KeyToRelativePath(key)) } +func (s *ContentStore) ShouldServeDirect() bool { + return setting.Packages.Storage.MinioConfig.ServeDirect +} + +func (s *ContentStore) GetServeDirectURL(key BlobHash256Key, filename string) (*url.URL, error) { + return s.store.URL(KeyToRelativePath(key), filename) +} + // FIXME: Workaround to be removed in v1.20 // https://github.com/go-gitea/gitea/issues/19586 func (s *ContentStore) Has(key BlobHash256Key) error { diff --git a/modules/packages/helm/metadata.go b/modules/packages/helm/metadata.go index fdbd9003b84..421fc5e7259 100644 --- a/modules/packages/helm/metadata.go +++ b/modules/packages/helm/metadata.go @@ -55,14 +55,14 @@ type Maintainer struct { } type Dependency struct { - Name string `json:"name" yaml:"name"` - Version string `json:"version,omitempty" yaml:"version,omitempty"` - Repository string `json:"repository" yaml:"repository"` - Condition string `json:"condition,omitempty" yaml:"condition,omitempty"` - Tags []string `json:"tags,omitempty" yaml:"tags,omitempty"` - Enabled bool `json:"enabled,omitempty" yaml:"enabled,omitempty"` - ImportValues []interface{} `json:"import_values,omitempty" yaml:"import-values,omitempty"` - Alias string `json:"alias,omitempty" yaml:"alias,omitempty"` + Name string `json:"name" yaml:"name"` + Version string `json:"version,omitempty" yaml:"version,omitempty"` + Repository string `json:"repository" yaml:"repository"` + Condition string `json:"condition,omitempty" yaml:"condition,omitempty"` + Tags []string `json:"tags,omitempty" yaml:"tags,omitempty"` + Enabled bool `json:"enabled,omitempty" yaml:"enabled,omitempty"` + ImportValues []any `json:"import_values,omitempty" yaml:"import-values,omitempty"` + Alias string `json:"alias,omitempty" yaml:"alias,omitempty"` } // ParseChartArchive parses the metadata of a Helm archive diff --git a/modules/packages/pub/metadata.go b/modules/packages/pub/metadata.go index 13a066afacc..afb464e4620 100644 --- a/modules/packages/pub/metadata.go +++ b/modules/packages/pub/metadata.go @@ -38,12 +38,12 @@ type Package struct { // Metadata represents the metadata of a Pub package type Metadata struct { - Description string `json:"description,omitempty"` - ProjectURL string `json:"project_url,omitempty"` - RepositoryURL string `json:"repository_url,omitempty"` - DocumentationURL string `json:"documentation_url,omitempty"` - Readme string `json:"readme,omitempty"` - Pubspec interface{} `json:"pubspec"` + Description string `json:"description,omitempty"` + ProjectURL string `json:"project_url,omitempty"` + RepositoryURL string `json:"repository_url,omitempty"` + DocumentationURL string `json:"documentation_url,omitempty"` + Readme string `json:"readme,omitempty"` + Pubspec any `json:"pubspec"` } type pubspecPackage struct { @@ -134,7 +134,7 @@ func ParsePubspecMetadata(r io.Reader) (*Package, error) { p.Repository = "" } - var pubspec interface{} + var pubspec any if err := yaml.Unmarshal(buf, &pubspec); err != nil { return nil, err } diff --git a/modules/packages/rubygems/marshal.go b/modules/packages/rubygems/marshal.go index efb2ba34a49..8878dcf9732 100644 --- a/modules/packages/rubygems/marshal.go +++ b/modules/packages/rubygems/marshal.go @@ -40,19 +40,19 @@ var ( // RubyUserMarshal is a Ruby object that has a marshal_load function. type RubyUserMarshal struct { Name string - Value interface{} + Value any } // RubyUserDef is a Ruby object that has a _load function. type RubyUserDef struct { Name string - Value interface{} + Value any } // RubyObject is a default Ruby object. type RubyObject struct { Name string - Member map[string]interface{} + Member map[string]any } // MarshalEncoder mimics Rubys Marshal class. @@ -71,7 +71,7 @@ func NewMarshalEncoder(w io.Writer) *MarshalEncoder { } // Encode encodes the given type -func (e *MarshalEncoder) Encode(v interface{}) error { +func (e *MarshalEncoder) Encode(v any) error { if _, err := e.w.Write([]byte{majorVersion, minorVersion}); err != nil { return err } @@ -83,7 +83,7 @@ func (e *MarshalEncoder) Encode(v interface{}) error { return e.w.Flush() } -func (e *MarshalEncoder) marshal(v interface{}) error { +func (e *MarshalEncoder) marshal(v any) error { if v == nil { return e.marshalNil() } diff --git a/modules/packages/rubygems/marshal_test.go b/modules/packages/rubygems/marshal_test.go index 501757bfeda..6d2354cd876 100644 --- a/modules/packages/rubygems/marshal_test.go +++ b/modules/packages/rubygems/marshal_test.go @@ -12,7 +12,7 @@ import ( func TestMinimalEncoder(t *testing.T) { cases := []struct { - Value interface{} + Value any Expected []byte Error error }{ @@ -73,7 +73,7 @@ func TestMinimalEncoder(t *testing.T) { { Value: &RubyObject{ Name: "Test", - Member: map[string]interface{}{ + Member: map[string]any{ "test": 4, }, }, diff --git a/modules/packages/rubygems/metadata.go b/modules/packages/rubygems/metadata.go index adc1c058087..8a9794860eb 100644 --- a/modules/packages/rubygems/metadata.go +++ b/modules/packages/rubygems/metadata.go @@ -65,12 +65,12 @@ type gemspec struct { Version struct { Version string `yaml:"version"` } `yaml:"version"` - Platform string `yaml:"platform"` - Authors []string `yaml:"authors"` - Autorequire interface{} `yaml:"autorequire"` - Bindir string `yaml:"bindir"` - CertChain []interface{} `yaml:"cert_chain"` - Date string `yaml:"date"` + Platform string `yaml:"platform"` + Authors []string `yaml:"authors"` + Autorequire any `yaml:"autorequire"` + Bindir string `yaml:"bindir"` + CertChain []any `yaml:"cert_chain"` + Date string `yaml:"date"` Dependencies []struct { Name string `yaml:"name"` Requirement requirement `yaml:"requirement"` @@ -78,34 +78,34 @@ type gemspec struct { Prerelease bool `yaml:"prerelease"` VersionRequirements requirement `yaml:"version_requirements"` } `yaml:"dependencies"` - Description string `yaml:"description"` - Executables []string `yaml:"executables"` - Extensions []interface{} `yaml:"extensions"` - ExtraRdocFiles []string `yaml:"extra_rdoc_files"` - Files []string `yaml:"files"` - Homepage string `yaml:"homepage"` - Licenses []string `yaml:"licenses"` + Description string `yaml:"description"` + Executables []string `yaml:"executables"` + Extensions []any `yaml:"extensions"` + ExtraRdocFiles []string `yaml:"extra_rdoc_files"` + Files []string `yaml:"files"` + Homepage string `yaml:"homepage"` + Licenses []string `yaml:"licenses"` Metadata struct { BugTrackerURI string `yaml:"bug_tracker_uri"` ChangelogURI string `yaml:"changelog_uri"` DocumentationURI string `yaml:"documentation_uri"` SourceCodeURI string `yaml:"source_code_uri"` } `yaml:"metadata"` - PostInstallMessage interface{} `yaml:"post_install_message"` - RdocOptions []interface{} `yaml:"rdoc_options"` - RequirePaths []string `yaml:"require_paths"` - RequiredRubyVersion requirement `yaml:"required_ruby_version"` - RequiredRubygemsVersion requirement `yaml:"required_rubygems_version"` - Requirements []interface{} `yaml:"requirements"` - RubygemsVersion string `yaml:"rubygems_version"` - SigningKey interface{} `yaml:"signing_key"` - SpecificationVersion int `yaml:"specification_version"` - Summary string `yaml:"summary"` - TestFiles []interface{} `yaml:"test_files"` + PostInstallMessage any `yaml:"post_install_message"` + RdocOptions []any `yaml:"rdoc_options"` + RequirePaths []string `yaml:"require_paths"` + RequiredRubyVersion requirement `yaml:"required_ruby_version"` + RequiredRubygemsVersion requirement `yaml:"required_rubygems_version"` + Requirements []any `yaml:"requirements"` + RubygemsVersion string `yaml:"rubygems_version"` + SigningKey any `yaml:"signing_key"` + SpecificationVersion int `yaml:"specification_version"` + Summary string `yaml:"summary"` + TestFiles []any `yaml:"test_files"` } type requirement struct { - Requirements [][]interface{} `yaml:"requirements"` + Requirements [][]any `yaml:"requirements"` } // AsVersionRequirement converts into []VersionRequirement @@ -119,7 +119,7 @@ func (r requirement) AsVersionRequirement() []VersionRequirement { if !ok { continue } - vm, ok := req[1].(map[string]interface{}) + vm, ok := req[1].(map[string]any) if !ok { continue } diff --git a/modules/private/manager.go b/modules/private/manager.go index 382986bf1db..6055e553bd0 100644 --- a/modules/private/manager.go +++ b/modules/private/manager.go @@ -85,11 +85,11 @@ type LoggerOptions struct { Logger string Writer string Mode string - Config map[string]interface{} + Config map[string]any } // AddLogger adds a logger -func AddLogger(ctx context.Context, logger, writer, mode string, config map[string]interface{}) ResponseExtra { +func AddLogger(ctx context.Context, logger, writer, mode string, config map[string]any) ResponseExtra { reqURL := setting.LocalURL + "api/internal/manager/add-logger" req := newInternalRequest(ctx, reqURL, "POST", LoggerOptions{ Logger: logger, diff --git a/modules/process/context.go b/modules/process/context.go index f1bc582ff06..26a80ebd620 100644 --- a/modules/process/context.go +++ b/modules/process/context.go @@ -24,7 +24,7 @@ func (c *Context) GetParent() *Context { } // Value is part of the interface for context.Context. We mostly defer to the internal context - but we return this in response to the ProcessContextKey -func (c *Context) Value(key interface{}) interface{} { +func (c *Context) Value(key any) any { if key == ProcessContextKey { return c } @@ -32,7 +32,7 @@ func (c *Context) Value(key interface{}) interface{} { } // ProcessContextKey is the key under which process contexts are stored -var ProcessContextKey interface{} = "process-context" +var ProcessContextKey any = "process-context" // GetContext will return a process context if one exists func GetContext(ctx context.Context) *Context { diff --git a/modules/process/manager.go b/modules/process/manager.go index 56908c04086..9c21f621521 100644 --- a/modules/process/manager.go +++ b/modules/process/manager.go @@ -211,7 +211,7 @@ func (pm *Manager) nextPID() (start time.Time, pid IDType) { pid = IDType(strconv.FormatInt(start.Unix(), 16)) if pm.next == 1 { - return + return start, pid } pid = IDType(string(pid) + "-" + strconv.FormatInt(pm.next, 10)) return start, pid diff --git a/modules/queue/workerqueue.go b/modules/queue/workerqueue.go index e0d5183bd9b..b28fd880270 100644 --- a/modules/queue/workerqueue.go +++ b/modules/queue/workerqueue.go @@ -93,7 +93,7 @@ func (q *WorkerPoolQueue[T]) GetQueueItemNumber() int { func (q *WorkerPoolQueue[T]) FlushWithContext(ctx context.Context, timeout time.Duration) (err error) { if q.isBaseQueueDummy() { - return + return nil } log.Debug("Try to flush queue %q with timeout %v", q.GetName(), timeout) diff --git a/modules/repository/branch.go b/modules/repository/branch.go index 7fd29e3f7d0..bffadd62f4d 100644 --- a/modules/repository/branch.go +++ b/modules/repository/branch.go @@ -77,9 +77,9 @@ func SyncRepoBranchesWithRepo(ctx context.Context, repo *repo_model.Repository, RepoID: repo.ID, Name: branch, CommitID: commit.ID.String(), - CommitMessage: commit.CommitMessage, + CommitMessage: commit.Summary(), PusherID: doerID, - CommitTime: timeutil.TimeStamp(commit.Author.When.Unix()), + CommitTime: timeutil.TimeStamp(commit.Committer.When.Unix()), }) } else if commit.ID.String() != dbb.CommitID { toUpdate = append(toUpdate, &git_model.Branch{ @@ -87,9 +87,9 @@ func SyncRepoBranchesWithRepo(ctx context.Context, repo *repo_model.Repository, RepoID: repo.ID, Name: branch, CommitID: commit.ID.String(), - CommitMessage: commit.CommitMessage, + CommitMessage: commit.Summary(), PusherID: doerID, - CommitTime: timeutil.TimeStamp(commit.Author.When.Unix()), + CommitTime: timeutil.TimeStamp(commit.Committer.When.Unix()), }) } } diff --git a/modules/session/db.go b/modules/session/db.go index 6fdfb81e031..f86f7d1e9cf 100644 --- a/modules/session/db.go +++ b/modules/session/db.go @@ -17,11 +17,11 @@ import ( type DBStore struct { sid string lock sync.RWMutex - data map[interface{}]interface{} + data map[any]any } // NewDBStore creates and returns a DB session store. -func NewDBStore(sid string, kv map[interface{}]interface{}) *DBStore { +func NewDBStore(sid string, kv map[any]any) *DBStore { return &DBStore{ sid: sid, data: kv, @@ -29,7 +29,7 @@ func NewDBStore(sid string, kv map[interface{}]interface{}) *DBStore { } // Set sets value to given key in session. -func (s *DBStore) Set(key, val interface{}) error { +func (s *DBStore) Set(key, val any) error { s.lock.Lock() defer s.lock.Unlock() @@ -38,7 +38,7 @@ func (s *DBStore) Set(key, val interface{}) error { } // Get gets value by given key in session. -func (s *DBStore) Get(key interface{}) interface{} { +func (s *DBStore) Get(key any) any { s.lock.RLock() defer s.lock.RUnlock() @@ -46,7 +46,7 @@ func (s *DBStore) Get(key interface{}) interface{} { } // Delete delete a key from session. -func (s *DBStore) Delete(key interface{}) error { +func (s *DBStore) Delete(key any) error { s.lock.Lock() defer s.lock.Unlock() @@ -79,7 +79,7 @@ func (s *DBStore) Flush() error { s.lock.Lock() defer s.lock.Unlock() - s.data = make(map[interface{}]interface{}) + s.data = make(map[any]any) return nil } @@ -102,9 +102,9 @@ func (p *DBProvider) Read(sid string) (session.RawStore, error) { return nil, err } - var kv map[interface{}]interface{} + var kv map[any]any if len(s.Data) == 0 || s.Expiry.Add(p.maxLifetime) <= timeutil.TimeStampNow() { - kv = make(map[interface{}]interface{}) + kv = make(map[any]any) } else { kv, err = session.DecodeGob(s.Data) if err != nil { @@ -136,9 +136,9 @@ func (p *DBProvider) Regenerate(oldsid, sid string) (_ session.RawStore, err err return nil, err } - var kv map[interface{}]interface{} + var kv map[any]any if len(s.Data) == 0 || s.Expiry.Add(p.maxLifetime) <= timeutil.TimeStampNow() { - kv = make(map[interface{}]interface{}) + kv = make(map[any]any) } else { kv, err = session.DecodeGob(s.Data) if err != nil { diff --git a/modules/session/redis.go b/modules/session/redis.go index 322470743a5..d89d8bc6e21 100644 --- a/modules/session/redis.go +++ b/modules/session/redis.go @@ -35,11 +35,11 @@ type RedisStore struct { prefix, sid string duration time.Duration lock sync.RWMutex - data map[interface{}]interface{} + data map[any]any } // NewRedisStore creates and returns a redis session store. -func NewRedisStore(c redis.UniversalClient, prefix, sid string, dur time.Duration, kv map[interface{}]interface{}) *RedisStore { +func NewRedisStore(c redis.UniversalClient, prefix, sid string, dur time.Duration, kv map[any]any) *RedisStore { return &RedisStore{ c: c, prefix: prefix, @@ -50,7 +50,7 @@ func NewRedisStore(c redis.UniversalClient, prefix, sid string, dur time.Duratio } // Set sets value to given key in session. -func (s *RedisStore) Set(key, val interface{}) error { +func (s *RedisStore) Set(key, val any) error { s.lock.Lock() defer s.lock.Unlock() @@ -59,7 +59,7 @@ func (s *RedisStore) Set(key, val interface{}) error { } // Get gets value by given key in session. -func (s *RedisStore) Get(key interface{}) interface{} { +func (s *RedisStore) Get(key any) any { s.lock.RLock() defer s.lock.RUnlock() @@ -67,7 +67,7 @@ func (s *RedisStore) Get(key interface{}) interface{} { } // Delete delete a key from session. -func (s *RedisStore) Delete(key interface{}) error { +func (s *RedisStore) Delete(key any) error { s.lock.Lock() defer s.lock.Unlock() @@ -100,7 +100,7 @@ func (s *RedisStore) Flush() error { s.lock.Lock() defer s.lock.Unlock() - s.data = make(map[interface{}]interface{}) + s.data = make(map[any]any) return nil } @@ -141,13 +141,13 @@ func (p *RedisProvider) Read(sid string) (session.RawStore, error) { } } - var kv map[interface{}]interface{} + var kv map[any]any kvs, err := p.c.Get(graceful.GetManager().HammerContext(), psid).Result() if err != nil { return nil, err } if len(kvs) == 0 { - kv = make(map[interface{}]interface{}) + kv = make(map[any]any) } else { kv, err = session.DecodeGob([]byte(kvs)) if err != nil { @@ -197,9 +197,9 @@ func (p *RedisProvider) Regenerate(oldsid, sid string) (_ session.RawStore, err return nil, err } - var kv map[interface{}]interface{} + var kv map[any]any if len(kvs) == 0 { - kv = make(map[interface{}]interface{}) + kv = make(map[any]any) } else { kv, err = session.DecodeGob([]byte(kvs)) if err != nil { diff --git a/modules/session/store.go b/modules/session/store.go index 7b0c550ace3..4fa4d2848f1 100644 --- a/modules/session/store.go +++ b/modules/session/store.go @@ -11,9 +11,9 @@ import ( // Store represents a session store type Store interface { - Get(interface{}) interface{} - Set(interface{}, interface{}) error - Delete(interface{}) error + Get(any) any + Set(any, any) error + Delete(any) error } // RegenerateSession regenerates the underlying session and returns the new store diff --git a/modules/session/virtual.go b/modules/session/virtual.go index fcbc44249dc..80352b6e721 100644 --- a/modules/session/virtual.go +++ b/modules/session/virtual.go @@ -62,7 +62,7 @@ func (o *VirtualSessionProvider) Read(sid string) (session.RawStore, error) { if o.provider.Exist(sid) { return o.provider.Read(sid) } - kv := make(map[interface{}]interface{}) + kv := make(map[any]any) kv["_old_uid"] = "0" return NewVirtualStore(o, sid, kv), nil } @@ -107,12 +107,12 @@ type VirtualStore struct { p *VirtualSessionProvider sid string lock sync.RWMutex - data map[interface{}]interface{} + data map[any]any released bool } // NewVirtualStore creates and returns a virtual session store. -func NewVirtualStore(p *VirtualSessionProvider, sid string, kv map[interface{}]interface{}) *VirtualStore { +func NewVirtualStore(p *VirtualSessionProvider, sid string, kv map[any]any) *VirtualStore { return &VirtualStore{ p: p, sid: sid, @@ -121,7 +121,7 @@ func NewVirtualStore(p *VirtualSessionProvider, sid string, kv map[interface{}]i } // Set sets value to given key in session. -func (s *VirtualStore) Set(key, val interface{}) error { +func (s *VirtualStore) Set(key, val any) error { s.lock.Lock() defer s.lock.Unlock() @@ -130,7 +130,7 @@ func (s *VirtualStore) Set(key, val interface{}) error { } // Get gets value by given key in session. -func (s *VirtualStore) Get(key interface{}) interface{} { +func (s *VirtualStore) Get(key any) any { s.lock.RLock() defer s.lock.RUnlock() @@ -138,7 +138,7 @@ func (s *VirtualStore) Get(key interface{}) interface{} { } // Delete delete a key from session. -func (s *VirtualStore) Delete(key interface{}) error { +func (s *VirtualStore) Delete(key any) error { s.lock.Lock() defer s.lock.Unlock() @@ -192,6 +192,6 @@ func (s *VirtualStore) Flush() error { s.lock.Lock() defer s.lock.Unlock() - s.data = make(map[interface{}]interface{}) + s.data = make(map[any]any) return nil } diff --git a/modules/setting/actions.go b/modules/setting/actions.go index 1c8075cd6cc..a13330dcd18 100644 --- a/modules/setting/actions.go +++ b/modules/setting/actions.go @@ -5,6 +5,9 @@ package setting import ( "fmt" + "strings" + + "code.gitea.io/gitea/modules/log" ) // Actions settings @@ -13,13 +16,36 @@ var ( LogStorage *Storage // how the created logs should be stored ArtifactStorage *Storage // how the created artifacts should be stored Enabled bool - DefaultActionsURL string `ini:"DEFAULT_ACTIONS_URL"` + DefaultActionsURL defaultActionsURL `ini:"DEFAULT_ACTIONS_URL"` }{ Enabled: false, - DefaultActionsURL: "https://gitea.com", + DefaultActionsURL: defaultActionsURLGitHub, } ) +type defaultActionsURL string + +func (url defaultActionsURL) URL() string { + switch url { + case defaultActionsURLGitHub: + return "https://github.com" + case defaultActionsURLSelf: + return strings.TrimSuffix(AppURL, "/") + default: + // This should never happen, but just in case, use GitHub as fallback + return "https://github.com" + } +} + +const ( + defaultActionsURLGitHub = "github" // https://github.com + defaultActionsURLSelf = "self" // the root URL of the self-hosted Gitea instance + // DefaultActionsURL only supports GitHub and the self-hosted Gitea. + // It's intentionally not supported more, so please be cautious before adding more like "gitea" or "gitlab". + // If you get some trouble with `uses: username/action_name@version` in your workflow, + // please consider to use `uses: https://the_url_you_want_to_use/username/action_name@version` instead. +) + func loadActionsFrom(rootCfg ConfigProvider) error { sec := rootCfg.Section("actions") err := sec.MapTo(&Actions) @@ -27,6 +53,19 @@ func loadActionsFrom(rootCfg ConfigProvider) error { return fmt.Errorf("failed to map Actions settings: %v", err) } + if urls := string(Actions.DefaultActionsURL); urls != defaultActionsURLGitHub && urls != defaultActionsURLSelf { + url := strings.Split(urls, ",")[0] + if strings.HasPrefix(url, "https://") || strings.HasPrefix(url, "http://") { + log.Error("[actions] DEFAULT_ACTIONS_URL does not support %q as custom URL any longer, fallback to %q", + urls, + defaultActionsURLGitHub, + ) + Actions.DefaultActionsURL = defaultActionsURLGitHub + } else { + return fmt.Errorf("unsupported [actions] DEFAULT_ACTIONS_URL: %q", urls) + } + } + // don't support to read configuration from [actions] Actions.LogStorage, err = getStorage(rootCfg, "actions_log", "", nil) if err != nil { diff --git a/modules/setting/actions_test.go b/modules/setting/actions_test.go index a1cc8fe333b..3645a3f5dad 100644 --- a/modules/setting/actions_test.go +++ b/modules/setting/actions_test.go @@ -8,6 +8,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func Test_getStorageInheritNameSectionTypeForActions(t *testing.T) { @@ -95,3 +96,86 @@ STORAGE_TYPE = minio assert.EqualValues(t, "local", Actions.ArtifactStorage.Type) assert.EqualValues(t, "actions_artifacts", filepath.Base(Actions.ArtifactStorage.Path)) } + +func Test_getDefaultActionsURLForActions(t *testing.T) { + oldActions := Actions + oldAppURL := AppURL + defer func() { + Actions = oldActions + AppURL = oldAppURL + }() + + AppURL = "http://test_get_default_actions_url_for_actions:3000/" + + tests := []struct { + name string + iniStr string + wantErr assert.ErrorAssertionFunc + wantURL string + }{ + { + name: "default", + iniStr: ` +[actions] +`, + wantErr: assert.NoError, + wantURL: "https://github.com", + }, + { + name: "github", + iniStr: ` +[actions] +DEFAULT_ACTIONS_URL = github +`, + wantErr: assert.NoError, + wantURL: "https://github.com", + }, + { + name: "self", + iniStr: ` +[actions] +DEFAULT_ACTIONS_URL = self +`, + wantErr: assert.NoError, + wantURL: "http://test_get_default_actions_url_for_actions:3000", + }, + { + name: "custom url", + iniStr: ` +[actions] +DEFAULT_ACTIONS_URL = https://gitea.com +`, + wantErr: assert.NoError, + wantURL: "https://github.com", + }, + { + name: "custom urls", + iniStr: ` +[actions] +DEFAULT_ACTIONS_URL = https://gitea.com,https://github.com +`, + wantErr: assert.NoError, + wantURL: "https://github.com", + }, + { + name: "invalid", + iniStr: ` +[actions] +DEFAULT_ACTIONS_URL = gitea +`, + wantErr: assert.Error, + wantURL: "https://github.com", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + cfg, err := NewConfigProviderFromData(tt.iniStr) + require.NoError(t, err) + if !tt.wantErr(t, loadActionsFrom(cfg)) { + return + } + assert.EqualValues(t, tt.wantURL, Actions.DefaultActionsURL.URL()) + }) + } +} diff --git a/modules/setting/config_env.go b/modules/setting/config_env.go index 63488037059..e23b64557f8 100644 --- a/modules/setting/config_env.go +++ b/modules/setting/config_env.go @@ -12,10 +12,31 @@ import ( "code.gitea.io/gitea/modules/log" ) +const ( + EnvConfigKeyPrefixGitea = "GITEA__" + EnvConfigKeySuffixFile = "__FILE" +) + const escapeRegexpString = "_0[xX](([0-9a-fA-F][0-9a-fA-F])+)_" var escapeRegex = regexp.MustCompile(escapeRegexpString) +func CollectEnvConfigKeys() (keys []string) { + for _, env := range os.Environ() { + if strings.HasPrefix(env, EnvConfigKeyPrefixGitea) { + k, _, _ := strings.Cut(env, "=") + keys = append(keys, k) + } + } + return keys +} + +func ClearEnvConfigKeys() { + for _, k := range CollectEnvConfigKeys() { + _ = os.Unsetenv(k) + } +} + // decodeEnvSectionKey will decode a portable string encoded Section__Key pair // Portable strings are considered to be of the form [A-Z0-9_]* // We will encode a disallowed value as the UTF8 byte string preceded by _0X and @@ -87,7 +108,7 @@ func decodeEnvironmentKey(prefixGitea, suffixFile, envKey string) (ok bool, sect return ok, section, key, useFileValue } -func EnvironmentToConfig(cfg ConfigProvider, prefixGitea, suffixFile string, envs []string) (changed bool) { +func EnvironmentToConfig(cfg ConfigProvider, envs []string) (changed bool) { for _, kv := range envs { idx := strings.IndexByte(kv, '=') if idx < 0 { @@ -97,7 +118,7 @@ func EnvironmentToConfig(cfg ConfigProvider, prefixGitea, suffixFile string, env // parse the environment variable to config section name and key name envKey := kv[:idx] envValue := kv[idx+1:] - ok, sectionName, keyName, useFileValue := decodeEnvironmentKey(prefixGitea, suffixFile, envKey) + ok, sectionName, keyName, useFileValue := decodeEnvironmentKey(EnvConfigKeyPrefixGitea, EnvConfigKeySuffixFile, envKey) if !ok { continue } diff --git a/modules/setting/config_env_test.go b/modules/setting/config_env_test.go index d574554bcc0..2c1dd2f5c71 100644 --- a/modules/setting/config_env_test.go +++ b/modules/setting/config_env_test.go @@ -72,7 +72,7 @@ func TestDecodeEnvironmentKey(t *testing.T) { func TestEnvironmentToConfig(t *testing.T) { cfg, _ := NewConfigProviderFromData("") - changed := EnvironmentToConfig(cfg, "GITEA__", "__FILE", nil) + changed := EnvironmentToConfig(cfg, nil) assert.False(t, changed) cfg, err := NewConfigProviderFromData(` @@ -81,16 +81,16 @@ key = old `) assert.NoError(t, err) - changed = EnvironmentToConfig(cfg, "GITEA__", "__FILE", []string{"GITEA__sec__key=new"}) + changed = EnvironmentToConfig(cfg, []string{"GITEA__sec__key=new"}) assert.True(t, changed) assert.Equal(t, "new", cfg.Section("sec").Key("key").String()) - changed = EnvironmentToConfig(cfg, "GITEA__", "__FILE", []string{"GITEA__sec__key=new"}) + changed = EnvironmentToConfig(cfg, []string{"GITEA__sec__key=new"}) assert.False(t, changed) tmpFile := t.TempDir() + "/the-file" _ = os.WriteFile(tmpFile, []byte("value-from-file"), 0o644) - changed = EnvironmentToConfig(cfg, "GITEA__", "__FILE", []string{"GITEA__sec__key__FILE=" + tmpFile}) + changed = EnvironmentToConfig(cfg, []string{"GITEA__sec__key__FILE=" + tmpFile}) assert.True(t, changed) assert.Equal(t, "value-from-file", cfg.Section("sec").Key("key").String()) } diff --git a/modules/setting/cron.go b/modules/setting/cron.go index 45bae4dde37..7c4cc442885 100644 --- a/modules/setting/cron.go +++ b/modules/setting/cron.go @@ -6,11 +6,11 @@ package setting import "reflect" // GetCronSettings maps the cron subsection to the provided config -func GetCronSettings(name string, config interface{}) (interface{}, error) { +func GetCronSettings(name string, config any) (any, error) { return getCronSettings(CfgProvider, name, config) } -func getCronSettings(rootCfg ConfigProvider, name string, config interface{}) (interface{}, error) { +func getCronSettings(rootCfg ConfigProvider, name string, config any) (any, error) { if err := rootCfg.Section("cron." + name).MapTo(config); err != nil { return config, err } diff --git a/modules/setting/log_test.go b/modules/setting/log_test.go index c07651f5488..87b14f0b1d8 100644 --- a/modules/setting/log_test.go +++ b/modules/setting/log_test.go @@ -30,7 +30,7 @@ func initLoggersByConfig(t *testing.T, config string) (*log.LoggerManager, func( return manager, manager.Close } -func toJSON(v interface{}) string { +func toJSON(v any) string { b, _ := json.MarshalIndent(v, "", "\t") return string(b) } diff --git a/modules/setting/path.go b/modules/setting/path.go index 163f1d15906..32ed8d81fa3 100644 --- a/modules/setting/path.go +++ b/modules/setting/path.go @@ -171,6 +171,9 @@ func InitWorkPathAndCfgProvider(getEnvFn func(name string) string, args ArgWorkP // only read the config but do not load/init anything more, because the AppWorkPath and CustomPath are not ready InitCfgProvider(tmpCustomConf.Value) + if HasInstallLock(CfgProvider) { + ClearEnvConfigKeys() // if the instance has been installed, do not pass the environment variables to sub-processes + } configWorkPath := ConfigSectionKeyString(CfgProvider.Section(""), "WORK_PATH") if configWorkPath != "" { if !filepath.IsAbs(configWorkPath) { diff --git a/modules/setting/security.go b/modules/setting/security.go index 5f1f9f4ade8..7064d7a008f 100644 --- a/modules/setting/security.go +++ b/modules/setting/security.go @@ -102,7 +102,7 @@ func generateSaveInternalToken(rootCfg ConfigProvider) { func loadSecurityFrom(rootCfg ConfigProvider) { sec := rootCfg.Section("security") - InstallLock = sec.Key("INSTALL_LOCK").MustBool(false) + InstallLock = HasInstallLock(rootCfg) LogInRememberDays = sec.Key("LOGIN_REMEMBER_DAYS").MustInt(7) CookieUserName = sec.Key("COOKIE_USERNAME").MustString("gitea_awesome") SecretKey = loadSecret(sec, "SECRET_KEY_URI", "SECRET_KEY") diff --git a/modules/setting/setting.go b/modules/setting/setting.go index 0d69847dbea..d444d9a0175 100644 --- a/modules/setting/setting.go +++ b/modules/setting/setting.go @@ -183,10 +183,14 @@ func loadRunModeFrom(rootCfg ConfigProvider) { } } +// HasInstallLock checks the install-lock in ConfigProvider directly, because sometimes the config file is not loaded into setting variables yet. +func HasInstallLock(rootCfg ConfigProvider) bool { + return rootCfg.Section("security").Key("INSTALL_LOCK").MustBool(false) +} + func mustCurrentRunUserMatch(rootCfg ConfigProvider) { // Does not check run user when the "InstallLock" is off. - installLock := rootCfg.Section("security").Key("INSTALL_LOCK").MustBool(false) - if installLock { + if HasInstallLock(rootCfg) { currentUser, match := IsRunUserMatchCurrentUser(RunUser) if !match { log.Fatal("Expect user '%s' but current user is: %s", RunUser, currentUser) diff --git a/modules/setting/ssh.go b/modules/setting/ssh.go index a5a9da0b367..bbb7f5ab6cd 100644 --- a/modules/setting/ssh.go +++ b/modules/setting/ssh.go @@ -173,7 +173,7 @@ func loadSSHFrom(rootCfg ConfigProvider) { } } - SSH.AuthorizedKeysBackup = sec.Key("SSH_AUTHORIZED_KEYS_BACKUP").MustBool(true) + SSH.AuthorizedKeysBackup = sec.Key("SSH_AUTHORIZED_KEYS_BACKUP").MustBool(false) SSH.CreateAuthorizedKeysFile = sec.Key("SSH_CREATE_AUTHORIZED_KEYS_FILE").MustBool(true) SSH.AuthorizedPrincipalsBackup = false diff --git a/modules/storage/minio.go b/modules/storage/minio.go index 81774fb9cfa..e2ce09d07a5 100644 --- a/modules/storage/minio.go +++ b/modules/storage/minio.go @@ -173,7 +173,7 @@ func (m minioFileInfo) Mode() os.FileMode { return os.ModePerm } -func (m minioFileInfo) Sys() interface{} { +func (m minioFileInfo) Sys() any { return nil } diff --git a/modules/storage/storage.go b/modules/storage/storage.go index c3396f0c7fb..8f970b5dfcc 100644 --- a/modules/storage/storage.go +++ b/modules/storage/storage.go @@ -20,7 +20,7 @@ var ErrURLNotSupported = errors.New("url method not supported") // ErrInvalidConfiguration is called when there is invalid configuration for a storage type ErrInvalidConfiguration struct { - cfg interface{} + cfg any err error } diff --git a/modules/structs/issue.go b/modules/structs/issue.go index a9fb6c6e797..1aec5cc6b86 100644 --- a/modules/structs/issue.go +++ b/modules/structs/issue.go @@ -140,10 +140,10 @@ const ( // IssueFormField represents a form field // swagger:model type IssueFormField struct { - Type IssueFormFieldType `json:"type" yaml:"type"` - ID string `json:"id" yaml:"id"` - Attributes map[string]interface{} `json:"attributes" yaml:"attributes"` - Validations map[string]interface{} `json:"validations" yaml:"validations"` + Type IssueFormFieldType `json:"type" yaml:"type"` + ID string `json:"id" yaml:"id"` + Attributes map[string]any `json:"attributes" yaml:"attributes"` + Validations map[string]any `json:"validations" yaml:"validations"` } // IssueTemplate represents an issue template for a repository diff --git a/modules/structs/repo_file.go b/modules/structs/repo_file.go index 48fcebe6f87..eb4f1c7dca5 100644 --- a/modules/structs/repo_file.go +++ b/modules/structs/repo_file.go @@ -166,7 +166,7 @@ type FilesResponse struct { // FileDeleteResponse contains information about a repo's file that was deleted type FileDeleteResponse struct { - Content interface{} `json:"content"` // to be set to nil + Content any `json:"content"` // to be set to nil Commit *FileCommitResponse `json:"commit"` Verification *PayloadCommitVerification `json:"verification"` } diff --git a/modules/structs/repo_watch.go b/modules/structs/repo_watch.go index 2e89a9eefcf..0d0b7c4ae04 100644 --- a/modules/structs/repo_watch.go +++ b/modules/structs/repo_watch.go @@ -9,10 +9,10 @@ import ( // WatchInfo represents an API watch status of one repository type WatchInfo struct { - Subscribed bool `json:"subscribed"` - Ignored bool `json:"ignored"` - Reason interface{} `json:"reason"` - CreatedAt time.Time `json:"created_at"` - URL string `json:"url"` - RepositoryURL string `json:"repository_url"` + Subscribed bool `json:"subscribed"` + Ignored bool `json:"ignored"` + Reason any `json:"reason"` + CreatedAt time.Time `json:"created_at"` + URL string `json:"url"` + RepositoryURL string `json:"repository_url"` } diff --git a/modules/svg/svg.go b/modules/svg/svg.go index 6db5c7109b2..0e2f6a00346 100644 --- a/modules/svg/svg.go +++ b/modules/svg/svg.go @@ -49,7 +49,7 @@ func Init() error { } // RenderHTML renders icons - arguments icon name (string), size (int), class (string) -func RenderHTML(icon string, others ...interface{}) template.HTML { +func RenderHTML(icon string, others ...any) template.HTML { size, class := html.ParseSizeAndClass(defaultSize, "", others...) if svgStr, ok := SVGs[icon]; ok { diff --git a/modules/templates/helper.go b/modules/templates/helper.go index 2f2ef440497..2b918f42c09 100644 --- a/modules/templates/helper.go +++ b/modules/templates/helper.go @@ -27,7 +27,7 @@ import ( // NewFuncMap returns functions for injecting to templates func NewFuncMap() template.FuncMap { - return map[string]interface{}{ + return map[string]any{ "DumpVar": dumpVar, // ----------------------------------------------------------------- @@ -142,8 +142,8 @@ func NewFuncMap() template.FuncMap { "DefaultTheme": func() string { return setting.UI.DefaultTheme }, - "NotificationSettings": func() map[string]interface{} { - return map[string]interface{}{ + "NotificationSettings": func() map[string]any { + return map[string]any{ "MinTimeout": int(setting.UI.Notification.MinTimeout / time.Millisecond), "TimeoutStep": int(setting.UI.Notification.TimeoutStep / time.Millisecond), "MaxTimeout": int(setting.UI.Notification.MaxTimeout / time.Millisecond), diff --git a/modules/templates/htmlrenderer.go b/modules/templates/htmlrenderer.go index e0cbc49ff44..d470435b63d 100644 --- a/modules/templates/htmlrenderer.go +++ b/modules/templates/htmlrenderer.go @@ -39,7 +39,7 @@ var ( var ErrTemplateNotInitialized = errors.New("template system is not initialized, check your log for errors") -func (h *HTMLRender) HTML(w io.Writer, status int, name string, data interface{}) error { +func (h *HTMLRender) HTML(w io.Writer, status int, name string, data any) error { if respWriter, ok := w.(http.ResponseWriter); ok { if respWriter.Header().Get("Content-Type") == "" { respWriter.Header().Set("Content-Type", "text/html; charset=utf-8") diff --git a/modules/templates/scopedtmpl/scopedtmpl.go b/modules/templates/scopedtmpl/scopedtmpl.go index a8b67ad0f90..2722ba97a28 100644 --- a/modules/templates/scopedtmpl/scopedtmpl.go +++ b/modules/templates/scopedtmpl/scopedtmpl.go @@ -15,7 +15,7 @@ import ( ) type TemplateExecutor interface { - Execute(wr io.Writer, data interface{}) error + Execute(wr io.Writer, data any) error } type ScopedTemplate struct { diff --git a/modules/templates/util_avatar.go b/modules/templates/util_avatar.go index 3badc97cb9f..9f8f8f87a9d 100644 --- a/modules/templates/util_avatar.go +++ b/modules/templates/util_avatar.go @@ -30,7 +30,7 @@ func AvatarHTML(src string, size int, class, name string) template.HTML { } // Avatar renders user avatars. args: user, size (int), class (string) -func Avatar(ctx context.Context, item interface{}, others ...interface{}) template.HTML { +func Avatar(ctx context.Context, item any, others ...any) template.HTML { size, class := gitea_html.ParseSizeAndClass(avatars.DefaultAvatarPixelSize, avatars.DefaultAvatarClass, others...) switch t := item.(type) { @@ -55,13 +55,13 @@ func Avatar(ctx context.Context, item interface{}, others ...interface{}) templa } // AvatarByAction renders user avatars from action. args: action, size (int), class (string) -func AvatarByAction(ctx context.Context, action *activities_model.Action, others ...interface{}) template.HTML { +func AvatarByAction(ctx context.Context, action *activities_model.Action, others ...any) template.HTML { action.LoadActUser(ctx) return Avatar(ctx, action.ActUser, others...) } // RepoAvatar renders repo avatars. args: repo, size(int), class (string) -func RepoAvatar(repo *repo_model.Repository, others ...interface{}) template.HTML { +func RepoAvatar(repo *repo_model.Repository, others ...any) template.HTML { size, class := gitea_html.ParseSizeAndClass(avatars.DefaultAvatarPixelSize, avatars.DefaultAvatarClass, others...) src := repo.RelAvatarLink() @@ -72,7 +72,7 @@ func RepoAvatar(repo *repo_model.Repository, others ...interface{}) template.HTM } // AvatarByEmail renders avatars by email address. args: email, name, size (int), class (string) -func AvatarByEmail(ctx context.Context, email, name string, others ...interface{}) template.HTML { +func AvatarByEmail(ctx context.Context, email, name string, others ...any) template.HTML { size, class := gitea_html.ParseSizeAndClass(avatars.DefaultAvatarPixelSize, avatars.DefaultAvatarClass, others...) src := avatars.GenerateEmailAvatarFastLink(ctx, email, size*setting.Avatar.RenderedSizeFactor) diff --git a/modules/templates/util_misc.go b/modules/templates/util_misc.go index d11251fcdf6..9cdabeb3ace 100644 --- a/modules/templates/util_misc.go +++ b/modules/templates/util_misc.go @@ -174,7 +174,7 @@ func FilenameIsImage(filename string) bool { return strings.HasPrefix(mimeType, "image/") } -func TabSizeClass(ec interface{}, filename string) string { +func TabSizeClass(ec any, filename string) string { var ( value *editorconfig.Editorconfig ok bool diff --git a/modules/test/context_tests.go b/modules/test/context_tests.go index cf8af32fc69..9e7095e116c 100644 --- a/modules/test/context_tests.go +++ b/modules/test/context_tests.go @@ -154,7 +154,7 @@ func (tr *mockRender) TemplateLookup(tmpl string) (templates.TemplateExecutor, e return nil, nil } -func (tr *mockRender) HTML(w io.Writer, status int, _ string, _ interface{}) error { +func (tr *mockRender) HTML(w io.Writer, status int, _ string, _ any) error { if resp, ok := w.(http.ResponseWriter); ok { resp.WriteHeader(status) } diff --git a/modules/testlogger/testlogger.go b/modules/testlogger/testlogger.go index 6a0cee4a299..4215567c004 100644 --- a/modules/testlogger/testlogger.go +++ b/modules/testlogger/testlogger.go @@ -137,7 +137,7 @@ func PrintCurrentTest(t testing.TB, skip ...int) func() { } // Printf takes a format and args and prints the string to os.Stdout -func Printf(format string, args ...interface{}) { +func Printf(format string, args ...any) { if log.CanColorStdout { for i := 0; i < len(args); i++ { args[i] = log.NewColoredValue(args[i]) diff --git a/modules/translation/i18n/format.go b/modules/translation/i18n/format.go index 637209ad807..e5e221831f5 100644 --- a/modules/translation/i18n/format.go +++ b/modules/translation/i18n/format.go @@ -9,12 +9,12 @@ import ( ) // Format formats provided arguments for a given translated message -func Format(format string, args ...interface{}) (msg string, err error) { +func Format(format string, args ...any) (msg string, err error) { if len(args) == 0 { return format, nil } - fmtArgs := make([]interface{}, 0, len(args)) + fmtArgs := make([]any, 0, len(args)) for _, arg := range args { val := reflect.ValueOf(arg) if val.Kind() == reflect.Slice { diff --git a/modules/translation/i18n/i18n.go b/modules/translation/i18n/i18n.go index 4048c9acd2d..42475545b34 100644 --- a/modules/translation/i18n/i18n.go +++ b/modules/translation/i18n/i18n.go @@ -11,7 +11,7 @@ var DefaultLocales = NewLocaleStore() type Locale interface { // Tr translates a given key and arguments for a language - Tr(trKey string, trArgs ...interface{}) string + Tr(trKey string, trArgs ...any) string // Has reports if a locale has a translation for a given key Has(trKey string) bool } @@ -21,7 +21,7 @@ type LocaleStore interface { io.Closer // Tr translates a given key and arguments for a language - Tr(lang, trKey string, trArgs ...interface{}) string + Tr(lang, trKey string, trArgs ...any) string // Has reports if a locale has a translation for a given key Has(lang, trKey string) bool // SetDefaultLang sets the default language to fall back to diff --git a/modules/translation/i18n/localestore.go b/modules/translation/i18n/localestore.go index aa784e866fc..f5a951a79f1 100644 --- a/modules/translation/i18n/localestore.go +++ b/modules/translation/i18n/localestore.go @@ -86,7 +86,7 @@ func (store *localeStore) SetDefaultLang(lang string) { } // Tr translates content to target language. fall back to default language. -func (store *localeStore) Tr(lang, trKey string, trArgs ...interface{}) string { +func (store *localeStore) Tr(lang, trKey string, trArgs ...any) string { l, _ := store.Locale(lang) return l.Tr(trKey, trArgs...) @@ -119,7 +119,7 @@ func (store *localeStore) Close() error { } // Tr translates content to locale language. fall back to default language. -func (l *locale) Tr(trKey string, trArgs ...interface{}) string { +func (l *locale) Tr(trKey string, trArgs ...any) string { format := trKey idx, ok := l.store.trKeyToIdxMap[trKey] diff --git a/modules/translation/mock.go b/modules/translation/mock.go index 6ce66166aa1..2d0cb173246 100644 --- a/modules/translation/mock.go +++ b/modules/translation/mock.go @@ -14,11 +14,11 @@ func (l MockLocale) Language() string { return "en" } -func (l MockLocale) Tr(s string, _ ...interface{}) string { +func (l MockLocale) Tr(s string, _ ...any) string { return s } -func (l MockLocale) TrN(_cnt interface{}, key1, _keyN string, _args ...interface{}) string { +func (l MockLocale) TrN(_cnt any, key1, _keyN string, _args ...any) string { return key1 } diff --git a/modules/util/error.go b/modules/util/error.go index e67b9977f08..0f3597147ce 100644 --- a/modules/util/error.go +++ b/modules/util/error.go @@ -37,7 +37,7 @@ func (w SilentWrap) Unwrap() error { } // NewSilentWrapErrorf returns an error that formats as the given text but unwraps as the provided error -func NewSilentWrapErrorf(unwrap error, message string, args ...interface{}) error { +func NewSilentWrapErrorf(unwrap error, message string, args ...any) error { if len(args) == 0 { return SilentWrap{Message: message, Err: unwrap} } @@ -45,21 +45,21 @@ func NewSilentWrapErrorf(unwrap error, message string, args ...interface{}) erro } // NewInvalidArgumentErrorf returns an error that formats as the given text but unwraps as an ErrInvalidArgument -func NewInvalidArgumentErrorf(message string, args ...interface{}) error { +func NewInvalidArgumentErrorf(message string, args ...any) error { return NewSilentWrapErrorf(ErrInvalidArgument, message, args...) } // NewPermissionDeniedErrorf returns an error that formats as the given text but unwraps as an ErrPermissionDenied -func NewPermissionDeniedErrorf(message string, args ...interface{}) error { +func NewPermissionDeniedErrorf(message string, args ...any) error { return NewSilentWrapErrorf(ErrPermissionDenied, message, args...) } // NewAlreadyExistErrorf returns an error that formats as the given text but unwraps as an ErrAlreadyExist -func NewAlreadyExistErrorf(message string, args ...interface{}) error { +func NewAlreadyExistErrorf(message string, args ...any) error { return NewSilentWrapErrorf(ErrAlreadyExist, message, args...) } // NewNotExistErrorf returns an error that formats as the given text but unwraps as an ErrNotExist -func NewNotExistErrorf(message string, args ...interface{}) error { +func NewNotExistErrorf(message string, args ...any) error { return NewSilentWrapErrorf(ErrNotExist, message, args...) } diff --git a/modules/util/pack.go b/modules/util/pack.go index 315d9f5066f..7fc074a2b03 100644 --- a/modules/util/pack.go +++ b/modules/util/pack.go @@ -9,7 +9,7 @@ import ( ) // PackData uses gob to encode the given data in sequence -func PackData(data ...interface{}) ([]byte, error) { +func PackData(data ...any) ([]byte, error) { var buf bytes.Buffer enc := gob.NewEncoder(&buf) for _, datum := range data { @@ -21,7 +21,7 @@ func PackData(data ...interface{}) ([]byte, error) { } // UnpackData uses gob to decode the given data in sequence -func UnpackData(buf []byte, data ...interface{}) error { +func UnpackData(buf []byte, data ...any) error { r := bytes.NewReader(buf) enc := gob.NewDecoder(r) for _, datum := range data { diff --git a/modules/util/paginate.go b/modules/util/paginate.go index f2721df1adb..87f31b76ed2 100644 --- a/modules/util/paginate.go +++ b/modules/util/paginate.go @@ -7,7 +7,7 @@ import "reflect" // PaginateSlice cut a slice as per pagination options // if page = 0 it do not paginate -func PaginateSlice(list interface{}, page, pageSize int) interface{} { +func PaginateSlice(list any, page, pageSize int) any { if page <= 0 || pageSize <= 0 { return list } diff --git a/modules/util/rotatingfilewriter/writer.go b/modules/util/rotatingfilewriter/writer.go index 5243bfe3538..c595f49c49b 100644 --- a/modules/util/rotatingfilewriter/writer.go +++ b/modules/util/rotatingfilewriter/writer.go @@ -39,10 +39,10 @@ type RotatingFileWriter struct { cancelReleaseReopen func() } -var ErrorPrintf func(format string, args ...interface{}) +var ErrorPrintf func(format string, args ...any) // errorf tries to print error messages. Since this writer could be used by a logger system, this is the last chance to show the error in some cases -func errorf(format string, args ...interface{}) { +func errorf(format string, args ...any) { if ErrorPrintf != nil { ErrorPrintf("rotatingfilewriter: "+format+"\n", args...) } diff --git a/modules/util/util.go b/modules/util/util.go index 148817bd470..9d5e6c1e893 100644 --- a/modules/util/util.go +++ b/modules/util/util.go @@ -174,7 +174,7 @@ func ToTitleCaseNoLower(s string) string { } // ToInt64 transform a given int into int64. -func ToInt64(number interface{}) (int64, error) { +func ToInt64(number any) (int64, error) { var value int64 switch v := number.(type) { case int: @@ -216,7 +216,7 @@ func ToInt64(number interface{}) (int64, error) { } // ToFloat64 transform a given int into float64. -func ToFloat64(number interface{}) (float64, error) { +func ToFloat64(number any) (float64, error) { var value float64 switch v := number.(type) { case int: @@ -256,3 +256,8 @@ func ToFloat64(number interface{}) (float64, error) { } return value, nil } + +// ToPointer returns the pointer of a copy of any given value +func ToPointer[T any](val T) *T { + return &val +} diff --git a/modules/util/util_test.go b/modules/util/util_test.go index 8cceafa2f66..c5830ce01cb 100644 --- a/modules/util/util_test.go +++ b/modules/util/util_test.go @@ -224,3 +224,12 @@ func TestToTitleCase(t *testing.T) { assert.Equal(t, ToTitleCase(`foo bar baz`), `Foo Bar Baz`) assert.Equal(t, ToTitleCase(`FOO BAR BAZ`), `Foo Bar Baz`) } + +func TestToPointer(t *testing.T) { + assert.Equal(t, "abc", *ToPointer("abc")) + assert.Equal(t, 123, *ToPointer(123)) + abc := "abc" + assert.False(t, &abc == ToPointer(abc)) + val123 := 123 + assert.False(t, &val123 == ToPointer(val123)) +} diff --git a/modules/validation/binding.go b/modules/validation/binding.go index 1f904979ffd..cb0a5063e50 100644 --- a/modules/validation/binding.go +++ b/modules/validation/binding.go @@ -46,7 +46,7 @@ func addGitRefNameBindingRule() { IsMatch: func(rule string) bool { return strings.HasPrefix(rule, "GitRefName") }, - IsValid: func(errs binding.Errors, name string, val interface{}) (bool, binding.Errors) { + IsValid: func(errs binding.Errors, name string, val any) (bool, binding.Errors) { str := fmt.Sprintf("%v", val) if !git.IsValidRefPattern(str) { @@ -64,7 +64,7 @@ func addValidURLBindingRule() { IsMatch: func(rule string) bool { return strings.HasPrefix(rule, "ValidUrl") }, - IsValid: func(errs binding.Errors, name string, val interface{}) (bool, binding.Errors) { + IsValid: func(errs binding.Errors, name string, val any) (bool, binding.Errors) { str := fmt.Sprintf("%v", val) if len(str) != 0 && !IsValidURL(str) { errs.Add([]string{name}, binding.ERR_URL, "Url") @@ -82,7 +82,7 @@ func addValidSiteURLBindingRule() { IsMatch: func(rule string) bool { return strings.HasPrefix(rule, "ValidSiteUrl") }, - IsValid: func(errs binding.Errors, name string, val interface{}) (bool, binding.Errors) { + IsValid: func(errs binding.Errors, name string, val any) (bool, binding.Errors) { str := fmt.Sprintf("%v", val) if len(str) != 0 && !IsValidSiteURL(str) { errs.Add([]string{name}, binding.ERR_URL, "Url") @@ -103,7 +103,7 @@ func addGlobPatternRule() { }) } -func globPatternValidator(errs binding.Errors, name string, val interface{}) (bool, binding.Errors) { +func globPatternValidator(errs binding.Errors, name string, val any) (bool, binding.Errors) { str := fmt.Sprintf("%v", val) if len(str) != 0 { @@ -125,7 +125,7 @@ func addRegexPatternRule() { }) } -func regexPatternValidator(errs binding.Errors, name string, val interface{}) (bool, binding.Errors) { +func regexPatternValidator(errs binding.Errors, name string, val any) (bool, binding.Errors) { str := fmt.Sprintf("%v", val) if _, err := regexp.Compile(str); err != nil { @@ -141,7 +141,7 @@ func addGlobOrRegexPatternRule() { IsMatch: func(rule string) bool { return rule == "GlobOrRegexPattern" }, - IsValid: func(errs binding.Errors, name string, val interface{}) (bool, binding.Errors) { + IsValid: func(errs binding.Errors, name string, val any) (bool, binding.Errors) { str := strings.TrimSpace(fmt.Sprintf("%v", val)) if len(str) >= 2 && strings.HasPrefix(str, "/") && strings.HasSuffix(str, "/") { @@ -157,7 +157,7 @@ func addUsernamePatternRule() { IsMatch: func(rule string) bool { return rule == "Username" }, - IsValid: func(errs binding.Errors, name string, val interface{}) (bool, binding.Errors) { + IsValid: func(errs binding.Errors, name string, val any) (bool, binding.Errors) { str := fmt.Sprintf("%v", val) if !IsValidUsername(str) { errs.Add([]string{name}, ErrUsername, "invalid username") @@ -173,7 +173,7 @@ func addValidGroupTeamMapRule() { IsMatch: func(rule string) bool { return strings.HasPrefix(rule, "ValidGroupTeamMap") }, - IsValid: func(errs binding.Errors, name string, val interface{}) (bool, binding.Errors) { + IsValid: func(errs binding.Errors, name string, val any) (bool, binding.Errors) { _, err := auth.UnmarshalGroupTeamMapping(fmt.Sprintf("%v", val)) if err != nil { errs.Add([]string{name}, ErrInvalidGroupTeamMap, err.Error()) diff --git a/modules/validation/binding_test.go b/modules/validation/binding_test.go index 9ca93784a85..01ff4e34356 100644 --- a/modules/validation/binding_test.go +++ b/modules/validation/binding_test.go @@ -20,7 +20,7 @@ const ( type ( validationTestCase struct { description string - data interface{} + data any expectedErrors binding.Errors } diff --git a/modules/web/middleware/binding.go b/modules/web/middleware/binding.go index 8b74a864d95..d9bcdf3b2ad 100644 --- a/modules/web/middleware/binding.go +++ b/modules/web/middleware/binding.go @@ -25,7 +25,7 @@ func init() { } // AssignForm assign form values back to the template data. -func AssignForm(form interface{}, data map[string]interface{}) { +func AssignForm(form any, data map[string]any) { typ := reflect.TypeOf(form) val := reflect.ValueOf(form) @@ -79,7 +79,7 @@ func GetInclude(field reflect.StructField) string { } // Validate validate TODO: -func Validate(errs binding.Errors, data map[string]interface{}, f Form, l translation.Locale) binding.Errors { +func Validate(errs binding.Errors, data map[string]any, f Form, l translation.Locale) binding.Errors { if errs.Len() == 0 { return errs } diff --git a/modules/web/route.go b/modules/web/route.go index db511aeb185..dc87e112ec6 100644 --- a/modules/web/route.go +++ b/modules/web/route.go @@ -25,12 +25,12 @@ func Bind[T any](_ T) http.HandlerFunc { } // SetForm set the form object -func SetForm(dataStore middleware.ContextDataStore, obj interface{}) { +func SetForm(dataStore middleware.ContextDataStore, obj any) { dataStore.GetData()["__form"] = obj } // GetForm returns the validate form information -func GetForm(dataStore middleware.ContextDataStore) interface{} { +func GetForm(dataStore middleware.ContextDataStore) any { return dataStore.GetData()["__form"] } @@ -38,7 +38,7 @@ func GetForm(dataStore middleware.ContextDataStore) interface{} { type Route struct { R chi.Router curGroupPrefix string - curMiddlewares []interface{} + curMiddlewares []any } // NewRoute creates a new route @@ -48,14 +48,16 @@ func NewRoute() *Route { } // Use supports two middlewares -func (r *Route) Use(middlewares ...interface{}) { +func (r *Route) Use(middlewares ...any) { for _, m := range middlewares { - r.R.Use(toHandlerProvider(m)) + if m != nil { + r.R.Use(toHandlerProvider(m)) + } } } // Group mounts a sub-Router along a `pattern` string. -func (r *Route) Group(pattern string, fn func(), middlewares ...interface{}) { +func (r *Route) Group(pattern string, fn func(), middlewares ...any) { previousGroupPrefix := r.curGroupPrefix previousMiddlewares := r.curMiddlewares r.curGroupPrefix += pattern @@ -79,15 +81,23 @@ func (r *Route) getPattern(pattern string) string { } func (r *Route) wrapMiddlewareAndHandler(h []any) ([]func(http.Handler) http.Handler, http.HandlerFunc) { - handlerProviders := make([]func(http.Handler) http.Handler, 0, len(r.curMiddlewares)+len(h)) + handlerProviders := make([]func(http.Handler) http.Handler, 0, len(r.curMiddlewares)+len(h)+1) for _, m := range r.curMiddlewares { - handlerProviders = append(handlerProviders, toHandlerProvider(m)) + if m != nil { + handlerProviders = append(handlerProviders, toHandlerProvider(m)) + } } for _, m := range h { - handlerProviders = append(handlerProviders, toHandlerProvider(m)) + if h != nil { + handlerProviders = append(handlerProviders, toHandlerProvider(m)) + } } middlewares := handlerProviders[:len(handlerProviders)-1] handlerFunc := handlerProviders[len(handlerProviders)-1](nil).ServeHTTP + mockPoint := RouteMockPoint(MockAfterMiddlewares) + if mockPoint != nil { + middlewares = append(middlewares, mockPoint) + } return middlewares, handlerFunc } @@ -111,53 +121,53 @@ func (r *Route) Mount(pattern string, subR *Route) { } // Any delegate requests for all methods -func (r *Route) Any(pattern string, h ...interface{}) { +func (r *Route) Any(pattern string, h ...any) { middlewares, handlerFunc := r.wrapMiddlewareAndHandler(h) r.R.With(middlewares...).HandleFunc(r.getPattern(pattern), handlerFunc) } // RouteMethods delegate special methods, it is an alias of "Methods", while the "pattern" is the first parameter -func (r *Route) RouteMethods(pattern, methods string, h ...interface{}) { +func (r *Route) RouteMethods(pattern, methods string, h ...any) { r.Methods(methods, pattern, h) } // Delete delegate delete method -func (r *Route) Delete(pattern string, h ...interface{}) { +func (r *Route) Delete(pattern string, h ...any) { r.Methods("DELETE", pattern, h) } // Get delegate get method -func (r *Route) Get(pattern string, h ...interface{}) { +func (r *Route) Get(pattern string, h ...any) { r.Methods("GET", pattern, h) } // GetOptions delegate get and options method -func (r *Route) GetOptions(pattern string, h ...interface{}) { +func (r *Route) GetOptions(pattern string, h ...any) { r.Methods("GET,OPTIONS", pattern, h) } // PostOptions delegate post and options method -func (r *Route) PostOptions(pattern string, h ...interface{}) { +func (r *Route) PostOptions(pattern string, h ...any) { r.Methods("POST,OPTIONS", pattern, h) } // Head delegate head method -func (r *Route) Head(pattern string, h ...interface{}) { +func (r *Route) Head(pattern string, h ...any) { r.Methods("HEAD", pattern, h) } // Post delegate post method -func (r *Route) Post(pattern string, h ...interface{}) { +func (r *Route) Post(pattern string, h ...any) { r.Methods("POST", pattern, h) } // Put delegate put method -func (r *Route) Put(pattern string, h ...interface{}) { +func (r *Route) Put(pattern string, h ...any) { r.Methods("PUT", pattern, h) } // Patch delegate patch method -func (r *Route) Patch(pattern string, h ...interface{}) { +func (r *Route) Patch(pattern string, h ...any) { r.Methods("PATCH", pattern, h) } @@ -172,7 +182,7 @@ func (r *Route) NotFound(h http.HandlerFunc) { } // Combo delegates requests to Combo -func (r *Route) Combo(pattern string, h ...interface{}) *Combo { +func (r *Route) Combo(pattern string, h ...any) *Combo { return &Combo{r, pattern, h} } @@ -180,35 +190,35 @@ func (r *Route) Combo(pattern string, h ...interface{}) *Combo { type Combo struct { r *Route pattern string - h []interface{} + h []any } // Get delegates Get method -func (c *Combo) Get(h ...interface{}) *Combo { +func (c *Combo) Get(h ...any) *Combo { c.r.Get(c.pattern, append(c.h, h...)...) return c } // Post delegates Post method -func (c *Combo) Post(h ...interface{}) *Combo { +func (c *Combo) Post(h ...any) *Combo { c.r.Post(c.pattern, append(c.h, h...)...) return c } // Delete delegates Delete method -func (c *Combo) Delete(h ...interface{}) *Combo { +func (c *Combo) Delete(h ...any) *Combo { c.r.Delete(c.pattern, append(c.h, h...)...) return c } // Put delegates Put method -func (c *Combo) Put(h ...interface{}) *Combo { +func (c *Combo) Put(h ...any) *Combo { c.r.Put(c.pattern, append(c.h, h...)...) return c } // Patch delegates Patch method -func (c *Combo) Patch(h ...interface{}) *Combo { +func (c *Combo) Patch(h ...any) *Combo { c.r.Patch(c.pattern, append(c.h, h...)...) return c } diff --git a/modules/web/routemock.go b/modules/web/routemock.go new file mode 100644 index 00000000000..cb41f63b91a --- /dev/null +++ b/modules/web/routemock.go @@ -0,0 +1,61 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package web + +import ( + "net/http" + + "code.gitea.io/gitea/modules/setting" +) + +// MockAfterMiddlewares is a general mock point, it's between middlewares and the handler +const MockAfterMiddlewares = "MockAfterMiddlewares" + +var routeMockPoints = map[string]func(next http.Handler) http.Handler{} + +// RouteMockPoint registers a mock point as a middleware for testing, example: +// +// r.Use(web.RouteMockPoint("my-mock-point-1")) +// r.Get("/foo", middleware2, web.RouteMockPoint("my-mock-point-2"), middleware2, handler) +// +// Then use web.RouteMock to mock the route execution. +// It only takes effect in testing mode (setting.IsInTesting == true). +func RouteMockPoint(pointName string) func(next http.Handler) http.Handler { + if !setting.IsInTesting { + return nil + } + routeMockPoints[pointName] = nil + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if h := routeMockPoints[pointName]; h != nil { + h(next).ServeHTTP(w, r) + } else { + next.ServeHTTP(w, r) + } + }) + } +} + +// RouteMock uses the registered mock point to mock the route execution, example: +// +// defer web.RouteMockReset() +// web.RouteMock(web.MockAfterMiddlewares, func(ctx *context.Context) { +// ctx.WriteResponse(...) +// } +// +// Then the mock function will be executed as a middleware at the mock point. +// It only takes effect in testing mode (setting.IsInTesting == true). +func RouteMock(pointName string, h any) { + if _, ok := routeMockPoints[pointName]; !ok { + panic("route mock point not found: " + pointName) + } + routeMockPoints[pointName] = toHandlerProvider(h) +} + +// RouteMockReset resets all mock points (no mock anymore) +func RouteMockReset() { + for k := range routeMockPoints { + routeMockPoints[k] = nil // keep the keys because RouteMock will check the keys to make sure no misspelling + } +} diff --git a/modules/web/routemock_test.go b/modules/web/routemock_test.go new file mode 100644 index 00000000000..04c6d1d82e5 --- /dev/null +++ b/modules/web/routemock_test.go @@ -0,0 +1,70 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package web + +import ( + "net/http" + "net/http/httptest" + "testing" + + "code.gitea.io/gitea/modules/setting" + + "github.com/stretchr/testify/assert" +) + +func TestRouteMock(t *testing.T) { + setting.IsInTesting = true + + r := NewRoute() + middleware1 := func(resp http.ResponseWriter, req *http.Request) { + resp.Header().Set("X-Test-Middleware1", "m1") + } + middleware2 := func(resp http.ResponseWriter, req *http.Request) { + resp.Header().Set("X-Test-Middleware2", "m2") + } + handler := func(resp http.ResponseWriter, req *http.Request) { + resp.Header().Set("X-Test-Handler", "h") + } + r.Get("/foo", middleware1, RouteMockPoint("mock-point"), middleware2, handler) + + // normal request + recorder := httptest.NewRecorder() + req, err := http.NewRequest("GET", "http://localhost:8000/foo", nil) + assert.NoError(t, err) + r.ServeHTTP(recorder, req) + assert.Len(t, recorder.Header(), 3) + assert.EqualValues(t, "m1", recorder.Header().Get("X-Test-Middleware1")) + assert.EqualValues(t, "m2", recorder.Header().Get("X-Test-Middleware2")) + assert.EqualValues(t, "h", recorder.Header().Get("X-Test-Handler")) + RouteMockReset() + + // mock at "mock-point" + RouteMock("mock-point", func(resp http.ResponseWriter, req *http.Request) { + resp.Header().Set("X-Test-MockPoint", "a") + resp.WriteHeader(http.StatusOK) + }) + recorder = httptest.NewRecorder() + req, err = http.NewRequest("GET", "http://localhost:8000/foo", nil) + assert.NoError(t, err) + r.ServeHTTP(recorder, req) + assert.Len(t, recorder.Header(), 2) + assert.EqualValues(t, "m1", recorder.Header().Get("X-Test-Middleware1")) + assert.EqualValues(t, "a", recorder.Header().Get("X-Test-MockPoint")) + RouteMockReset() + + // mock at MockAfterMiddlewares + RouteMock(MockAfterMiddlewares, func(resp http.ResponseWriter, req *http.Request) { + resp.Header().Set("X-Test-MockPoint", "b") + resp.WriteHeader(http.StatusOK) + }) + recorder = httptest.NewRecorder() + req, err = http.NewRequest("GET", "http://localhost:8000/foo", nil) + assert.NoError(t, err) + r.ServeHTTP(recorder, req) + assert.Len(t, recorder.Header(), 3) + assert.EqualValues(t, "m1", recorder.Header().Get("X-Test-Middleware1")) + assert.EqualValues(t, "m2", recorder.Header().Get("X-Test-Middleware2")) + assert.EqualValues(t, "b", recorder.Header().Get("X-Test-MockPoint")) + RouteMockReset() +} diff --git a/modules/web/routing/context.go b/modules/web/routing/context.go index 0d5e7655431..c5e85a415b1 100644 --- a/modules/web/routing/context.go +++ b/modules/web/routing/context.go @@ -37,7 +37,7 @@ func MarkLongPolling(resp http.ResponseWriter, req *http.Request) { } // UpdatePanicError updates a context's error info, a panic may be recovered by other middlewares, but we still need to know that. -func UpdatePanicError(ctx context.Context, err interface{}) { +func UpdatePanicError(ctx context.Context, err any) { record, ok := ctx.Value(contextKey).(*requestRecord) if !ok { return diff --git a/modules/web/routing/funcinfo.go b/modules/web/routing/funcinfo.go index 499bc2c7aad..f4e9731a630 100644 --- a/modules/web/routing/funcinfo.go +++ b/modules/web/routing/funcinfo.go @@ -35,7 +35,7 @@ func (info *FuncInfo) String() string { } // GetFuncInfo returns the FuncInfo for a provided function and friendlyname -func GetFuncInfo(fn interface{}, friendlyName ...string) *FuncInfo { +func GetFuncInfo(fn any, friendlyName ...string) *FuncInfo { // ptr represents the memory position of the function passed in as v. // This will be used as program counter in FuncForPC below ptr := reflect.ValueOf(fn).Pointer() diff --git a/modules/web/routing/requestrecord.go b/modules/web/routing/requestrecord.go index 34a2d338930..cc61fc4d348 100644 --- a/modules/web/routing/requestrecord.go +++ b/modules/web/routing/requestrecord.go @@ -24,5 +24,5 @@ type requestRecord struct { // mutable fields isLongPolling bool funcInfo *FuncInfo - panicError interface{} + panicError any } diff --git a/options/license/Asterisk-exception b/options/license/Asterisk-exception new file mode 100644 index 00000000000..88253f12d36 --- /dev/null +++ b/options/license/Asterisk-exception @@ -0,0 +1,5 @@ +In addition, when this program is distributed with Asterisk in any +form that would qualify as a 'combined work' or as a 'derivative work' +(but not mere aggregation), you can redistribute and/or modify the +combination under the terms of the license provided with that copy +of Asterisk, instead of the license terms granted here. diff --git a/options/license/Boehm-GC b/options/license/Boehm-GC new file mode 100644 index 00000000000..95427c0b590 --- /dev/null +++ b/options/license/Boehm-GC @@ -0,0 +1,12 @@ +Copyright (c) ... + +THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED +OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + +Permission is hereby granted to use or copy this program +for any purpose, provided the above notices are retained on all copies. +Permission to modify the code and to distribute modified code is granted, +provided the above notices are retained, and a notice that the code was +modified is included with the above copyright notice. + +A few files have other copyright holders. diff --git a/options/license/Inner-Net-2.0 b/options/license/Inner-Net-2.0 new file mode 100644 index 00000000000..f8db440f2ae --- /dev/null +++ b/options/license/Inner-Net-2.0 @@ -0,0 +1,34 @@ +The Inner Net License, Version 2.00 + +The author(s) grant permission for redistribution and use in source and +binary forms, with or without modification, of the software and documentation +provided that the following conditions are met: + +0. If you receive a version of the software that is specifically labelled + as not being for redistribution (check the version message and/or README), + you are not permitted to redistribute that version of the software in any + way or form. +1. All terms of the all other applicable copyrights and licenses must be + followed. +2. Redistributions of source code must retain the authors' copyright + notice(s), this list of conditions, and the following disclaimer. +3. Redistributions in binary form must reproduce the authors' copyright + notice(s), this list of conditions, and the following disclaimer in the + documentation and/or other materials provided with the distribution. +4. [The copyright holder has authorized the removal of this clause.] +5. Neither the name(s) of the author(s) nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY ITS AUTHORS AND CONTRIBUTORS ``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 THE AUTHORS OR CONTRIBUTORS 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. + +If these license terms cause you a real problem, contact the author. diff --git a/options/license/LLGPL b/options/license/LLGPL new file mode 100644 index 00000000000..889d0b92e5b --- /dev/null +++ b/options/license/LLGPL @@ -0,0 +1,56 @@ +Preamble to the Gnu Lesser General Public License + +Copyright (c) 2016 Franz Inc., Berkeley, CA 94704 + +The concept of the GNU Lesser General Public License version 2.1 ("LGPL") +has been adopted to govern the use and distribution of above-mentioned +application. However, the LGPL uses terminology that is more appropriate +for a program written in C than one written in Lisp. Nevertheless, the +LGPL can still be applied to a Lisp program if certain clarifications +are made. This document details those clarifications. Accordingly, the +license for the open-source Lisp applications consists of this document +plus the LGPL. Wherever there is a conflict between this document and +the LGPL, this document takes precedence over the LGPL. + +A "Library" in Lisp is a collection of Lisp functions, data and foreign +modules. The form of the Library can be Lisp source code (for processing +by an interpreter) or object code (usually the result of compilation of +source code or built with some other mechanisms). Foreign modules are +object code in a form that can be linked into a Lisp executable. When +we speak of functions we do so in the most general way to include, in +addition, methods and unnamed functions. Lisp "data" is also a general +term that includes the data structures resulting from defining Lisp +classes. A Lisp application may include the same set of Lisp objects +as does a Library, but this does not mean that the application is +necessarily a "work based on the Library" it contains. + +The Library consists of everything in the distribution file set before +any modifications are made to the files. If any of the functions or +classes in the Library are redefined in other files, then those +redefinitions ARE considered a work based on the Library. If additional +methods are added to generic functions in the Library, those additional +methods are NOT considered a work based on the Library. If Library classes +are subclassed, these subclasses are NOT considered a work based on the Library. +If the Library is modified to explicitly call other functions that are neither +part of Lisp itself nor an available add-on module to Lisp, then the functions +called by the modified Library ARE considered a work based on the Library. +The goal is to ensure that the Library will compile and run without getting +undefined function errors. + +It is permitted to add proprietary source code to the Library, but it must +be done in a way such that the Library will still run without that proprietary +code present. Section 5 of the LGPL distinguishes between the case of a +library being dynamically linked at runtime and one being statically linked +at build time. Section 5 of the LGPL states that the former results in an +executable that is a "work that uses the Library." Section 5 of the LGPL +states that the latter results in one that is a "derivative of the Library", +which is therefore covered by the LGPL. Since Lisp only offers one choice, +which is to link the Library into an executable at build time, we declare that, +for the purpose applying the LGPL to the Library, an executable that results +from linking a "work that uses the Library" with the Library is considered a +"work that uses the Library" and is therefore NOT covered by the LGPL. + +Because of this declaration, section 6 of LGPL is not applicable to the Library. +However, in connection with each distribution of this executable, you must also +deliver, in accordance with the terms and conditions of the LGPL, the source code +of Library (or your derivative thereof) that is incorporated into this executable. diff --git a/options/license/Linux-man-pages-one-para b/options/license/Linux-man-pages-1-para similarity index 100% rename from options/license/Linux-man-pages-one-para rename to options/license/Linux-man-pages-1-para diff --git a/options/license/Linux-man-pages-copyleft-2-para b/options/license/Linux-man-pages-copyleft-2-para new file mode 100644 index 00000000000..b0871675b3c --- /dev/null +++ b/options/license/Linux-man-pages-copyleft-2-para @@ -0,0 +1,8 @@ +Permission is granted to make and distribute verbatim copies of this +manual provided the copyright notice and this permission notice are +preserved on all copies. + +Permission is granted to copy and distribute modified versions of this +manual under the conditions for verbatim copying, provided that the +entire resulting derived work is distributed under the terms of a +permission notice identical to this one. diff --git a/options/license/Linux-man-pages-copyleft-var b/options/license/Linux-man-pages-copyleft-var new file mode 100644 index 00000000000..1742303553b --- /dev/null +++ b/options/license/Linux-man-pages-copyleft-var @@ -0,0 +1,16 @@ +Permission is granted to make and distribute verbatim copies of +this manual provided the copyright notice and this permission +notice are preserved on all copies. + +Permission is granted to copy and distribute modified versions of +this manual under the conditions for verbatim copying, provided +that the entire resulting derived work is distributed under the +terms of a permission notice identical to this one. + +Since the Linux kernel and libraries are constantly changing, this +manual page may be incorrect or out-of-date. The author(s) assume +no responsibility for errors or omissions, or for damages resulting +from the use of the information contained herein. + +Formatted or processed versions of this manual, if unaccompanied by +the source, must acknowledge the copyright and authors of this work. diff --git a/options/license/OPL-UK-3.0 b/options/license/OPL-UK-3.0 new file mode 100644 index 00000000000..ee8ca4dd81c --- /dev/null +++ b/options/license/OPL-UK-3.0 @@ -0,0 +1,114 @@ +United Kingdom Open Parliament Licence v3.0 + +Open Parliament Licence + +You are encouraged to use and re-use the information that +is available under this licence freely and flexibly, with +only a few conditions. Using information under this licence + +Use of copyright and database right material made +available under this licence (the ‘information’) indicates +your acceptance of the terms and conditions below. + +The Licensor grants you a worldwide, royalty-free, +perpetual, non-exclusive licence to use the +information subject to the conditions below. + +This licence does not affect your freedom under +fair dealing or fair use or any other copyright +or database right exceptions and limitations. + +You are free to: + * copy, publish, distribute and transmit the information + * adapt the information + * exploit the information commercially and non-commercially, + for example, by combining it with other information, + or by including it in your own product or application + +You must (where you do any of the above): + * acknowledge the source of the information in your + product or application by including the following + attribution statement and, where possible, provide a + link to this licence: Contains Parliamentary information + licensed under the Open Parliament Licence v3.0. + +These are important conditions of this licence and +if you fail to comply with them the rights granted to +you under this licence, or any similar licence granted +by the Licensor, will end automatically. + +Exemptions + +This licence does not cover the use of: + * personal data in the information; + * information that has neither been published nor disclosed + under information access legislation (including the + Freedom of Information Acts for the UK and Scotland) by or + with the consent of the Licensor; + * the Royal Arms and the Crowned Portcullis; + * third party rights the Licensor is not authorised to license; + * information subject to other intellectual property rights, + including patents, trademarks, and design rights + +Non-endorsment + +This licence does not grant you any right to use the +information in a way that suggests any official status or +that the Licensor endorses you or your use of the Information. + +No warranty + +The information is licensed ‘as is’ and the +Licensor excludes all representations, warranties, +obligations and liabilities in relation to the +information to the maximum extent permitted by law. +The Licensor is not liable for any errors or omissions in +the information and shall not be liable for any loss, injury +or damage of any kind caused by its use. The Licensor does +not guarantee the continued supply of the information. + +Governing law + +This licence is governed by the laws of England and Wales. + +Definitions + +In this licence, the terms below have the following meanings: + +‘Information’ means information protected by copyright +or by database right (for example, literary and +artistic works, content, data and source code) +offered for use under the terms of this licence. + +‘Information Provider’ means either House of Parliament. + +‘Licensor’ means— +(a) in relation to copyright, the Speaker of the House of +Commons and the Clerk of the Parliaments representing +the House of Commons and House of Lords respectively, and +(b) in relation to database right, the Corporate +Officer of the House of Commons and the Corporate +Officer of the House of Lords respectively. + +‘Use’ means doing any act which is restricted by copyright +or database right, whether in the original medium or in any +other medium, and includes without limitation distributing, +copying, adapting and modifying as may be technically +necessary to use it in a different mode or format. + +‘You’ means the natural or legal person, or body of persons +corporate or incorporate, acquiring rights under this licence. + +About the Open Parliament Licence + +This is version 3.0 of the Open Parliament Licence. The +Licensor may, from time to time, issue new versions of the +Open Parliament Licence. However, you may continue to use +information licensed under this version should you wish to do so. + +The information licensed under the Open Parliament +Licence includes Parliamentary information in which +Crown copyright subsists. Further context, best practice +and guidance relating to the re-use of public sector +information can be found in the UK Government Licensing +Framework section on The National Archives website. diff --git a/options/license/dtoa b/options/license/dtoa new file mode 100644 index 00000000000..6de2b084fc6 --- /dev/null +++ b/options/license/dtoa @@ -0,0 +1,14 @@ +The author of this software is David M. Gay. + +Copyright (c) 1991, 2000, 2001 by Lucent Technologies. + +Permission to use, copy, modify, and distribute this software for any +purpose without fee is hereby granted, provided that this entire notice +is included in all copies of any software which is or includes a copy +or modification of this software and in all copies of the supporting +documentation for such software. + +THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED +WARRANTY. IN PARTICULAR, NEITHER THE AUTHOR NOR LUCENT MAKES ANY +REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY +OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. diff --git a/options/locale/locale_cs-CZ.ini b/options/locale/locale_cs-CZ.ini index da550884c79..0559b1f12f1 100644 --- a/options/locale/locale_cs-CZ.ini +++ b/options/locale/locale_cs-CZ.ini @@ -383,7 +383,7 @@ hi_user_x=Ahoj %s, activate_account=Prosíme, aktivujte si váš účet activate_account.title=%s, prosím aktivujte si váš účet -activate_account.text_1=Ahoj %[1]s, děkujeme za registraci na %[2]! +activate_account.text_1=Ahoj %[1]s, děkujeme za registraci na %[2]s! activate_account.text_2=Pro aktivaci vašeho účtu do %s klikněte na následující odkaz: activate_email=Ověřte vaši e-mailovou adresu @@ -402,8 +402,8 @@ reset_password.text=Klikněte prosím na následující odkaz pro obnovení vaš register_success=Registrace byla úspěšná -issue_assigned.pull=@%[1]s vás přiřadil/a k požadavku na natažení %[2]v repozitáři %[3]s. -issue_assigned.issue=@%[1]s vás přiřadil/a k úkolu %[2]v repozitáři %[3]s. +issue_assigned.pull=@%[1]s vás přiřadil/a k požadavku na natažení %[2]s repozitáři %[3]s. +issue_assigned.issue=@%[1]s vás přiřadil/a k úkolu %[2]s repozitáři %[3]s. issue.x_mentioned_you=@%s vás zmínil/a: issue.action.force_push=%[1]s vynutil/a nahrání %[2]s z %[3]s do %[4]s. @@ -2310,7 +2310,7 @@ members.member_role=Role člena: members.owner=Vlastník members.member=Člen members.remove=Smazat -members.remove.detail=Odstranit %[1]s z %[2]? +members.remove.detail=Odstranit %[1]s z %[2]s? members.leave=Opustit members.leave.detail=Opustit %s? members.invite_desc=Přidat nového člena do %s: diff --git a/options/locale/locale_de-DE.ini b/options/locale/locale_de-DE.ini index 10f83b34aad..0cffa249381 100644 --- a/options/locale/locale_de-DE.ini +++ b/options/locale/locale_de-DE.ini @@ -19,6 +19,7 @@ active_stopwatch=Aktive Zeiterfassung create_new=Erstellen… user_profile_and_more=Profil und Einstellungen… signed_in_as=Angemeldet als +enable_javascript=Diese Website benötigt JavaScript. toc=Inhaltsverzeichnis licenses=Lizenzen return_to_gitea=Zurück zu Gitea @@ -92,6 +93,7 @@ copy_url=URL kopieren copy_branch=Branchenname kopieren copy_success=Kopiert! copy_error=Kopieren fehlgeschlagen +copy_type_unsupported=Dieser Dateityp kann nicht kopiert werden write=Verfassen preview=Vorschau @@ -104,22 +106,42 @@ error=Fehler error404=Die Seite, die du gerade versuchst aufzurufen, existiert entweder nicht oder du bist nicht berechtigt, diese anzusehen. never=Niemals +unknown=Unbekannt rss_feed=RSS Feed +artifacts=Artefakte +concept_system_global=Global concept_code_repository=Repository concept_user_organization=Organisation +show_log_seconds=Sekunden anzeigen [aria] +footer.software=Über die Software +footer.links=Links [heatmap] +less=Weniger +more=Mehr [editor] +buttons.heading.tooltip=Titel hinzufügen +buttons.quote.tooltip=Text zitieren +buttons.code.tooltip=Code hinzufügen +buttons.link.tooltip=Link hinzufügen +buttons.list.ordered.tooltip=Nummerierte Liste hinzufügen +buttons.list.task.tooltip=Aufgabenliste hinzufügen +buttons.mention.tooltip=Benutzer oder Team erwähnen +buttons.ref.tooltip=Issue oder Pull-Request referenzieren +buttons.enable_monospace_font=Monospace-Schrift aktivieren +buttons.disable_monospace_font=Monospace-Schrift deaktivieren [filter] +string.asc=A–Z +string.desc=Z–A [error] occurred=Ein Fehler ist aufgetreten @@ -228,6 +250,7 @@ install_btn_confirm=Gitea installieren test_git_failed=Fehler beim Test des „git“-Befehls: %v sqlite3_not_available=Diese Gitea-Version unterstützt SQLite3 nicht. Bitte lade die offizielle binäre Version von %s herunter (nicht die „gobuild“-Version). invalid_db_setting=Datenbankeinstellungen sind ungültig: %v +invalid_db_table=Die Datenbanktabelle "%s" ist ungültig: %v invalid_repo_path=Repository-Verzeichnis ist ungültig: %v invalid_app_data_path=Der App-Daten-Pfad ist ungültig: %v run_user_not_match=Der „Ausführen als“-Benutzername ist nicht der aktuelle Benutzername: %s -> %s @@ -245,6 +268,7 @@ default_enable_timetracking_popup=Zeiterfassung standardmäßig für neue Reposi no_reply_address=Versteckte E-Mail-Domain no_reply_address_helper=Domain-Name für Benutzer mit einer versteckten Emailadresse. Zum Beispiel wird der Benutzername „Joe“ in Git als „joe@noreply.example.org“ protokolliert, wenn die versteckte E-Mail-Domain „noreply.example.org“ festgelegt ist. password_algorithm=Passwort Hashing Algorithmus +invalid_password_algorithm=Ungültiger Passwort-Hash-Algorithmus [home] uname_holder=E-Mail-Adresse oder Benutzername @@ -279,6 +303,7 @@ users=Benutzer organizations=Organisationen search=Suche code=Code +search.type.tooltip=Suchmodus search.fuzzy=Ähnlich search.match=Genau code_search_unavailable=Derzeit ist die Code-Suche nicht verfügbar. Bitte wende dich an den Website-Administrator. @@ -545,6 +570,7 @@ location=Standort update_theme=Theme ändern update_profile=Profil aktualisieren update_language=Sprache aktualisieren +update_language_not_found=Sprache "%s" ist nicht verfügbar. update_language_success=Sprache wurde aktualisiert. update_profile_success=Dein Profil wurde aktualisiert. change_username=Dein Benutzername wurde geändert. @@ -655,6 +681,7 @@ gpg_token_help=Du kannst eine Signatur wie folgt generieren: gpg_token_code=echo "%s" | gpg -a --default-key %s --detach-sig gpg_token_signature=GPG Textsignatur (armored signature) key_signature_gpg_placeholder=Beginnt mit '-----BEGIN PGP SIGNATURE-----' +verify_gpg_key_success=GPG-Schlüssel "%s" wurde verifiziert. ssh_key_verified=Verifizierter Schlüssel ssh_key_verified_long=Der Schlüssel wurde mit einem Token verifiziert. Er kann verwendet werden, um Commits zu verifizieren, die mit irgendeiner für diesen Nutzer aktivierten E-Mail-Adresse und irgendeiner Identität dieses Schlüssels übereinstimmen. ssh_key_verify=Verifizieren @@ -975,6 +1002,7 @@ download_archive=Repository herunterladen no_desc=Keine Beschreibung quick_guide=Kurzanleitung clone_this_repo=Dieses Repository klonen +cite_this_repo=Dieses Repository zitieren create_new_repo_command=Erstelle ein neues Repository von der Kommandozeile aus push_exist_repo=Bestehendes Repository via Kommandozeile pushen empty_message=Dieses Repository hat keinen Inhalt. @@ -993,6 +1021,7 @@ issues=Issues pulls=Pull-Requests project_board=Projekte packages=Pakete +actions=Actions labels=Label org_labels_desc=Labels der Organisationsebene, die mit allen Repositories in dieser Organisation verwendet werden können org_labels_desc_manage=verwalten @@ -1012,6 +1041,7 @@ file_view_rendered=Ansicht rendern file_view_raw=Originalformat anzeigen file_permalink=Permalink file_too_large=Die Datei ist zu groß zum Anzeigen. +invisible_runes_header=`Diese Datei enthält unsichtbare Unicode-Zeichen!` ambiguous_character=`%[1]c [U+%04[1]X] kann mit %[2]c [U+%04[2]X] verwechselt werden` escape_control_characters=Escapen @@ -1032,6 +1062,7 @@ download_file=Datei herunterladen normal_view=Normale Ansicht line=zeile lines=Zeilen +from_comment=(Kommentar) editor.add_file=Datei hinzufügen editor.new_file=Neue Datei @@ -1046,6 +1077,7 @@ editor.must_be_on_a_branch=Du musst dich in einem Branch befinden, um Änderunge editor.fork_before_edit=Du musst dieses Repository forken, um Änderungen an dieser Datei vorzuschlagen oder vorzunehmen. editor.delete_this_file=Datei löschen editor.must_have_write_access=Du benötigst Schreibzugriff, um Änderungen an dieser Datei vorzuschlagen oder vorzunehmen. +editor.file_delete_success=Datei "%s" wurde gelöscht. editor.name_your_file=Dateinamen eingeben… editor.filename_help=Füge einen Ordner hinzu, indem du seinen Namen und anschließend '/' eingibst. Entferne einen Ordner indem du die Zurücktaste am Anfang des Feldes drückst. editor.or=oder @@ -1681,6 +1713,7 @@ activity.git_stats_deletion_n=%d Löschungen search=Suchen search.search_repo=Repository durchsuchen +search.type.tooltip=Suchmodus search.fuzzy=Ähnlich search.match=Genau search.results=Suchergebnisse für „%s“ in %s @@ -2158,6 +2191,8 @@ branch.delete_html=Branch löschen branch.delete_desc=Das Löschen eines Branches ist permanent. Es KANN NICHT rückgängig gemacht werden. Fortfahren? branch.create_branch=Erstelle Branch %s branch.deleted_by=Von %s gelöscht +branch.restore=Branch "%s" wiederherstellen +branch.download=Branch "%s" herunterladen branch.included_desc=Dieser Branch ist im Standard-Branch enthalten branch.included=Enthalten branch.create_new_branch=Branch aus Branch erstellen: @@ -2165,12 +2200,15 @@ branch.confirm_create_branch=Branch erstellen branch.confirm_rename_branch=Branch umbennen branch.create_branch_operation=Branch erstellen branch.new_branch=Neue Branch erstellen +branch.new_branch_from=Neuen Branch von "%s" erstellen branch.renamed=Branch %s wurde in %s umbenannt. tag.create_tag=Tag %s erstellen tag.create_tag_operation=Tag erstellen tag.confirm_create_tag=Tag erstellen +tag.create_tag_from=Neuen Tag von "%s" erstellen +tag.create_success=Tag "%s" wurde erstellt. topic.manage_topics=Themen verwalten topic.done=Fertig @@ -2407,6 +2445,7 @@ users.created=Registriert am users.last_login=Letzte Anmeldung users.never_login=Hat sich noch nie eingeloggt users.send_register_notify=Benutzer-Registrierungsbenachrichtigung senden +users.new_success=Der Account "%s" wurde erstellt. users.edit=Bearbeiten users.auth_source=Authentifizierungsquelle users.local=Lokal @@ -2770,7 +2809,7 @@ monitor.queue.review=Konfiguration überprüfen monitor.queue.review_add=Worker hinzufügen/prüfen monitor.queue.settings.title=Pool-Einstellungen monitor.queue.settings.maxnumberworkers=Maximale Anzahl an Workern -monitor.queue.settings.maxnumberworkers.placeholder=Derzeit %[1]v +monitor.queue.settings.maxnumberworkers.placeholder=Derzeit %[1]d monitor.queue.settings.maxnumberworkers.error=Die Anzahl der Worker muss eine Zahl sein monitor.queue.settings.submit=Einstellungen aktualisieren monitor.queue.settings.changed=Einstellungen aktualisiert @@ -2979,6 +3018,7 @@ settings.delete.description=Das Löschen eines Pakets ist dauerhaft und kann nic settings.delete.notice=Du bist dabei, %s (%s) zu löschen. Dieser Vorgang ist unwiderruflich. Bist du sicher? settings.delete.success=Das Paket wurde gelöscht. settings.delete.error=Löschen des Pakets fehlgeschlagen. +owner.settings.cargo.initialize=Index initialisieren owner.settings.cleanuprules.enabled=Aktiviert [secrets] @@ -2986,8 +3026,10 @@ value=Wert name=Name [actions] +actions=Actions +status.unknown=Unbekannt runners.id=ID runners.name=Name @@ -2997,10 +3039,16 @@ runners.labels=Labels runners.task_list.run=Ausführen runners.task_list.repository=Repository runners.task_list.commit=Commit +runners.status.unspecified=Unbekannt +runners.status.idle=Inaktiv runners.status.active=Aktiv +runners.status.offline=Offline runners.version=Version +runs.all_workflows=Alle Workflows runs.commit=Commit +runs.pushed_by=Gepushed von +runs.no_matching_runner_helper=Kein passender Runner: %s [projects] diff --git a/options/locale/locale_el-GR.ini b/options/locale/locale_el-GR.ini index 288181afcdf..e7bd6a9884c 100644 --- a/options/locale/locale_el-GR.ini +++ b/options/locale/locale_el-GR.ini @@ -79,6 +79,8 @@ milestones=Ορόσημα ok=OK cancel=Ακύρωση +rerun=Επανεκτέλεση +rerun_all=Επανεκτέλεση όλων save=Αποθήκευση add=Προσθήκη add_all=Προσθήκη Όλων @@ -113,11 +115,19 @@ unknown=Άγνωστη rss_feed=Ροή RSS +pin=Στήριξη +unpin=Άφεση +artifacts=Αντικείμενα +concept_system_global=Γενικό +concept_user_individual=Ατομικό concept_code_repository=Αποθετήριο concept_user_organization=Οργανισμός +show_timestamps=Εμφάνιση χρονοσημάνσεων +show_log_seconds=Εμφάνιση δευτερολέπτων +show_full_screen=Εμφάνιση πλήρους οθόνης [aria] navbar=Γραμμή Πλοήγησης @@ -314,6 +324,7 @@ repos=Αποθετήρια users=Χρήστες organizations=Οργανισμοί search=Αναζήτηση +go_to=Μετάβαση σε code=Κώδικας search.type.tooltip=Τύπος αναζήτησης search.fuzzy=Fuzzy @@ -408,7 +419,7 @@ hi_user_x=Γειά σου %s, activate_account=Παρακαλώ ενεργοποιήστε το λογαριασμό σας activate_account.title=%s, παρακαλώ ενεργοποιήστε το λογαριασμό σας -activate_account.text_1=Γεια σας %[1]s, ευχαριστούμε για την εγγραφή στο %[2]! +activate_account.text_1=Γεια σας %[1]s, ευχαριστούμε για την εγγραφή στο %[2]s! activate_account.text_2=Παρακαλούμε κάντε κλικ στον παρακάτω σύνδεσμο για να ενεργοποιήσετε το λογαριασμό σας μέσα σε %s: activate_email=Επιβεβαιώστε τη διεύθυνση email σας @@ -462,7 +473,7 @@ repo.collaborator.added.subject=%s σας πρόσθεσε στο %s repo.collaborator.added.text=Έχετε προστεθεί ως συνεργάτης του αποθετηρίου: team_invite.subject=%[1]s σας προσκάλεσε να συμμετέχετε στον οργανισμό %[2]s -team_invite.text_1=%[1]s σας προσκάλεσε να συμμετέχετε στην ομάδα %[2]s στον οργανισμός %[3]. +team_invite.text_1=%[1]s σας προσκάλεσε να συμμετέχετε στην ομάδα %[2]s στον οργανισμός %[3]s. team_invite.text_2=Παρακαλώ κάντε κλικ στον παρακάτω σύνδεσμο για να συμμετάσχετε στην ομάδα: team_invite.text_3=Σημείωση: Αυτή η πρόσκληση προοριζόταν για %[1]s. Αν δεν περιμένατε αυτή την πρόσκληση, μπορείτε να αγνοήσετε αυτό το email. @@ -517,6 +528,7 @@ lang_select_error=Επιλέξτε μια γλώσσα από τη λίστα. username_been_taken=Το όνομα χρήστη χρησιμοποιείται ήδη. username_change_not_local_user=Δεν επιτρέπεται στους μη τοπικούς χρήστες να αλλάξουν το όνομα χρήστη τους. +username_has_not_been_changed=Το όνομα χρήστη δεν άλλαξε repo_name_been_taken=Το όνομα του αποθετηρίου χρησιμοποιείται ήδη. repository_force_private=Η επιλογή Μόνο Ιδιωτικά είναι ενεργοποιημένη: τα ιδιωτικά αποθετήρια δεν μπορούν να δημοσιευθούν. repository_files_already_exist=Αρχεία υπάρχουν ήδη για αυτό το αποθετήριο. Επικοινωνήστε με το διαχειριστή του συστήματος. @@ -565,6 +577,7 @@ target_branch_not_exist=Ο κλάδος προορισμού δεν υπάρχε [user] change_avatar=Αλλαγή του avatar σας… +joined_on=Εγγράφηκε την %s repositories=Αποθετήρια activity=Δημόσια Δραστηριότητα followers=Ακόλουθοι @@ -760,6 +773,8 @@ ssh_principal_deletion_desc=Η διαγραφή μιας αρχής πιστοπ ssh_key_deletion_success=Το SSH κλειδί έχει διαγραφεί. gpg_key_deletion_success=Το κλειδί GPG έχει διαγραφεί. ssh_principal_deletion_success=Η αρχή πιστοποιητικού έχει διαγραφεί. +added_on=Προστέθηκαν στις %s +valid_until_date=Έγκυρο μέχρι τη %s valid_forever=Έγκυρο για πάντα last_used=Τελευταία χρήση στις no_activity=Καμία πρόσφατη δραστηριότητα @@ -791,6 +806,13 @@ access_token_deletion_cancel_action=Άκυρο access_token_deletion_confirm_action=Διαγραφή access_token_deletion_desc=Η διαγραφή ενός διακριτικού θα ανακαλέσει οριστικά την πρόσβαση στο λογαριασμό σας για εφαρμογές που το χρησιμοποιούν. Συνέχεια; delete_token_success=Το διακριτικό έχει διαγραφεί. Οι εφαρμογές που το χρησιμοποιούν δεν έχουν πλέον πρόσβαση στο λογαριασμό σας. +repo_and_org_access=Πρόσβαση στο Αποθετήριο και Οργανισμό +permissions_public_only=Δημόσια μόνο +permissions_access_all=Όλα (δημόσια, ιδιωτικά, και περιορισμένα) +select_permissions=Επιλέξτε δικαιώματα +scoped_token_desc=Το επιλεγμένο διακριτικό περιορίζει την ταυτοποίηση μόνο στις αντίστοιχες διαδρομές API. Συμβουλευτείτε την τεκμηρίωση για περισσότερες πληροφορίες. +at_least_one_permission=Πρέπει να επιλέξετε τουλάχιστον ένα δικαίωμα για να δημιουργήσετε ένα διακριτικό +permissions_list=Δικαιώματα: manage_oauth2_applications=Διαχείριση Εφαρμογών Oauth2 edit_oauth2_application=Επεξεργασία Εφαρμογής Oauth2 @@ -804,6 +826,7 @@ create_oauth2_application_success=Δημιουργήσατε επιτυχώς μ update_oauth2_application_success=Ενημερώσατε επιτυχώς την εφαρμογή OAuth2. oauth2_application_name=Όνομα Εφαρμογής oauth2_confidential_client=Εμπιστευτικός Πελάτης. Επιλέξτε το για εφαρμογές που διατηρούν το μυστικό κωδικό κρυφό, όπως πχ οι εφαρμογές ιστού. Μην επιλέγετε για εγγενείς εφαρμογές, συμπεριλαμβανομένων εφαρμογών επιφάνειας εργασίας και εφαρμογών για κινητά. +oauth2_redirect_uris=URI Ανακατεύθυνσης. Χρησιμοποιήστε μια νέα γραμμή για κάθε URI. save_application=Αποθήκευση oauth2_client_id=Ταυτότητα Πελάτη oauth2_client_secret=Μυστικό Πελάτη @@ -949,6 +972,7 @@ mirror_password_blank_placeholder=(Μη ορισμένο) mirror_password_help=Αλλάξτε το όνομα χρήστη για να διαγράψετε έναν αποθηκευμένο κωδικό πρόσβασης. watchers=Παρατηρητές stargazers=Θαυμαστές +stars_remove_warning=Αυτό θα αφαιρέσει όλα τα αστέρια από αυτό το αποθετήριο. forks=Forks reactions_more=και %d περισσότερα unit_disabled=Ο διαχειριστής του ιστότοπου έχει απενεργοποιήσει αυτήν την ενότητα αποθετηρίου. @@ -992,6 +1016,7 @@ template.one_item=Πρέπει να επιλέξετε τουλάχιστον έ template.invalid=Πρέπει να επιλέξετε ένα πρότυπο αποθετήριο archive.title=Αυτό το αποθετήριο αρχειοθετήθηκε. Μπορείτε να δείτε αρχεία και να το κλωνοποιήσετε, αλλά δεν μπορείτε να γράψετε ή να ανοίξετε ζητήματα/pull-requests. +archive.title_date=Αυτό το αποθετήριο έχει αρχειοθετηθεί τη %s. Μπορείτε να προβάλετε αρχεία και να κλωνοποιήσετε, αλλά δεν μπορείτε να ωθήσετε ή να ανοίξετε ζητήματα και pull-requests. archive.issue.nocomment=Αυτό το αποθετήριο αρχειοθετήθηκε. Δεν μπορείτε να σχολιάσετε σε ζητήματα. archive.pull.nocomment=Αυτό το repo αρχειοθετήθηκε. Δεν μπορείτε να σχολιάσετε στα pull requests. @@ -1033,6 +1058,7 @@ migrated_from_fake=Μεταφέρθηκε από %[1]s migrate.migrate=Μεταφορά Από %s migrate.migrating=Γίνεται μεταφορά από %s... migrate.migrating_failed=Η μετεγρατάσταση από %s απέτυχε. +migrate.migrating_failed.error=Αποτυχία μεταφοράς: %s migrate.migrating_failed_no_addr=Η μεταφορά απέτυχε. migrate.github.description=Μεταφορά δεδομένων από το github.com ή άλλους διακομιστές GitHub. migrate.git.description=Μεταφορά μόνο του αποθετηρίου από μια οποιαδήποτε υπηρεσία Git. @@ -1049,6 +1075,8 @@ migrate.migrating_labels=Μεταφορά Σημάτων migrate.migrating_releases=Μεταφορά Κυκλοφοριών migrate.migrating_issues=Μετανάστευση Θεμάτων migrate.migrating_pulls=Μεταφορά Pull Requests +migrate.cancel_migrating_title=Ακύρωση Μεταφοράς +migrate.cancel_migrating_confirm=Θέλετε να ακυρώσετε αυτή τη μεταφορά; mirror_from=είδωλο του forked_from=forked από @@ -1178,6 +1206,8 @@ editor.filename_is_invalid=Το όνομα αρχείου δεν είναι έγ editor.branch_does_not_exist=Ο κλάδος "%s" δεν υπάρχει σε αυτό το αποθετήριο. editor.branch_already_exists=Ο κλάδος "%s" υπάρχει ήδη σε αυτό το αποθετήριο. editor.directory_is_a_file=Το όνομα φακέλου "%s" χρησιμοποιείται ήδη ως όνομα αρχείου σε αυτό το αποθετήριο. +editor.file_is_a_symlink=`Το "%s" είναι συμβολικός σύνδεσμος. Οι συμβολικοί σύνδεσμοι δεν μπορούν να επεξεργαστούν στην ενσωματωμένη εφαρμογή` +editor.filename_is_a_directory=Το όνομα αρχείου "%s" χρησιμοποιείται ήδη ως όνομα φακέλου σε αυτό το αποθετήριο. editor.file_editing_no_longer_exists=Το αρχείο "%s" που επεξεργάζεται, δεν υπάρχει πλέον σε αυτό το αποθετήριο. editor.file_deleting_no_longer_exists=Το αρχείο "%s" που διαγράφεται, δεν υπάρχει πλέον σε αυτό το αποθετήριο. editor.file_changed_while_editing=Τα περιεχόμενα του αρχείου άλλαξαν από τότε που ξεκίνησε η επεξεργασία. Κάντε κλικ εδώ για να τα δείτε ή Υποβολή Αλλαγών ξανά για να τα αντικαταστήσετε. @@ -1343,6 +1373,10 @@ issues.filter_label_exclude=`Χρησιμοποιήστε alt + github.com/gobwas/glob για τη σύνταξη του μοτίβου. Πχ: .drone.yml, /docs/**/*.txt. settings.protect_unprotected_file_patterns=Μοτίβα μη προστατευμένων αρχείων (διαχωρισμένα με ερωτηματικό ';'): @@ -2384,10 +2450,13 @@ branch.protected_deletion_failed=Ο κλάδος "%s" προστατεύεται branch.default_deletion_failed=Ο κλάδος "%s" είναι ο προεπιλεγμένος κλάδος. Δεν μπορεί να διαγραφεί. branch.restore=`Επαναφορά του Κλάδου "%s"` branch.download=`Λήψη του Κλάδου "%s"` +branch.rename=`Μετονομασία Κλάδου "%s"` branch.included_desc=Αυτός ο κλάδος είναι μέρος του προεπιλεγμένου κλάδου branch.included=Περιλαμβάνεται branch.create_new_branch=Δημιουργία κλάδου από κλάδο: branch.confirm_create_branch=Δημιουργία κλάδου +branch.warning_rename_default_branch=Μετονομάζετε τον προεπιλεγμένο κλάδο. +branch.rename_branch_to=Μετονομασία του "%s" σε: branch.confirm_rename_branch=Μετονομασία κλάδου branch.create_branch_operation=Δημιουργία κλάδου branch.new_branch=Δημιουργία νέου κλάδου @@ -2565,7 +2634,7 @@ dashboard.task.process=Εργασία: %[1]s dashboard.task.cancelled=Εργασία: %[1]s ακυρώθηκε: %[3]s dashboard.task.error=Σφάλμα στην Εργασία: %[1]s: %[3]s dashboard.task.finished=Εργασία: %[1]s που εκκινήθηκε από %[2]s τελείωσε -dashboard.task.unknown=Άγνωστη εργασία: %[1] +dashboard.task.unknown=Άγνωστη εργασία: %[1]s dashboard.cron.started=Εκκίνηση Προγραμματισμένης Εργασίας: %[1]s dashboard.cron.process=Προγραμματισμένη Εργασία: %[1]s dashboard.cron.cancelled=Προγραμματισμένη Εργασία: %s ακυρώθηκε: %[3]s @@ -2946,6 +3015,7 @@ config.mailer_sendmail_timeout=Χρονικό Όριο Sendmail config.mailer_use_dummy=Ψεύτικο config.test_email_placeholder=Email (π.χ. test@example.com) config.send_test_mail=Αποστολή Δοκιμαστικού Email +config.send_test_mail_submit=Αποστολή config.test_mail_failed=Αποτυχία αποστολής ενός δοκιμαστικού email στο"%s": %v config.test_mail_sent=Στάλθηκε ένα δοκιμαστικό email στο "%s". @@ -2985,13 +3055,16 @@ config.git_pull_timeout=Χρονικό Όριο Pull config.git_gc_timeout=Χρονικό Όριο Λειτουργίας GC config.log_config=Ρύθμιση Καταγραφών +config.logger_name_fmt=Καταγραφέας: %s config.disabled_logger=Απενεργοποιημένο config.access_log_mode=Λειτουργία Καταγραφών Πρόσβασης +config.access_log_template=Πρότυπο Καταγραφής Προσβάσεων config.xorm_log_sql=Καταγραφή SQL config.get_setting_failed=Αποτυχία λήψης ρύθμισης %s config.set_setting_failed=Αποτυχία ορισμού της ρύθμισης %s +monitor.stats=Στατιστικά monitor.cron=Προγραμματισμένες Εργασίες monitor.name=Όνομα @@ -3001,6 +3074,8 @@ monitor.previous=Προηγούμενη Ώρα monitor.execute_times=Εκτελέσεις monitor.process=Εκτελούμενες Διεργασίες monitor.stacktrace=Ιχνηλατήσεις Στοίβας +monitor.processes_count=%d Διεργασίες +monitor.download_diagnosis_report=Λήψη αναφοράς διάγνωσης monitor.desc=Περιγραφή monitor.start=Ώρα Έναρξης monitor.execute_time=Χρόνος Εκτέλεσης @@ -3021,11 +3096,14 @@ monitor.queue.numberinqueue=Πλήθος Ουράς monitor.queue.review=Εξέταση Ρυθμίσεων monitor.queue.review_add=Εξέταση/Προσθήκη Εργατών monitor.queue.settings.title=Ρυθμίσεις Δεξαμενής +monitor.queue.settings.desc=Οι δεξαμενές αυξάνονται δυναμικά όταν υπάρχει φραγή της ουράς των εργατών τους. monitor.queue.settings.maxnumberworkers=Μέγιστος Αριθμός Εργατών monitor.queue.settings.maxnumberworkers.placeholder=Αυτή τη στιγμή %[1]d monitor.queue.settings.maxnumberworkers.error=Ο μέγιστος αριθμός εργατών πρέπει να είναι αριθμός monitor.queue.settings.submit=Ενημέρωση Ρυθμίσεων monitor.queue.settings.changed=Οι Ρυθμίσεις Ενημερώθηκαν +monitor.queue.settings.remove_all_items=Αφαίρεση όλων +monitor.queue.settings.remove_all_items_done=Όλα τα αντικείμενα στην ουρά αφαιρέθηκαν. notices.system_notice_list=Ειδοποιήσεις Συστήματος notices.view_detail_header=Προβολή Λεπτομερειών Ειδοποίησης @@ -3135,7 +3213,7 @@ error.unit_not_allowed=Δεν σας επιτρέπεται να έχετε πρ title=Πακέτα desc=Διαχείριση πακέτων μητρώου. empty=Δεν υπάρχουν πακέτα ακόμα. -empty.documentation=Για περισσότερες πληροφορίες σχετικά με το μητρώο πακέτων, ανατρέξτε στην τεκμηρίωση. +empty.documentation=Για περισσότερες πληροφορίες σχετικά με το μητρώο πακέτων, ανατρέξτε στην τεκμηρίωση. empty.repo=Μήπως ανεβάσατε ένα πακέτο, αλλά δεν εμφανίζεται εδώ; Πηγαίνετε στις ρυθμίσεις πακέτων και συνδέστε το σε αυτό το αποθετήριο. filter.type=Τύπος filter.type.all=Όλα @@ -3160,9 +3238,15 @@ versions=Εκδόσεις versions.view_all=Προβολή όλων dependency.id=ID dependency.version=Έκδοση +alpine.registry=Ρυθμίστε αυτό το μητρώο προσθέτοντας το url στο αρχείο /etc/apk/repositories: +alpine.registry.key=Αποθηκεύστε το δημόσιο κλειδί RSA του μητρώου στο φάκελο /etc/apk/keys/ για να επαληθεύσετε την υπογραφή ευρετηρίου: +alpine.registry.info=Επιλέξτε $branch και $repository από την παρακάτω λίστα. alpine.install=Για να εγκαταστήσετε το πακέτο, εκτελέστε την ακόλουθη εντολή: +alpine.documentation=Για περισσότερες πληροφορίες σχετικά με το μητρώο Alpine, ανατρέξτε στην τεκμηρίωση. +alpine.repository=Πληροφορίες Αποθετηρίου alpine.repository.branches=Κλάδοι alpine.repository.repositories=Αποθετήρια +alpine.repository.architectures=Αρχιτεκτονικές cargo.registry=Ρυθμίστε αυτό το μητρώο στις ρυθμίσεις του Cargo (για παράδειγμα ~/.cargo/config.toml): cargo.install=Για να εγκαταστήσετε το πακέτο χρησιμοποιώντας το Cargo, εκτελέστε την ακόλουθη εντολή: cargo.documentation=Για περισσότερες πληροφορίες σχετικά με το μητρώο Cargo, ανατρέξτε στην τεκμηρίωση. @@ -3195,11 +3279,21 @@ container.layers=Στρώματα Εικόνας container.labels=Ετικέτες container.labels.key=Κλειδί container.labels.value=Τιμή +cran.registry=Ρυθμίστε αυτό το μητρώο στο αρχείο Rprofile.site: cran.install=Για να εγκαταστήσετε το πακέτο, εκτελέστε την ακόλουθη εντολή: +cran.documentation=Για περισσότερες πληροφορίες σχετικά με το μητρώο CRAN, ανατρέξτε στην τεκμηρίωση. debian.registry=Ρυθμίστε αυτό το μητρώο από τη γραμμή εντολών: +debian.registry.info=Επιλέξτε $distribution και $component από την παρακάτω λίστα. debian.install=Για να εγκαταστήσετε το πακέτο, εκτελέστε την ακόλουθη εντολή: +debian.documentation=Για περισσότερες πληροφορίες σχετικά με το μητρώο Debian, ανατρέξτε στην τεκμηρίωση. +debian.repository=Πληροφορίες Αποθετηρίου +debian.repository.distributions=Διανομές +debian.repository.components=Συστατικά +debian.repository.architectures=Αρχιτεκτονικές generic.download=Λήψη πακέτου από τη γραμμή εντολών: generic.documentation=Για περισσότερες πληροφορίες σχετικά με το γενικό μητρώο, ανατρέξτε στην τεκμηρίωση. +go.install=Εγκαταστήστε το πακέτο από τη γραμμή εντολών: +go.documentation=Για περισσότερες πληροφορίες σχετικά με το μητρώο Go, ανατρέξτε στην τεκμηρίωση. helm.registry=Ρυθμίστε αυτό το μητρώο από τη γραμμή εντολών: helm.install=Για να εγκαταστήσετε το πακέτο, εκτελέστε την ακόλουθη εντολή: helm.documentation=Για περισσότερες πληροφορίες σχετικά με το μητρώο Helm, ανατρέξτε στην τεκμηρίωση. @@ -3228,6 +3322,7 @@ pypi.install=Για να εγκαταστήσετε το πακέτο χρησι pypi.documentation=Για περισσότερες πληροφορίες σχετικά με το μητρώο PyPI, ανατρέξτε στην τεκμηρίωση. rpm.registry=Ρυθμίστε αυτό το μητρώο από τη γραμμή εντολών: rpm.install=Για να εγκαταστήσετε το πακέτο, εκτελέστε την ακόλουθη εντολή: +rpm.documentation=Για περισσότερες πληροφορίες σχετικά με το μητρώο RPM, ανατρέξτε στην τεκμηρίωση. rubygems.install=Για να εγκαταστήσετε το πακέτο χρησιμοποιώντας το gem, εκτελέστε την ακόλουθη εντολή: rubygems.install2=ή προσθέστε το στο Gemfile: rubygems.dependencies.runtime=Εξαρτήσεις Εκτέλεσης @@ -3300,6 +3395,7 @@ deletion=Αφαίρεση μυστικού deletion.description=Η αφαίρεση ενός μυστικού είναι μόνιμη και δεν μπορεί να αναιρεθεί. Συνέχεια; deletion.success=Το μυστικό έχει αφαιρεθεί. deletion.failed=Αποτυχία αφαίρεσης μυστικού. +management=Διαχείριση Μυστικών [actions] actions=Δράσεις diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index df4d95458d8..746b313ba32 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -224,7 +224,7 @@ repo_path_helper = Remote Git repositories will be saved to this directory. lfs_path = Git LFS Root Path lfs_path_helper = Files tracked by Git LFS will be stored in this directory. Leave empty to disable. run_user = Run As Username -run_user_helper = Enter the operating system username that Gitea runs as. Note that this user must have access to the repository root path. +run_user_helper = The operating system username that Gitea runs as. Note that this user must have access to the repository root path. domain = Server Domain domain_helper = Domain or host address for the server. ssh_port = SSH Server Port @@ -296,6 +296,8 @@ invalid_password_algorithm = Invalid password hash algorithm password_algorithm_helper = Set the password hashing algorithm. Algorithms have differing requirements and strength. The argon2 algorithm is rather secure but uses a lot of memory and may be inappropriate for small systems. enable_update_checker = Enable Update Checker enable_update_checker_helper = Checks for new version releases periodically by connecting to gitea.io. +env_config_keys = Environment Configuration +env_config_keys_prompt = The following environment variables will also be applied to your configuration file: [home] uname_holder = Username or Email Address @@ -1439,6 +1441,7 @@ issues.next = Next issues.open_title = Open issues.closed_title = Closed issues.draft_title = Draft +issues.num_comments_1 = %d comment issues.num_comments = %d comments issues.commented_at = `commented %s` issues.delete_comment_confirm = Are you sure you want to delete this comment? @@ -1769,6 +1772,8 @@ pulls.auto_merge_canceled_schedule_comment = `canceled auto merging this pull re pulls.delete.title = Delete this pull request? pulls.delete.text = Do you really want to delete this pull request? (This will permanently remove all content. Consider closing it instead, if you intend to keep it archived) +pulls.recently_pushed_new_branches = You pushed on branch %[1]s %[2]s + milestones.new = New Milestone milestones.closed = Closed %s milestones.update_ago = Updated %s @@ -2653,7 +2658,7 @@ dashboard.task.finished=Task: %[1]s started by %[2]s has finished dashboard.task.unknown=Unknown task: %[1]s dashboard.cron.started=Started Cron: %[1]s dashboard.cron.process=Cron: %[1]s -dashboard.cron.cancelled=Cron: %s cancelled: %[3]s +dashboard.cron.cancelled=Cron: %[1]s cancelled: %[3]s dashboard.cron.error=Error in Cron: %s: %[3]s dashboard.cron.finished=Cron: %[1]s has finished dashboard.delete_inactive_accounts = Delete all unactivated accounts @@ -3241,6 +3246,7 @@ desc = Manage repository packages. empty = There are no packages yet. empty.documentation = For more information on the package registry, see the documentation. empty.repo = Did you upload a package, but it's not shown here? Go to package settings and link it to this repo. +registry.documentation = For more information on the %s registry, see the documentation. filter.type = Type filter.type.all = All filter.no_result = Your filter produced no results. @@ -3268,38 +3274,31 @@ alpine.registry = Setup this registry by adding the url in your /etc/apk/r alpine.registry.key = Download the registry public RSA key into the /etc/apk/keys/ folder to verify the index signature: alpine.registry.info = Choose $branch and $repository from the list below. alpine.install = To install the package, run the following command: -alpine.documentation = For more information on the Alpine registry, see the documentation. alpine.repository = Repository Info alpine.repository.branches = Branches alpine.repository.repositories = Repositories alpine.repository.architectures = Architectures cargo.registry = Setup this registry in the Cargo configuration file (for example ~/.cargo/config.toml): cargo.install = To install the package using Cargo, run the following command: -cargo.documentation = For more information on the Cargo registry, see the documentation. cargo.details.repository_site = Repository Site cargo.details.documentation_site = Documentation Site chef.registry = Setup this registry in your ~/.chef/config.rb file: chef.install = To install the package, run the following command: -chef.documentation = For more information on the Chef registry, see the documentation. composer.registry = Setup this registry in your ~/.composer/config.json file: composer.install = To install the package using Composer, run the following command: -composer.documentation = For more information on the Composer registry, see the documentation. composer.dependencies = Dependencies composer.dependencies.development = Development Dependencies conan.details.repository = Repository conan.registry = Setup this registry from the command line: conan.install = To install the package using Conan, run the following command: -conan.documentation = For more information on the Conan registry, see the documentation. conda.registry = Setup this registry as a Conda repository in your .condarc file: conda.install = To install the package using Conda, run the following command: -conda.documentation = For more information on the Conda registry, see the documentation. conda.details.repository_site = Repository Site conda.details.documentation_site = Documentation Site container.details.type = Image Type container.details.platform = Platform container.pull = Pull the image from the command line: container.digest = Digest: -container.documentation = For more information on the Container registry, see the documentation. container.multi_arch = OS / Arch container.layers = Image Layers container.labels = Labels @@ -3307,61 +3306,47 @@ container.labels.key = Key container.labels.value = Value cran.registry = Setup this registry in your Rprofile.site file: cran.install = To install the package, run the following command: -cran.documentation = For more information on the CRAN registry, see the documentation. debian.registry = Setup this registry from the command line: debian.registry.info = Choose $distribution and $component from the list below. debian.install = To install the package, run the following command: -debian.documentation = For more information on the Debian registry, see the documentation. debian.repository = Repository Info debian.repository.distributions = Distributions debian.repository.components = Components debian.repository.architectures = Architectures generic.download = Download package from the command line: -generic.documentation = For more information on the generic registry, see the documentation. go.install = Install the package from the command line: -go.documentation = For more information on the Go registry, see the documentation. helm.registry = Setup this registry from the command line: helm.install = To install the package, run the following command: -helm.documentation = For more information on the Helm registry, see the documentation. maven.registry = Setup this registry in your project pom.xml file: maven.install = To use the package include the following in the dependencies block in the pom.xml file: maven.install2 = Run via command line: maven.download = To download the dependency, run via command line: -maven.documentation = For more information on the Maven registry, see the documentation. nuget.registry = Setup this registry from the command line: nuget.install = To install the package using NuGet, run the following command: -nuget.documentation = For more information on the NuGet registry, see the documentation. nuget.dependency.framework = Target Framework npm.registry = Setup this registry in your project .npmrc file: npm.install = To install the package using npm, run the following command: npm.install2 = or add it to the package.json file: -npm.documentation = For more information on the npm registry, see the documentation. npm.dependencies = Dependencies npm.dependencies.development = Development Dependencies npm.dependencies.peer = Peer Dependencies npm.dependencies.optional = Optional Dependencies npm.details.tag = Tag pub.install = To install the package using Dart, run the following command: -pub.documentation = For more information on the Pub registry, see the documentation. pypi.requires = Requires Python pypi.install = To install the package using pip, run the following command: -pypi.documentation = For more information on the PyPI registry, see the documentation. rpm.registry = Setup this registry from the command line: rpm.install = To install the package, run the following command: -rpm.documentation = For more information on the RPM registry, see the documentation. rubygems.install = To install the package using gem, run the following command: rubygems.install2 = or add it to the Gemfile: rubygems.dependencies.runtime = Runtime Dependencies rubygems.dependencies.development = Development Dependencies rubygems.required.ruby = Requires Ruby version rubygems.required.rubygems = Requires RubyGem version -rubygems.documentation = For more information on the RubyGems registry, see the documentation. swift.registry = Setup this registry from the command line: swift.install = Add the package in your Package.swift file: swift.install2 = and run the following command: -swift.documentation = For more information on the Swift registry, see the documentation. vagrant.install = To add a Vagrant box, run the following command: -vagrant.documentation = For more information on the Vagrant registry, see the documentation. settings.link = Link this package to a repository settings.link.description = If you link a package with a repository, the package is listed in the repository's package list. settings.link.select = Select Repository @@ -3473,7 +3458,7 @@ runners.reset_registration_token_success = Runner registration token reset succe runs.all_workflows = All Workflows runs.commit = Commit -runs.pushed_by = Pushed by +runs.pushed_by = pushed by runs.invalid_workflow_helper = Workflow config file is invalid. Please check your config file: %s runs.no_matching_runner_helper = No matching runner: %s runs.actor = Actor diff --git a/options/locale/locale_fa-IR.ini b/options/locale/locale_fa-IR.ini index a2995f5100d..7d9f49c1a88 100644 --- a/options/locale/locale_fa-IR.ini +++ b/options/locale/locale_fa-IR.ini @@ -357,7 +357,7 @@ reset_password.text=لطفاً روی پیوند زیر کلیک کنید تا register_success=ثبت‌نام با موفقیت انجام شد -issue_assigned.pull=@%[1] به شما برای درخواست pull %[2] در ریپازیتوری %[3] محول شده. +issue_assigned.pull=@%[1]s به شما برای درخواست pull %[2]s در ریپازیتوری %[3]s محول شده. issue_assigned.issue=@%[1]s به شما واگذار شده است برای صدور %[2]s در انبار %[3]s. issue.x_mentioned_you=@%s به شما اشاره کرد: diff --git a/options/locale/locale_fr-FR.ini b/options/locale/locale_fr-FR.ini index 2e53255aab1..58d24fc69ca 100644 --- a/options/locale/locale_fr-FR.ini +++ b/options/locale/locale_fr-FR.ini @@ -3133,7 +3133,7 @@ error.unit_not_allowed=Vous n'êtes pas autorisé à accéder à cette section d title=Paquets desc=Gérer les paquets du dépôt. empty=Il n'y pas de paquet pour le moment. -empty.documentation=Pour plus d'informations sur le registre de paquets, voir la documentation. +empty.documentation=Pour plus d'informations sur le registre de paquets, voir la documentation. empty.repo=Avez-vous téléchargé un paquet, mais il n'est pas affiché ici? Allez dans les paramètres du paquet et liez le à ce dépôt. filter.type=Type filter.type.all=Tous diff --git a/options/locale/locale_is-IS.ini b/options/locale/locale_is-IS.ini index 42ed8c5f249..977f22bf292 100644 --- a/options/locale/locale_is-IS.ini +++ b/options/locale/locale_is-IS.ini @@ -803,7 +803,7 @@ issues.action_milestone=Tímamót issues.action_milestone_no_select=Ekkert tímamót issues.action_assignee=Úthlutað að issues.opened_by=opnað %[1]s af %[3]s -issues.opened_by_fake=opnað %[1] af %[2]s +issues.opened_by_fake=opnað %[1]s af %[2]s issues.previous=Fyrri issues.next=Áfram issues.open_title=Opið diff --git a/options/locale/locale_ja-JP.ini b/options/locale/locale_ja-JP.ini index 75971caa903..224ce42d7a7 100644 --- a/options/locale/locale_ja-JP.ini +++ b/options/locale/locale_ja-JP.ini @@ -3213,7 +3213,7 @@ error.unit_not_allowed=このセクションへのアクセスが許可されて title=パッケージ desc=リポジトリ パッケージを管理します。 empty=パッケージはまだありません。 -empty.documentation=パッケージレジストリの詳細については、 ドキュメント を参照してください。 +empty.documentation=パッケージレジストリの詳細については、 ドキュメント を参照してください。 empty.repo=パッケージはアップロードしたけども、ここに表示されない? パッケージ設定を開いて、パッケージをこのリポジトリにリンクしてください。 filter.type=タイプ filter.type.all=すべて diff --git a/options/locale/locale_lv-LV.ini b/options/locale/locale_lv-LV.ini index 2d5b40bd0b6..1fb359b5226 100644 --- a/options/locale/locale_lv-LV.ini +++ b/options/locale/locale_lv-LV.ini @@ -19,6 +19,7 @@ active_stopwatch=Aktīvā laika uzskaite create_new=Izveidot… user_profile_and_more=Profils un iestatījumi… signed_in_as=Pierakstījies kā +enable_javascript=Šai lapas darbībai ir nepieciešams JavaScript. toc=Satura rādītājs licenses=Licences return_to_gitea=Atgriezties Gitea @@ -78,11 +79,14 @@ milestones=Atskaites punkti ok=Labi cancel=Atcelt +rerun=Palaist atkārtoti +rerun_all=Palaist atkārtoti visus darbus save=Saglabāt add=Pievienot add_all=Pievienot visus remove=Noņemt remove_all=Noņemt visus +remove_label_str=`Noņemt ierakstu "%s"` edit=Labot enabled=Iespējots @@ -111,11 +115,19 @@ unknown=Nezināms rss_feed=RSS barotne +pin=Piespraust +unpin=Atspraust +artifacts=Artefakti +concept_system_global=Globāls +concept_user_individual=Individuāls concept_code_repository=Repozitorijs concept_user_organization=Organizācija +show_timestamps=Rādīt laika zīmogus +show_log_seconds=Rādīt sekundes +show_full_screen=Atvērt pilnā logā [aria] navbar=Navigācijas josla @@ -124,8 +136,26 @@ footer.software=Par programmatūru footer.links=Saites [heatmap] +number_of_contributions_in_the_last_12_months=%s darbības pēdējo 12 mēnešu laikā +no_contributions=Nav aktivitātes +less=Mazāk +more=Vairāk [editor] +buttons.heading.tooltip=Pievienot virsrakstu +buttons.bold.tooltip=Izcelt tekstu +buttons.italic.tooltip=Slīpraksta teksts +buttons.quote.tooltip=Citēt tekstu +buttons.code.tooltip=Pievienot kodu +buttons.link.tooltip=Pievienot saiti +buttons.list.unordered.tooltip=Pievienot sarakstu +buttons.list.ordered.tooltip=Pievienot numurētu sarakstu +buttons.list.task.tooltip=Pievienot uzdevumu sarakstu +buttons.mention.tooltip=Pieminēt lietotāju vai komandu +buttons.ref.tooltip=Atsaukties uz problēmu vai izmaiņu pieprasījumu +buttons.switch_to_legacy.tooltip=Izmantot vēsturisko redaktoru +buttons.enable_monospace_font=Izmantot vienāda izmēra fontu +buttons.disable_monospace_font=Neizmantot vienāda izmēra fontu [filter] string.asc=A - Z @@ -228,6 +258,7 @@ openid_signup_popup=Iespējot lietotāju reģistrāciju pirms tam autorizējotie enable_captcha=Pieprasīt drošības kodu lietotāju reģistrācijā enable_captcha_popup=Lietotājam reģistrējoties, pieprasīt ievadīt drošības kodu. require_sign_in_view=Iespējot nepieciešamību autorizēties, lai aplūkotu lapas +require_sign_in_view_popup=Tikai autorizēti lietotāji var aplūkot lapas. Apmeklētāji redzēs tikai autorizācijas un reģistrācijas lapu. admin_setting_desc=Nav nepieciešams izveidot administratora kontu uzreiz, pirmais reģistrētais lietotājs saņems administratora tiesības automātiski. admin_title=Administratora konta iestatījumi admin_name=Administratora lietotāja vārds @@ -238,6 +269,7 @@ install_btn_confirm=Instalēt Gitea test_git_failed=Kļūda pārbaudot 'git' komandu: %v sqlite3_not_available=Jūsu pašreizējā versija neatbalsta SQLite3, lūdzu lejupielādējiet oficiālo bināro versiju no %s, NEVIS gobuild versiju. invalid_db_setting=Nederīgi datu bāzes iestatījumi: %v +invalid_db_table=Datubāzes tabula "%s" ir kļūdaina: %v invalid_repo_path=Nederīga repozitorija glabāšanas vieta: %v invalid_app_data_path=Lietojumprogrammas datu ceļš ir kļūdains: %v run_user_not_match=Izpildes lietotājs nav pašreizējais lietotājs: %s -> %s @@ -256,6 +288,7 @@ no_reply_address=Neatbildēt e-pasta adreses domēns no_reply_address_helper=Domēns lietotāja e-pasta adresei git žurnālos, ja lietotājs izvēlas paturēt savu e-pasta adresi privātu. Piemēram, ja lietotājs ir 'janis' un domēns 'neatbildet.piemers.lv', tad e-pasta adrese būs 'janis@neatbildet.piemers.lv'. password_algorithm=Paroles jaucējsummas algoritms invalid_password_algorithm=Kļūdaina paroles jaucējfunkcija +password_algorithm_helper=Norādiet paroles jaucējalgoritmu. Algoritmi atšķirās pēc prasībām pret resursiem un stipruma. Argon2 algoritms ir drošs, bet tam nepieciešams daudz operatīvās atmiņas, līdz ar ko tas var nebūt piemērots sistēmām ar maz pieejamajiem resursiem. enable_update_checker=Iespējot jaunu versiju paziņojumus enable_update_checker_helper=Periodiski pārbaudīt jaunu version pieejamību, izgūstot datus no gitea.io. @@ -291,6 +324,7 @@ repos=Repozitoriji users=Lietotāji organizations=Organizācijas search=Meklēt +go_to=Iet uz code=Kods search.type.tooltip=Meklēšanas veids search.fuzzy=Aptuveni @@ -302,6 +336,7 @@ repo_no_results=Netika atrasts neviens repozitorijs, kas atbilstu kritērijiem. user_no_results=Netika atrasts neviens lietotājs, kas atbilstu kritērijiem. org_no_results=Netika atrasta neviena organizācija, kas atbilstu kritērijiem. code_no_results=Netika atrasts pirmkods, kas atbilstu kritērijiem. +code_search_results=`Meklēšanas rezultāti "%s"` code_last_indexed_at=Pēdējo reizi indeksēts %s relevant_repositories_tooltip=Repozitoriju, kas ir atdalīti vai kuriem nav tēmas, ikonas un apraksta ir paslēpti. relevant_repositories=Tikai būtiskie repozitoriji tiek rādīti, pārādīt nefiltrētus rezultātus. @@ -438,13 +473,14 @@ repo.collaborator.added.subject=%s pievienoja Jūs repozitorijam %s repo.collaborator.added.text=Jūs tikāt pievienots kā līdzstrādnieks repozitorijam: team_invite.subject=%[1]s uzaicināja Jūs pievienoties organizācijai %[2]s -team_invite.text_1=%[1]s uzaicināja Jūs pievienoties komandai %[2] organizācijā %[3]s. +team_invite.text_1=%[1]s uzaicināja Jūs pievienoties komandai %[2]s organizācijā %[3]s. team_invite.text_2=Uzspiediet uz šīs saites, lai pievienoties komandai: team_invite.text_3=Piezīme: Šis uzaicinājums ir paredzēts %[1]s. Ja uzskatāt, ka tas nav domāts Jums, varat ignorēt šo e-pastu. [modal] yes=Jā no=Nē +confirm=Apstiprināt cancel=Atcelt modify=Atjaunināt @@ -479,6 +515,8 @@ size_error=` jābūt %s simbolus garam.` min_size_error=` jabūt vismaz %s simbolu garumā.` max_size_error=` jabūt ne mazāk kā %s simbolu garumā.` email_error=` nav derīga e-pasta adrese.` +url_error=`"%s" nav korekts URL.` +include_error=` ir jāsatur tekstu "%s".` glob_pattern_error=` glob šablons nav korekts: %s.` regex_pattern_error=` regulārā izteiksme nav korekta: %s.` username_error=` drīkst saturēt tikai burtus un ciparus ('0-9','a-z','A-Z'), domuzīme ('-'), apakšsvītra ('_') un punkts ('.'). Nevar sākties vai beigties ar simbolu, kas nav burts vai skaitlis, kā arī nevar būt vairāki simboli pēc kārtas, kas nav burti vai skaitļi.` @@ -490,6 +528,7 @@ lang_select_error=Izvēlieties valodu no saraksta. username_been_taken=Lietotājvārds jau ir aizņemts. username_change_not_local_user=Ne-lokālie lietotāji nevar mainīt savus lietotājvārdus. +username_has_not_been_changed=Lietotāja vārds netika mainīts repo_name_been_taken=Jau eksistē repozitorijs ar šādu nosaukumu. repository_force_private=Ir ieslēgts piespiedu privātais režīms: repozitorijus nav iespējams padarīt publiskus. repository_files_already_exist=Šī repozitorija faili jau eksistē, sazinieties ar sistēmas administratoru. @@ -503,6 +542,7 @@ team_name_been_taken=Komandas nosaukums jau ir aizņemts. team_no_units_error=Komandai ir jābūt iespējotai vismaz vienai sadaļai. email_been_used=E-pasta adrese jau ir izmantota. email_invalid=Epasta adrese nav korekta. +openid_been_used=OpenID adrese "%s" jau ir izmantota. username_password_incorrect=Nepareizs lietotājvārds vai parole. password_complexity=Parole neatbilst drošības prasībām: password_lowercase_one=Vismaz viens mazais burts @@ -524,13 +564,20 @@ invalid_ssh_key=Nav iespējams pārbaudīt SSH atslēgu: %s invalid_gpg_key=Nav iespējams pārbaudīt GPG atslēgu: %s invalid_ssh_principal=Kļūdaina identitāte: %s must_use_public_key=Atslēga, ko norādījāt ir privātā atslēga. Nekad nenodotiet savu privātu atslēgu nevienam. Izmantojiet publisko atslēgu. +unable_verify_ssh_key=SSH atslēgu nav iespējams pārbaudīt, pārliecinieties, ka tajā nav kļūdu. auth_failed=Autentifikācija neizdevās: %v +still_own_repo=Šis konts ir vismaz viena repozitorija īpašnieks, tos sākumā ir nepieciešams izdzēst vai mainīt to īpašnieku. +still_has_org=Jūsu konts ir piesaistīts vismaz vienai organizācijai, sākumā nepieciešams to pamest. +still_own_packages=Jūsu kontam pieder viena vai vairākas pakotnes, tās nepieciešams izdzēst. +org_still_own_repo=Organizācijai pieder repozitoriji, tos sākumā ir nepieciešams izdzēst vai mainīt to īpašnieku. +org_still_own_packages=Šai organizācijai pieder viena vai vārākas pakotnes, tās nepieciešams izdzēst. target_branch_not_exist=Mērķa atzars neeksistē [user] change_avatar=Mainīt profila attēlu… +joined_on=Pievienojās %s repositories=Repozitoriji activity=Publiskā aktivitāte followers=Sekotāji @@ -545,7 +592,12 @@ unfollow=Nesekot heatmap.loading=Ielādē intensitātes karti… user_bio=Biogrāfija disabled_public_activity=Šis lietotājs ir atslēdzies iespēju aplūkot tā aktivitāti. +email_visibility.limited=E-pasta adrese ir redzama visiem autentificētajiem lietotājiem +email_visibility.private=E-pasta adrese ir redzama tikai administratoriem +form.name_reserved=Lietotājvārdu "%s" nedrīkst izmantot. +form.name_pattern_not_allowed=Lietotājvārds "%s" nav atļauts. +form.name_chars_not_allowed=Lietotāja vārds "%s" satur neatļautus simbolus. [settings] profile=Profils @@ -576,6 +628,7 @@ location=Atrašanās vieta update_theme=Mainīt motīvu update_profile=Mainīt profilu update_language=Mainīt valodu +update_language_not_found=Valoda "%s" nav pieejama. update_language_success=Valoda tika nomainīta. update_profile_success=Jūsu profila informācija tika saglabāta. change_username=Lietotājvārds mainīts. @@ -586,6 +639,9 @@ cancel=Atcelt language=Valoda ui=Motīvs hidden_comment_types=Attēlojot paslēpt šauds komentārus: +hidden_comment_types_description=Komentāru veidi, kas atzīmēti, netiks rādīti problēmas lapā. Piemēram, atzīmējot "Etiķetes" netiks rādīti komentāri " pievienoja/noņēma ". +hidden_comment_types.ref_tooltip=Komentāri, kad problēmai tiek pievienota atsauce uz citu probēmu, komentāru, … +hidden_comment_types.issue_ref_tooltip=Komentāri par lietotāja izmaiņām ar problēmas saistīto atzaru/tagu comment_type_group_reference=Atsauces comment_type_group_label=Etiķetes comment_type_group_milestone=Atskaites punktus @@ -649,10 +705,12 @@ add_new_email=Pievienot jaunu e-pasta adresi add_new_openid=Pievienot jaunu OpenID vietrādi add_email=Pievienot e-pasta adresi add_openid=Pievienot OpenID vietrādi +add_email_confirmation_sent=Jauns apstiprināšanas e-pasts tika nosūtīts uz "%s". Pārbaudiet savu e-pasta kontu tuvāko %s laikā, lai apstiprinātu savu e-pasta adresi. add_email_success=Jūsu jaunā e-pasta adrese tika veiksmīgi pievienota. email_preference_set_success=E-pasta izvēle tika veiksmīgi saglabāta. add_openid_success=Jūsu jaunā OpenID adrese tika veiksmīgi pievienota. keep_email_private=Paslēpt e-pasta adresi +keep_email_private_popup=E-pasta adrese būs redzama tikai administratoriem openid_desc=Jūsu OpenID adreses ļauj autorizēties, izmantojot, Jūsu izvēlēto pakalpojumu sniedzēju. manage_ssh_keys=Pārvaldīt SSH atslēgas @@ -686,6 +744,7 @@ gpg_token_help=Parakstu ir iespējams uzģenerēt izmantojot komandu: gpg_token_code=echo "%s" | gpg -a --default-key %s --detach-sig gpg_token_signature=Tekstuāls GPG paraksts key_signature_gpg_placeholder=Sākas ar '-----BEGIN PGP SIGNATURE-----' +verify_gpg_key_success=GPG atslēga "%s" veiksmīgi pārbaudīta. ssh_key_verified=Pārbaudīta atslēga ssh_key_verified_long=Atslēga tika pārbaudīta ar parakstītu talonu un var tikt izmantota, lai pārbaudītu revīzijas, kas atbilst jebkurai apstiprinātai lietotāja e-pasta adresei. ssh_key_verify=Pārbaudīt @@ -695,11 +754,15 @@ ssh_token=Talons ssh_token_help=Parakstu ir iespējams uzģenerēt izmantojot komandu: ssh_token_signature=Aizsargāts SSH paraksts key_signature_ssh_placeholder=Sākas ar '-----BEGIN SSH SIGNATURE-----' +verify_ssh_key_success=SSH atslēga "%s" veiksmīgi pārbaudīta. subkeys=Apakšatslēgas key_id=Atslēgas ID key_name=Atslēgas nosaukums key_content=Saturs principal_content=Saturs +add_key_success=SSH atslēga "%s" tika pievienota. +add_gpg_key_success=GPG atslēga "%s" tika pievienota. +add_principal_success=SSH sertifikāte identitāte "%s" tika pievienota. delete_key=Noņemt ssh_key_deletion=Noņemt SSH atslēgu gpg_key_deletion=Noņemt GPG atslēgu @@ -710,6 +773,8 @@ ssh_principal_deletion_desc=Noņemot SSH sertifikāta identitāti, ar to vairs n ssh_key_deletion_success=SSH atslēga tika izdzēsta. gpg_key_deletion_success=GPG atslēga tika izdzēsta. ssh_principal_deletion_success=Identitāte tika noņemta. +added_on=Pievienots %s +valid_until_date=Derīgs līdz %s valid_forever=Derīgs mūžīgi last_used=Pēdējo reizi izmantota no_activity=Nav nesenas aktivitātes @@ -721,6 +786,7 @@ principal_state_desc=Šī identitāte ir lietota pēdējās 7 dienās show_openid=Rādīt profilā hide_openid=Paslēpt no profila ssh_disabled=SSH atspējots +ssh_signonly=SSH ir atspējots, līdz ar to šīs atslēgas tiks izmantotas tikai revīziju parakstu pārbaudei. ssh_externally_managed=Šim lietotājam SSH atslēga tiek pāvaldīta attālināti manage_social=Pārvaldīt piesaistītos sociālos kontus social_desc=Šis ir saraksts ar Jūsu Gitea kontam piesaistītajiem sociālajiem kontiem. Drošības nolūkos, pārliecinieties, ka atpazīstat visus no tiem, jo tos var izmantot, lai pieslēgtos Jūsu Gitea kontam. @@ -740,6 +806,13 @@ access_token_deletion_cancel_action=Atcelt access_token_deletion_confirm_action=Dzēst access_token_deletion_desc=Izdzēšot talonu, tam tiks liegta piekļuve šim kontam. Šī darbība ir neatgriezeniska. Vai turpināt? delete_token_success=Piekļuves talons tika noņemts. Neaizmirstiet atjaunot informāciju lietojumprogrammās, kas izmantoja šo talonu. +repo_and_org_access=Repozitorija un organizācijas piekļuve +permissions_public_only=Tikai publiskie +permissions_access_all=Visi (publiskie, privātie un ierobežotie) +select_permissions=Norādiet tiesības +scoped_token_desc=Atzīmētie talona apgabali ierobežo autorizāciju tikai atbilstošiem API izsaukumiem. Sīkāka informācija pieejama dokumentācijā. +at_least_one_permission=Nepieciešams norādīt vismaz vienu tiesību, lai izveidotu talonu +permissions_list=Tiesības: manage_oauth2_applications=Pārvaldīt OAuth2 lietotnes edit_oauth2_application=Labot OAuth2 lietotni @@ -753,6 +826,7 @@ create_oauth2_application_success=OAuth2 lietotne veiksmīgi izveidota. update_oauth2_application_success=OAuth2 lietotne veiksmīgi atjaunināta. oauth2_application_name=Lietotnes nosaukums oauth2_confidential_client=Konfidenciāls klients. Norādiet lietotēm, kas glabā noslēpumu slepenībā, piemēram, tīmekļa lietotnēm. Nenorādiet instalējamām lietotnēm, tai skaitā darbavirsmas vai mobilajām lietotnēm. +oauth2_redirect_uris=Pārsūtīšanas URI. Norādiet katru URI savā rindā. save_application=Saglabāt oauth2_client_id=Klienta ID oauth2_client_secret=Klienta noslēpums @@ -822,7 +896,9 @@ email_notifications.andyourown=Iekļaut savus paziņojumus visibility=Lietotāja redzamība visibility.public=Publisks +visibility.public_tooltip=Redzams ikvienam visibility.limited=Ierobežota +visibility.limited_tooltip=Redzams tikai autorizētiem lietotājiem visibility.private=Privāts visibility.private_tooltip=Redzams tikai organizāciju dalībniekiem @@ -896,6 +972,7 @@ mirror_password_blank_placeholder=(nav uzstādīts) mirror_password_help=Nomainiet lietotāju, lai izdzēstu saglabāto paroli. watchers=Novērotāji stargazers=Zvaigžņdevēji +stars_remove_warning=Tiks noņemtas visas atzīmētās zvaigznes šim repozitorijam. forks=Atdalītie repozitoriji reactions_more=un vēl %d unit_disabled=Administrators ir atspējojies šo repozitorija sadaļu. @@ -910,6 +987,7 @@ delete_preexisting=Dzēst jau eksistējošos failus delete_preexisting_content=Dzēst failus direktorijā %s delete_preexisting_success=Dzēst nepārņemtos failus direktorijā %s blame_prior=Aplūkot vainīgo par izmaiņām pirms šīs revīzijas +author_search_tooltip=Tiks attēloti ne vairāk kā 30 lietotāji transfer.accept=Apstiprināt īpašnieka maiņu transfer.accept_desc=`Mainīt īpašnieku uz "%s"` @@ -938,11 +1016,14 @@ template.one_item=Norādiet vismaz vienu sagataves vienību template.invalid=Norādiet sagataves repozitoriju archive.title=Repozitorijs ir arhivēts. Tam var aplūkot failus un to var klonēt, bet nevar iesūtīt jaunas izmaiņas, kā arī atvērt jaunas problēmas/izmaiņu pieprasījumus. +archive.title_date=Šis repozitorijs tika arhivēts %s. Ir iespējams aplūkot tā failus un to konēt, bet nav iespējams iesūtīt izmaiņas, kā arī atvērt jaunas problēmas vai izmaiņu pieprasījumus. archive.issue.nocomment=Repozitorijs ir arhivēts. Problēmām nevar pievienot jaunus komentārus. archive.pull.nocomment=Repozitorijs ir arhivēts. Izmaiņu pieprasījumiem nevar pievienot jaunus komentārus. form.reach_limit_of_creation_1=Sasniegts Jums noteiktais %d repozitorija ierobežojums. form.reach_limit_of_creation_n=Sasniegts Jums noteiktais %d repozitoriju ierobežojums. +form.name_reserved=Repozitorija nosaukums "%s" ir jau rezervēts. +form.name_pattern_not_allowed=Repozitorija nosaukums "%s" nav atļauts. need_auth=Autorizācija migrate_options=Migrācijas opcijas @@ -968,6 +1049,7 @@ migrate.github_token_desc=Ir iespējams izmantot vienu vai ar komantiem atdalīt migrate.clone_local_path=vai servera lokālais ceļš migrate.permission_denied=Jums nav tiesību importēt lokālu repozitoriju. migrate.permission_denied_blocked=Nav iespējams importēt no neatļautām adresēm, prasiet administratoram pārskatīt ALLOWED_DOMAINS/ALLOW_LOCALNETWORKS/BLOCKED_DOMAINS iestatījumus. +migrate.invalid_local_path=Nederīgs lokālais ceļš. Tas neeksistē vai nav direktorija. migrate.invalid_lfs_endpoint=LFS galapunkts nav korekts. migrate.failed=Migrācija neizdevās: %v migrate.migrate_items_options=Piekļuves talons ir nepieciešams, lai migrētu papildus datus @@ -976,6 +1058,7 @@ migrated_from_fake=Migrēts no %[1]s migrate.migrate=Migrēt no %s migrate.migrating=Migrācija no %s ... migrate.migrating_failed=Migrācija no %s neizdevās. +migrate.migrating_failed.error=Migrācija neizdevās: %s migrate.migrating_failed_no_addr=Migrācija neizdevās. migrate.github.description=Migrēt datus no github.com vai citām GitHub instancēm. migrate.git.description=Migrēt repozitorija datus no jebkura Git servisa. @@ -992,6 +1075,8 @@ migrate.migrating_labels=Migrē etiķetes migrate.migrating_releases=Migrē laidienus migrate.migrating_issues=Migrācijas problēmas migrate.migrating_pulls=Migrē izmaiņu pieprasījumus +migrate.cancel_migrating_title=Atcelt migrāciju +migrate.cancel_migrating_confirm=Vai patiešam vēlaties atcelt šo migrāciju? mirror_from=spogulis no forked_from=atdalīts no @@ -1042,6 +1127,7 @@ release=Laidiens releases=Laidieni tag=Tags released_this=izveidoja šo laidienu +tagged_this=izveidoja tagu revīzijai file.title=%s atzarā %s file_raw=Neapstrādāts file_history=Vēsture @@ -1076,6 +1162,7 @@ download_file=Lejupielādēt failu normal_view=Parastais skats line=rinda lines=rindas +from_comment=(komentārs) editor.add_file=Pievienot editor.new_file=Jauna datne @@ -1090,6 +1177,7 @@ editor.must_be_on_a_branch=Ir jābūt izvēlētam atzaram, lai varētu veikt vai editor.fork_before_edit=Lai varētu labot failu, ir nepieciešams atdalīt repozitoriju. editor.delete_this_file=Dzēst failu editor.must_have_write_access=Jums ir jābūt rakstīšanas tiesībām, lai varētu veikt vai piedāvāt izmaiņas šim failam. +editor.file_delete_success=Fails "%s" tika izdzēsts. editor.name_your_file=Ievadiet faila nosaukumu… editor.filename_help=Lai pievienotu direktoriju, ierakstiet tās nosaukumu un slīpsvītru ('/'). Lai noņemtu direktoriju, ielieciet kursoru pirms faila nosaukuma un nospiediet atpakaļatkāpes taustiņu. editor.or=vai @@ -1097,8 +1185,12 @@ editor.cancel_lower=Atcelt editor.commit_signed_changes=Apstiprināt parakstītu revīziju editor.commit_changes=Pabeigt revīziju editor.add_tmpl=Pievienot '' +editor.add=Pievienot %s +editor.update=Atjaunot %s +editor.delete=Dzēst %s editor.patch=Pielietot ielāpu editor.patching=Pielieto ielāpu: +editor.fail_to_apply_patch=`Neizdevās pielietot ielāpu "%s"` editor.new_patch=Jauns ielāps editor.commit_message_desc=Pievienot neobligātu paplašinātu aprakstu… editor.signoff_desc=Pievienot revīzijas žurnāla ziņojuma beigās Signed-off-by ar revīzijas autoru. @@ -1110,15 +1202,29 @@ editor.new_branch_name=Jaunā atzara nosaukums šai revīzijai editor.new_branch_name_desc=Jaunā atzara nosaukums… editor.cancel=Atcelt editor.filename_cannot_be_empty=Faila nosaukums nevar būt tukšs. +editor.filename_is_invalid=Faila nosaukums "%s" nav korekts. +editor.branch_does_not_exist=Šajā repozitorijā neeksistē atzars "%s". +editor.branch_already_exists=Atzars "%s" šajā repozitorijā jau eksistē. +editor.directory_is_a_file=Direktorijas nosaukums "%s" vecāka ceļā ir fails nevis direktorija šajā repozitorijā. +editor.file_is_a_symlink=Fails "%s" ir norāde, kuru nav iespējams labot no tīmekļa redaktora +editor.filename_is_a_directory=Faila nosaukums "%s" sakrīt ar direktorijas nosaukumu šajā repozitorijā. +editor.file_editing_no_longer_exists=Fails "%s", ko labojat, vairs neeksistē šajā repozitorijā. +editor.file_deleting_no_longer_exists=Fails "%s", ko dzēšat, vairs neeksistē šajā repozitorijā. editor.file_changed_while_editing=Faila saturs ir mainījies kopš sākāt to labot. Noklikšķiniet šeit, lai apskatītu, vai Nosūtiet izmaiņas atkārtoti, lai pārrakstītu. +editor.file_already_exists=Fails ar nosaukumu "%s" šajā repozitorijā jau eksistē. editor.commit_empty_file_header=Iesūtīt tukšu failu editor.commit_empty_file_text=Fails, ko vēlaties iesūtīt, ir tukšs. Vai turpināt? editor.no_changes_to_show=Nav izmaiņu, ko rādīt. +editor.fail_to_update_file=Neizdevās atjaunot/izveidot failu "%s". editor.fail_to_update_file_summary=Kļūdas ziņojums: editor.push_rejected_no_message=Izmaiņu iesūtīšana tika noraidīta, bet serveris neatgrieza paziņojumu. Pārbaudiet git āķus šim repozitorijam. editor.push_rejected=Serveris noraidīja šo izmaiņu. Pārbaudiet git āķus. editor.push_rejected_summary=Pilns noraidīšanas ziņojums: editor.add_subdir=Pievienot direktoriju… +editor.unable_to_upload_files=Neizdevās augšupielādēt failus uz direktoriju "%s", kļūda: %v +editor.upload_file_is_locked=Failu "%s" ir nobloķējis %s. +editor.upload_files_to_dir=`Augšupielādēt failus uz direktoriju "%s"` +editor.cannot_commit_to_protected_branch=Nav atļauts veikt izmaiņas aizsargātam atzaram "%s". editor.no_commit_to_branch=Nevar apstiprināt revīzijas atzarā: editor.user_no_push_to_branch=Lietotājs nevar iesūtīt izmaiņas šajā atzarā editor.require_signed_commit=Atzarā var iesūtīt tikai parakstītas revīzijas @@ -1127,8 +1233,10 @@ editor.revert=Atgriezt %s uz: commits.desc=Pārlūkot pirmkoda izmaiņu vēsturi. commits.commits=Revīzijas +commits.no_commits=Nav kopīgu revīziju. Atzariem "%s" un "%s" ir pilnībā atšķirīga izmaiņu vēsture. commits.nothing_to_compare=Atzari ir vienādi. commits.search=Meklēt revīzijas… +commits.search.tooltip=Jūs varat izmantot atslēgas vārdus "author:", "committer:", "after:" vai "before:", piemēram, "revert author:Alice before:2019-01-13". commits.find=Meklēt commits.search_all=Visi atzari commits.author=Autors @@ -1161,12 +1269,14 @@ projects.create=Izveidot projektu projects.title=Nosaukums projects.new=Jauns projekts projects.new_subheader=Koordinē, seko un atjauno savu darbu centralizēti, lai projekts būtu izsekojams un vienmēr laikā. +projects.create_success=Projekts "%s" tika izveidots. projects.deletion=Dzēst projektu projects.deletion_desc=Dzēšot projektu no tā tiks atsaistītās visas tam piesaistītās problēmas. Vai turpināt? projects.deletion_success=Šis projekts tika izdzēsts. projects.edit=Labot projektu projects.edit_subheader=Projekti organizē problēmas un ļauj izsekot to progresam. projects.modify=Mainīt projektu +projects.edit_success=Projekta "%s" izmaiņas tika saglabātas. projects.type.none=Nav projects.type.basic_kanban=`Vienkāršots "Kanban"` projects.type.bug_triage=Kļūdu šķirošana @@ -1180,6 +1290,8 @@ projects.column.new_submit=Izveidot kolonnu projects.column.new=Jauna kolonna projects.column.set_default=Izvēlēties kā noklusēto projects.column.set_default_desc=Izvēlēties šo kolonnu kā noklusēto nekategorizētām problēmām un izmaiņu pieteikumiem +projects.column.unset_default=Atiestatīt noklusēto +projects.column.unset_default_desc=Noņemt šo kolonnu kā noklusēto projects.column.delete=Dzēst kolonnu projects.column.deletion_desc=Dzēšot projekta kolonnu visas tam piesaistītās problēmas tiks pārliktas kā nekategorizētas. Vai turpināt? projects.column.color=Krāsa @@ -1222,6 +1334,7 @@ issues.choose.blank=Noklusējuma issues.choose.blank_about=Izveidot problēmu ar noklusējuma sagatavi. issues.choose.ignore_invalid_templates=Kļūdainās sagataves tika izlaistas issues.choose.invalid_templates=%v ķļūdaina sagatave(s) atrastas +issues.choose.invalid_config=Problēmu konfigurācija satur kļūdas: issues.no_ref=Nav norādīts atzars/tags issues.create=Pieteikt problēmu issues.new_label=Jauna etiķete @@ -1232,6 +1345,7 @@ issues.label_templates.title=Ielādēt sākotnēji noteiktu etiķešu kopu issues.label_templates.info=Nav izveidota neviena etiķete. Jūs varat noklikšķināt uz "Jauna etiķete" augstāk, lai to izveidotu vai izmantot zemāk piedāvātās etiķetes: issues.label_templates.helper=Izvēlieties etiķešu kopu issues.label_templates.use=Izmantot etiķešu kopu +issues.label_templates.fail_to_load_file=Neizdevās ielādēt etiķetes sagataves failu "%s": %v issues.add_label=pievienoja %s etiķeti %s issues.add_labels=pievienoja %s etiķetes %s issues.remove_label=noņēma %s etiķeti %s @@ -1259,6 +1373,10 @@ issues.filter_label_exclude=`Izmantojiet alt + peles klikšķ issues.filter_label_no_select=Visas etiķetes issues.filter_label_select_no_label=Nav etiķešu issues.filter_milestone=Atskaites punkts +issues.filter_milestone_all=Visi atskaites punkti +issues.filter_milestone_none=Nav atskaites punkta +issues.filter_milestone_open=Atvērtie atskaites punkti +issues.filter_milestone_closed=Aizvērtie atskaites punkti issues.filter_project=Projektus issues.filter_project_all=Visi projekti issues.filter_project_none=Nav projektu @@ -1309,7 +1427,7 @@ issues.open_title=Atvērta issues.closed_title=Slēgta issues.draft_title=Melnraksts issues.num_comments=%d komentāri -issues.commented_at=` komentēja %s` +issues.commented_at=`komentēja %s` issues.delete_comment_confirm=Vai patiešām vēlaties dzēst šo komentāru? issues.context.copy_link=Kopēt saiti issues.context.quote_reply=Atbildēt citējot @@ -1317,6 +1435,9 @@ issues.context.reference_issue=Atsaukties uz šo jaunā problēmā issues.context.edit=Labot issues.context.delete=Dzēst issues.no_content=Vēl nav satura. +issues.close=Slēgt problēmu +issues.comment_pull_merged_at=saplidināta revīzija %[1]s atzarā %[2]s %[3]s +issues.comment_manually_pull_merged_at=manuāli saplidināta revīzija %[1]s atzarā %[2]s %[3]s issues.close_comment_issue=Komentēt un aizvērt issues.reopen_issue=Atvērt atkārtoti issues.reopen_comment_issue=Komentēt un atvērt atkārtoti @@ -1367,6 +1488,10 @@ issues.attachment.open_tab=`Noklikšķiniet, lai apskatītos "%s" jaunā logā` issues.attachment.download=`Noklikšķiniet, lai lejupielādētu "%s"` issues.subscribe=Abonēt issues.unsubscribe=Atrakstīties +issues.unpin_issue=Atspraust problēmu +issues.max_pinned=Nevar piespraust vairāk problēmas +issues.pin_comment=piesprauda šo %s +issues.unpin_comment=atsprauda šo %s issues.lock=Slēgt komentēšanu issues.unlock=Atļaut komentēšanu issues.lock.unknown_reason=Neizdevās slēgt problēmas komentēšanu. @@ -1432,6 +1557,9 @@ issues.due_date_invalid=Datums līdz nav korekts. Izmantojiet formātu 'gggg-mm- issues.dependency.title=Atkarības issues.dependency.issue_no_dependencies=Nav atkarību. issues.dependency.pr_no_dependencies=Nav atkarību. +issues.dependency.no_permission_1=Nav tiesību nolasīt %d atkarību +issues.dependency.no_permission_n=Nav tiesību nolasīt %d atkarības +issues.dependency.no_permission.can_remove=Nav tiesību nolasīt šo atkarību, bet iespējams to noņemt issues.dependency.add=Pievienot atkarību… issues.dependency.cancel=Atcelt issues.dependency.remove=Noņemt @@ -1443,6 +1571,7 @@ issues.dependency.issue_closing_blockedby=Šīs problēmas aizvēršanu bloķē issues.dependency.issue_close_blocks=Šī problēma bloķē sekojošu problēmu aizvēršanu issues.dependency.pr_close_blocks=Šis izmaiņu pieprasījums bloķē sekojošu problēmu aizvēršanu issues.dependency.issue_close_blocked=Nepieciešams aizvērt visas problēmas, kas bloķē šo problēmu, lai to varētu aizērt. +issues.dependency.issue_batch_close_blocked=Nav iespējams aizvērt vairākas atzīmētās problēmas, jo problēmai #%d ir atvērtas atkarības issues.dependency.pr_close_blocked=Nepieciešams aizvērt visas problēmas, kas bloķē šo izmaiņu pieprasījumu, lai to varētu sapludināt. issues.dependency.blocks_short=Bloķē issues.dependency.blocked_by_short=Atkarīgs no @@ -1470,6 +1599,7 @@ issues.review.add_review_request=pieprasīja recenziju no %s %s issues.review.remove_review_request=noņema recenzijas pieprasījumu no %s %s issues.review.remove_review_request_self=atteicās recenzēt %s issues.review.pending=Nav iesūtīts +issues.review.pending.tooltip=Šis komentārs nav redzams citiem lietotājiem. Lai padarītu neiesūtītos komentārus pieejamus citiem, nospiediet "%s" -> "%s/%s/%s" lapas augšpusē. issues.review.review=Recenzija issues.review.reviewers=Recenzenti issues.review.outdated=Novecojis @@ -1504,6 +1634,8 @@ pulls.compare_changes_desc=Izvēlieties atzaru, kurā sapludināt izmaiņas un a pulls.has_viewed_file=Skatīts pulls.has_changed_since_last_review=Mainīts kopš pēdējās recenzijas pulls.viewed_files_label=%[1]d no %[2]d failiem apskatīts +pulls.expand_files=Izvērst visus failus +pulls.collapse_files=Savērst visus failus pulls.compare_base=pamata pulls.compare_compare=salīdzināmais pulls.switch_comparison_type=Mainīt salīdzināšanas tipu @@ -1523,7 +1655,10 @@ pulls.tab_files=Izmainītie faili pulls.reopen_to_merge=Atkārtoti atveriet izmaiņu pieprasījumu, lai veiktu sapludināšanu. pulls.cant_reopen_deleted_branch=Šo izmaiņu pieprasīju nevar atkāroti atvērt, jo atzars ir izdzēsts. pulls.merged=Sapludināts +pulls.merged_success=Izmaiņu pieprasījums vieksmīgi sapludināts un aizvērts +pulls.closed=Izmaiņu pieprasījums aizvērts pulls.manually_merged=Manuāli sapludināts +pulls.merged_info_text=Atzaru %s tagad var dzēst. pulls.is_closed=Izmaiņu pieprasījums tika aizvērts. pulls.title_wip_desc=`Sāciet virsrakstu ar %s, lai ierobežotu, ka izmaiņu pieprasījums netīšām tiktu sapludināts.` pulls.cannot_merge_work_in_progress=Šis izmaiņu pieprasījums ir atzīmēts, ka pie tā vēl notiek izstrāde. @@ -1594,6 +1729,7 @@ pulls.update_branch_rebase=Atjaunot atzaru, izmantojot, pārbāzēšanu pulls.update_branch_success=Atzara atjaunināšana veiksmīgi pabeigta pulls.update_not_allowed=Jums nav tiesību veikt atzara atjaunošanu pulls.outdated_with_base_branch=Atzars ir novecojis salīdzinot ar bāzes atzaru +pulls.close=Aizvērt izmaiņu pieprasījumu pulls.closed_at=`aizvēra šo izmaiņu pieprasījumu %[2]s` pulls.reopened_at=`atkārtoti atvēra šo izmaiņu pieprasījumu %[2]s` pulls.merge_instruction_hint=`Varat aplūkot arī komandrindas instrukcijas.` @@ -1619,6 +1755,7 @@ pulls.delete.text=Vai patiešām vēlaties dzēst šo izmaiņu pieprasījumu? (N milestones.new=Jauns atskaites punkts milestones.closed=Aizvērts %s +milestones.update_ago=Atjaunots %s milestones.no_due_date=Bez termiņa milestones.open=Atvērta milestones.close=Aizvērt @@ -1630,10 +1767,12 @@ milestones.desc=Apraksts milestones.due_date=Termiņš (neobligāts) milestones.clear=Notīrīt milestones.invalid_due_date_format=Izpildes termiņam ir jābūt formāta 'yyyy-mm-dd'. +milestones.create_success=Atskaites punkts "%s" tika veiksmīgi izveidots. milestones.edit=Labot atskaites punktu milestones.edit_subheader=Atskaites punkti, ļauj organizēt problēmas un sekot to progresam. milestones.cancel=Atcelt milestones.modify=Labot atskaites punktu +milestones.edit_success=Izmaiņas atskaites punktā "%s" tika veiksmīgi saglabātas. milestones.deletion=Dzēst atskaites punktu milestones.deletion_desc=Dzēšot šo atskaites punktu, tas tiks noņemts no visām saistītajām problēmām un izmaiņu pieprasījumiem. Vai turpināt? milestones.deletion_success=Atskaites punkts tika veiksmīgi izdzēsts. @@ -1644,6 +1783,7 @@ milestones.filter_sort.most_complete=Visvairāk pabeigtais milestones.filter_sort.most_issues=Visvairāk problēmu milestones.filter_sort.least_issues=Vismazāk problēmu +signing.will_sign=`Šī revīzijas tiks parakstīta ar atslēgu "%s"` signing.wont_sign.error=Notika kļūda pārbaudot vai revīzija var tikt parakstīta signing.wont_sign.nokey=Nav pieejama atslēga ar ko parakstīt šo revīziju signing.wont_sign.never=Revīzijas nekad netiek parakstītas @@ -1668,6 +1808,8 @@ wiki.create_first_page=Izveidot pirmo lapu wiki.page=Lapa wiki.filter_page=Meklēt lapu wiki.new_page=Lapa +wiki.page_title=Lapas virsraksts +wiki.page_content=Lapas saturs wiki.default_commit_message=Ierakstiet piezīmes par šīs lapas izmaiņām (neobligāts). wiki.save_page=Saglabāt lapu wiki.last_commit_info=%s laboja lapu %s @@ -1677,10 +1819,13 @@ wiki.file_revision=Labas revīzija wiki.wiki_page_revisions=Vikivietnes lapas revīzijas wiki.back_to_wiki=Atpakaļ uz vikivietnes lapu wiki.delete_page_button=Dzēst lapu +wiki.delete_page_notice_1=Šī darbība izdzēsīs vikivietnes lapu "%s". Vai turpināt? wiki.page_already_exists=Vikivietnes lapa ar šādu nosaukumu jau eksistē. +wiki.reserved_page=Vikivietnes lapas nosaukums "%s" ir rezervēts. wiki.pages=Lapas wiki.last_updated=Pēdējo reizi labota %s wiki.page_name_desc=Ievadiet vikivietnes lapas nosaukumu. Speciālie nosaukumi ir: 'Home', '_Sidebar' un '_Footer'. +wiki.original_git_entry_tooltip=Attēlot oriģinālo Git faila nosaukumu. activity=Aktivitāte activity.period.filter_label=Laika periods: @@ -1772,6 +1917,16 @@ settings.hooks=Tīmekļa āķi settings.githooks=Git āķi settings.basic_settings=Pamatiestatījumi settings.mirror_settings=Spoguļa iestatījumi +settings.mirror_settings.docs=Iestatiet, ka tiks viekta automātiska revīziju, tagu un atzaru sinhronizācija ar citu repozitoriju. +settings.mirror_settings.docs.disabled_pull_mirror.instructions=Iestatiet, ka visas revīzijas, tagi un atzari tiks automātiski nosūtītu uz citu repozitoriju. Izgūšanas spoguļus administrators ir aizliedzis izmantot. +settings.mirror_settings.docs.disabled_push_mirror.instructions=Iestatiet, ka visas revīzijas, tagi un atzari tiks automātiski pārņemti no cita repozitorija. +settings.mirror_settings.docs.disabled_push_mirror.pull_mirror_warning=Pašlaik to var izdarīt tikai, izmantojot, sadaļu "Jauna migrācija". Sīkākai informācijai, skatieties: +settings.mirror_settings.docs.disabled_push_mirror.info=Iesūtīšanas spoguļus administrators ir aizliedzis izmantot. +settings.mirror_settings.docs.no_new_mirrors=Šis repozitorijs spoguļo izmaiņas uz vai no cita repozitorija. Pašlaik vairāk nav iespējams izveidot jaunus spoguļa repozitorijus. +settings.mirror_settings.docs.can_still_use=Lai arī nav iespējams mainīt esošos vai izveidot jaunus spoguļa repozitorijus, esošie turpinās strādāt. +settings.mirror_settings.docs.pull_mirror_instructions=Lai ietatītu atvilkšanas spoguli, sekojiet instrukcijām: +settings.mirror_settings.docs.doc_link_title=Kā spoguļot repozitorijus? +settings.mirror_settings.docs.pulling_remote_title=Atvilkt no attāla repozitorija settings.mirror_settings.mirrored_repository=Spoguļotais repozitorijs settings.mirror_settings.direction=Virziens settings.mirror_settings.direction.pull=Izmaiņu saņemšana @@ -1784,6 +1939,8 @@ settings.sync_mirror=Sinhronizēt tagad settings.mirror_sync_in_progress=Notiek spoguļa sinhronizācija. Atjaunojiet lapu, lai pārbaudītu atkārtoti, pēc brīža. settings.site=Mājas lapa settings.update_settings=Mainīt iestatījumus +settings.update_mirror_settings=Atjaunot spoguļa iestatījumus +settings.branches.switch_default_branch=Mainīt noklusēto atzaru settings.branches.update_default_branch=Atjaunot noklusēto atzaru settings.branches.add_new_rule=Pievienot jaunu noteikumu settings.advanced_settings=Papildu iestatījumi @@ -1866,6 +2023,7 @@ settings.trust_model.collaborator.long=Līdzstrādnieka: Uzticēties līdzstrād settings.trust_model.collaborator.desc=Ticami līdzstrādnieku paraksti tiks atzīmēti kā "uzticami" (neatkarīgi no tā vai tie atbilst revīzijas iesūtītājam vai nē). Citos gadījumos ticami paraksti tiks atzīmēti kā "neuzticami", ja paraksts atbilst revīzijas iesūtītājam vai "nesakrītošs", ja neatbilst. settings.trust_model.committer=Revīzijas iesūtītāja settings.trust_model.committer.long=Revīzijas iesūtītāja: Uzticēties parakstiem, kas atbilst revīzijas iesūtītājiem (Šis atbilst GitHub uzvedībai un piespiedīs Gitea parakstītām revīzijām būt Gitea kā revīzijas iesūtītājam) +settings.trust_model.committer.desc=Ticami paraksti tiks atzīmēti kā "uzticami", ja tie atbilst revīzijas iesūtītājam, citos gadījumos tie tiks atzīmēti kā "nesakrītoši". Šis nozīmē, ka Gitea būs kā revīzijas iesūtītājs parakstītām revīzijām, kur īstais revīzijas iesūtītājs tiks atīzmēts revīzijas komentāra beigās ar tekstu Co-authored-by: un Co-committed-by:. Noklusētajai Gitea atslēgai ir jāatbilst lietotājam datu bāzē. settings.trust_model.collaboratorcommitter=Līdzstrādnieka un revīzijas iesūtītāja settings.trust_model.collaboratorcommitter.long=Līdzstrādnieka un revīzijas iesūtītāja: Uzticēties līdzstrādnieku parakstiem, kas atbilst revīzijas iesūtītājam settings.trust_model.collaboratorcommitter.desc=Ticami līdzstrādnieku paraksti tiks atzīmēti kā "uzticami", ja tie atbilst revīzijas iesūtītājam, citos gadījumos tie tiks atzīmēti kā "neuzticami", ja paraksts atbilst revīzijas iesūtītajam, vai "nesakrītoši", ja neatbilst. Šis nozīmē, ka Gitea būs kā revīzijas iesūtītājs parakstītām revīzijām, kur īstais revīzijas iesūtītājs tiks atīzmēts revīzijas komentāra beigās ar tekstu Co-Authored-By: un Co-Committed-By:. Noklusētajai Gitea atslēgai ir jāatbilst lietotājam datu bāzē. @@ -1881,6 +2039,7 @@ settings.delete_notices_2=- Šī darbība neatgriezeniski izdzēsīs visu repozi settings.delete_notices_fork_1=- Visi atdalītie repozitoriju pēc dzēšanas kļūs neatkarīgi. settings.deletion_success=Repozitorijs tika izdzēsts. settings.update_settings_success=Repozitorija iestatījumi tika saglabāti. +settings.update_settings_no_unit=Repozitorijam ir jābūt piešķirtām vismaz kādām tiesībām. settings.confirm_delete=Dzēst repozitoriju settings.add_collaborator=Pievienot līdzstrādnieku settings.add_collaborator_success=Jauns līdzstrādnieks tika pievienots. @@ -1978,6 +2137,10 @@ settings.event_pull_request_review=Izmaiņu pieprasījums recenzēts settings.event_pull_request_review_desc=Izmaiņu pieprasījums apstiprināts, noraidīts vai atstāts komentārs. settings.event_pull_request_sync=Izmaiņu pieprasījums sinhronizēts settings.event_pull_request_sync_desc=Izmaiņu pieprasījums sinhronizēts. +settings.event_pull_request_review_request=Izmaiņu pieprasījuma recenzīju pieprasīšana +settings.event_pull_request_review_request_desc=Izmaiņu pieprasījuma recenzījas pieprasījums vai recenzijas pieprasījuma atcelšana. +settings.event_pull_request_approvals=Izmaiņu pieprasījuma apstiprināšana +settings.event_pull_request_merge=Izmaiņu pieprasījuma sapludināšana settings.event_package=Pakotne settings.event_package_desc=Repozitorijā izveidota vai dzēsta pakotne. settings.branch_filter=Atzaru filtrs @@ -2022,6 +2185,7 @@ settings.title=Virsraksts settings.deploy_key_content=Saturs settings.key_been_used=Izvietošanas atslēga ar šādu saturu jau ir pievienota. settings.key_name_used=Izvietošanas atslēga ar šādu nosaukumu jau eksistē. +settings.add_key_success=Izvietošanas atslēga "%s" tika pievienota. settings.deploy_key_deletion=Noņemt izvietošanas atslēgu settings.deploy_key_deletion_desc=Noņemot izvietošanas atslēgu, tai tiks liegta piekļuve šim repozitorija. Vai turpināt? settings.deploy_key_deletion_success=Izvietošanas atslēga tika noņemta. @@ -2039,6 +2203,8 @@ settings.protect_disable_push=Neļaut iesūtīt izmaiņas settings.protect_disable_push_desc=Izmaiņu iesūtīšana šajā atzarā netiks atļauta. settings.protect_enable_push=Atļaut iesūtīt izmaiņas settings.protect_enable_push_desc=Ikviens, kam ir rakstīšanas tiesības uz šo repozitoriju, varēs iesūtīt izmaiņas šajā atzarā (piespiedu izmaiņu iesūtīšanas netiks atļauta). +settings.protect_enable_merge=Iespējot sapludināšanu +settings.protect_enable_merge_desc=Ikviens ar rakstīšanas tiesībām varēst sapludināt izmaiņu pieprasījumus šajā atzarā. settings.protect_whitelist_committers=Atļaut iesūtīt izmaiņas norādītajiem lietotājiem vai komandām settings.protect_whitelist_committers_desc=Tikai norādītiem lietotāji vai komandas varēs iesūtīt izmaiņas šajā atzarā (piespiedu izmaiņu iesūtīšanas netiks atļauta). settings.protect_whitelist_deploy_keys=Atļaut izvietošanas atslēgām ar rakstīšanas tiesībām nosūtīt izmaiņas. @@ -2051,8 +2217,13 @@ settings.protect_merge_whitelist_committers_desc=Atļaut tikai noteiktiem lietot settings.protect_merge_whitelist_users=Lietotāji, kas var veikt izmaiņu sapludināšanu: settings.protect_merge_whitelist_teams=Komandas, kas var veikt izmaiņu sapludināšanu: settings.protect_check_status_contexts=Iespējot statusu pārbaudi +settings.protect_status_check_patterns=Statusa pārbaudes šabloni: +settings.protect_status_check_patterns_desc=Norādiet šablonus, kurām statusa pārbaudēm ir jāatbilst pirms atzaru iespējams sapludināt šajā atzarā, kas atbilst šim nosacījumam. Katru šablonu norādīt savā rindā, tie nevar būt tukši. settings.protect_check_status_contexts_desc=Nepieciešamas veiksmīgas statusa pārbaudes pirms sapludināšanas. Izvēlieties, kurām statusa pārbaudēm ir jāizpildās pirms ir iespejams tās sapludināt. Ja iespējots, tad revīzijas sākotnēji jānosūta uz atsevišķu atzaru, pēc kā var tikt saplusinātas vai tieši nosūtītas uz atzariem, kas atbildst veiksmīgām norādītajām stautsa pārbaudēm. Ja konteksts nav norādīts, pēdējai revīzijai ir jābūt veiksmīga neatkarīgi no konteksta. settings.protect_check_status_contexts_list=Statusu pārbaudes, kas šim repozitorijam bijušas pēdējās nedēļas laikā +settings.protect_status_check_matched=Atbilst +settings.protect_invalid_status_check_pattern=Kļūdains statusa pārbaudes šablons: "%s". +settings.protect_no_valid_status_check_patterns=Nav korekta statusa pārbaudes šablona. settings.protect_required_approvals=Vajadzīgi apstiprinājumi: settings.protect_required_approvals_desc=Atļaut sapludināt izmaiņu pieprasījumu tikai ar pietiekamu skaitu pozitīvu recenziju. settings.protect_approvals_whitelist_enabled=Ierobežot apstiprinājumus norādītajiem lietotājiem vai komandām @@ -2064,8 +2235,16 @@ settings.dismiss_stale_approvals_desc=Kad tiek iesūtītas jaunas revīzijas, ka settings.require_signed_commits=Pieprasīt parakstītas revīzijas settings.require_signed_commits_desc=Noraidīt iesūtītās izmaiņas šim atzaram, ja tās nav parakstītas vai nav iespējams pārbaudīt. settings.protect_branch_name_pattern=Aizsargātā zara šablons +settings.protect_patterns=Šabloni +settings.protect_protected_file_patterns=Aizsargāto failu šablons (vairākus var norādīt atdalot ar semikolu ';'): +settings.protect_protected_file_patterns_desc=Aizsargātie faili, ko nevar mainīt, pat ja lietotājam ir tiesības veidot jaunus, labot vai dzēst failus šajā atzarā. Vairākus šablons ir iespējams norādīt atdalot tos ar semikolu (';'). Sīkāka informācija par šabloniem pieejama github.com/gobwas/glob dokumentācijā. Piemēram, .drone.yml, /docs/**/*.txt. +settings.protect_unprotected_file_patterns=Neaizsargāto failu šablons (vairākus var norādīt atdalot ar semikolu ';'): +settings.protect_unprotected_file_patterns_desc=Neaizsargātie faili, ko iespējams mainīt apejot iesūtīšanas ierobežojumus, ja lietotājam ir tiesības iesūtīt izmaiņas šajā atzarā. Vairākus šablons ir iespējams norādīt atdalot tos ar semikolu (';'). Sīkāka informācija par šabloniem pieejama github.com/gobwas/glob dokumentācijā. Piemēram, .drone.yml, /docs/**/*.txt. settings.add_protected_branch=Iespējot aizsargāšanu settings.delete_protected_branch=Atspējot aizsargāšanu +settings.update_protect_branch_success=Atzara aizsardzības nosacījums "%s" tika saglabāta. +settings.remove_protected_branch_success=Atzara aizsardzības nosacījums "%s" tika noņemts. +settings.remove_protected_branch_failed=Neizdevās izdzēst atzara aizsardzības nosacījumu "%s". settings.protected_branch_deletion=Atspējot atzara aizsardzību settings.protected_branch_deletion_desc=Atspējojot atzara aizsardzību, ļaus lietotājiem ar rakstīšanas tiesībām nosūtīt izmaiņas uz atzaru. Vai turpināt? settings.block_rejected_reviews=Neļaut sapludināt izmaiņu pieprasījumus, kam ir pieprasītas izmaiņas @@ -2188,13 +2367,17 @@ diff.review.header=Iesūtīt recenziju diff.review.placeholder=Recenzijas komentārs diff.review.comment=Komentēt diff.review.approve=Apstiprināt +diff.review.self_reject=Izmaiņu pieprasījuma autors nevar pieprasīt izmaiņas savam izmaiņu pieprasījumam diff.review.reject=Pieprasīt izmaiņas +diff.review.self_approve=Izmaiņu pieprasījuma autors nevar apstiprināt savu izmaiņu pieprasījumu diff.committed_by=revīziju iesūtīja diff.protected=Aizsargāts diff.image.side_by_side=Blakus diff.image.swipe=Pārvelkot diff.image.overlay=Pārklājoši diff.has_escaped=Šajā līnijā ir paslēpti unikoda simboli +diff.show_file_tree=Parādīt failu koku +diff.hide_file_tree=Paslēpt failu koku releases.desc=Pārvaldiet projekta versijas un lejupielādes. release.releases=Laidieni @@ -2208,6 +2391,7 @@ release.compare=Salīdzināt release.edit=labot release.ahead.commits=%d revīzijas release.ahead.target=no %s kopš laidiena publicēšanas +tag.ahead.target=revīzijas atzarā %s no šī taga izveidošanas release.source_code=Izejas kods release.new_subheader=Laidieni palīdz organizēt projekta versijas. release.edit_subheader=Laidieni palīdz organizēt projekta versijas. @@ -2216,6 +2400,9 @@ release.target=Mērķis release.tag_helper=Izvēlieties jau esošu tagu vai izveidojiet jaunu. release.tag_helper_new=Jauns tags. Šis tags tiks izveidots no mērķa. release.tag_helper_existing=Esošs tags. +release.title=Laidiena nosaukums +release.title_empty=Nosaukums nevar būt tukšs. +release.message=Aprakstiet šo laidienu release.prerelease_desc=Atzīmēt kā pirmslaidiena versiju release.prerelease_helper=Atzīmēt, ka šo laidienu nav ieteicams lietot produkcijā. release.cancel=Atcelt @@ -2242,24 +2429,46 @@ release.tags_for=Repozitorija %s tagi branch.name=Atzara nosaukums branch.search=Meklēt atzarus +branch.already_exists=Atzars ar nosaukumu "%s" jau eksistē. branch.delete_head=Dzēst +branch.delete=`Dzēst atzaru "%s"` branch.delete_html=Dzēst atzaru branch.delete_desc=Atzara dzēšana ir neatgriezeniska, kā arī tā ir NEATGRIEZENISKA. Vai turpināt? +branch.deletion_success=Atzars "%s" tika izdzēsts. +branch.deletion_failed=Neizdevās izdzēst atzaru "%s". +branch.delete_branch_has_new_commits=Atzars "%s" nevar tik dzēsts, jo pēc sapludināšanas, tam ir pievienotas jaunas revīzijas. branch.create_branch=Izveidot atzaru %s +branch.create_from=`no "%s"` +branch.create_success=Tika izveidots atzars "%s". +branch.branch_already_exists=Atzars "%s" šajā repozitorijā jau eksistē. +branch.branch_name_conflict=Atzara nosaukums "%s" konfliktē ar jau esošu atzaru "%s" šajā repozitorijā. +branch.tag_collision=Atzaru "%s" nevar izveidot, jo repozitorijā eksistē tags ar tādu pašu nosaukumu. branch.deleted_by=Izdzēsa %s +branch.restore_success=Tika atjaunots atzars "%s". +branch.restore_failed=Neizdevās atjaunot atzaru "%s". +branch.protected_deletion_failed=Atzars "%s" ir aizsargāts. To nevar dzēst. +branch.default_deletion_failed=Atzars "%s" ir noklusētais atzars un to nevar dzēst. +branch.restore=`Atjaunot atzaru "%s"` +branch.download=`Lejupielādēt atzaru "%s"` +branch.rename=`Pārsaukt atzaru "%s"` branch.included_desc=Šis atzars ir daļa no noklusēta atzara branch.included=Iekļauts branch.create_new_branch=Izveidot jaunu atzaru no atzara: branch.confirm_create_branch=Izveidot atzaru +branch.warning_rename_default_branch=Tiks pārsaukts noklusētais atzars. +branch.rename_branch_to=Pārsaukt "%s" uz: branch.confirm_rename_branch=Pārsaukt atzaru branch.create_branch_operation=Izveidot atzaru branch.new_branch=Izveidot jaunu atzaru +branch.new_branch_from=`Izveidot jaunu atzaru no "%s"` branch.renamed=Atzars %s tika pārsaukts par %s. tag.create_tag=Izveidot tagu %s tag.create_tag_operation=Izveidot tagu tag.confirm_create_tag=Izveidot tagu +tag.create_tag_from=`Izveidot tagu no "%s"` +tag.create_success=Tags "%s" tika izveidots. topic.manage_topics=Pārvaldīt tēmas topic.done=Gatavs @@ -2296,6 +2505,8 @@ team_permission_desc=Atļauja team_unit_desc=Atļaut piekļuvi repozitorija sadaļām team_unit_disabled=(Atspējots) +form.name_reserved=Organizācijas nosaukums "%s" ir rezervēts. +form.name_pattern_not_allowed=Organizācijas nosaukums "%s" nav atļauts. form.create_org_not_allowed=Jums nav tiesību veidot jauno organizāciju. settings=Iestatījumi @@ -2307,6 +2518,7 @@ settings.permission=Tiesības settings.repoadminchangeteam=Repozitorija administrators var pievienot vai noņemt piekļuvi komandām settings.visibility=Redzamība settings.visibility.public=Publiska +settings.visibility.limited=Ierobežots (redzams tikai autentificētiem lietotājiem) settings.visibility.limited_shortname=Ierobežota settings.visibility.private=Privāta (redzama tikai organizācijas dalībniekiem) settings.visibility.private_shortname=Privāta @@ -2378,6 +2590,7 @@ teams.remove_all_repos_title=Noņemt visus komandas repozitorijus teams.remove_all_repos_desc=Šī darbība noņems visus repozitorijus no komandas. teams.add_all_repos_title=Pievienot visus repozitorijus teams.add_all_repos_desc=Šī darbība pievienos visus organizācijas repozitorijus šai komandai. +teams.add_nonexistent_repo=Repozitorijs, kuru mēģinat pievienot neeksistē, sākumā izveidojiet to. teams.add_duplicate_users=Lietotājs jau ir šajā komandā. teams.repos.none=Šai komandai nav piekļuves nevienam repozitorijam. teams.members.none=Šajā komandā nav pievienots neviens lietotājs. @@ -2407,6 +2620,7 @@ first_page=Pirmā last_page=Pēdējā total=Kopā: %d +dashboard.new_version_hint=Ir pieejama Gitea versija %s, pašreizējā versija %s. Papildus informācija par jauno versiju ir pieejama mājas lapā. dashboard.statistic=Kopsavilkums dashboard.operations=Uzturēšanas darbības dashboard.system_status=Sistēmas statuss @@ -2500,6 +2714,7 @@ users.created=Izveidots users.last_login=Pēdējā autorizācija users.never_login=Nekad nav autorizējies users.send_register_notify=Nosūtīt lietotājam reģistrācijas paziņojumu +users.new_success=Lietotāja konts "%s" tika izveidots. users.edit=Labot users.auth_source=Autentificēšanas avots users.local=Iebūvētā @@ -2524,6 +2739,7 @@ users.still_own_repo=Lietotājam pieder repozitoriji, tos sākumā ir nepiecieš users.still_has_org=Šis lietotājs ir vienas vai vairāku organizāciju biedrs, lietotāju sākumā ir nepieciešams pamest šīs organizācijas vai viņu no tām ir jāizdzēš. users.purge=Attīrīt lietotu users.purge_help=Piespiedu dzēst lietotāju un visus tā repozitorijus, organizācijas un pakotnes. Arī visi lietotāja komentāri tiks dzēsti. +users.still_own_packages=Šim lietotājam pieder viena vai vairākas pakotnes, tās nepieciešams izdzēst. users.deletion_success=Lietotāja konts veiksmīgi izdzēsts. users.reset_2fa=Noņemt 2FA users.list_status_filter.menu_text=Filtrs @@ -2698,6 +2914,7 @@ auths.tip.yandex=`Izveidojiet jaunu aplikāciju adresē https://oauth.yandex.com auths.tip.mastodon=Norādiet pielāgotu mastodon instances URL, ar kuru vēlaties autorizēties (vai izmantojiet noklusēto) auths.edit=Labot autentifikācijas avotu auths.activated=Autentifikācijas avots ir atkivizēts +auths.new_success=Jauna autentifikācija "%s" tika pievienota. auths.update_success=Autentifikācijas avots tika atjaunots. auths.update=Atjaunot autentifikācijas avotu auths.delete=Dzēst autentifikācijas avotu @@ -2705,6 +2922,7 @@ auths.delete_auth_title=Dzēst autentifikācijas avotu auths.delete_auth_desc=Izdzēšot autentifikācijas avotu, tā lietotājiem nebūs iespējams autorizēties. Vai turpināt? auths.still_in_used=Šo autentificēšanās avotu joprojām izmanto viens vai vairāki lietotāji, tos nepieciešams izdzēst vai pārvietot uz citu autentificēšanās avotu. auths.deletion_success=Autentifikācijas avots tika atjaunots. +auths.login_source_exist=Autentifikācijas avots ar nosaukumu "%s" jau eksistē. auths.login_source_of_type_exist=Autentifikācijas avots ar šādu veidu jau eksistē. auths.unable_to_initialize_openid=Nevarēja inicializēt OpenID Connect sliedzēju: %s auths.invalid_openIdConnectAutoDiscoveryURL=Kļūdains automātiskās atklāšanas URL (jābūt korektam URL, kas sākas ar http:// vai https://) @@ -2797,6 +3015,9 @@ config.mailer_sendmail_timeout=Sendmail noildze config.mailer_use_dummy=Tukšs config.test_email_placeholder=E-pasts (piemēram, test@example.com) config.send_test_mail=Nosūtīt pārbaudes e-pastu +config.send_test_mail_submit=Sūtīt +config.test_mail_failed=Neizdevās nosūtīt pārbaudes e-pastu uz "%s": %v +config.test_mail_sent=Pārbaudes e-pasts tika nosūtīts uz "%s". config.oauth_config=OAuth konfigurācija config.oauth_enabled=Iespējots @@ -2834,13 +3055,16 @@ config.git_pull_timeout=Izmaiņu saņemšanas darbības noilgums config.git_gc_timeout=GC darbības noilgums config.log_config=Žurnalizēšanas konfigurācija +config.logger_name_fmt=Žurnalizētājs: %s config.disabled_logger=Atspējots config.access_log_mode=Piekļuves žurnalizēšanas veids +config.access_log_template=Piekļuves žurnāla sagatave config.xorm_log_sql=SQL žurnalizēšana config.get_setting_failed=`Neizdevās izgūt iestatījumu "%s"` config.set_setting_failed=`Neizdevās uzstādīt iestatījumu "%s"` +monitor.stats=Statistika monitor.cron=Cron uzdevumi monitor.name=Nosaukums @@ -2850,6 +3074,8 @@ monitor.previous=Pēdējās izpildes laiks monitor.execute_times=Izpildes monitor.process=Darbojošies procesi monitor.stacktrace=Steka izsekojamība +monitor.processes_count=%d procesi +monitor.download_diagnosis_report=Lejupielādēt diagnostikas atskaiti monitor.desc=Apraksts monitor.start=Sākuma laiks monitor.execute_time=Izpildes laiks @@ -2870,11 +3096,14 @@ monitor.queue.numberinqueue=Skaits rindā monitor.queue.review=Pārbaudīt konfigurāciju monitor.queue.review_add=Pārbaudīt/Pievienot strādņus monitor.queue.settings.title=Pūla iestatījumi +monitor.queue.settings.desc=Pūls dinamiski tiek palielināts atkarībā no bloķētiem darbiem rindā. monitor.queue.settings.maxnumberworkers=Maksimālais strādņu skaits monitor.queue.settings.maxnumberworkers.placeholder=Pašalaik %[1]d monitor.queue.settings.maxnumberworkers.error=Maksimālajam strādņu skaitam ir jābūt skaitlim monitor.queue.settings.submit=Saglabāt iestatījumus monitor.queue.settings.changed=Iestatījumi saglabāti +monitor.queue.settings.remove_all_items=Noņemt visus +monitor.queue.settings.remove_all_items_done=Visi ieraksti rindā tika noņemti. notices.system_notice_list=Sistēmas paziņojumi notices.view_detail_header=Skatīt paziņojuma detaļas @@ -2984,6 +3213,7 @@ error.unit_not_allowed=Jums nav tiesību piekļūt šai repozitorija sadaļai. title=Pakotnes desc=Pārvaldīt repozitorija pakotnes. empty=Pašlaik šeit nav nevienas pakotnes. +empty.documentation=Papildus informācija par pakotņu reģistru pieejama dokumentācijā. empty.repo=Neparādās augšupielādēta pakotne? Apmeklējiet pakotņu iestatījumus, lai sasaistītu ar repozitoriju. filter.type=Veids filter.type.all=Visas @@ -3008,9 +3238,15 @@ versions=Versijas versions.view_all=Parādīt visas dependency.id=ID dependency.version=Versija +alpine.registry=Iestaties šo reģistru pievienojot tā URL /etc/apk/repositories failā: +alpine.registry.key=Lejupielādējiet reģistra publisko RSA atslēgu direktorijā /etc/apk/keys/, lai pārbaudītu indeksa parakstu: +alpine.registry.info=Izvēlieties $branch un $repository no saraksta zemāk. alpine.install=Lai instalētu pakotni, nepieciešams izpildīt sekojošu komandu: +alpine.documentation=Papildus informācija par Alpine reģistru pieejama dokumentācijā. +alpine.repository=Repozitorija informācija alpine.repository.branches=Atzari alpine.repository.repositories=Repozitoriji +alpine.repository.architectures=Arhitektūras cargo.registry=Uzstādiet šo reģistru Cargo konfigurācijas failā, piemēram, ~/.cargo/config.toml: cargo.install=Lai instalētu Cargo pakotni, izpildiet sekojošu komandu: cargo.documentation=Papildus informācija par Cargo reģistru pieejama dokumentācijā. @@ -3043,11 +3279,21 @@ container.layers=Attēla slāņi container.labels=Etiķetes container.labels.key=Atslēga container.labels.value=Vērtība +cran.registry=Iestaties šo reģistru savā Rprofile.site failā: cran.install=Lai instalētu pakotni, nepieciešams izpildīt sekojošu komandu: +cran.documentation=Papildus informācija par CRAN reģistru pieejama dokumentācijā. debian.registry=Konfigurējiet šo reģistru no komandrindas: +debian.registry.info=Izvēlieties $distribution un $component no saraksta zemāk. debian.install=Lai instalētu pakotni, nepieciešams izpildīt sekojošu komandu: +debian.documentation=Papildus informācija par Debian reģistru pieejama dokumentācijā. +debian.repository=Repozitorija informācija +debian.repository.distributions=Distribūcijas +debian.repository.components=Komponentes +debian.repository.architectures=Arhitektūras generic.download=Lejupielādēt pakotni, izmantojot, komandrindu: generic.documentation=Papildus informācija par ģenerisku pakotņu reģistru pieejama dokumentācijā. +go.install=Instalēt pakotni no komandrindas: +go.documentation=Papildus informācija par Go reģistru pieejama dokumentācijā. helm.registry=Konfigurējiet šo reģistru no komandrindas: helm.install=Lai instalētu pakotni, nepieciešams izpildīt sekojošu komandu: helm.documentation=Papildus informācija par Helm reģistru pieejama dokumentācijā. @@ -3076,6 +3322,7 @@ pypi.install=Lai instalētu pip pakotni, izpildiet sekojošu komandu: pypi.documentation=Papildus informācija par PyPI reģistru pieejama dokumentācijā. rpm.registry=Konfigurējiet šo reģistru no komandrindas: rpm.install=Lai instalētu pakotni, nepieciešams izpildīt sekojošu komandu: +rpm.documentation=Papildus informācija par RPM reģistru pieejama dokumentācijā. rubygems.install=Lai instalētu gem pakotni, izpildiet sekojošu komandu: rubygems.install2=vai pievienojiet Gemfile: rubygems.dependencies.runtime=Izpildlaika atkarības @@ -3084,6 +3331,9 @@ rubygems.required.ruby=Nepieciešamā Ruby versija rubygems.required.rubygems=Nepieciešamā RubyGem versija rubygems.documentation=Papildus informācija par RubyGems reģistru pieejama dokumentācijā. swift.registry=Konfigurējiet šo reģistru no komandrindas: +swift.install=Pievienojiet pakotni savā Package.swift failā: +swift.install2=un izpildiet sekojošu komandu: +swift.documentation=Papildus informācija par Swift reģistru pieejama dokumentācijā. vagrant.install=Lai pievienotu Vagrant kasti, izpildiet sekojošu komandu: vagrant.documentation=Papildus informācija par Vagrant reģistru pieejama dokumentācijā. settings.link=Piesaistīt pakotni šim repozitorijam @@ -3139,11 +3389,13 @@ name=Nosaukums creation=Pievienot noslēpumu creation.name_placeholder=reģistr-nejūtīgs, tikai burti, cipari un apakšsvītras, nevar sākties ar GITEA_ vai GITHUB_ creation.value_placeholder=Ievadiet jebkādu saturu. Atstarpes sākumā un beigā tiks noņemtas. +creation.success=Noslēpums "%s" tika pievienots. creation.failed=Neizdevās pievienot noslēpumu. deletion=Dzēst noslēpumu deletion.description=Noslēpuma dzēšana ir neatgriezeniska. Vai turpināt? deletion.success=Noslēpums tika izdzēsts. deletion.failed=Neizdevās dzēst noslēpumu. +management=Noslēpumu pārvaldība [actions] actions=Darbības @@ -3195,12 +3447,18 @@ runners.status.idle=Dīkstāvē runners.status.active=Aktīvs runners.status.offline=Bezsaistē runners.version=Versija +runners.reset_registration_token_success=Izpildītāja reģistrācijas talons tikai atiestatīts runs.all_workflows=Visas darbaplūsmas runs.commit=Revīzija runs.pushed_by=Iesūtītājs +runs.invalid_workflow_helper=Darbaplūsmas konfigurācijas fails ir kļūdains. Pārbaudiet konfiugrācijas failu: %s +runs.no_matching_runner_helper=Nav atbilstošu izpildītāju: %s need_approval_desc=Nepieciešams apstiprinājums, lai izpildītu izmaiņu pieprasījumu darbaplūsmas no atdalītiem repozitorijiem. [projects] +type-1.display_name=Individuālais projekts +type-2.display_name=Repozitorija projekts +type-3.display_name=Organizācijas projekts diff --git a/options/locale/locale_nl-NL.ini b/options/locale/locale_nl-NL.ini index f68ec11bbaf..2eca35e81e9 100644 --- a/options/locale/locale_nl-NL.ini +++ b/options/locale/locale_nl-NL.ini @@ -384,7 +384,7 @@ reset_password.text=Klik op de volgende link om je account te herstellen binnen register_success=Registratie succesvol issue_assigned.pull=@%[1]s heeft u toegewezen aan de pull request %[2]s in repository %[3]s. -issue_assigned.issue=@%[1]heeft u toegewezen aan issue %[2]s in repository %[3]s. +issue_assigned.issue=@%[1]s heeft u toegewezen aan issue %[2]s in repository %[3]s. issue.x_mentioned_you=@%s heeft u vermeld: issue.action.force_push=%[1]s heeft een force-push uitgevoerd %[2]s van %[3]s naar %[4]s. @@ -392,7 +392,7 @@ issue.action.push_1=@%[1]s heeft %[3]d commits gepusht naar %[2]s issue.action.push_n=@%[1]s heeft %[3]d commits gepusht naar %[2]s issue.action.close=@%[1]s sloot #%[2]d. issue.action.reopen=@%[1]s heropend #%[2]d. -issue.action.merge=@%[1] heeft een merge uitgevoerd van #%[2]d naar %[3]s. +issue.action.merge=@%[1]s heeft een merge uitgevoerd van #%[2]d naar %[3]s. issue.action.approve=@%[1]s heeft deze pull request goedgekeurd. issue.action.reject=@%[1]s vraagt om wijzigingen op deze pull request. issue.action.review=@%[1]s heeft gereageerd op deze pull request. diff --git a/options/locale/locale_pl-PL.ini b/options/locale/locale_pl-PL.ini index 32a4582db36..09384f28bfc 100644 --- a/options/locale/locale_pl-PL.ini +++ b/options/locale/locale_pl-PL.ini @@ -2494,7 +2494,7 @@ monitor.queue.review=Przejrzyj konfigurację monitor.queue.review_add=Przejrzyj/Dodaj procesy pracujące monitor.queue.settings.title=Ustawienia Puli monitor.queue.settings.maxnumberworkers=Maksymalna liczba procesów pracujących -monitor.queue.settings.maxnumberworkers.placeholder=Obecnie %[1]v +monitor.queue.settings.maxnumberworkers.placeholder=Obecnie %[1]d monitor.queue.settings.maxnumberworkers.error=Maksymalna liczba procesów pracujących musi być liczbą monitor.queue.settings.submit=Aktualizuj ustawienia monitor.queue.settings.changed=Zaktualizowano ustawienia diff --git a/options/locale/locale_pt-BR.ini b/options/locale/locale_pt-BR.ini index 528c394c635..a4cf7dd4531 100644 --- a/options/locale/locale_pt-BR.ini +++ b/options/locale/locale_pt-BR.ini @@ -437,7 +437,7 @@ reset_password.text=Por favor clique no link a seguir para recuperar sua conta e register_success=Cadastro bem-sucedido -issue_assigned.pull=@%[1]atribuiu a você o pull request %[2]s no repositório %[3]s. +issue_assigned.pull=@%[1]s atribuiu a você o pull request %[2]s no repositório %[3]s. issue_assigned.issue=@%[1]s atribuiu a você a issue %[2]s no repositório %[3]s. issue.x_mentioned_you=@%s mencionou você: @@ -805,6 +805,7 @@ repo_and_org_access=Acesso ao Repositório e Organização permissions_public_only=Apenas público permissions_access_all=Todos (público, privado e limitado) select_permissions=Selecionar permissões +at_least_one_permission=Você deve selecionar pelo menos uma permissão para criar um token permissions_list=Permissões: manage_oauth2_applications=Gerenciar aplicativos OAuth2 @@ -964,6 +965,7 @@ mirror_password_blank_placeholder=(não definida) mirror_password_help=Altere o nome de usuário para apagar uma senha armazenada. watchers=Observadores stargazers=Usuários que estrelaram +stars_remove_warning=Isto irá remover todos os favoritos dados a este repositório. forks=Forks reactions_more=e %d mais unit_disabled=O administrador do site desabilitou esta seção do repositório. @@ -978,6 +980,7 @@ delete_preexisting=Excluir arquivos pré-existentes delete_preexisting_content=Excluir arquivos em %s delete_preexisting_success=Arquivos órfãos excluídos em %s blame_prior=Ver a responsabilização anterior a esta modificação +author_search_tooltip=Mostra um máximo de 30 usuários transfer.accept=Aceitar transferência transfer.accept_desc=`Transferir para "%s"` @@ -1006,11 +1009,14 @@ template.one_item=Deve-se selecionar pelo menos um item de modelo template.invalid=Deve-se selecionar um repositório de modelo archive.title=Este repositório está arquivado. Você pode visualizar os arquivos e realizar clone, mas não poderá realizar push nem abrir issues e pull requests. +archive.title_date=Este repositório foi arquivado em %s. Você pode visualizar os arquivos e realizar clone, mas não poderá realizar push nem abrir issues e pull requests. archive.issue.nocomment=Este repositório está arquivado. Você não pode comentar nas issues. archive.pull.nocomment=Este repositório está arquivado. Você não pode comentar nos pull requests. form.reach_limit_of_creation_1=Você já atingiu o seu limite de %d repositório. form.reach_limit_of_creation_n=Você já atingiu o limite de %d repositórios. +form.name_reserved=O nome de repositório "%s" está reservado. +form.name_pattern_not_allowed=O padrão "%s" não é permitido em um nome de repositório. need_auth=Autorização migrate_options=Opções de Migração @@ -1036,6 +1042,7 @@ migrate.github_token_desc=Você pode colocar aqui um ou mais tokens separados po migrate.clone_local_path=ou um caminho de servidor local migrate.permission_denied=Você não pode importar repositórios locais. migrate.permission_denied_blocked=Você não pode importar dos hosts não permitidos, por favor peça ao administrador para verificar as configurações ALLOWED_DOMAINS/ALLOW_LOCALNETWORKS/BLOCKED_DOMAINS. +migrate.invalid_local_path=O caminho local é inválido. Ele não existe ou não é um diretório. migrate.invalid_lfs_endpoint=O destino LFS não é válido. migrate.failed=Migração falhou: %v migrate.migrate_items_options=Um Token de Acesso é necessário para migrar itens adicionais @@ -1162,6 +1169,7 @@ editor.must_be_on_a_branch=Você deve estar em um branch para propor alteraçõe editor.fork_before_edit=Você deve fazer um fork desse repositório para fazer ou propor alterações neste arquivo. editor.delete_this_file=Excluir arquivo editor.must_have_write_access=Você deve ter permissão de escrita para fazer ou propor alterações neste arquivo. +editor.file_delete_success=O arquivo "%s" foi excluído. editor.name_your_file=Nomeie o seu arquivo… editor.filename_help=Adicione um diretório digitando seu nome seguido por uma barra ('/'). Remova um diretório digitando o backspace no início do campo de entrada. editor.or=ou @@ -1174,6 +1182,7 @@ editor.update=Atualizar %s editor.delete=Excluir %s editor.patch=Aplicar Correção editor.patching=Corrigindo: +editor.fail_to_apply_patch=`Não foi possível aplicar a correção "%s"` editor.new_patch=Nova correção editor.commit_message_desc=Adicione uma descrição detalhada (opcional)... editor.signoff_desc=Adicione um assinado-por-committer no final do log do commit. @@ -1185,15 +1194,29 @@ editor.new_branch_name=Nome do novo branch para este commit editor.new_branch_name_desc=Novo nome do branch... editor.cancel=Cancelar editor.filename_cannot_be_empty=Nome do arquivo não pode ser em branco. +editor.filename_is_invalid=O nome do arquivo é inválido: "%s". +editor.branch_does_not_exist=Branch "%s" não existe neste repositório. +editor.branch_already_exists=Branch "%s" já existe neste repositório. +editor.directory_is_a_file=O nome do diretório "%s" já é usado como um nome de arquivo neste repositório. +editor.file_is_a_symlink=`"%s" é um link simbólico. Links simbólicos não podem ser editados no editor da web` +editor.filename_is_a_directory=O nome do arquivo "%s" já é usado como um nome de diretório neste repositório. +editor.file_editing_no_longer_exists=O arquivo que está sendo editado, "%s", não existe mais neste repositório. +editor.file_deleting_no_longer_exists=O arquivo a ser excluído, "%s", não existe mais neste repositório. editor.file_changed_while_editing=O conteúdo do arquivo mudou desde que você começou a editar. Clique aqui para ver o que foi editado ou clique em Aplicar commit das alterações novamemente para sobreescrever estas alterações. +editor.file_already_exists=Um arquivo com nome "%s" já existe neste repositório. editor.commit_empty_file_header=Fazer commit de um arquivo vazio editor.commit_empty_file_text=O arquivo que você está prestes fazer commit está vazio. Continuar? editor.no_changes_to_show=Nenhuma alteração a mostrar. +editor.fail_to_update_file=Falha ao atualizar/criar arquivo "%s". editor.fail_to_update_file_summary=Mensagem de erro: editor.push_rejected_no_message=A alteração foi rejeitada pelo servidor sem uma mensagem. Por favor, verifique os Hooks Git. editor.push_rejected=A alteração foi rejeitada pelo servidor. Por favor, verifique os Hooks Git. editor.push_rejected_summary=Mensagem completa de rejeição: editor.add_subdir=Adicionar um subdiretório... +editor.unable_to_upload_files=Ocorreu um erro ao enviar arquivos para "%s": %v +editor.upload_file_is_locked=Arquivo "%s" está bloqueado por %s. +editor.upload_files_to_dir=`Enviar arquivos para "%s"` +editor.cannot_commit_to_protected_branch=Não foi possível enviar commits para o branch protegido "%s". editor.no_commit_to_branch=Não foi possível fazer commit diretamente no branch porque: editor.user_no_push_to_branch=O usuário não pode fazer push no branch editor.require_signed_commit=Branch requer um commit assinado @@ -1202,6 +1225,7 @@ editor.revert=Reverter %s para: commits.desc=Veja o histórico de alterações do código de fonte. commits.commits=Commits +commits.no_commits=Nenhum commit em comum. "%s" e "%s" tem históricos completamente diferentes. commits.nothing_to_compare=Estes branches são iguais. commits.search=Pesquisar commits... commits.find=Pesquisar @@ -1236,12 +1260,14 @@ projects.create=Criar Projeto projects.title=Título projects.new=Novo projeto projects.new_subheader=Coordene, acompanhe e atualize seu trabalho em um só lugar, para que os projetos permaneçam transparentes e dentro do cronograma. +projects.create_success=Projeto "%s" criado. projects.deletion=Apagar Projeto projects.deletion_desc=Excluir um projeto o remove de todas as issues relacionadas. Deseja continuar? projects.deletion_success=O projeto foi excluido. projects.edit=Editar Projetos projects.edit_subheader=Projetos organizam issues e acompanham o progresso. projects.modify=Atualizar Projeto +projects.edit_success=Projeto "%s" atualizado. projects.type.none=Nenhum projects.type.basic_kanban=Kanban básico projects.type.bug_triage=Triagem de Bugs @@ -1448,6 +1474,9 @@ issues.attachment.download=`Clique para baixar "%s"` issues.subscribe=Inscrever-se issues.unsubscribe=Desinscrever issues.unpin_issue=Desfixar issue +issues.max_pinned=Você não pode fixar mais issues +issues.pin_comment=fixou isto %s +issues.unpin_comment=desafixou isto %s issues.lock=Bloquear conversação issues.unlock=Desbloquear conversação issues.lock.unknown_reason=Não pode-se bloquear uma issue com um motivo desconhecido. @@ -1506,13 +1535,16 @@ issues.due_date_form_remove=Remover issues.due_date_not_writer=Você deve ter permissão de escrita no repositório para atualizar a data limite de uma issue. issues.due_date_not_set=Data limite não informada. issues.due_date_added=adicionou a data limite %s %s -issues.due_date_modified=modificou a data limite de %[2]para %[1]s %[3]s +issues.due_date_modified=modificou a data limite de %[2]s para %[1]s %[3]s issues.due_date_remove=removeu a data limite %s %s issues.due_date_overdue=Em atraso issues.due_date_invalid=A data limite é inválida ou está fora do intervalo. Por favor, use o formato 'dd/mm/aaaa'. issues.dependency.title=Dependências issues.dependency.issue_no_dependencies=Nenhuma dependência definida. issues.dependency.pr_no_dependencies=Nenhuma dependência definida. +issues.dependency.no_permission_1=Você não tem permissão para ler %d dependência +issues.dependency.no_permission_n=Você não tem permissão para ler %d dependências +issues.dependency.no_permission.can_remove=Você não tem permissão para ler esta dependência, mas pode remover esta dependência issues.dependency.add=Adicione… issues.dependency.cancel=Cancelar issues.dependency.remove=Remover @@ -1551,6 +1583,7 @@ issues.review.add_review_request=solicitou revisão de %s %s issues.review.remove_review_request=removeu a solicitação de revisão para %s %s issues.review.remove_review_request_self=recusou revisar %s issues.review.pending=Pendente +issues.review.pending.tooltip=Este comentário não está atualmente visível para outros usuários. Para enviar seus comentários pendentes, selecione "%s" -> "%s/%s/%s" no topo da página. issues.review.review=Revisão issues.review.reviewers=Revisores issues.review.outdated=Desatualizado @@ -1585,6 +1618,8 @@ pulls.compare_changes_desc=Selecione o branch de destino (push) e o branch de or pulls.has_viewed_file=Visto pulls.has_changed_since_last_review=Alterado desde a última revisão pulls.viewed_files_label=%[1]d / %[2]d arquivos visualizados +pulls.expand_files=Expandir todos os arquivos +pulls.collapse_files=Colapsar todos os arquivos pulls.compare_base=merge em pulls.compare_compare=pull de pulls.switch_comparison_type=Mudar tipo de comparação @@ -1605,6 +1640,7 @@ pulls.reopen_to_merge=Por favor reabra este pull request para aplicar o merge. pulls.cant_reopen_deleted_branch=Este pull request não pode ser reaberto porque o branch foi excluído. pulls.merged=Merge aplicado pulls.manually_merged=Merge aplicado manualmente +pulls.merged_info_text=O branch %s pode ser excluído. pulls.is_closed=O pull request foi fechado. pulls.title_wip_desc=`Inicie o título com o prefixo %s para prevenir o merge do pull request até que o mesmo esteja pronto.` pulls.cannot_merge_work_in_progress=Este pull request está marcado como um trabalho em andamento. @@ -1701,6 +1737,7 @@ pulls.delete.text=Você realmente deseja excluir este pull request? (Isto irá r milestones.new=Novo marco milestones.closed=Fechado %s +milestones.update_ago=Atualizado há %s milestones.no_due_date=Sem data limite milestones.open=Reabrir milestones.close=Fechar @@ -1728,6 +1765,7 @@ milestones.filter_sort.most_complete=Mais completo milestones.filter_sort.most_issues=Com mais issues milestones.filter_sort.least_issues=Com menos issues +signing.will_sign=`Este commit será assinado com a chave "%s"` signing.wont_sign.error=Houve um erro ao verificar se o commit poderia ser assinado signing.wont_sign.nokey=Não há chave disponível para assinar este commit signing.wont_sign.never=Commits nunca são assinados @@ -1752,6 +1790,8 @@ wiki.create_first_page=Criar a primeira página wiki.page=Página wiki.filter_page=Filtrar página wiki.new_page=Página +wiki.page_title=Título da página +wiki.page_content=Conteúdo wiki.default_commit_message=Escreva uma nota sobre a atualização nesta página (opcional). wiki.save_page=Salvar página wiki.last_commit_info=%s editou esta página %s @@ -1856,6 +1896,7 @@ settings.hooks=Webhooks settings.githooks=Hooks do Git settings.basic_settings=Configurações básicas settings.mirror_settings=Opções de espelhamento +settings.mirror_settings.docs.doc_link_title=Como posso espelhar repositórios? settings.mirror_settings.mirrored_repository=Repositório espelhado settings.mirror_settings.direction=Sentido settings.mirror_settings.direction.pull=Pull @@ -2343,6 +2384,7 @@ branch.delete=`Excluir branch "%s"` branch.delete_html=Excluir Branch branch.delete_desc=A exclusão de um branch é permanente. Isto NÃO PODERÁ ser desfeito. Continuar? branch.create_branch=Criar branch %s +branch.branch_already_exists=Branch "%s" já existe neste repositório. branch.deleted_by=Excluído por %s branch.included_desc=Este branch faz parte do branch padrão branch.included=Incluído @@ -2515,7 +2557,7 @@ dashboard.clean_unbind_oauth_success=Todas as conexões de OAuth não vinculadas dashboard.task.started=Tarefa Iniciada: %[1]s dashboard.task.process=Tarefa: %[1]s dashboard.task.cancelled=Tarefa: %[1]s cancelada: %[3]s -dashboard.task.error=Erro na Tarefa: %[1]: %[3]s +dashboard.task.error=Erro na Tarefa: %[1]s: %[3]s dashboard.task.finished=Tarefa: %[1]s iniciada por %[2]s foi finalizada dashboard.task.unknown=Tarefa desconhecida: %[1]s dashboard.cron.started=Cron Iniciado: %[1]s @@ -2938,6 +2980,7 @@ config.xorm_log_sql=Log SQL config.get_setting_failed=Falha ao obter configuração %s config.set_setting_failed=Falha ao definir configuração %s +monitor.stats=Estatísticas monitor.cron=Tarefas cron monitor.name=Nome @@ -2947,6 +2990,8 @@ monitor.previous=Vez anterior monitor.execute_times=Execuções monitor.process=Processos em execução monitor.stacktrace=Stacktraces +monitor.processes_count=%d processos +monitor.download_diagnosis_report=Baixar relatório de diagnóstico monitor.desc=Descrição monitor.start=Hora de início monitor.execute_time=Tempo de execução @@ -2972,6 +3017,7 @@ monitor.queue.settings.maxnumberworkers.placeholder=Atualmente %[1]d monitor.queue.settings.maxnumberworkers.error=Número máximo de executores deve ser um número monitor.queue.settings.submit=Atualizar configurações monitor.queue.settings.changed=Configurações atualizadas +monitor.queue.settings.remove_all_items=Remover tudo notices.system_notice_list=Avisos do sistema notices.view_detail_header=Ver detalhes do aviso @@ -3106,8 +3152,10 @@ versions.view_all=Ver todas dependency.id=ID dependency.version=Versão alpine.install=Para instalar o pacote, execute o seguinte comando: +alpine.repository=Informações do repositório alpine.repository.branches=Branches alpine.repository.repositories=Repositórios +alpine.repository.architectures=Arquiteturas cargo.registry=Configurar este registro no arquivo de configuração de Cargo (por exemplo ~/.cargo/config.toml): cargo.install=Para instalar o pacote usando Cargo, execute o seguinte comando: cargo.documentation=Para obter mais informações sobre o registro Cargo, consulte a documentação. @@ -3142,7 +3190,12 @@ container.labels.key=Chave container.labels.value=Valor cran.install=Para instalar o pacote, execute o seguinte comando: debian.registry=Configure este registro pela linha de comando: +debian.registry.info=Escolha uma $distribution e um $component da lista abaixo: debian.install=Para instalar o pacote, execute o seguinte comando: +debian.repository=Informações do repositório +debian.repository.distributions=Distribuições +debian.repository.components=Componentes +debian.repository.architectures=Arquiteturas generic.download=Baixar pacote pela linha de comando: generic.documentation=Para obter mais informações sobre o registro genérico, consulte a documentação. helm.registry=Configurar este registro pela linha de comando: @@ -3295,13 +3348,18 @@ runners.status.idle=Inativo runners.status.active=Ativo runners.status.offline=Offiline runners.version=Versão +runners.reset_registration_token_success=Token de registro de runner redefinido com sucesso runs.all_workflows=Todos os Workflows runs.commit=Commit runs.pushed_by=Push realizado por runs.invalid_workflow_helper=O arquivo de configuração do workflow é inválido. Por favor, verifique seu arquivo de configuração: %s +runs.no_matching_runner_helper=Nenhum runner correspondente: %s need_approval_desc=Precisa de aprovação para executar workflows para pull request do fork. [projects] +type-1.display_name=Projeto individual +type-2.display_name=Projeto do repositório +type-3.display_name=Projeto da organização diff --git a/options/locale/locale_ru-RU.ini b/options/locale/locale_ru-RU.ini index 3688a42d71f..0fe62776953 100644 --- a/options/locale/locale_ru-RU.ini +++ b/options/locale/locale_ru-RU.ini @@ -472,7 +472,7 @@ repo.transfer.body=Для того чтобы принять или отклон repo.collaborator.added.subject=%s добавил(а) вас в %s repo.collaborator.added.text=Вы были добавлены в качестве соавтора репозитория: -team_invite.subject=%[1] приглашает вас присоединиться к организации %[2] +team_invite.subject=%[1]s приглашает вас присоединиться к организации %[2]s team_invite.text_1=%[1]s приглашает вас присоединиться к команде %[2]s в организации %[3]s. team_invite.text_2=Перейдите по ссылке, чтобы присоединиться к команде: team_invite.text_3=Примечание: Это приглашение было направлено для %[1]s. Если вы не ожидали этого приглашения, можете проигнорировать это письмо. diff --git a/options/locale/locale_si-LK.ini b/options/locale/locale_si-LK.ini index 58e2fe044a4..009594dfdb1 100644 --- a/options/locale/locale_si-LK.ini +++ b/options/locale/locale_si-LK.ini @@ -353,17 +353,17 @@ issue.action.push_1=@%[1]s pushed%[3]d%[2]s කිරීමට කැප issue.action.push_n=@%[1]s pushed%[3]d%[2]s දක්වා කැපේ issue.action.close=@%[1]s වසා #%[2]d. issue.action.reopen=@%[1]s නැවත විවෘත කරන ලද #%[2]d. -issue.action.merge=@%[1]ගේ ඒකාබද්ධ #%[2]ඈ into%[3]s. +issue.action.merge=@%[1]s ගේ ඒකාබද්ධ #%[2]d ඈ into%[3]s. issue.action.approve=@%[1]s මෙම අදින්න ඉල්ලීම අනුමත. issue.action.reject=@%[1]s මෙම අදින්න ඉල්ලීම මත වෙනස්කම් ඉල්ලා. issue.action.review=@%[1]s මෙම අදින්න ඉල්ලීම පිළිබඳව අදහස් දැක්වීය. issue.action.review_dismissed=@%[1]s මෙම අදින්න ඉල්ලීම සඳහා%[2]s සිට පසුගිය සමාලෝචනය බැහැර. issue.action.ready_for_review=@%[1]s සමාලෝචනය සඳහා සූදානම් මෙම අදින්න ඉල්ලීම සලකුණු. -issue.action.new=@%[1]ගේ නිර්මාණය #%[2]ඈ. +issue.action.new=@%[1]s ගේ නිර්මාණය #%[2]d ඈ. issue.in_tree_path=%sදී: release.new.subject=%s %s නිදහස් -release.new.text=@%[1]හි නිකුතුව%[2]හි[3]හි +release.new.text=@%[1]s හි නිකුතුව%[2]s හි %[3]s හි release.title=සිරැසිය: %s release.note=සටහන: release.downloads=බාගැනීම්: @@ -844,7 +844,7 @@ migrate.permission_denied_blocked=ඔබට අවසර නොලත් ධා migrate.invalid_lfs_endpoint=මෙම LFS අවසන් ලක්ෂ්යය වලංගු නොවේ. migrate.failed=සංක්රමණය අසාර්ථකයි: %v migrate.migrate_items_options=අමතර අයිතම සංක්රමණය කිරීම සඳහා ප්රවේශ ටෝකනය අවශ්ය වේ -migrated_from=%[2]සිටදක්වා සංක්රමණය වී ඇත +migrated_from=%[2]s සිටදක්වා සංක්රමණය වී ඇත migrated_from_fake=සංක්රමණය වූ ගෙම්%[1]s migrate.migrate=%sසිට සංක්රමණය migrate.migrating=%s සිට සංක්රමණය වීම... @@ -1071,7 +1071,7 @@ issues.remove_ref_at=`ඉවත් කරන ලද යොමු %s %s` issues.add_ref_at=`එකතු කරන ලද යොමු %s %s` issues.delete_branch_at=`මකාදැමූ ශාඛාව %s %s` issues.filter_label=ලේබලය -issues.filter_label_exclude=Labels` ඉවත් කිරීමට alt + ක්ලික් කරන්න/ඇතුළු කරන්න +issues.filter_label_exclude=ලේබල බැහැර කිරීමට alt + click/enter භාවිත කරන්න issues.filter_label_no_select=සියලු ලේබල issues.filter_label_select_no_label=ලේබලයක් නැත issues.filter_milestone=සන්ධිස්ථානය @@ -1127,7 +1127,7 @@ issues.reopen_comment_issue=අදහස් දක්වා විවෘත ක issues.create_comment=අදහස issues.closed_at=`මෙම ගැටළුව වසා %[2]s` issues.reopened_at=`මෙම ගැටළුව නැවත විවෘත කරන ලදි %[2]s` -issues.ref_issue_from=මෙම නිකුතුව%[4]හි %[2]s +issues.ref_issue_from=මෙම නිකුතුව %[4]s හි %[2]s issues.ref_pull_from=මෙම අදින්න ඉල්ලීම%[4]s %[2]s issues.ref_closing_from=මෙම ගැටළුව වසා දමනු ඇත%[4]s මෙම ගැටළුව %[2]s issues.ref_reopening_from=මෙම ගැටළුව නැවත විවෘත කරනු ඇත%[4]s මෙම ගැටළුව %[2]s @@ -1214,7 +1214,7 @@ issues.error_modifying_due_date=නියමිත දිනය වෙනස් issues.error_removing_due_date=නියමිත දිනය ඉවත් කිරීමට අපොහොසත් විය. issues.push_commit_1=එකතු %d කැප %s issues.push_commits_n=එකතු %d විවරයන් %s -issues.force_push_codes=`බලය-pushed%[1]s සිට %[2]s %[4]ගේ %[6]s` +issues.force_push_codes=`බලය-pushed%[1]s සිට %[2]s %[4]s ගේ %[6]s` issues.force_push_compare=සසඳන්න issues.due_date_form=Yyy-mm-dd issues.due_date_form_add=නියමිත දිනය එකතු කරන්න @@ -2039,7 +2039,7 @@ members.member_role=සාමාජික කාර්යභාරය: members.owner=හිමිකරු members.member=සාමාජික members.remove=ඉවත් කරන්න -members.remove.detail=%[1]ගේ සිට%[2]ගේ ඉවත්? +members.remove.detail=%[1]s ගේ සිට%[2]s ගේ ඉවත්? members.leave=හැරයන්න members.leave.detail=%s හැරයනවාද? members.invite_desc=%sවෙත නව සාමාජිකයෙකු එක් කරන්න: @@ -2111,13 +2111,13 @@ dashboard.clean_unbind_oauth=පිරිසිදු නොබැඳි OAUTH dashboard.clean_unbind_oauth_success=සියලුම නොබැඳි OAUTH සම්බන්ධතා මකා දමා ඇත. dashboard.task.started=ආරම්භ කාර්යය:%[1]s dashboard.task.process=කාර්යය:%[1]s -dashboard.task.cancelled=කාර්යය:%[1]ගේ අවලංගු:%[3]ගේ +dashboard.task.cancelled=කාර්යය: %[1]s ගේ අවලංගු: %[3]s ගේ dashboard.task.error=කාර්යයයේ දෝෂය:%[1]s:%[3]s dashboard.task.finished=කාර්යය:%[1]s[2]s විසින් ආරම්භ කර ඇත dashboard.task.unknown=නොදන්නා කාර්යය:%[1]s dashboard.cron.started=ආරම්භ Con:%[1]s dashboard.cron.process=ක්රෝන්:%[1]s -dashboard.cron.cancelled=Con: %s අවලංගු:%[3]ගේ +dashboard.cron.cancelled=Cron: %s අවලංගු:%[3]sගේ dashboard.cron.error=ක්රෝන් හි දෝෂය: %s:%[3]s dashboard.cron.finished=ක්රෝන්:%[1]s අවසන් වී ඇත dashboard.delete_inactive_accounts=සියලුම අක්රීය ගිණුම් මකන්න @@ -2551,16 +2551,16 @@ comment_issue=`නිකුතුව පිළිබඳ අදහස් %[3]s #%[2]s` merge_pull_request=`ඒකාබද්ධ අදින්න ඉල්ලීම %[3]s #%[2]s` transfer_repo=මාරු කරන ලද ගබඩාව %s සිට %s -push_tag=තල්ලු ටැගය %[3]ගේ %[4]ගේ -delete_tag=මකාදැමුවා ටැගය%[2]සිට %[3]s -delete_branch=මකාදැමූ ශාඛාව%[2]සිට %[3]s +push_tag=තල්ලු ටැගය %[3]s ගේ %[4]s ගේ +delete_tag=මකාදැමුවා ටැගය%[2]s සිට %[3]s +delete_branch=මකාදැමූ ශාඛාව %[2]s සිට %[3]s compare_branch=සසඳන්න compare_commits=%d විවරයන් සසඳා බලන්න compare_commits_general=විවරයන් සසඳා බලන්න mirror_sync_push=සමමුහුර්ත %[3]s%[4]s කැඩපත සිට mirror_sync_create=සමමුහුර්ත නව යොමු %[3]s සිට %[4]s කැඩපත සිට mirror_sync_delete=සමමුහුර්ත සහ මකාදැමූ යොමු %[2]s හි %[3]s කැඩපතෙන් -approve_pull_request=`අනුමත %[3]s #%[2]ගේ` +approve_pull_request=`අනුමත %[3]s #%[2]s ගේ` reject_pull_request=%[3]s #%[2]sසඳහා යෝජිත වෙනස්කම් publish_release=`නිදහස් "%[4]s" හි %[3]s` review_dismissed_reason=හේතුව: diff --git a/options/locale/locale_sk-SK.ini b/options/locale/locale_sk-SK.ini index 2ac69fe196e..4132b2d4a3a 100644 --- a/options/locale/locale_sk-SK.ini +++ b/options/locale/locale_sk-SK.ini @@ -382,7 +382,7 @@ reset_password.text=Pre obnovenie vašeho účtu kliknite, prosím, na nasledovn register_success=Registrácia prebehla úspešne -issue_assigned.pull=@%[1]s vám pridelil pull request %[2] v repozitári %[3]s. +issue_assigned.pull=@%[1]s vám pridelil pull request %[2]s repozitári %[3]s. issue_assigned.issue=@%[1]s vám pridelil úkol %[2]s v repozitári %[3]s. issue.x_mentioned_you=@%s vás zmienil: @@ -397,7 +397,7 @@ issue.action.reject=@%[1]s požadoval zmeny v tomto pull requeste. issue.action.review=@%[1]s okomentoval tento pull request. issue.action.review_dismissed=@%[1]s zamietol poslednú recenziu od %[2]s pre tento pull request. issue.action.ready_for_review=@%[1]s označil tento pull request ako pripravený na revíziu. -issue.action.new=@%[1] vytvoril/a #%[2]d. +issue.action.new=@%[1]s vytvoril/a #%[2]d. issue.in_tree_path=V %s: release.new.subject=%s v %s vydané diff --git a/options/locale/locale_tr-TR.ini b/options/locale/locale_tr-TR.ini index 71b92cbee11..c75b60af44e 100644 --- a/options/locale/locale_tr-TR.ini +++ b/options/locale/locale_tr-TR.ini @@ -461,10 +461,10 @@ repo.transfer.body=Kabul veya reddetmek için %s ziyaret edin veya görmezden ge repo.collaborator.added.subject=%s sizi %s ekledi repo.collaborator.added.text=Bu depo için katkıcı olarak eklendiniz: -team_invite.subject=%[1] sizi %[2] organizasyonuna katılmaya davet etti -team_invite.text_1=%[1] sizi %[3] organizasyonundaki %[2] takımına katılmaya davet etti. +team_invite.subject=%[1]s sizi %[2]s organizasyonuna katılmaya davet etti +team_invite.text_1=%[1]s sizi %[3]s organizasyonundaki %[2]s takımına katılmaya davet etti. team_invite.text_2=Takıma katılmak lütfen aşağıdaki bağlantıya tıklayın: -team_invite.text_3=Not: Bu davet %[1] içindi. Bu daveti beklemiyorsanız, e-postayı yok sayabilirsiniz. +team_invite.text_3=Not: Bu davet %[1]s içindi. Bu daveti beklemiyorsanız, e-postayı yok sayabilirsiniz. [modal] yes=Evet diff --git a/options/locale/locale_uk-UA.ini b/options/locale/locale_uk-UA.ini index 9a4476d1d15..1eaf6ae3b54 100644 --- a/options/locale/locale_uk-UA.ini +++ b/options/locale/locale_uk-UA.ini @@ -1182,10 +1182,10 @@ issues.create_comment=Коментар issues.closed_at=`закрив цю задачу %[2]s` issues.reopened_at=`повторно відкрив цю задачу %[2]s` issues.commit_ref_at=`згадано цю задачу в коміті %[2]s` -issues.ref_issue_from=`посилання на цю задачу %[4] %[2]s` +issues.ref_issue_from=`посилання на цю задачу %[4]s %[2]s` issues.ref_pull_from=`послався на цей запит злиття %[4]s %[2]s` -issues.ref_closing_from=`згадав запит на злиття %[4]с, які закриють цю задачу %[2]s` -issues.ref_reopening_from=`згадав запит на злиття %[4]с, які повторно відкриють цю задачу %[2]s` +issues.ref_closing_from=`згадав запит на злиття %[4]s, які закриють цю задачу %[2]s` +issues.ref_reopening_from=`згадав запит на злиття %[4]s, які повторно відкриють цю задачу %[2]s` issues.ref_closed_from=`закрив цю задачу %[4]s %[2]s` issues.ref_reopened_from=`повторно відкрито цю задачу %[4]s %[2]s` issues.ref_from=`із %[1]s` @@ -1269,7 +1269,7 @@ issues.error_modifying_due_date=Не вдалося змінити дату за issues.error_removing_due_date=Не вдалося видалити дату завершення. issues.push_commit_1=додав %d коміт %s issues.push_commits_n=додав %d коміти(-ів) %s -issues.force_push_codes=`примусово залито %[1]s з %[2] до %[4]s %[6]s` +issues.force_push_codes=`примусово залито %[1]s з %[2]s до %[4]s %[6]s` issues.force_push_compare=Порівняти issues.due_date_form=рррр-мм-дд issues.due_date_form_add=Додати дату завершення @@ -2613,7 +2613,7 @@ comment_issue=`прокоментував задачу %[3]s#%[2 comment_pull=`прокоментував запит злиття %[3]s#%[2]s` merge_pull_request=`прийняв запит злиття %[3]s#%[2]s` transfer_repo=перенесено репозиторій %s у %s -push_tag=створив мітку %[3] в %[4]s +push_tag=створив мітку %[3]s в %[4]s delete_tag=видалено мітку %[2]s з %[3]s delete_branch=видалено гілку %[2]s з %[3]s compare_branch=Порівняти @@ -2622,14 +2622,14 @@ compare_commits_general=Порівняти коміти mirror_sync_push=синхронізував коміти в %[3]s в %[4]s із дзеркала mirror_sync_create=синхронізував нове посилання %[3]s в %[4]s із дзеркала mirror_sync_delete=синхронізовано й видалено посилання %[2]s на %[3]s із дзеркала -approve_pull_request=`схвалив %[3]s#%[2]` +approve_pull_request=`схвалив %[3]s#%[2]s` reject_pull_request=`запропонував зміни до %[3]s#%[2]s` publish_release=`опублікував випуск "%[4]s" з %[3]s` -review_dismissed=`відхилив відгук від %[4] для %[3]s#%[2]s` +review_dismissed=`відхилив відгук від %[4]s для %[3]s#%[2]s` review_dismissed_reason=Причина: create_branch=створив гілку %[3]s в %[4]s starred_repo=додав %[2]s у обране -watched_repo=почав слідкувати за %[2] +watched_repo=почав слідкувати за %[2]s [tool] now=зараз diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini index 03d0317cc3d..155db3d32c0 100644 --- a/options/locale/locale_zh-CN.ini +++ b/options/locale/locale_zh-CN.ini @@ -3135,7 +3135,7 @@ error.unit_not_allowed=您没有权限访问此仓库单元 title=软件包 desc=管理仓库软件包。 empty=还没有软件包。 -empty.documentation=关于软件包注册中心的更多信息,请参阅 文档 。 +empty.documentation=关于软件包注册中心的更多信息,请参阅 文档 。 empty.repo=您上传了一个包,但没有显示在这里吗?转到 包设置 并将其链接到这个仓库中。 filter.type=类型 filter.type.all=所有 diff --git a/options/locale/locale_zh-TW.ini b/options/locale/locale_zh-TW.ini index 35c8fa4ce6f..af7c19a164f 100644 --- a/options/locale/locale_zh-TW.ini +++ b/options/locale/locale_zh-TW.ini @@ -3110,7 +3110,7 @@ error.unit_not_allowed=您未被允許訪問此儲存庫區域 title=套件 desc=管理儲存庫套件。 empty=目前還沒有套件。 -empty.documentation=關於套件註冊中心的詳情請參閱說明文件。 +empty.documentation=關於套件註冊中心的詳情請參閱說明文件。 empty.repo=已經上傳了一個套件,但是沒有顯示在這裡嗎?打開套件設定並將其連結到這個儲存庫。 filter.type=類型 filter.type.all=所有 diff --git a/package-lock.json b/package-lock.json index 7b1ad9bae40..92e4087b084 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,21 +25,23 @@ "easymde": "2.18.0", "esbuild-loader": "3.0.1", "escape-goat": "4.0.0", - "fast-glob": "3.2.12", + "fast-glob": "3.3.0", "jquery": "3.7.0", "jquery.are-you-sure": "1.9.0", "katex": "0.16.8", "license-checker-webpack-plugin": "0.2.1", - "mermaid": "10.2.3", + "lightningcss-loader": "2.1.0", + "mermaid": "10.2.4", "mini-css-extract-plugin": "2.7.6", - "minimatch": "9.0.2", - "monaco-editor": "0.39.0", + "minimatch": "9.0.3", + "monaco-editor": "0.40.0", "monaco-editor-webpack-plugin": "7.0.1", "pdfobject": "2.2.12", "pretty-ms": "8.0.0", "sortablejs": "1.15.0", "swagger-ui-dist": "5.1.0", "throttle-debounce": "5.0.0", + "tinycolor2": "1.6.0", "tippy.js": "6.3.7", "toastify-js": "1.12.0", "tributejs": "5.1.3", @@ -48,17 +50,16 @@ "vue-bar-graph": "2.0.0", "vue-loader": "17.2.2", "vue3-calendar-heatmap": "2.0.5", - "webpack": "5.88.0", + "webpack": "5.88.1", "webpack-cli": "5.1.4", "wrap-ansi": "8.1.0" }, "devDependencies": { "@eslint-community/eslint-plugin-eslint-comments": "3.2.1", "@playwright/test": "1.35.1", - "@rollup/pluginutils": "5.0.2", "@stoplight/spectral-cli": "6.8.0", "@vitejs/plugin-vue": "4.2.3", - "eslint": "8.43.0", + "eslint": "8.44.0", "eslint-plugin-array-func": "3.1.8", "eslint-plugin-custom-elements": "0.0.8", "eslint-plugin-import": "2.27.5", @@ -78,13 +79,23 @@ "stylelint-declaration-strict-value": "1.9.2", "stylelint-stylistic": "0.4.2", "svgo": "3.0.2", - "updates": "14.2.8", - "vitest": "0.32.2" + "updates": "14.3.2", + "vite-string-plugin": "1.1.0", + "vitest": "0.33.0" }, "engines": { "node": ">= 16.0.0" } }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/@asyncapi/specs": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/@asyncapi/specs/-/specs-4.3.1.tgz", @@ -198,9 +209,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.5.tgz", - "integrity": "sha512-DFZMC9LJUG9PLOclRC32G63UXwzqS2koQC8dkx+PLdmt1xSePYpbT/NbsrJy8Q/muXz7o/h/d4A7Fuyixm559Q==", + "version": "7.22.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.7.tgz", + "integrity": "sha512-7NF8pOkHP5o2vpmGgNGcfAeCvOYhGLyA3Z4eBQkT1RJlWu47n63bCs93QfJ2hIAFCil7L5P2IWhs1oToVgrL0Q==", "bin": { "parser": "bin/babel-parser.js" }, @@ -209,9 +220,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.5.tgz", - "integrity": "sha512-ecjvYlnAaZ/KVneE/OdKYBYfgXV3Ptu6zQWmgEF7vwKhQnvVS6bjMD2XYgj+SNvQ1GfK/pjgokfPkC/2CO8CuA==", + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.6.tgz", + "integrity": "sha512-wDb5pWm4WDdF6LFUde3Jl8WzPA+3ZbxYqkC6xAXuD3irdEHN1k0NfTRrJD8ZD378SJ61miMLCqIOXYhd8x+AJQ==", "dependencies": { "regenerator-runtime": "^0.13.11" }, @@ -368,9 +379,9 @@ } }, "node_modules/@csstools/css-parser-algorithms": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-2.2.0.tgz", - "integrity": "sha512-9BoQ/jSrPq4vv3b9jjLW+PNNv56KlDH5JMx5yASSNrCtvq70FCNZUjXRvbCeR9hYj9ZyhURtqpU/RFIgg6kiOw==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-2.3.0.tgz", + "integrity": "sha512-dTKSIHHWc0zPvcS5cqGP+/TPFUJB0ekJ9dGKvMAFoNuBFhDPBt9OMGNZiIA5vTiNdGHHBeScYPXIGBMnVOahsA==", "dev": true, "funding": [ { @@ -403,9 +414,9 @@ } }, "node_modules/@csstools/media-query-list-parser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-2.1.1.tgz", - "integrity": "sha512-pUjtFbaKbiFNjJo8pprrIaXLvQvWIlwPiFnRI4sEnc4F0NIGTOsw8kaJSR3CmZAKEvV8QYckovgAnWQC0bgLLQ==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@csstools/media-query-list-parser/-/media-query-list-parser-2.1.2.tgz", + "integrity": "sha512-M8cFGGwl866o6++vIY7j1AKuq9v57cf+dGepScwCcbut9ypJNr4Cj+LLTWligYUZ0uyhEoJDKt5lvyBfh2L3ZQ==", "dev": true, "funding": [ { @@ -421,7 +432,7 @@ "node": "^14 || ^16 || >=18" }, "peerDependencies": { - "@csstools/css-parser-algorithms": "^2.2.0", + "@csstools/css-parser-algorithms": "^2.3.0", "@csstools/css-tokenizer": "^2.1.1" } }, @@ -820,14 +831,14 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.3.tgz", - "integrity": "sha512-+5gy6OQfk+xx3q0d6jGZZC3f3KzAkXc/IanVxd1is/VIIziRqqt3ongQz0FiTUXqTk0c7aDB3OaFuKnuSoJicQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.0.tgz", + "integrity": "sha512-Lj7DECXqIVCqnqjjHMPna4vn6GJcMgul/wuS0je9OZ9gsL0zzDpKPVtcG1HaDVc+9y+qgXneTeUMbCqXJNpH1A==", "dev": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.5.2", + "espree": "^9.6.0", "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", @@ -887,9 +898,9 @@ } }, "node_modules/@eslint/js": { - "version": "8.43.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.43.0.tgz", - "integrity": "sha512-s2UHCoiXfxMvmfzqoN+vrQ84ahUSYde9qNO1MdxmoEhyHWsfmwOpFlwYV+ePJEVc7gFnATGUi376WowX1N7tFg==", + "version": "8.44.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.44.0.tgz", + "integrity": "sha512-Ag+9YM4ocKQx9AarydN0KY2j0ErMHNIocPDrVo8zAE44xLTjEtz81OdR68/cydGtk6m6jDb5Za3r2useMzYmSw==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -1040,6 +1051,18 @@ "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, + "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==", + "dev": true, + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", @@ -1070,9 +1093,9 @@ } }, "node_modules/@jridgewell/source-map": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.3.tgz", - "integrity": "sha512-b+fsZXeLYi9fEULmfBrhxn4IrPlINf8fiNarzTof004v3lFdntdwa9PF7vFJqm3mg7s+ScJMxXaE3Acp1irZcg==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz", + "integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==", "dependencies": { "@jridgewell/gen-mapping": "^0.3.0", "@jridgewell/trace-mapping": "^0.3.9" @@ -1264,7 +1287,7 @@ "rollup": "^2.68.0" } }, - "node_modules/@rollup/plugin-commonjs/node_modules/@rollup/pluginutils": { + "node_modules/@rollup/pluginutils": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==", @@ -1281,40 +1304,18 @@ "rollup": "^1.20.0||^2.0.0" } }, - "node_modules/@rollup/plugin-commonjs/node_modules/@rollup/pluginutils/node_modules/estree-walker": { + "node_modules/@rollup/pluginutils/node_modules/estree-walker": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==", "dev": true }, - "node_modules/@rollup/plugin-commonjs/node_modules/@types/estree": { - "version": "0.0.39", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", - "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", "dev": true }, - "node_modules/@rollup/pluginutils": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.0.2.tgz", - "integrity": "sha512-pTd9rIsP92h+B6wWwFbW8RkZv4hiR/xKsqre4SIuAOaOEQRxi0lqLke9k2/7WegC85GgUs9pjmOjCUi3In4vwA==", - "dev": true, - "dependencies": { - "@types/estree": "^1.0.0", - "estree-walker": "^2.0.2", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "rollup": "^1.20.0||^2.0.0||^3.0.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } - } - }, "node_modules/@stoplight/better-ajv-errors": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@stoplight/better-ajv-errors/-/better-ajv-errors-1.0.3.tgz", @@ -1439,10 +1440,38 @@ "node": "^12.20 || >= 14.13" } }, + "node_modules/@stoplight/spectral-cli/node_modules/fast-glob": { + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", + "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/@stoplight/spectral-cli/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/@stoplight/spectral-core": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/@stoplight/spectral-core/-/spectral-core-1.18.0.tgz", - "integrity": "sha512-0aj+IELHvhjoPWoOFj41EJilPbaexUuWFg7GCsiJ3BXrniRp3GnPl+TIZkC1ZuuAr/oi77RviDhW9Gm7ndKB9Q==", + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/@stoplight/spectral-core/-/spectral-core-1.18.1.tgz", + "integrity": "sha512-tgUoQZ68hdhFG6CJToBZQtY2fCkh66T4BUtBhdzQ0ZqLU6MhgdL4PlE0yT2tObwsSExNuzaRZH6ssjvbjnHtQQ==", "dev": true, "dependencies": { "@stoplight/better-ajv-errors": "1.0.3", @@ -1623,9 +1652,9 @@ } }, "node_modules/@stoplight/spectral-ruleset-migrator": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/@stoplight/spectral-ruleset-migrator/-/spectral-ruleset-migrator-1.9.3.tgz", - "integrity": "sha512-lqdBW7pZT+V1DNbOxG2EkjqMIMC5YjQ4bFkgvziH6HFAnGLlvoa2chJHt+X22+DhuvBF8tzpaYrT51iL/BhxXg==", + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@stoplight/spectral-ruleset-migrator/-/spectral-ruleset-migrator-1.9.4.tgz", + "integrity": "sha512-bQjmYTf1COdhXdFg4dRzfZ7Ukc9ylr9f9J8c1PO3NGZtryUavw/109BrYfdQGgO0Hfkc/yVsRbkI4mKYNlvnXg==", "dev": true, "dependencies": { "@stoplight/json": "~3.20.1", @@ -1801,9 +1830,9 @@ } }, "node_modules/@types/eslint": { - "version": "8.40.2", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.40.2.tgz", - "integrity": "sha512-PRVjQ4Eh9z9pmmtaq8nTjZjQwKFk7YIHIud3lRoKRBgUQjgjRmoGxxGEPXQkF+lH7QkHJRNr5F4aBgYCW0lqpQ==", + "version": "8.44.0", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.44.0.tgz", + "integrity": "sha512-gsF+c/0XOguWgaOgvFs+xnnRqt9GwgTvIks36WpE6ueeI4KCEHHd8K/CKHqhOqrJKsYH8m27kRzQEvWXAwXUTw==", "dependencies": { "@types/estree": "*", "@types/json-schema": "*" @@ -1819,9 +1848,9 @@ } }, "node_modules/@types/estree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.1.tgz", - "integrity": "sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==" + "version": "0.0.39", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", + "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==" }, "node_modules/@types/json-schema": { "version": "7.0.12", @@ -1859,9 +1888,9 @@ "integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==" }, "node_modules/@types/node": { - "version": "20.3.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.3.2.tgz", - "integrity": "sha512-vOBLVQeCQfIcF/2Y7eKFTqrMnizK5lRNQ7ykML/5RuwVXVWxYkgwS7xbt4B6fKCUPgbSL5FSsjHQpaGQP/dQmw==" + "version": "20.4.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.4.1.tgz", + "integrity": "sha512-JIzsAvJeA/5iY6Y/OxZbv1lUcc8dNSE77lb2gnBH+/PJ3lFR1Ccvgwl5JWnHAkNHcRsT0TbpVOsiMKZ1F/yyJg==" }, "node_modules/@types/normalize-package-data": { "version": "2.4.1", @@ -1902,13 +1931,13 @@ } }, "node_modules/@vitest/expect": { - "version": "0.32.2", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-0.32.2.tgz", - "integrity": "sha512-6q5yzweLnyEv5Zz1fqK5u5E83LU+gOMVBDuxBl2d2Jfx1BAp5M+rZgc5mlyqdnxquyoiOXpXmFNkcGcfFnFH3Q==", + "version": "0.33.0", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-0.33.0.tgz", + "integrity": "sha512-sVNf+Gla3mhTCxNJx+wJLDPp/WcstOe0Ksqz4Vec51MmgMth/ia0MGFEkIZmVGeTL5HtjYR4Wl/ZxBxBXZJTzQ==", "dev": true, "dependencies": { - "@vitest/spy": "0.32.2", - "@vitest/utils": "0.32.2", + "@vitest/spy": "0.33.0", + "@vitest/utils": "0.33.0", "chai": "^4.3.7" }, "funding": { @@ -1916,15 +1945,14 @@ } }, "node_modules/@vitest/runner": { - "version": "0.32.2", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-0.32.2.tgz", - "integrity": "sha512-06vEL0C1pomOEktGoLjzZw+1Fb+7RBRhmw/06WkDrd1akkT9i12su0ku+R/0QM69dfkIL/rAIDTG+CSuQVDcKw==", + "version": "0.33.0", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-0.33.0.tgz", + "integrity": "sha512-UPfACnmCB6HKRHTlcgCoBh6ppl6fDn+J/xR8dTufWiKt/74Y9bHci5CKB8tESSV82zKYtkBJo9whU3mNvfaisg==", "dev": true, "dependencies": { - "@vitest/utils": "0.32.2", - "concordance": "^5.0.4", + "@vitest/utils": "0.33.0", "p-limit": "^4.0.0", - "pathe": "^1.1.0" + "pathe": "^1.1.1" }, "funding": { "url": "https://opencollective.com/vitest" @@ -1958,52 +1986,58 @@ } }, "node_modules/@vitest/snapshot": { - "version": "0.32.2", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-0.32.2.tgz", - "integrity": "sha512-JwhpeH/PPc7GJX38vEfCy9LtRzf9F4er7i4OsAJyV7sjPwjj+AIR8cUgpMTWK4S3TiamzopcTyLsZDMuldoi5A==", + "version": "0.33.0", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-0.33.0.tgz", + "integrity": "sha512-tJjrl//qAHbyHajpFvr8Wsk8DIOODEebTu7pgBrP07iOepR5jYkLFiqLq2Ltxv+r0uptUb4izv1J8XBOwKkVYA==", "dev": true, "dependencies": { - "magic-string": "^0.30.0", - "pathe": "^1.1.0", - "pretty-format": "^27.5.1" + "magic-string": "^0.30.1", + "pathe": "^1.1.1", + "pretty-format": "^29.5.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, + "node_modules/@vitest/snapshot/node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, "node_modules/@vitest/snapshot/node_modules/magic-string": { - "version": "0.30.0", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.0.tgz", - "integrity": "sha512-LA+31JYDJLs82r2ScLrlz1GjSgu66ZV518eyWT+S8VhyQn/JL0u9MeBOvQMGYiPk1DBiSN9DDMOcXvigJZaViQ==", + "version": "0.30.1", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.1.tgz", + "integrity": "sha512-mbVKXPmS0z0G4XqFDCTllmDQ6coZzn94aMlb0o/A4HEHJCKcanlDZwYJgwnkmgD3jyWhUgj9VsPrfd972yPffA==", "dev": true, "dependencies": { - "@jridgewell/sourcemap-codec": "^1.4.13" + "@jridgewell/sourcemap-codec": "^1.4.15" }, "engines": { "node": ">=12" } }, "node_modules/@vitest/spy": { - "version": "0.32.2", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-0.32.2.tgz", - "integrity": "sha512-Q/ZNILJ4ca/VzQbRM8ur3Si5Sardsh1HofatG9wsJY1RfEaw0XKP8IVax2lI1qnrk9YPuG9LA2LkZ0EI/3d4ug==", + "version": "0.33.0", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-0.33.0.tgz", + "integrity": "sha512-Kv+yZ4hnH1WdiAkPUQTpRxW8kGtH8VRTnus7ZTGovFYM1ZezJpvGtb9nPIjPnptHbsyIAxYZsEpVPYgtpjGnrg==", "dev": true, "dependencies": { - "tinyspy": "^2.1.0" + "tinyspy": "^2.1.1" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/utils": { - "version": "0.32.2", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-0.32.2.tgz", - "integrity": "sha512-lnJ0T5i03j0IJaeW73hxe2AuVnZ/y1BhhCOuIcl9LIzXnbpXJT9Lrt6brwKHXLOiA7MZ6N5hSJjt0xE1dGNCzQ==", + "version": "0.33.0", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-0.33.0.tgz", + "integrity": "sha512-pF1w22ic965sv+EN6uoePkAOTkAPWM03Ri/jXNyMIKBb/XHLDPfhLvf/Fa9g0YECevAIz56oVYXhodLvLQ/awA==", "dev": true, "dependencies": { "diff-sequences": "^29.4.3", "loupe": "^2.3.6", - "pretty-format": "^27.5.1" + "pretty-format": "^29.5.0" }, "funding": { "url": "https://opencollective.com/vitest" @@ -2046,12 +2080,17 @@ "source-map-js": "^1.0.2" } }, + "node_modules/@vue/compiler-sfc/node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" + }, "node_modules/@vue/compiler-sfc/node_modules/magic-string": { - "version": "0.30.0", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.0.tgz", - "integrity": "sha512-LA+31JYDJLs82r2ScLrlz1GjSgu66ZV518eyWT+S8VhyQn/JL0u9MeBOvQMGYiPk1DBiSN9DDMOcXvigJZaViQ==", + "version": "0.30.1", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.1.tgz", + "integrity": "sha512-mbVKXPmS0z0G4XqFDCTllmDQ6coZzn94aMlb0o/A4HEHJCKcanlDZwYJgwnkmgD3jyWhUgj9VsPrfd972yPffA==", "dependencies": { - "@jridgewell/sourcemap-codec": "^1.4.13" + "@jridgewell/sourcemap-codec": "^1.4.15" }, "engines": { "node": ">=12" @@ -2086,12 +2125,17 @@ "magic-string": "^0.30.0" } }, + "node_modules/@vue/reactivity-transform/node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" + }, "node_modules/@vue/reactivity-transform/node_modules/magic-string": { - "version": "0.30.0", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.0.tgz", - "integrity": "sha512-LA+31JYDJLs82r2ScLrlz1GjSgu66ZV518eyWT+S8VhyQn/JL0u9MeBOvQMGYiPk1DBiSN9DDMOcXvigJZaViQ==", + "version": "0.30.1", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.1.tgz", + "integrity": "sha512-mbVKXPmS0z0G4XqFDCTllmDQ6coZzn94aMlb0o/A4HEHJCKcanlDZwYJgwnkmgD3jyWhUgj9VsPrfd972yPffA==", "dependencies": { - "@jridgewell/sourcemap-codec": "^1.4.13" + "@jridgewell/sourcemap-codec": "^1.4.15" }, "engines": { "node": ">=12" @@ -2339,9 +2383,9 @@ } }, "node_modules/acorn": { - "version": "8.9.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.9.0.tgz", - "integrity": "sha512-jaVNAFBHNLXspO543WnNNPZFRtavh3skAkITqD0/2aeMkKZTN+254PyhwxFYrk3vQ1xfY+2wbesJMs/JC8/PwQ==", + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", + "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", "bin": { "acorn": "bin/acorn" }, @@ -2702,12 +2746,6 @@ "node": "*" } }, - "node_modules/blueimp-md5": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/blueimp-md5/-/blueimp-md5-2.19.0.tgz", - "integrity": "sha512-DRQrD6gJyy8FbiE4s+bDoXS9hiW3Vbx5uCdwvcCf3zLHL+Iv7LtGHLpr+GZV8rHG8tK766FGYBwRbu8pELTt+w==", - "dev": true - }, "node_modules/boolbase": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", @@ -2877,9 +2915,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001508", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001508.tgz", - "integrity": "sha512-sdQZOJdmt3GJs1UMNpCCCyeuS2IEGLXnHyAo9yIO5JJDjbjoVRij4M1qep6P6gFpptD1PqIYgzM+gwJbOi92mw==", + "version": "1.0.30001513", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001513.tgz", + "integrity": "sha512-pnjGJo7SOOjAGytZZ203Em95MRM8Cr6jhCXNF/FAXTpCTRTECnqQWLpiTRqrFtdYcth8hf4WECUpkezuYsMVww==", "funding": [ { "type": "opencollective", @@ -3123,25 +3161,6 @@ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, - "node_modules/concordance": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/concordance/-/concordance-5.0.4.tgz", - "integrity": "sha512-OAcsnTEYu1ARJqWVGwf4zh4JDfHZEaSNlNccFmt8YjB2l/n19/PF2viLINHc57vO4FKIAFl2FWASIGZZWZ2Kxw==", - "dev": true, - "dependencies": { - "date-time": "^3.1.0", - "esutils": "^2.0.3", - "fast-diff": "^1.2.0", - "js-string-escape": "^1.0.1", - "lodash": "^4.17.15", - "md5-hex": "^3.0.1", - "semver": "^7.3.2", - "well-known-symbols": "^2.0.0" - }, - "engines": { - "node": ">=10.18.0 <11 || >=12.14.0 <13 || >=14" - } - }, "node_modules/core-util-is": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", @@ -3812,22 +3831,10 @@ "node": ">=14" } }, - "node_modules/date-time": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/date-time/-/date-time-3.1.0.tgz", - "integrity": "sha512-uqCUKXE5q1PNBXjPqvwhwJf9SwMoAHBgWJ6DcrnS5o+W2JOiIILl0JEdVD8SGujrNS02GGxgwAg2PN2zONgtjg==", - "dev": true, - "dependencies": { - "time-zone": "^1.0.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/dayjs": { - "version": "1.11.8", - "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.8.tgz", - "integrity": "sha512-LcgxzFoWMEPO7ggRv1Y2N31hUf2R0Vj7fuy/m+Bg1K8rr+KAs1AEy4y9jd5DXe8pbHgX+srkHNS7TH6Q6ZhYeQ==" + "version": "1.11.9", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.9.tgz", + "integrity": "sha512-QvzAURSbQ0pKdIye2txOzNaHmxtUBXerpY0FJsFXUMKbIZeFm5ht1LS/jFsrncjnmtv8HsG0W2g6c0zUjZWmpA==" }, "node_modules/debug": { "version": "4.3.4", @@ -4010,6 +4017,17 @@ "node": ">=6" } }, + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/diff": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz", @@ -4162,9 +4180,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.4.441", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.441.tgz", - "integrity": "sha512-LlCgQ8zgYZPymf5H4aE9itwiIWH4YlCiv1HFLmmcBeFYi5E+3eaIFnjHzYtcFQbaKfAW+CqZ9pgxo33DZuoqPg==" + "version": "1.4.454", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.454.tgz", + "integrity": "sha512-pmf1rbAStw8UEQ0sr2cdJtWl48ZMuPD9Sto8HVQOq9vx9j2WgDEN6lYoaqFvqEHYOmGA9oRGn7LqWI9ta0YugQ==" }, "node_modules/elkjs": { "version": "0.8.2", @@ -4515,15 +4533,15 @@ } }, "node_modules/eslint": { - "version": "8.43.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.43.0.tgz", - "integrity": "sha512-aaCpf2JqqKesMFGgmRPessmVKjcGXqdlAYLLC3THM8t5nBRZRQ+st5WM/hoJXkdioEXLLbXgclUpM0TXo5HX5Q==", + "version": "8.44.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.44.0.tgz", + "integrity": "sha512-0wpHoUbDUHgNCyvFB5aXLiQVfK9B0at6gUvzy83k4kAsQ/u769TQDX6iKC+aO4upIHO9WSaA3QoXYQDHbNwf1A==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.4.0", - "@eslint/eslintrc": "^2.0.3", - "@eslint/js": "8.43.0", + "@eslint/eslintrc": "^2.1.0", + "@eslint/js": "8.44.0", "@humanwhocodes/config-array": "^0.11.10", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", @@ -4535,7 +4553,7 @@ "escape-string-regexp": "^4.0.0", "eslint-scope": "^7.2.0", "eslint-visitor-keys": "^3.4.1", - "espree": "^9.5.2", + "espree": "^9.6.0", "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -4555,7 +4573,7 @@ "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", - "optionator": "^0.9.1", + "optionator": "^0.9.3", "strip-ansi": "^6.0.1", "strip-json-comments": "^3.1.0", "text-table": "^0.2.0" @@ -4937,12 +4955,12 @@ } }, "node_modules/espree": { - "version": "9.5.2", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.2.tgz", - "integrity": "sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw==", + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.0.tgz", + "integrity": "sha512-1FH/IiruXZ84tpUlm0aCUEwMl2Ho5ilqVh0VvQXw+byAz/4SAciyHLlfmL5WYqsvD38oymdUwBss0LtK8m4s/A==", "dev": true, "dependencies": { - "acorn": "^8.8.0", + "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^3.4.1" }, @@ -5033,16 +5051,10 @@ "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.2.12", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", - "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.0.tgz", + "integrity": "sha512-ChDuvbOypPuNjO8yIDf36x7BlZX1smcUMTTcyoIjycexOxd6DFsKsg21qVBzEmr3G7fUKIRy2/psii+CIUt7FA==", "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", @@ -5599,9 +5611,9 @@ "dev": true }, "node_modules/gsap": { - "version": "3.12.1", - "resolved": "https://registry.npmjs.org/gsap/-/gsap-3.12.1.tgz", - "integrity": "sha512-FXtb2YbBE9l8I9Pl5DFLpCMedaiMPztRlr0Ln0CMSnJn+pbTaeKlzgth8cLNPc7PzNwIZe+SEQiBBAWaBKJdVA==" + "version": "3.12.2", + "resolved": "https://registry.npmjs.org/gsap/-/gsap-3.12.2.tgz", + "integrity": "sha512-EkYnpG8qHgYBFAwsgsGEqvT1WUidX0tt/ijepx7z8EUJHElykg91RvW1XbkT59T0gZzzszOpjQv7SE41XuIXyQ==" }, "node_modules/hard-rejection": { "version": "2.1.0", @@ -6413,15 +6425,6 @@ "integrity": "sha512-fzreKVq1eD7eGcQr7MtRpQH94f8gIfhdrc7yeih38xh684TNMK9v5aAu2wxfIRMk/GpAJRrzcirMAPIaSDaByQ==", "dev": true }, - "node_modules/js-string-escape": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/js-string-escape/-/js-string-escape-1.0.1.tgz", - "integrity": "sha512-Smw4xcfIQ5LVjAOuJCvN/zIodzA/BBSsluuoSykP+lUvScIi4U6RJLfwHet5cxFnCswUjISV8oAXaqaJDY3chg==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/js-tokens": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-8.0.1.tgz", @@ -6717,6 +6720,204 @@ "node": ">=8" } }, + "node_modules/lightningcss": { + "version": "1.21.5", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.21.5.tgz", + "integrity": "sha512-/pEUPeih2EwIx9n4T82aOG6CInN83tl/mWlw6B5gWLf36UplQi1L+5p3FUHsdt4fXVfOkkh9KIaM3owoq7ss8A==", + "dependencies": { + "detect-libc": "^1.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-darwin-arm64": "1.21.5", + "lightningcss-darwin-x64": "1.21.5", + "lightningcss-linux-arm-gnueabihf": "1.21.5", + "lightningcss-linux-arm64-gnu": "1.21.5", + "lightningcss-linux-arm64-musl": "1.21.5", + "lightningcss-linux-x64-gnu": "1.21.5", + "lightningcss-linux-x64-musl": "1.21.5", + "lightningcss-win32-x64-msvc": "1.21.5" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.21.5", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.21.5.tgz", + "integrity": "sha512-z05hyLX85WY0UfhkFUOrWEFqD69lpVAmgl3aDzMKlIZJGygbhbegqb4PV8qfUrKKNBauut/qVNPKZglhTaDDxA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.21.5", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.21.5.tgz", + "integrity": "sha512-MSJhmej/U9MrdPxDk7+FWhO8+UqVoZUHG4VvKT5RQ4RJtqtANTiWiI97LvoVNMtdMnHaKs1Pkji6wHUFxjJsHQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.21.5", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.21.5.tgz", + "integrity": "sha512-xN6+5/JsMrbZHL1lPl+MiNJ3Xza12ueBKPepiyDCFQzlhFRTj7D0LG+cfNTzPBTO8KcYQynLpl1iBB8LGp3Xtw==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.21.5", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.21.5.tgz", + "integrity": "sha512-KfzFNhC4XTbmG3ma/xcTs/IhCwieW89XALIusKmnV0N618ZDXEB0XjWOYQRCXeK9mfqPdbTBpurEHV/XZtkniQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.21.5", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.21.5.tgz", + "integrity": "sha512-bc0GytQO5Mn9QM6szaZ+31fQHNdidgpM1sSCwzPItz8hg3wOvKl8039rU0veMJV3ZgC9z0ypNRceLrSHeRHmXw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.21.5", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.21.5.tgz", + "integrity": "sha512-JwMbgypPQgc2kW2av3OwzZ8cbrEuIiDiXPJdXRE6aVxu67yHauJawQLqJKTGUhiAhy6iLDG8Wg0a3/ziL+m+Kw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.21.5", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.21.5.tgz", + "integrity": "sha512-Ib8b6IQ/OR/VrPU6YBgy4T3QnuHY7DUa95O+nz+cwrTkMSN6fuHcTcIaz4t8TJ6HI5pl3uxUOZjmtls2pyQWow==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-loader": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/lightningcss-loader/-/lightningcss-loader-2.1.0.tgz", + "integrity": "sha512-mB+M/lvs/GdXT4yc8ZiNgLUAbYpPI9grDyC3ybz/Zo6s4GZv53iZnLTnkJT/Qm3Sh89dbFUm+omoHFXCfZtcXw==", + "dependencies": { + "browserslist": "^4.21.4", + "lightningcss": "^1.16.0", + "webpack-sources": "^3.2.3" + }, + "peerDependencies": { + "webpack": ">=5" + } + }, + "node_modules/lightningcss-loader/node_modules/webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.21.5", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.21.5.tgz", + "integrity": "sha512-A8cSi8lUpBeVmoF+DqqW7cd0FemDbCuKr490IXdjyeI+KL8adpSKUs8tcqO0OXPh1EoDqK7JNkD/dELmd4Iz5g==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", @@ -6997,18 +7198,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/md5-hex": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/md5-hex/-/md5-hex-3.0.1.tgz", - "integrity": "sha512-BUiRtTtV39LIJwinWBjqVsU9xhdnz7/i889V859IBFpuqGAj6LuOvHv5XLbgZ2R7ptJoJaEcxkv88/h25T7Ciw==", - "dev": true, - "dependencies": { - "blueimp-md5": "^2.10.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/mdast-util-from-markdown": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-1.3.1.tgz", @@ -7153,9 +7342,9 @@ } }, "node_modules/mermaid": { - "version": "10.2.3", - "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-10.2.3.tgz", - "integrity": "sha512-cMVE5s9PlQvOwfORkyVpr5beMsLdInrycAosdr+tpZ0WFjG4RJ/bUHST7aTgHNJbujHkdBRAm+N50P3puQOfPw==", + "version": "10.2.4", + "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-10.2.4.tgz", + "integrity": "sha512-zHGjEI7lBvWZX+PQYmlhSA2p40OzW6QbGodTCSzDeVpqaTnyAC+2sRGqrpXO+uQk3CnoeClHQPraQUMStdqy2g==", "dependencies": { "@braintree/sanitize-url": "^6.0.2", "cytoscape": "^3.23.0", @@ -7656,9 +7845,9 @@ } }, "node_modules/minimatch": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.2.tgz", - "integrity": "sha512-PZOT9g5v2ojiTL7r1xF6plNHLtOeTpSlDI007As2NlA2aYBMfVom17yqa6QzhmDP8QOhn7LjHTg7DFCVSSa6yg==", + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -7714,9 +7903,9 @@ } }, "node_modules/monaco-editor": { - "version": "0.39.0", - "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.39.0.tgz", - "integrity": "sha512-zhbZ2Nx93tLR8aJmL2zI1mhJpsl87HMebNBM6R8z4pLfs8pj604pIVIVwyF1TivcfNtIPpMXL+nb3DsBmE/x6Q==" + "version": "0.40.0", + "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.40.0.tgz", + "integrity": "sha512-1wymccLEuFSMBvCk/jT1YDW/GuxMLYwnFwF9CDyYCxoTw2Pt379J3FUhwy9c43j51JdcxVPjwk0jm0EVDsBS2g==" }, "node_modules/monaco-editor-webpack-plugin": { "version": "7.0.1", @@ -7815,9 +8004,9 @@ } }, "node_modules/node-fetch": { - "version": "2.6.11", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.11.tgz", - "integrity": "sha512-4I6pdBY1EthSqDmJkiNk3JIT8cswwR9nfeW/cPdUagJYEQG7R95WRH74wpz7ma8Gh/9dI9FP+OU+0E4FvtA55w==", + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.12.tgz", + "integrity": "sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g==", "dependencies": { "whatwg-url": "^5.0.0" }, @@ -7853,9 +8042,9 @@ } }, "node_modules/node-releases": { - "version": "2.0.12", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.12.tgz", - "integrity": "sha512-QzsYKWhXTWx8h1kIvqfnC++o0pEmpRQA/aenALsL2F4pqNVr7YzcdMlDij5WBnwftRbJCNJL/O7zdKaxKPHqgQ==" + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", + "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==" }, "node_modules/non-layered-tidy-tree-layout": { "version": "2.0.2", @@ -7905,9 +8094,9 @@ } }, "node_modules/nwsapi": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.5.tgz", - "integrity": "sha512-6xpotnECFy/og7tKSBVmUNft7J3jyXAka4XvG6AUhFWRz+Q/Ljus7znJAA3bxColfQLdS+XsjoodtJfCgeTEFQ==", + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.7.tgz", + "integrity": "sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ==", "dev": true }, "node_modules/obj-props": { @@ -7989,17 +8178,17 @@ } }, "node_modules/optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", "dev": true, "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" + "type-check": "^0.4.0" }, "engines": { "node": ">= 0.8.0" @@ -8195,13 +8384,13 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, "node_modules/path-scurry": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.9.2.tgz", - "integrity": "sha512-qSDLy2aGFPm8i4rsbHd4MNyTcrzHFsLQykrtbuGRknZZCBBVXSv2tSCDN2Cg6Rt/GFRw8GoW9y9Ecw5rIPG1sg==", + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", + "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", "dev": true, "dependencies": { - "lru-cache": "^9.1.1", - "minipass": "^5.0.0 || ^6.0.2" + "lru-cache": "^9.1.1 || ^10.0.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -8211,9 +8400,9 @@ } }, "node_modules/path-scurry/node_modules/lru-cache": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-9.1.2.tgz", - "integrity": "sha512-ERJq3FOzJTxBbFjZ7iDs+NiK4VI9Wz+RdrrAB8dio1oV+YvdPzUEE4QNiT2VD51DkIbCYRUUzCRkssXCHqSnKQ==", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.0.0.tgz", + "integrity": "sha512-svTf/fzsKHffP42sujkO/Rjs37BCIsQVRCeNYIm9WN8rgT7ffoUnRtZCqU+6BqcSBdv8gwJeTz8knJpgACeQMw==", "dev": true, "engines": { "node": "14 || >=16.14" @@ -8371,9 +8560,9 @@ } }, "node_modules/postcss": { - "version": "8.4.24", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.24.tgz", - "integrity": "sha512-M0RzbcI0sO/XJNucsGjvWU9ERWxb/ytp1w6dKtxTKgixdtQDq4rmx/g8W1hnaheq9jgwL/oyEdH5Bc4WwJKMqg==", + "version": "8.4.25", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.25.tgz", + "integrity": "sha512-7taJ/8t2av0Z+sQEvNzCkpDynl0tX3uJMCODi6nT3PfASC7dYCWV9aQ+uiCf+KBD4SEFcu+GvJdGdwzQ6OSjCw==", "funding": [ { "type": "opencollective", @@ -8522,17 +8711,17 @@ } }, "node_modules/pretty-format": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", - "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "version": "29.6.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.6.1.tgz", + "integrity": "sha512-7jRj+yXO0W7e4/tSJKoR7HRIHLPPjtNaUGG2xxKQnGvPNRkgWcQ0AZX6P4KBRJN4FcTBWb3sa7DVUJmocYuoog==", "dev": true, "dependencies": { - "ansi-regex": "^5.0.1", + "@jest/schemas": "^29.6.0", "ansi-styles": "^5.0.0", - "react-is": "^17.0.1" + "react-is": "^18.0.0" }, "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/pretty-format/node_modules/ansi-styles": { @@ -8708,9 +8897,9 @@ } }, "node_modules/react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", "dev": true }, "node_modules/read-pkg": { @@ -9210,9 +9399,9 @@ } }, "node_modules/semver": { - "version": "7.5.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.3.tgz", - "integrity": "sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dependencies": { "lru-cache": "^6.0.0" }, @@ -10071,25 +10260,21 @@ "node": ">=12.22" } }, - "node_modules/time-zone": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/time-zone/-/time-zone-1.0.0.tgz", - "integrity": "sha512-TIsDdtKo6+XrPtiTm1ssmMngN1sAhyKnTO2kunQWqNPWIVvCm15Wmw4SWInwTVgJ5u/Tr04+8Ei9TNcw4x4ONA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/tinybench": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.5.0.tgz", "integrity": "sha512-kRwSG8Zx4tjF9ZiyH4bhaebu+EDz1BOx9hOigYHlUW4xxI/wKIUQUqo018UlU4ar6ATPBsaMrdbKZ+tmPdohFA==", "dev": true }, + "node_modules/tinycolor2": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.6.0.tgz", + "integrity": "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==" + }, "node_modules/tinypool": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.5.0.tgz", - "integrity": "sha512-paHQtnrlS1QZYKF/GnLoOM/DN9fqaGOFbCbxzAhwniySnzl9Ebk8w73/dd34DAhe/obUbPAOldTyYXQZxnPBPQ==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.6.0.tgz", + "integrity": "sha512-FdswUUo5SxRizcBc6b1GSuLpLjisa8N8qMyYoP3rl+bym+QauhtJP5bvZY1ytt8krKGmMLYIRl36HBZfeAoqhQ==", "dev": true, "engines": { "node": ">=14.0.0" @@ -10369,9 +10554,9 @@ } }, "node_modules/updates": { - "version": "14.2.8", - "resolved": "https://registry.npmjs.org/updates/-/updates-14.2.8.tgz", - "integrity": "sha512-Ca+M1vKKBBRiQSi3yrN8OdncmP9osIf1oJM/HpEIHeDvyGLs/noTi9X2LS4zl50VXRTSCqssF5CZN0XWzSPigg==", + "version": "14.3.2", + "resolved": "https://registry.npmjs.org/updates/-/updates-14.3.2.tgz", + "integrity": "sha512-cgEaWadntOSW8aFG6b956U3TVrMbRzZnF+iHjJhLEGvyV36v7cZ0RVFW3eZ4iLYyWZ2E7/aV3DrA/W6rLwpS1g==", "dev": true, "bin": { "updates": "bin/updates.js" @@ -10463,14 +10648,14 @@ } }, "node_modules/vite": { - "version": "4.3.9", - "resolved": "https://registry.npmjs.org/vite/-/vite-4.3.9.tgz", - "integrity": "sha512-qsTNZjO9NoJNW7KnOrgYwczm0WctJ8m/yqYAMAK9Lxt4SoySUfS5S8ia9K7JHpa3KEeMfyF8LoJ3c5NeBJy6pg==", + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-4.4.2.tgz", + "integrity": "sha512-zUcsJN+UvdSyHhYa277UHhiJ3iq4hUBwHavOpsNUGsTgjBeoBlK8eDt+iT09pBq0h9/knhG/SPrZiM7cGmg7NA==", "dev": true, "dependencies": { - "esbuild": "^0.17.5", - "postcss": "^8.4.23", - "rollup": "^3.21.0" + "esbuild": "^0.18.10", + "postcss": "^8.4.24", + "rollup": "^3.25.2" }, "bin": { "vite": "bin/vite.js" @@ -10478,12 +10663,16 @@ "engines": { "node": "^14.18.0 || >=16.0.0" }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, "optionalDependencies": { "fsevents": "~2.3.2" }, "peerDependencies": { "@types/node": ">= 14", "less": "*", + "lightningcss": "^1.21.0", "sass": "*", "stylus": "*", "sugarss": "*", @@ -10496,6 +10685,9 @@ "less": { "optional": true }, + "lightningcss": { + "optional": true + }, "sass": { "optional": true }, @@ -10511,15 +10703,15 @@ } }, "node_modules/vite-node": { - "version": "0.32.2", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-0.32.2.tgz", - "integrity": "sha512-dTQ1DCLwl2aEseov7cfQ+kDMNJpM1ebpyMMMwWzBvLbis8Nla/6c9WQcqpPssTwS6Rp/+U6KwlIj8Eapw4bLdA==", + "version": "0.33.0", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-0.33.0.tgz", + "integrity": "sha512-19FpHYbwWWxDr73ruNahC+vtEdza52kA90Qb3La98yZ0xULqV8A5JLNPUff0f5zID4984tW7l3DH2przTJUZSw==", "dev": true, "dependencies": { "cac": "^6.7.14", "debug": "^4.3.4", - "mlly": "^1.2.0", - "pathe": "^1.1.0", + "mlly": "^1.4.0", + "pathe": "^1.1.1", "picocolors": "^1.0.0", "vite": "^3.0.0 || ^4.0.0" }, @@ -10533,10 +10725,408 @@ "url": "https://opencollective.com/vitest" } }, + "node_modules/vite-string-plugin": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/vite-string-plugin/-/vite-string-plugin-1.1.0.tgz", + "integrity": "sha512-RMxstCLl6Zyyz4tkzzHDFrrVvom1GAxjo2lTmgfus5CZYOnFNcJ2iQiDaFpc4KAOHSNkBNtkj5YauSs+f1gqCA==", + "dev": true, + "engines": { + "node": ">=16" + } + }, + "node_modules/vite/node_modules/@esbuild/android-arm": { + "version": "0.18.11", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.11.tgz", + "integrity": "sha512-q4qlUf5ucwbUJZXF5tEQ8LF7y0Nk4P58hOsGk3ucY0oCwgQqAnqXVbUuahCddVHfrxmpyewRpiTHwVHIETYu7Q==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/android-arm64": { + "version": "0.18.11", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.11.tgz", + "integrity": "sha512-snieiq75Z1z5LJX9cduSAjUr7vEI1OdlzFPMw0HH5YI7qQHDd3qs+WZoMrWYDsfRJSq36lIA6mfZBkvL46KoIw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/android-x64": { + "version": "0.18.11", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.11.tgz", + "integrity": "sha512-iPuoxQEV34+hTF6FT7om+Qwziv1U519lEOvekXO9zaMMlT9+XneAhKL32DW3H7okrCOBQ44BMihE8dclbZtTuw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-arm64": { + "version": "0.18.11", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.11.tgz", + "integrity": "sha512-Gm0QkI3k402OpfMKyQEEMG0RuW2LQsSmI6OeO4El2ojJMoF5NLYb3qMIjvbG/lbMeLOGiW6ooU8xqc+S0fgz2w==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-x64": { + "version": "0.18.11", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.11.tgz", + "integrity": "sha512-N15Vzy0YNHu6cfyDOjiyfJlRJCB/ngKOAvoBf1qybG3eOq0SL2Lutzz9N7DYUbb7Q23XtHPn6lMDF6uWbGv9Fw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { + "version": "0.18.11", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.11.tgz", + "integrity": "sha512-atEyuq6a3omEY5qAh5jIORWk8MzFnCpSTUruBgeyN9jZq1K/QI9uke0ATi3MHu4L8c59CnIi4+1jDKMuqmR71A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-x64": { + "version": "0.18.11", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.11.tgz", + "integrity": "sha512-XtuPrEfBj/YYYnAAB7KcorzzpGTvOr/dTtXPGesRfmflqhA4LMF0Gh/n5+a9JBzPuJ+CGk17CA++Hmr1F/gI0Q==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm": { + "version": "0.18.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.11.tgz", + "integrity": "sha512-Idipz+Taso/toi2ETugShXjQ3S59b6m62KmLHkJlSq/cBejixmIydqrtM2XTvNCywFl3VC7SreSf6NV0i6sRyg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm64": { + "version": "0.18.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.11.tgz", + "integrity": "sha512-c6Vh2WS9VFKxKZ2TvJdA7gdy0n6eSy+yunBvv4aqNCEhSWVor1TU43wNRp2YLO9Vng2G+W94aRz+ILDSwAiYog==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ia32": { + "version": "0.18.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.11.tgz", + "integrity": "sha512-S3hkIF6KUqRh9n1Q0dSyYcWmcVa9Cg+mSoZEfFuzoYXXsk6196qndrM+ZiHNwpZKi3XOXpShZZ+9dfN5ykqjjw==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-loong64": { + "version": "0.18.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.11.tgz", + "integrity": "sha512-MRESANOoObQINBA+RMZW+Z0TJWpibtE7cPFnahzyQHDCA9X9LOmGh68MVimZlM9J8n5Ia8lU773te6O3ILW8kw==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-mips64el": { + "version": "0.18.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.11.tgz", + "integrity": "sha512-qVyPIZrXNMOLYegtD1u8EBccCrBVshxMrn5MkuFc3mEVsw7CCQHaqZ4jm9hbn4gWY95XFnb7i4SsT3eflxZsUg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ppc64": { + "version": "0.18.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.11.tgz", + "integrity": "sha512-T3yd8vJXfPirZaUOoA9D2ZjxZX4Gr3QuC3GztBJA6PklLotc/7sXTOuuRkhE9W/5JvJP/K9b99ayPNAD+R+4qQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-riscv64": { + "version": "0.18.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.11.tgz", + "integrity": "sha512-evUoRPWiwuFk++snjH9e2cAjF5VVSTj+Dnf+rkO/Q20tRqv+644279TZlPK8nUGunjPAtQRCj1jQkDAvL6rm2w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-s390x": { + "version": "0.18.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.11.tgz", + "integrity": "sha512-/SlRJ15XR6i93gRWquRxYCfhTeC5PdqEapKoLbX63PLCmAkXZHY2uQm2l9bN0oPHBsOw2IswRZctMYS0MijFcg==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-x64": { + "version": "0.18.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.11.tgz", + "integrity": "sha512-xcncej+wF16WEmIwPtCHi0qmx1FweBqgsRtEL1mSHLFR6/mb3GEZfLQnx+pUDfRDEM4DQF8dpXIW7eDOZl1IbA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/netbsd-x64": { + "version": "0.18.11", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.11.tgz", + "integrity": "sha512-aSjMHj/F7BuS1CptSXNg6S3M4F3bLp5wfFPIJM+Km2NfIVfFKhdmfHF9frhiCLIGVzDziggqWll0B+9AUbud/Q==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/openbsd-x64": { + "version": "0.18.11", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.11.tgz", + "integrity": "sha512-tNBq+6XIBZtht0xJGv7IBB5XaSyvYPCm1PxJ33zLQONdZoLVM0bgGqUrXnJyiEguD9LU4AHiu+GCXy/Hm9LsdQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/sunos-x64": { + "version": "0.18.11", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.11.tgz", + "integrity": "sha512-kxfbDOrH4dHuAAOhr7D7EqaYf+W45LsAOOhAet99EyuxxQmjbk8M9N4ezHcEiCYPaiW8Dj3K26Z2V17Gt6p3ng==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-arm64": { + "version": "0.18.11", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.11.tgz", + "integrity": "sha512-Sh0dDRyk1Xi348idbal7lZyfSkjhJsdFeuC13zqdipsvMetlGiFQNdO+Yfp6f6B4FbyQm7qsk16yaZk25LChzg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-ia32": { + "version": "0.18.11", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.11.tgz", + "integrity": "sha512-o9JUIKF1j0rqJTFbIoF4bXj6rvrTZYOrfRcGyL0Vm5uJ/j5CkBD/51tpdxe9lXEDouhRgdr/BYzUrDOvrWwJpg==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-x64": { + "version": "0.18.11", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.11.tgz", + "integrity": "sha512-rQI4cjLHd2hGsM1LqgDI7oOCYbQ6IBOVsX9ejuRMSze0GqXUG2ekwiKkiBU1pRGSeCqFFHxTrcEydB2Hyoz9CA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/esbuild": { + "version": "0.18.11", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.11.tgz", + "integrity": "sha512-i8u6mQF0JKJUlGR3OdFLKldJQMMs8OqM9Cc3UCi9XXziJ9WERM5bfkHaEAy0YAvPRMgqSW55W7xYn84XtEFTtA==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/android-arm": "0.18.11", + "@esbuild/android-arm64": "0.18.11", + "@esbuild/android-x64": "0.18.11", + "@esbuild/darwin-arm64": "0.18.11", + "@esbuild/darwin-x64": "0.18.11", + "@esbuild/freebsd-arm64": "0.18.11", + "@esbuild/freebsd-x64": "0.18.11", + "@esbuild/linux-arm": "0.18.11", + "@esbuild/linux-arm64": "0.18.11", + "@esbuild/linux-ia32": "0.18.11", + "@esbuild/linux-loong64": "0.18.11", + "@esbuild/linux-mips64el": "0.18.11", + "@esbuild/linux-ppc64": "0.18.11", + "@esbuild/linux-riscv64": "0.18.11", + "@esbuild/linux-s390x": "0.18.11", + "@esbuild/linux-x64": "0.18.11", + "@esbuild/netbsd-x64": "0.18.11", + "@esbuild/openbsd-x64": "0.18.11", + "@esbuild/sunos-x64": "0.18.11", + "@esbuild/win32-arm64": "0.18.11", + "@esbuild/win32-ia32": "0.18.11", + "@esbuild/win32-x64": "0.18.11" + } + }, "node_modules/vite/node_modules/rollup": { - "version": "3.25.3", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.25.3.tgz", - "integrity": "sha512-ZT279hx8gszBj9uy5FfhoG4bZx8c+0A1sbqtr7Q3KNWIizpTdDEPZbV2xcbvHsnFp4MavCQYZyzApJ+virB8Yw==", + "version": "3.26.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.26.2.tgz", + "integrity": "sha512-6umBIGVz93er97pMgQO08LuH3m6PUb3jlDUUGFsNJB6VgTCUaDFpupf5JfU30529m/UKOgmiX+uY6Sx8cOYpLA==", "dev": true, "bin": { "rollup": "dist/bin/rollup" @@ -10550,35 +11140,34 @@ } }, "node_modules/vitest": { - "version": "0.32.2", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-0.32.2.tgz", - "integrity": "sha512-hU8GNNuQfwuQmqTLfiKcqEhZY72Zxb7nnN07koCUNmntNxbKQnVbeIS6sqUgR3eXSlbOpit8+/gr1KpqoMgWCQ==", + "version": "0.33.0", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-0.33.0.tgz", + "integrity": "sha512-1CxaugJ50xskkQ0e969R/hW47za4YXDUfWJDxip1hwbnhUjYolpfUn2AMOulqG/Dtd9WYAtkHmM/m3yKVrEejQ==", "dev": true, "dependencies": { "@types/chai": "^4.3.5", "@types/chai-subset": "^1.3.3", "@types/node": "*", - "@vitest/expect": "0.32.2", - "@vitest/runner": "0.32.2", - "@vitest/snapshot": "0.32.2", - "@vitest/spy": "0.32.2", - "@vitest/utils": "0.32.2", - "acorn": "^8.8.2", + "@vitest/expect": "0.33.0", + "@vitest/runner": "0.33.0", + "@vitest/snapshot": "0.33.0", + "@vitest/spy": "0.33.0", + "@vitest/utils": "0.33.0", + "acorn": "^8.9.0", "acorn-walk": "^8.2.0", "cac": "^6.7.14", "chai": "^4.3.7", - "concordance": "^5.0.4", "debug": "^4.3.4", "local-pkg": "^0.4.3", - "magic-string": "^0.30.0", - "pathe": "^1.1.0", + "magic-string": "^0.30.1", + "pathe": "^1.1.1", "picocolors": "^1.0.0", - "std-env": "^3.3.2", + "std-env": "^3.3.3", "strip-literal": "^1.0.1", "tinybench": "^2.5.0", - "tinypool": "^0.5.0", + "tinypool": "^0.6.0", "vite": "^3.0.0 || ^4.0.0", - "vite-node": "0.32.2", + "vite-node": "0.33.0", "why-is-node-running": "^2.2.2" }, "bin": { @@ -10627,13 +11216,19 @@ } } }, + "node_modules/vitest/node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, "node_modules/vitest/node_modules/magic-string": { - "version": "0.30.0", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.0.tgz", - "integrity": "sha512-LA+31JYDJLs82r2ScLrlz1GjSgu66ZV518eyWT+S8VhyQn/JL0u9MeBOvQMGYiPk1DBiSN9DDMOcXvigJZaViQ==", + "version": "0.30.1", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.1.tgz", + "integrity": "sha512-mbVKXPmS0z0G4XqFDCTllmDQ6coZzn94aMlb0o/A4HEHJCKcanlDZwYJgwnkmgD3jyWhUgj9VsPrfd972yPffA==", "dev": true, "dependencies": { - "@jridgewell/sourcemap-codec": "^1.4.13" + "@jridgewell/sourcemap-codec": "^1.4.15" }, "engines": { "node": ">=12" @@ -10772,9 +11367,9 @@ } }, "node_modules/webpack": { - "version": "5.88.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.88.0.tgz", - "integrity": "sha512-O3jDhG5e44qIBSi/P6KpcCcH7HD+nYIHVBhdWFxcLOcIGN8zGo5nqF3BjyNCxIh4p1vFdNnreZv2h2KkoAw3lw==", + "version": "5.88.1", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.88.1.tgz", + "integrity": "sha512-FROX3TxQnC/ox4N+3xQoWZzvGXSuscxR32rbzjpXgEzWudJFEJBpdlkkob2ylrv5yzzufD1zph1OoFsLtm6stQ==", "dependencies": { "@types/eslint-scope": "^3.7.3", "@types/estree": "^1.0.0", @@ -10890,6 +11485,11 @@ "source-map": "~0.6.1" } }, + "node_modules/webpack/node_modules/@types/estree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.1.tgz", + "integrity": "sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==" + }, "node_modules/webpack/node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -10963,15 +11563,6 @@ "node": ">=10.13.0" } }, - "node_modules/well-known-symbols": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/well-known-symbols/-/well-known-symbols-2.0.0.tgz", - "integrity": "sha512-ZMjC3ho+KXo0BfJb7JgtQ5IBuvnShdlACNkKkdsqBmYw3bPAaJfPeYUo6tLUaT5tG/Gkh7xkpBhKRQ9e7pyg9Q==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/whatwg-encoding": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", diff --git a/package.json b/package.json index da95145e4ab..10956595b68 100644 --- a/package.json +++ b/package.json @@ -24,21 +24,23 @@ "easymde": "2.18.0", "esbuild-loader": "3.0.1", "escape-goat": "4.0.0", - "fast-glob": "3.2.12", + "fast-glob": "3.3.0", "jquery": "3.7.0", "jquery.are-you-sure": "1.9.0", "katex": "0.16.8", "license-checker-webpack-plugin": "0.2.1", - "mermaid": "10.2.3", + "lightningcss-loader": "2.1.0", + "mermaid": "10.2.4", "mini-css-extract-plugin": "2.7.6", - "minimatch": "9.0.2", - "monaco-editor": "0.39.0", + "minimatch": "9.0.3", + "monaco-editor": "0.40.0", "monaco-editor-webpack-plugin": "7.0.1", "pdfobject": "2.2.12", "pretty-ms": "8.0.0", "sortablejs": "1.15.0", "swagger-ui-dist": "5.1.0", "throttle-debounce": "5.0.0", + "tinycolor2": "1.6.0", "tippy.js": "6.3.7", "toastify-js": "1.12.0", "tributejs": "5.1.3", @@ -47,17 +49,16 @@ "vue-bar-graph": "2.0.0", "vue-loader": "17.2.2", "vue3-calendar-heatmap": "2.0.5", - "webpack": "5.88.0", + "webpack": "5.88.1", "webpack-cli": "5.1.4", "wrap-ansi": "8.1.0" }, "devDependencies": { "@eslint-community/eslint-plugin-eslint-comments": "3.2.1", "@playwright/test": "1.35.1", - "@rollup/pluginutils": "5.0.2", "@stoplight/spectral-cli": "6.8.0", "@vitejs/plugin-vue": "4.2.3", - "eslint": "8.43.0", + "eslint": "8.44.0", "eslint-plugin-array-func": "3.1.8", "eslint-plugin-custom-elements": "0.0.8", "eslint-plugin-import": "2.27.5", @@ -77,8 +78,9 @@ "stylelint-declaration-strict-value": "1.9.2", "stylelint-stylistic": "0.4.2", "svgo": "3.0.2", - "updates": "14.2.8", - "vitest": "0.32.2" + "updates": "14.3.2", + "vite-string-plugin": "1.1.0", + "vitest": "0.33.0" }, "browserslist": [ "defaults", diff --git a/pyproject.toml b/pyproject.toml index 7a30f591406..43c6b050bb6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,4 +12,4 @@ djlint = "1.31.1" [tool.djlint] profile="golang" -ignore="H005,H006,H008,H013,H014,H016,H020,H021,H023,H026,H030,H031,T027" +ignore="H005,H006,H008,H013,H016,H020,H021,H026,H030,H031,T027" diff --git a/routers/api/actions/runner/utils.go b/routers/api/actions/runner/utils.go index ab70f622b39..e95df7a00fa 100644 --- a/routers/api/actions/runner/utils.go +++ b/routers/api/actions/runner/utils.go @@ -116,7 +116,7 @@ func getVariablesOfTask(ctx context.Context, task *actions_model.ActionTask) map } func generateTaskContext(t *actions_model.ActionTask) *structpb.Struct { - event := map[string]interface{}{} + event := map[string]any{} _ = json.Unmarshal([]byte(t.Job.Run.EventPayload), &event) // TriggerEvent is added in https://github.com/go-gitea/gitea/pull/25229 @@ -129,14 +129,24 @@ func generateTaskContext(t *actions_model.ActionTask) *structpb.Struct { baseRef := "" headRef := "" + ref := t.Job.Run.Ref + sha := t.Job.Run.CommitSHA if pullPayload, err := t.Job.Run.GetPullRequestEventPayload(); err == nil && pullPayload.PullRequest != nil && pullPayload.PullRequest.Base != nil && pullPayload.PullRequest.Head != nil { baseRef = pullPayload.PullRequest.Base.Ref headRef = pullPayload.PullRequest.Head.Ref + + // if the TriggerEvent is pull_request_target, ref and sha need to be set according to the base of pull request + // In GitHub's documentation, ref should be the branch or tag that triggered workflow. But when the TriggerEvent is pull_request_target, + // the ref will be the base branch. + if t.Job.Run.TriggerEvent == actions_module.GithubEventPullRequestTarget { + ref = git.BranchPrefix + pullPayload.PullRequest.Base.Name + sha = pullPayload.PullRequest.Base.Sha + } } - refName := git.RefName(t.Job.Run.Ref) + refName := git.RefName(ref) - taskContext, err := structpb.NewStruct(map[string]interface{}{ + taskContext, err := structpb.NewStruct(map[string]any{ // standard contexts, see https://docs.github.com/en/actions/learn-github-actions/contexts#github-context "action": "", // string, The name of the action currently running, or the id of a step. GitHub removes special characters, and uses the name __run when the current step runs a script without an id. If you use the same action more than once in the same job, the name will include a suffix with the sequence number with underscore before it. For example, the first script you run will have the name __run, and the second script will be named __run_2. Similarly, the second invocation of actions/checkout will be actionscheckout2. "action_path": "", // string, The path where an action is located. This property is only supported in composite actions. You can use this path to access files located in the same repository as the action. @@ -153,7 +163,7 @@ func generateTaskContext(t *actions_model.ActionTask) *structpb.Struct { "graphql_url": "", // string, The URL of the GitHub GraphQL API. "head_ref": headRef, // string, The head_ref or source branch of the pull request in a workflow run. This property is only available when the event that triggers a workflow run is either pull_request or pull_request_target. "job": fmt.Sprint(t.JobID), // string, The job_id of the current job. - "ref": t.Job.Run.Ref, // string, The fully-formed ref of the branch or tag that triggered the workflow run. For workflows triggered by push, this is the branch or tag ref that was pushed. For workflows triggered by pull_request, this is the pull request merge branch. For workflows triggered by release, this is the release tag created. For other triggers, this is the branch or tag ref that triggered the workflow run. This is only set if a branch or tag is available for the event type. The ref given is fully-formed, meaning that for branches the format is refs/heads/, for pull requests it is refs/pull//merge, and for tags it is refs/tags/. For example, refs/heads/feature-branch-1. + "ref": ref, // string, The fully-formed ref of the branch or tag that triggered the workflow run. For workflows triggered by push, this is the branch or tag ref that was pushed. For workflows triggered by pull_request, this is the pull request merge branch. For workflows triggered by release, this is the release tag created. For other triggers, this is the branch or tag ref that triggered the workflow run. This is only set if a branch or tag is available for the event type. The ref given is fully-formed, meaning that for branches the format is refs/heads/, for pull requests it is refs/pull//merge, and for tags it is refs/tags/. For example, refs/heads/feature-branch-1. "ref_name": refName.ShortName(), // string, The short ref name of the branch or tag that triggered the workflow run. This value matches the branch or tag name shown on GitHub. For example, feature-branch-1. "ref_protected": false, // boolean, true if branch protections are configured for the ref that triggered the workflow run. "ref_type": refName.RefType(), // string, The type of ref that triggered the workflow run. Valid values are branch or tag. @@ -167,14 +177,14 @@ func generateTaskContext(t *actions_model.ActionTask) *structpb.Struct { "run_attempt": fmt.Sprint(t.Job.Attempt), // string, A unique number for each attempt of a particular workflow run in a repository. This number begins at 1 for the workflow run's first attempt, and increments with each re-run. "secret_source": "Actions", // string, The source of a secret used in a workflow. Possible values are None, Actions, Dependabot, or Codespaces. "server_url": setting.AppURL, // string, The URL of the GitHub server. For example: https://github.com. - "sha": t.Job.Run.CommitSHA, // string, The commit SHA that triggered the workflow. The value of this commit SHA depends on the event that triggered the workflow. For more information, see "Events that trigger workflows." For example, ffac537e6cbbf934b08745a378932722df287a53. + "sha": sha, // string, The commit SHA that triggered the workflow. The value of this commit SHA depends on the event that triggered the workflow. For more information, see "Events that trigger workflows." For example, ffac537e6cbbf934b08745a378932722df287a53. "token": t.Token, // string, A token to authenticate on behalf of the GitHub App installed on your repository. This is functionally equivalent to the GITHUB_TOKEN secret. For more information, see "Automatic token authentication." "triggering_actor": "", // string, The username of the user that initiated the workflow run. If the workflow run is a re-run, this value may differ from github.actor. Any workflow re-runs will use the privileges of github.actor, even if the actor initiating the re-run (github.triggering_actor) has different privileges. "workflow": t.Job.Run.WorkflowID, // string, The name of the workflow. If the workflow file doesn't specify a name, the value of this property is the full path of the workflow file in the repository. "workspace": "", // string, The default working directory on the runner for steps, and the default location of your repository when using the checkout action. // additional contexts - "gitea_default_actions_url": setting.Actions.DefaultActionsURL, + "gitea_default_actions_url": setting.Actions.DefaultActionsURL.URL(), }) if err != nil { log.Error("structpb.NewStruct failed: %v", err) diff --git a/routers/api/packages/alpine/alpine.go b/routers/api/packages/alpine/alpine.go index 9a551a219b6..51a5c784e02 100644 --- a/routers/api/packages/alpine/alpine.go +++ b/routers/api/packages/alpine/alpine.go @@ -24,7 +24,7 @@ import ( alpine_service "code.gitea.io/gitea/services/packages/alpine" ) -func apiError(ctx *context.Context, status int, obj interface{}) { +func apiError(ctx *context.Context, status int, obj any) { helper.LogAndProcessError(ctx, status, obj, func(message string) { ctx.PlainText(status, message) }) @@ -68,7 +68,7 @@ func GetRepositoryFile(ctx *context.Context) { return } - s, pf, err := packages_service.GetFileStreamByPackageVersion( + s, u, pf, err := packages_service.GetFileStreamByPackageVersion( ctx, pv, &packages_service.PackageFileInfo{ @@ -84,12 +84,8 @@ func GetRepositoryFile(ctx *context.Context) { } return } - defer s.Close() - ctx.ServeContent(s, &context.ServeHeaderOptions{ - Filename: pf.Name, - LastModified: pf.CreatedUnix.AsLocalTime(), - }) + helper.ServePackageFile(ctx, s, u, pf) } func UploadPackageFile(ctx *context.Context) { @@ -200,7 +196,7 @@ func DownloadPackageFile(ctx *context.Context) { return } - s, pf, err := packages_service.GetPackageFileStream(ctx, pfs[0]) + s, u, pf, err := packages_service.GetPackageFileStream(ctx, pfs[0]) if err != nil { if errors.Is(err, util.ErrNotExist) { apiError(ctx, http.StatusNotFound, err) @@ -209,12 +205,8 @@ func DownloadPackageFile(ctx *context.Context) { } return } - defer s.Close() - ctx.ServeContent(s, &context.ServeHeaderOptions{ - Filename: pf.Name, - LastModified: pf.CreatedUnix.AsLocalTime(), - }) + helper.ServePackageFile(ctx, s, u, pf) } func DeletePackageFile(ctx *context.Context) { diff --git a/routers/api/packages/cargo/cargo.go b/routers/api/packages/cargo/cargo.go index b666bdde6c9..a2e835df578 100644 --- a/routers/api/packages/cargo/cargo.go +++ b/routers/api/packages/cargo/cargo.go @@ -33,7 +33,7 @@ type StatusMessage struct { Message string `json:"detail"` } -func apiError(ctx *context.Context, status int, obj interface{}) { +func apiError(ctx *context.Context, status int, obj any) { helper.LogAndProcessError(ctx, status, obj, func(message string) { ctx.JSON(status, StatusResponse{ OK: false, @@ -165,7 +165,7 @@ func ListOwners(ctx *context.Context) { // DownloadPackageFile serves the content of a package func DownloadPackageFile(ctx *context.Context) { - s, pf, err := packages_service.GetFileStreamByPackageNameAndVersion( + s, u, pf, err := packages_service.GetFileStreamByPackageNameAndVersion( ctx, &packages_service.PackageInfo{ Owner: ctx.Package.Owner, @@ -185,12 +185,8 @@ func DownloadPackageFile(ctx *context.Context) { apiError(ctx, http.StatusInternalServerError, err) return } - defer s.Close() - ctx.ServeContent(s, &context.ServeHeaderOptions{ - Filename: pf.Name, - LastModified: pf.CreatedUnix.AsLocalTime(), - }) + helper.ServePackageFile(ctx, s, u, pf) } // https://doc.rust-lang.org/cargo/reference/registries.html#publish diff --git a/routers/api/packages/chef/chef.go b/routers/api/packages/chef/chef.go index b48b1778c42..908f9fc4be1 100644 --- a/routers/api/packages/chef/chef.go +++ b/routers/api/packages/chef/chef.go @@ -24,7 +24,7 @@ import ( packages_service "code.gitea.io/gitea/services/packages" ) -func apiError(ctx *context.Context, status int, obj interface{}) { +func apiError(ctx *context.Context, status int, obj any) { type Error struct { ErrorMessages []string `json:"error_messages"` } @@ -341,17 +341,13 @@ func DownloadPackage(ctx *context.Context) { pf := pd.Files[0].File - s, _, err := packages_service.GetPackageFileStream(ctx, pf) + s, u, _, err := packages_service.GetPackageFileStream(ctx, pf) if err != nil { apiError(ctx, http.StatusInternalServerError, err) return } - defer s.Close() - ctx.ServeContent(s, &context.ServeHeaderOptions{ - Filename: pf.Name, - LastModified: pf.CreatedUnix.AsLocalTime(), - }) + helper.ServePackageFile(ctx, s, u, pf) } // https://github.com/chef/chef/blob/main/knife/lib/chef/knife/supermarket_unshare.rb diff --git a/routers/api/packages/composer/composer.go b/routers/api/packages/composer/composer.go index d93b11efdf6..bf5bda743f5 100644 --- a/routers/api/packages/composer/composer.go +++ b/routers/api/packages/composer/composer.go @@ -26,7 +26,7 @@ import ( "github.com/hashicorp/go-version" ) -func apiError(ctx *context.Context, status int, obj interface{}) { +func apiError(ctx *context.Context, status int, obj any) { helper.LogAndProcessError(ctx, status, obj, func(message string) { type Error struct { Status int `json:"status"` @@ -162,7 +162,7 @@ func PackageMetadata(ctx *context.Context) { // DownloadPackageFile serves the content of a package func DownloadPackageFile(ctx *context.Context) { - s, pf, err := packages_service.GetFileStreamByPackageNameAndVersion( + s, u, pf, err := packages_service.GetFileStreamByPackageNameAndVersion( ctx, &packages_service.PackageInfo{ Owner: ctx.Package.Owner, @@ -182,12 +182,8 @@ func DownloadPackageFile(ctx *context.Context) { apiError(ctx, http.StatusInternalServerError, err) return } - defer s.Close() - ctx.ServeContent(s, &context.ServeHeaderOptions{ - Filename: pf.Name, - LastModified: pf.CreatedUnix.AsLocalTime(), - }) + helper.ServePackageFile(ctx, s, u, pf) } // UploadPackage creates a new package diff --git a/routers/api/packages/conan/conan.go b/routers/api/packages/conan/conan.go index caeb8c11bc4..6e1727fd764 100644 --- a/routers/api/packages/conan/conan.go +++ b/routers/api/packages/conan/conan.go @@ -47,7 +47,7 @@ var ( ) ) -func jsonResponse(ctx *context.Context, status int, obj interface{}) { +func jsonResponse(ctx *context.Context, status int, obj any) { // https://github.com/conan-io/conan/issues/6613 ctx.Resp.Header().Set("Content-Type", "application/json") ctx.Status(status) @@ -56,7 +56,7 @@ func jsonResponse(ctx *context.Context, status int, obj interface{}) { } } -func apiError(ctx *context.Context, status int, obj interface{}) { +func apiError(ctx *context.Context, status int, obj any) { helper.LogAndProcessError(ctx, status, obj, func(message string) { jsonResponse(ctx, status, map[string]string{ "message": message, @@ -453,7 +453,7 @@ func downloadFile(ctx *context.Context, fileFilter container.Set[string], fileKe return } - s, pf, err := packages_service.GetFileStreamByPackageNameAndVersion( + s, u, pf, err := packages_service.GetFileStreamByPackageNameAndVersion( ctx, &packages_service.PackageInfo{ Owner: ctx.Package.Owner, @@ -474,12 +474,8 @@ func downloadFile(ctx *context.Context, fileFilter container.Set[string], fileKe apiError(ctx, http.StatusInternalServerError, err) return } - defer s.Close() - ctx.ServeContent(s, &context.ServeHeaderOptions{ - Filename: pf.Name, - LastModified: pf.CreatedUnix.AsLocalTime(), - }) + helper.ServePackageFile(ctx, s, u, pf) } // DeleteRecipeV1 deletes the requested recipe(s) @@ -800,13 +796,13 @@ func listRevisionFiles(ctx *context.Context, fileKey string) { return } - files := make(map[string]interface{}) + files := make(map[string]any) for _, pf := range pfs { files[pf.Name] = nil } type FileList struct { - Files map[string]interface{} `json:"files"` + Files map[string]any `json:"files"` } jsonResponse(ctx, http.StatusOK, &FileList{ diff --git a/routers/api/packages/conda/conda.go b/routers/api/packages/conda/conda.go index f7786906309..0bf0fc1f625 100644 --- a/routers/api/packages/conda/conda.go +++ b/routers/api/packages/conda/conda.go @@ -24,7 +24,7 @@ import ( "github.com/dsnet/compress/bzip2" ) -func apiError(ctx *context.Context, status int, obj interface{}) { +func apiError(ctx *context.Context, status int, obj any) { helper.LogAndProcessError(ctx, status, obj, func(message string) { ctx.JSON(status, struct { Reason string `json:"reason"` @@ -292,15 +292,11 @@ func DownloadPackageFile(ctx *context.Context) { pf := pfs[0] - s, _, err := packages_service.GetPackageFileStream(ctx, pf) + s, u, _, err := packages_service.GetPackageFileStream(ctx, pf) if err != nil { apiError(ctx, http.StatusInternalServerError, err) return } - defer s.Close() - ctx.ServeContent(s, &context.ServeHeaderOptions{ - Filename: pf.Name, - LastModified: pf.CreatedUnix.AsLocalTime(), - }) + helper.ServePackageFile(ctx, s, u, pf) } diff --git a/routers/api/packages/container/container.go b/routers/api/packages/container/container.go index 63c49809a7b..8f79805cc8b 100644 --- a/routers/api/packages/container/container.go +++ b/routers/api/packages/container/container.go @@ -75,7 +75,7 @@ func setResponseHeaders(resp http.ResponseWriter, h *containerHeaders) { resp.WriteHeader(h.Status) } -func jsonResponse(ctx *context.Context, status int, obj interface{}) { +func jsonResponse(ctx *context.Context, status int, obj any) { setResponseHeaders(ctx.Resp, &containerHeaders{ Status: status, ContentType: "application/json", @@ -203,17 +203,25 @@ func InitiateUploadBlob(ctx *context.Context) { Digest: mount, }) if blob != nil { - if err := mountBlob(&packages_service.PackageInfo{Owner: ctx.Package.Owner, Name: image}, blob.Blob); err != nil { + accessible, err := packages_model.IsBlobAccessibleForUser(ctx, blob.Blob.ID, ctx.Doer) + if err != nil { apiError(ctx, http.StatusInternalServerError, err) return } - setResponseHeaders(ctx.Resp, &containerHeaders{ - Location: fmt.Sprintf("/v2/%s/%s/blobs/%s", ctx.Package.Owner.LowerName, image, mount), - ContentDigest: mount, - Status: http.StatusCreated, - }) - return + if accessible { + if err := mountBlob(&packages_service.PackageInfo{Owner: ctx.Package.Owner, Name: image}, blob.Blob); err != nil { + apiError(ctx, http.StatusInternalServerError, err) + return + } + + setResponseHeaders(ctx.Resp, &containerHeaders{ + Location: fmt.Sprintf("/v2/%s/%s/blobs/%s", ctx.Package.Owner.LowerName, image, mount), + ContentDigest: mount, + Status: http.StatusCreated, + }) + return + } } } @@ -482,22 +490,7 @@ func GetBlob(ctx *context.Context) { return } - s, _, err := packages_service.GetPackageFileStream(ctx, blob.File) - if err != nil { - apiError(ctx, http.StatusInternalServerError, err) - return - } - defer s.Close() - - setResponseHeaders(ctx.Resp, &containerHeaders{ - ContentDigest: blob.Properties.GetByName(container_module.PropertyDigest), - ContentType: blob.Properties.GetByName(container_module.PropertyMediaType), - ContentLength: blob.Blob.Size, - Status: http.StatusOK, - }) - if _, err := io.Copy(ctx.Resp, s); err != nil { - log.Error("Error whilst copying content to response: %v", err) - } + serveBlob(ctx, blob) } // https://github.com/opencontainers/distribution-spec/blob/main/spec.md#deleting-blobs @@ -636,22 +629,7 @@ func GetManifest(ctx *context.Context) { return } - s, _, err := packages_service.GetPackageFileStream(ctx, manifest.File) - if err != nil { - apiError(ctx, http.StatusInternalServerError, err) - return - } - defer s.Close() - - setResponseHeaders(ctx.Resp, &containerHeaders{ - ContentDigest: manifest.Properties.GetByName(container_module.PropertyDigest), - ContentType: manifest.Properties.GetByName(container_module.PropertyMediaType), - ContentLength: manifest.Blob.Size, - Status: http.StatusOK, - }) - if _, err := io.Copy(ctx.Resp, s); err != nil { - log.Error("Error whilst copying content to response: %v", err) - } + serveBlob(ctx, manifest) } // https://github.com/opencontainers/distribution-spec/blob/main/spec.md#deleting-tags @@ -686,6 +664,36 @@ func DeleteManifest(ctx *context.Context) { }) } +func serveBlob(ctx *context.Context, pfd *packages_model.PackageFileDescriptor) { + s, u, _, err := packages_service.GetPackageBlobStream(ctx, pfd.File, pfd.Blob) + if err != nil { + apiError(ctx, http.StatusInternalServerError, err) + return + } + + headers := &containerHeaders{ + ContentDigest: pfd.Properties.GetByName(container_module.PropertyDigest), + ContentType: pfd.Properties.GetByName(container_module.PropertyMediaType), + ContentLength: pfd.Blob.Size, + Status: http.StatusOK, + } + + if u != nil { + headers.Status = http.StatusTemporaryRedirect + headers.Location = u.String() + + setResponseHeaders(ctx.Resp, headers) + return + } + + defer s.Close() + + setResponseHeaders(ctx.Resp, headers) + if _, err := io.Copy(ctx.Resp, s); err != nil { + log.Error("Error whilst copying content to response: %v", err) + } +} + // https://github.com/opencontainers/distribution-spec/blob/main/spec.md#content-discovery func GetTagList(ctx *context.Context) { image := ctx.Params("image") diff --git a/routers/api/packages/cran/cran.go b/routers/api/packages/cran/cran.go index eb3f9a452b1..0ef6eff88d6 100644 --- a/routers/api/packages/cran/cran.go +++ b/routers/api/packages/cran/cran.go @@ -21,7 +21,7 @@ import ( packages_service "code.gitea.io/gitea/services/packages" ) -func apiError(ctx *context.Context, status int, obj interface{}) { +func apiError(ctx *context.Context, status int, obj any) { helper.LogAndProcessError(ctx, status, obj, func(message string) { ctx.PlainText(status, message) }) @@ -249,7 +249,7 @@ func downloadPackageFile(ctx *context.Context, opts *cran_model.SearchOptions) { return } - s, _, err := packages_service.GetPackageFileStream(ctx, pf) + s, u, _, err := packages_service.GetPackageFileStream(ctx, pf) if err != nil { if errors.Is(err, util.ErrNotExist) { apiError(ctx, http.StatusNotFound, err) @@ -258,10 +258,6 @@ func downloadPackageFile(ctx *context.Context, opts *cran_model.SearchOptions) { } return } - defer s.Close() - ctx.ServeContent(s, &context.ServeHeaderOptions{ - Filename: pf.Name, - LastModified: pf.CreatedUnix.AsLocalTime(), - }) + helper.ServePackageFile(ctx, s, u, pf) } diff --git a/routers/api/packages/debian/debian.go b/routers/api/packages/debian/debian.go index cfc03ae522a..f7270e0ae0e 100644 --- a/routers/api/packages/debian/debian.go +++ b/routers/api/packages/debian/debian.go @@ -23,7 +23,7 @@ import ( debian_service "code.gitea.io/gitea/services/packages/debian" ) -func apiError(ctx *context.Context, status int, obj interface{}) { +func apiError(ctx *context.Context, status int, obj any) { helper.LogAndProcessError(ctx, status, obj, func(message string) { ctx.PlainText(status, message) }) @@ -59,7 +59,7 @@ func GetRepositoryFile(ctx *context.Context) { key += "|" + component + "|" + architecture } - s, pf, err := packages_service.GetFileStreamByPackageVersion( + s, u, pf, err := packages_service.GetFileStreamByPackageVersion( ctx, pv, &packages_service.PackageFileInfo{ @@ -75,12 +75,8 @@ func GetRepositoryFile(ctx *context.Context) { } return } - defer s.Close() - ctx.ServeContent(s, &context.ServeHeaderOptions{ - Filename: pf.Name, - LastModified: pf.CreatedUnix.AsLocalTime(), - }) + helper.ServePackageFile(ctx, s, u, pf) } // https://wiki.debian.org/DebianRepository/Format#indices_acquisition_via_hashsums_.28by-hash.29 @@ -110,7 +106,7 @@ func GetRepositoryFileByHash(ctx *context.Context) { return } - s, pf, err := packages_service.GetPackageFileStream(ctx, pfs[0]) + s, u, pf, err := packages_service.GetPackageFileStream(ctx, pfs[0]) if err != nil { if errors.Is(err, util.ErrNotExist) { apiError(ctx, http.StatusNotFound, err) @@ -119,12 +115,8 @@ func GetRepositoryFileByHash(ctx *context.Context) { } return } - defer s.Close() - ctx.ServeContent(s, &context.ServeHeaderOptions{ - Filename: pf.Name, - LastModified: pf.CreatedUnix.AsLocalTime(), - }) + helper.ServePackageFile(ctx, s, u, pf) } func UploadPackageFile(ctx *context.Context) { @@ -217,7 +209,7 @@ func DownloadPackageFile(ctx *context.Context) { name := ctx.Params("name") version := ctx.Params("version") - s, pf, err := packages_service.GetFileStreamByPackageNameAndVersion( + s, u, pf, err := packages_service.GetFileStreamByPackageNameAndVersion( ctx, &packages_service.PackageInfo{ Owner: ctx.Package.Owner, @@ -238,9 +230,8 @@ func DownloadPackageFile(ctx *context.Context) { } return } - defer s.Close() - ctx.ServeContent(s, &context.ServeHeaderOptions{ + helper.ServePackageFile(ctx, s, u, pf, &context.ServeHeaderOptions{ ContentType: "application/vnd.debian.binary-package", Filename: pf.Name, LastModified: pf.CreatedUnix.AsLocalTime(), diff --git a/routers/api/packages/generic/generic.go b/routers/api/packages/generic/generic.go index 0c873119ef5..c5866ef9c35 100644 --- a/routers/api/packages/generic/generic.go +++ b/routers/api/packages/generic/generic.go @@ -22,7 +22,7 @@ var ( filenameRegex = packageNameRegex ) -func apiError(ctx *context.Context, status int, obj interface{}) { +func apiError(ctx *context.Context, status int, obj any) { helper.LogAndProcessError(ctx, status, obj, func(message string) { ctx.PlainText(status, message) }) @@ -30,7 +30,7 @@ func apiError(ctx *context.Context, status int, obj interface{}) { // DownloadPackageFile serves the specific generic package. func DownloadPackageFile(ctx *context.Context) { - s, pf, err := packages_service.GetFileStreamByPackageNameAndVersion( + s, u, pf, err := packages_service.GetFileStreamByPackageNameAndVersion( ctx, &packages_service.PackageInfo{ Owner: ctx.Package.Owner, @@ -50,12 +50,8 @@ func DownloadPackageFile(ctx *context.Context) { apiError(ctx, http.StatusInternalServerError, err) return } - defer s.Close() - ctx.ServeContent(s, &context.ServeHeaderOptions{ - Filename: pf.Name, - LastModified: pf.CreatedUnix.AsLocalTime(), - }) + helper.ServePackageFile(ctx, s, u, pf) } // UploadPackage uploads the specific generic package. diff --git a/routers/api/packages/goproxy/goproxy.go b/routers/api/packages/goproxy/goproxy.go index d0bc9c1e98c..bacdc4ec62f 100644 --- a/routers/api/packages/goproxy/goproxy.go +++ b/routers/api/packages/goproxy/goproxy.go @@ -20,7 +20,7 @@ import ( packages_service "code.gitea.io/gitea/services/packages" ) -func apiError(ctx *context.Context, status int, obj interface{}) { +func apiError(ctx *context.Context, status int, obj any) { helper.LogAndProcessError(ctx, status, obj, func(message string) { ctx.PlainText(status, message) }) @@ -105,7 +105,7 @@ func DownloadPackageFile(ctx *context.Context) { return } - s, _, err := packages_service.GetPackageFileStream(ctx, pfs[0]) + s, u, _, err := packages_service.GetPackageFileStream(ctx, pfs[0]) if err != nil { if errors.Is(err, util.ErrNotExist) { apiError(ctx, http.StatusNotFound, err) @@ -114,12 +114,8 @@ func DownloadPackageFile(ctx *context.Context) { } return } - defer s.Close() - ctx.ServeContent(s, &context.ServeHeaderOptions{ - Filename: pfs[0].Name, - LastModified: pfs[0].CreatedUnix.AsLocalTime(), - }) + helper.ServePackageFile(ctx, s, u, pfs[0]) } func resolvePackage(ctx *context.Context, ownerID int64, name, version string) (*packages_model.PackageVersion, error) { diff --git a/routers/api/packages/helm/helm.go b/routers/api/packages/helm/helm.go index b7edc8b7fef..9097adf29e7 100644 --- a/routers/api/packages/helm/helm.go +++ b/routers/api/packages/helm/helm.go @@ -26,7 +26,7 @@ import ( "gopkg.in/yaml.v3" ) -func apiError(ctx *context.Context, status int, obj interface{}) { +func apiError(ctx *context.Context, status int, obj any) { helper.LogAndProcessError(ctx, status, obj, func(message string) { type Error struct { Error string `json:"error"` @@ -121,7 +121,7 @@ func DownloadPackageFile(ctx *context.Context) { return } - s, pf, err := packages_service.GetFileStreamByPackageVersion( + s, u, pf, err := packages_service.GetFileStreamByPackageVersion( ctx, pvs[0], &packages_service.PackageFileInfo{ @@ -136,12 +136,8 @@ func DownloadPackageFile(ctx *context.Context) { apiError(ctx, http.StatusInternalServerError, err) return } - defer s.Close() - ctx.ServeContent(s, &context.ServeHeaderOptions{ - Filename: pf.Name, - LastModified: pf.CreatedUnix.AsLocalTime(), - }) + helper.ServePackageFile(ctx, s, u, pf) } // UploadPackage creates a new package diff --git a/routers/api/packages/helper/helper.go b/routers/api/packages/helper/helper.go index 660aaec1a3a..aadb10376c8 100644 --- a/routers/api/packages/helper/helper.go +++ b/routers/api/packages/helper/helper.go @@ -5,8 +5,11 @@ package helper import ( "fmt" + "io" "net/http" + "net/url" + packages_model "code.gitea.io/gitea/models/packages" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" @@ -14,7 +17,7 @@ import ( // LogAndProcessError logs an error and calls a custom callback with the processed error message. // If the error is an InternalServerError the message is stripped if the user is not an admin. -func LogAndProcessError(ctx *context.Context, status int, obj interface{}, cb func(string)) { +func LogAndProcessError(ctx *context.Context, status int, obj any, cb func(string)) { var message string if err, ok := obj.(error); ok { message = err.Error() @@ -35,3 +38,26 @@ func LogAndProcessError(ctx *context.Context, status int, obj interface{}, cb fu cb(message) } } + +// Serves the content of the package file +// If the url is set it will redirect the request, otherwise the content is copied to the response. +func ServePackageFile(ctx *context.Context, s io.ReadSeekCloser, u *url.URL, pf *packages_model.PackageFile, forceOpts ...*context.ServeHeaderOptions) { + if u != nil { + ctx.Redirect(u.String()) + return + } + + defer s.Close() + + var opts *context.ServeHeaderOptions + if len(forceOpts) > 0 { + opts = forceOpts[0] + } else { + opts = &context.ServeHeaderOptions{ + Filename: pf.Name, + LastModified: pf.CreatedUnix.AsLocalTime(), + } + } + + ctx.ServeContent(s, opts) +} diff --git a/routers/api/packages/maven/maven.go b/routers/api/packages/maven/maven.go index dd270ff0edd..84e33243678 100644 --- a/routers/api/packages/maven/maven.go +++ b/routers/api/packages/maven/maven.go @@ -47,7 +47,7 @@ var ( illegalCharacters = regexp.MustCompile(`[\\/:"<>|?\*]`) ) -func apiError(ctx *context.Context, status int, obj interface{}) { +func apiError(ctx *context.Context, status int, obj any) { helper.LogAndProcessError(ctx, status, obj, func(message string) { ctx.PlainText(status, message) }) @@ -210,21 +210,15 @@ func servePackageFile(ctx *context.Context, params parameters, serveContent bool return } - s, err := packages_module.NewContentStore().Get(packages_module.BlobHash256Key(pb.HashSHA256)) + s, u, _, err := packages_service.GetPackageBlobStream(ctx, pf, pb) if err != nil { apiError(ctx, http.StatusInternalServerError, err) - } - defer s.Close() - - if pf.IsLead { - if err := packages_model.IncrementDownloadCounter(ctx, pv.ID); err != nil { - log.Error("Error incrementing download counter: %v", err) - } + return } opts.Filename = pf.Name - ctx.ServeContent(s, opts) + helper.ServePackageFile(ctx, s, u, pf, opts) } // UploadPackageFile adds a file to the package. If the package does not exist, it gets created. diff --git a/routers/api/packages/npm/npm.go b/routers/api/packages/npm/npm.go index 89476a776a0..3616211d618 100644 --- a/routers/api/packages/npm/npm.go +++ b/routers/api/packages/npm/npm.go @@ -30,7 +30,7 @@ import ( // errInvalidTagName indicates an invalid tag name var errInvalidTagName = errors.New("The tag name is invalid") -func apiError(ctx *context.Context, status int, obj interface{}) { +func apiError(ctx *context.Context, status int, obj any) { helper.LogAndProcessError(ctx, status, obj, func(message string) { ctx.JSON(status, map[string]string{ "error": message, @@ -83,7 +83,7 @@ func DownloadPackageFile(ctx *context.Context) { packageVersion := ctx.Params("version") filename := ctx.Params("filename") - s, pf, err := packages_service.GetFileStreamByPackageNameAndVersion( + s, u, pf, err := packages_service.GetFileStreamByPackageNameAndVersion( ctx, &packages_service.PackageInfo{ Owner: ctx.Package.Owner, @@ -103,12 +103,8 @@ func DownloadPackageFile(ctx *context.Context) { apiError(ctx, http.StatusInternalServerError, err) return } - defer s.Close() - ctx.ServeContent(s, &context.ServeHeaderOptions{ - Filename: pf.Name, - LastModified: pf.CreatedUnix.AsLocalTime(), - }) + helper.ServePackageFile(ctx, s, u, pf) } // DownloadPackageFileByName finds the version and serves the contents of a package @@ -134,7 +130,7 @@ func DownloadPackageFileByName(ctx *context.Context) { return } - s, pf, err := packages_service.GetFileStreamByPackageVersion( + s, u, pf, err := packages_service.GetFileStreamByPackageVersion( ctx, pvs[0], &packages_service.PackageFileInfo{ @@ -149,12 +145,8 @@ func DownloadPackageFileByName(ctx *context.Context) { apiError(ctx, http.StatusInternalServerError, err) return } - defer s.Close() - ctx.ServeContent(s, &context.ServeHeaderOptions{ - Filename: pf.Name, - LastModified: pf.CreatedUnix.AsLocalTime(), - }) + helper.ServePackageFile(ctx, s, u, pf) } // UploadPackage creates a new package diff --git a/routers/api/packages/nuget/nuget.go b/routers/api/packages/nuget/nuget.go index 716d8a969d6..edeba19b3bb 100644 --- a/routers/api/packages/nuget/nuget.go +++ b/routers/api/packages/nuget/nuget.go @@ -25,7 +25,7 @@ import ( packages_service "code.gitea.io/gitea/services/packages" ) -func apiError(ctx *context.Context, status int, obj interface{}) { +func apiError(ctx *context.Context, status int, obj any) { helper.LogAndProcessError(ctx, status, obj, func(message string) { ctx.JSON(status, map[string]string{ "Message": message, @@ -33,7 +33,7 @@ func apiError(ctx *context.Context, status int, obj interface{}) { }) } -func xmlResponse(ctx *context.Context, status int, obj interface{}) { +func xmlResponse(ctx *context.Context, status int, obj any) { ctx.Resp.Header().Set("Content-Type", "application/atom+xml; charset=utf-8") ctx.Resp.WriteHeader(status) if _, err := ctx.Resp.Write([]byte(xml.Header)); err != nil { @@ -362,7 +362,7 @@ func DownloadPackageFile(ctx *context.Context) { packageVersion := ctx.Params("version") filename := ctx.Params("filename") - s, pf, err := packages_service.GetFileStreamByPackageNameAndVersion( + s, u, pf, err := packages_service.GetFileStreamByPackageNameAndVersion( ctx, &packages_service.PackageInfo{ Owner: ctx.Package.Owner, @@ -382,12 +382,8 @@ func DownloadPackageFile(ctx *context.Context) { apiError(ctx, http.StatusInternalServerError, err) return } - defer s.Close() - ctx.ServeContent(s, &context.ServeHeaderOptions{ - Filename: pf.Name, - LastModified: pf.CreatedUnix.AsLocalTime(), - }) + helper.ServePackageFile(ctx, s, u, pf) } // UploadPackage creates a new package with the metadata contained in the uploaded nupgk file @@ -600,7 +596,7 @@ func DownloadSymbolFile(ctx *context.Context) { return } - s, pf, err := packages_service.GetPackageFileStream(ctx, pfs[0]) + s, u, pf, err := packages_service.GetPackageFileStream(ctx, pfs[0]) if err != nil { if err == packages_model.ErrPackageNotExist || err == packages_model.ErrPackageFileNotExist { apiError(ctx, http.StatusNotFound, err) @@ -609,12 +605,8 @@ func DownloadSymbolFile(ctx *context.Context) { apiError(ctx, http.StatusInternalServerError, err) return } - defer s.Close() - ctx.ServeContent(s, &context.ServeHeaderOptions{ - Filename: pf.Name, - LastModified: pf.CreatedUnix.AsLocalTime(), - }) + helper.ServePackageFile(ctx, s, u, pf) } // DeletePackage hard deletes the package diff --git a/routers/api/packages/pub/pub.go b/routers/api/packages/pub/pub.go index ae0c6e78598..ef07836b886 100644 --- a/routers/api/packages/pub/pub.go +++ b/routers/api/packages/pub/pub.go @@ -25,7 +25,7 @@ import ( packages_service "code.gitea.io/gitea/services/packages" ) -func jsonResponse(ctx *context.Context, status int, obj interface{}) { +func jsonResponse(ctx *context.Context, status int, obj any) { resp := ctx.Resp resp.Header().Set("Content-Type", "application/vnd.pub.v2+json") resp.WriteHeader(status) @@ -34,7 +34,7 @@ func jsonResponse(ctx *context.Context, status int, obj interface{}) { } } -func apiError(ctx *context.Context, status int, obj interface{}) { +func apiError(ctx *context.Context, status int, obj any) { type Error struct { Code string `json:"code"` Message string `json:"message"` @@ -60,10 +60,10 @@ type packageVersions struct { } type versionMetadata struct { - Version string `json:"version"` - ArchiveURL string `json:"archive_url"` - Published time.Time `json:"published"` - Pubspec interface{} `json:"pubspec,omitempty"` + Version string `json:"version"` + ArchiveURL string `json:"archive_url"` + Published time.Time `json:"published"` + Pubspec any `json:"pubspec,omitempty"` } func packageDescriptorToMetadata(baseURL string, pd *packages_model.PackageDescriptor) *versionMetadata { @@ -273,15 +273,11 @@ func DownloadPackageFile(ctx *context.Context) { pf := pd.Files[0].File - s, _, err := packages_service.GetPackageFileStream(ctx, pf) + s, u, _, err := packages_service.GetPackageFileStream(ctx, pf) if err != nil { apiError(ctx, http.StatusInternalServerError, err) return } - defer s.Close() - ctx.ServeContent(s, &context.ServeHeaderOptions{ - Filename: pf.Name, - LastModified: pf.CreatedUnix.AsLocalTime(), - }) + helper.ServePackageFile(ctx, s, u, pf) } diff --git a/routers/api/packages/pypi/pypi.go b/routers/api/packages/pypi/pypi.go index 90a37ec2a8f..d97b894bbed 100644 --- a/routers/api/packages/pypi/pypi.go +++ b/routers/api/packages/pypi/pypi.go @@ -37,7 +37,7 @@ var versionMatcher = regexp.MustCompile(`\Av?` + `(?:\+[a-z0-9]+(?:[-_\.][a-z0-9]+)*)?` + // local version `\z`) -func apiError(ctx *context.Context, status int, obj interface{}) { +func apiError(ctx *context.Context, status int, obj any) { helper.LogAndProcessError(ctx, status, obj, func(message string) { ctx.PlainText(status, message) }) @@ -80,7 +80,7 @@ func DownloadPackageFile(ctx *context.Context) { packageVersion := ctx.Params("version") filename := ctx.Params("filename") - s, pf, err := packages_service.GetFileStreamByPackageNameAndVersion( + s, u, pf, err := packages_service.GetFileStreamByPackageNameAndVersion( ctx, &packages_service.PackageInfo{ Owner: ctx.Package.Owner, @@ -100,12 +100,8 @@ func DownloadPackageFile(ctx *context.Context) { apiError(ctx, http.StatusInternalServerError, err) return } - defer s.Close() - ctx.ServeContent(s, &context.ServeHeaderOptions{ - Filename: pf.Name, - LastModified: pf.CreatedUnix.AsLocalTime(), - }) + helper.ServePackageFile(ctx, s, u, pf) } // UploadPackageFile adds a file to the package. If the package does not exist, it gets created. diff --git a/routers/api/packages/rpm/rpm.go b/routers/api/packages/rpm/rpm.go index 73e457237ab..930b20208ae 100644 --- a/routers/api/packages/rpm/rpm.go +++ b/routers/api/packages/rpm/rpm.go @@ -25,7 +25,7 @@ import ( rpm_service "code.gitea.io/gitea/services/packages/rpm" ) -func apiError(ctx *context.Context, status int, obj interface{}) { +func apiError(ctx *context.Context, status int, obj any) { helper.LogAndProcessError(ctx, status, obj, func(message string) { ctx.PlainText(status, message) }) @@ -65,7 +65,7 @@ func GetRepositoryFile(ctx *context.Context) { return } - s, pf, err := packages_service.GetFileStreamByPackageVersion( + s, u, pf, err := packages_service.GetFileStreamByPackageVersion( ctx, pv, &packages_service.PackageFileInfo{ @@ -80,12 +80,8 @@ func GetRepositoryFile(ctx *context.Context) { } return } - defer s.Close() - ctx.ServeContent(s, &context.ServeHeaderOptions{ - Filename: pf.Name, - LastModified: pf.CreatedUnix.AsLocalTime(), - }) + helper.ServePackageFile(ctx, s, u, pf) } func UploadPackageFile(ctx *context.Context) { @@ -173,7 +169,7 @@ func DownloadPackageFile(ctx *context.Context) { name := ctx.Params("name") version := ctx.Params("version") - s, pf, err := packages_service.GetFileStreamByPackageNameAndVersion( + s, u, pf, err := packages_service.GetFileStreamByPackageNameAndVersion( ctx, &packages_service.PackageInfo{ Owner: ctx.Package.Owner, @@ -193,13 +189,8 @@ func DownloadPackageFile(ctx *context.Context) { } return } - defer s.Close() - ctx.ServeContent(s, &context.ServeHeaderOptions{ - ContentType: "application/x-rpm", - Filename: pf.Name, - LastModified: pf.CreatedUnix.AsLocalTime(), - }) + helper.ServePackageFile(ctx, s, u, pf) } func DeletePackageFile(webctx *context.Context) { diff --git a/routers/api/packages/rubygems/rubygems.go b/routers/api/packages/rubygems/rubygems.go index 740efa9baba..88d70f10bdc 100644 --- a/routers/api/packages/rubygems/rubygems.go +++ b/routers/api/packages/rubygems/rubygems.go @@ -21,7 +21,7 @@ import ( packages_service "code.gitea.io/gitea/services/packages" ) -func apiError(ctx *context.Context, status int, obj interface{}) { +func apiError(ctx *context.Context, status int, obj any) { helper.LogAndProcessError(ctx, status, obj, func(message string) { ctx.PlainText(status, message) }) @@ -65,9 +65,9 @@ func enumeratePackages(ctx *context.Context, filename string, pvs []*packages_mo return } - specs := make([]interface{}, 0, len(pds)) + specs := make([]any, 0, len(pds)) for _, p := range pds { - specs = append(specs, []interface{}{ + specs = append(specs, []any{ p.Package.Name, &rubygems_module.RubyUserMarshal{ Name: "Gem::Version", @@ -129,7 +129,7 @@ func ServePackageSpecification(ctx *context.Context) { // create a Ruby Gem::Specification object spec := &rubygems_module.RubyUserDef{ Name: "Gem::Specification", - Value: []interface{}{ + Value: []any{ "3.2.3", // @rubygems_version 4, // @specification_version, pd.Package.Name, @@ -142,7 +142,7 @@ func ServePackageSpecification(ctx *context.Context) { nil, // @required_ruby_version nil, // @required_rubygems_version metadata.Platform, // @original_platform - []interface{}{}, // @dependencies + []any{}, // @dependencies nil, // rubyforge_project "", // @email metadata.Authors, @@ -175,7 +175,7 @@ func DownloadPackageFile(ctx *context.Context) { return } - s, pf, err := packages_service.GetFileStreamByPackageVersion( + s, u, pf, err := packages_service.GetFileStreamByPackageVersion( ctx, pvs[0], &packages_service.PackageFileInfo{ @@ -190,12 +190,8 @@ func DownloadPackageFile(ctx *context.Context) { apiError(ctx, http.StatusInternalServerError, err) return } - defer s.Close() - ctx.ServeContent(s, &context.ServeHeaderOptions{ - Filename: pf.Name, - LastModified: pf.CreatedUnix.AsLocalTime(), - }) + helper.ServePackageFile(ctx, s, u, pf) } // UploadPackageFile adds a file to the package. If the package does not exist, it gets created. diff --git a/routers/api/packages/swift/swift.go b/routers/api/packages/swift/swift.go index 06f592dd648..bd4b8095c2c 100644 --- a/routers/api/packages/swift/swift.go +++ b/routers/api/packages/swift/swift.go @@ -69,7 +69,7 @@ func setResponseHeaders(resp http.ResponseWriter, h *headers) { } // https://github.com/apple/swift-package-manager/blob/main/Documentation/Registry.md#33-error-handling -func apiError(ctx *context.Context, status int, obj interface{}) { +func apiError(ctx *context.Context, status int, obj any) { // https://www.rfc-editor.org/rfc/rfc7807 type Problem struct { Status int `json:"status"` @@ -397,18 +397,17 @@ func DownloadPackageFile(ctx *context.Context) { pf := pd.Files[0].File - s, _, err := packages_service.GetPackageFileStream(ctx, pf) + s, u, _, err := packages_service.GetPackageFileStream(ctx, pf) if err != nil { apiError(ctx, http.StatusInternalServerError, err) return } - defer s.Close() setResponseHeaders(ctx.Resp, &headers{ Digest: pd.Files[0].Blob.HashSHA256, }) - ctx.ServeContent(s, &context.ServeHeaderOptions{ + helper.ServePackageFile(ctx, s, u, pf, &context.ServeHeaderOptions{ Filename: pf.Name, ContentType: "application/zip", LastModified: pf.CreatedUnix.AsLocalTime(), diff --git a/routers/api/packages/vagrant/vagrant.go b/routers/api/packages/vagrant/vagrant.go index cefdc45b10f..9fe7ab56f66 100644 --- a/routers/api/packages/vagrant/vagrant.go +++ b/routers/api/packages/vagrant/vagrant.go @@ -22,7 +22,7 @@ import ( "github.com/hashicorp/go-version" ) -func apiError(ctx *context.Context, status int, obj interface{}) { +func apiError(ctx *context.Context, status int, obj any) { helper.LogAndProcessError(ctx, status, obj, func(message string) { ctx.JSON(status, struct { Errors []string `json:"errors"` @@ -216,7 +216,7 @@ func UploadPackageFile(ctx *context.Context) { } func DownloadPackageFile(ctx *context.Context) { - s, pf, err := packages_service.GetFileStreamByPackageNameAndVersion( + s, u, pf, err := packages_service.GetFileStreamByPackageNameAndVersion( ctx, &packages_service.PackageInfo{ Owner: ctx.Package.Owner, @@ -236,10 +236,6 @@ func DownloadPackageFile(ctx *context.Context) { apiError(ctx, http.StatusInternalServerError, err) return } - defer s.Close() - ctx.ServeContent(s, &context.ServeHeaderOptions{ - Filename: pf.Name, - LastModified: pf.CreatedUnix.AsLocalTime(), - }) + helper.ServePackageFile(ctx, s, u, pf) } diff --git a/routers/api/v1/activitypub/reqsignature.go b/routers/api/v1/activitypub/reqsignature.go index 2d945c27a51..3f60ed7776a 100644 --- a/routers/api/v1/activitypub/reqsignature.go +++ b/routers/api/v1/activitypub/reqsignature.go @@ -25,19 +25,16 @@ func getPublicKeyFromResponse(b []byte, keyID *url.URL) (p crypto.PublicKey, err person := ap.PersonNew(ap.IRI(keyID.String())) err = person.UnmarshalJSON(b) if err != nil { - err = fmt.Errorf("ActivityStreams type cannot be converted to one known to have publicKey property: %w", err) - return + return nil, fmt.Errorf("ActivityStreams type cannot be converted to one known to have publicKey property: %w", err) } pubKey := person.PublicKey if pubKey.ID.String() != keyID.String() { - err = fmt.Errorf("cannot find publicKey with id: %s in %s", keyID, string(b)) - return + return nil, fmt.Errorf("cannot find publicKey with id: %s in %s", keyID, string(b)) } pubKeyPem := pubKey.PublicKeyPem block, _ := pem.Decode([]byte(pubKeyPem)) if block == nil || block.Type != "PUBLIC KEY" { - err = fmt.Errorf("could not decode publicKeyPem to PUBLIC KEY pem block type") - return + return nil, fmt.Errorf("could not decode publicKeyPem to PUBLIC KEY pem block type") } p, err = x509.ParsePKIXPublicKey(block.Bytes) return p, err @@ -49,13 +46,12 @@ func fetch(iri *url.URL) (b []byte, err error) { req.Header("User-Agent", "Gitea/"+setting.AppVer) resp, err := req.Response() if err != nil { - return + return nil, err } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { - err = fmt.Errorf("url IRI fetch [%s] failed with status (%d): %s", iri, resp.StatusCode, resp.Status) - return + return nil, fmt.Errorf("url IRI fetch [%s] failed with status (%d): %s", iri, resp.StatusCode, resp.Status) } b, err = io.ReadAll(io.LimitReader(resp.Body, setting.Federation.MaxSize)) return b, err @@ -67,21 +63,21 @@ func verifyHTTPSignatures(ctx *gitea_context.APIContext) (authenticated bool, er // 1. Figure out what key we need to verify v, err := httpsig.NewVerifier(r) if err != nil { - return + return false, err } ID := v.KeyId() idIRI, err := url.Parse(ID) if err != nil { - return + return false, err } // 2. Fetch the public key of the other actor b, err := fetch(idIRI) if err != nil { - return + return false, err } pubKey, err := getPublicKeyFromResponse(b, idIRI) if err != nil { - return + return false, err } // 3. Verify the other actor's key algo := httpsig.Algorithm(setting.Federation.Algorithms[0]) diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index 0e28bde6830..eee97cbc209 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -1034,7 +1034,7 @@ func Routes() *web.Route { m.Group("/assets", func() { m.Combo("").Get(repo.ListReleaseAttachments). Post(reqToken(), reqRepoWriter(unit.TypeReleases), repo.CreateReleaseAttachment) - m.Combo("/{asset}").Get(repo.GetReleaseAttachment). + m.Combo("/{attachment_id}").Get(repo.GetReleaseAttachment). Patch(reqToken(), reqRepoWriter(unit.TypeReleases), bind(api.EditAttachmentOptions{}), repo.EditReleaseAttachment). Delete(reqToken(), reqRepoWriter(unit.TypeReleases), repo.DeleteReleaseAttachment) }) @@ -1179,7 +1179,7 @@ func Routes() *web.Route { m.Combo(""). Get(repo.ListIssueCommentAttachments). Post(reqToken(), mustNotBeArchived, repo.CreateIssueCommentAttachment) - m.Combo("/{asset}"). + m.Combo("/{attachment_id}"). Get(repo.GetIssueCommentAttachment). Patch(reqToken(), mustNotBeArchived, bind(api.EditAttachmentOptions{}), repo.EditIssueCommentAttachment). Delete(reqToken(), mustNotBeArchived, repo.DeleteIssueCommentAttachment) @@ -1231,7 +1231,7 @@ func Routes() *web.Route { m.Combo(""). Get(repo.ListIssueAttachments). Post(reqToken(), mustNotBeArchived, repo.CreateIssueAttachment) - m.Combo("/{asset}"). + m.Combo("/{attachment_id}"). Get(repo.GetIssueAttachment). Patch(reqToken(), mustNotBeArchived, bind(api.EditAttachmentOptions{}), repo.EditIssueAttachment). Delete(reqToken(), mustNotBeArchived, repo.DeleteIssueAttachment) diff --git a/routers/api/v1/org/team.go b/routers/api/v1/org/team.go index 1aaefacdd59..0e11acc901f 100644 --- a/routers/api/v1/org/team.go +++ b/routers/api/v1/org/team.go @@ -792,7 +792,7 @@ func SearchTeam(ctx *context.APIContext) { teams, maxResults, err := organization.SearchTeam(opts) if err != nil { log.Error("SearchTeam failed: %v", err) - ctx.JSON(http.StatusInternalServerError, map[string]interface{}{ + ctx.JSON(http.StatusInternalServerError, map[string]any{ "ok": false, "error": "SearchTeam internal failure", }) @@ -807,7 +807,7 @@ func SearchTeam(ctx *context.APIContext) { ctx.SetLinkHeader(int(maxResults), listOptions.PageSize) ctx.SetTotalCountHeader(maxResults) - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "ok": true, "data": apiTeams, }) diff --git a/routers/api/v1/repo/branch.go b/routers/api/v1/repo/branch.go index 4900ecf4d08..5e2c9878f08 100644 --- a/routers/api/v1/repo/branch.go +++ b/routers/api/v1/repo/branch.go @@ -118,6 +118,21 @@ func DeleteBranch(ctx *context.APIContext) { // "404": // "$ref": "#/responses/notFound" + if ctx.Repo.Repository.IsEmpty { + ctx.Error(http.StatusNotFound, "", "Git Repository is empty.") + return + } + + if ctx.Repo.Repository.IsArchived { + ctx.Error(http.StatusForbidden, "", "Git Repository is archived.") + return + } + + if ctx.Repo.Repository.IsMirror { + ctx.Error(http.StatusForbidden, "", "Git Repository is a mirror.") + return + } + branchName := ctx.Params("*") if ctx.Repo.Repository.IsEmpty { @@ -195,17 +210,30 @@ func CreateBranch(ctx *context.APIContext) { // responses: // "201": // "$ref": "#/responses/Branch" + // "403": + // description: The branch is archived or a mirror. // "404": // description: The old branch does not exist. // "409": // description: The branch with the same name already exists. - opt := web.GetForm(ctx).(*api.CreateBranchRepoOption) if ctx.Repo.Repository.IsEmpty { ctx.Error(http.StatusNotFound, "", "Git Repository is empty.") return } + if ctx.Repo.Repository.IsArchived { + ctx.Error(http.StatusForbidden, "", "Git Repository is archived.") + return + } + + if ctx.Repo.Repository.IsMirror { + ctx.Error(http.StatusForbidden, "", "Git Repository is a mirror.") + return + } + + opt := web.GetForm(ctx).(*api.CreateBranchRepoOption) + var oldCommit *git.Commit var err error @@ -313,7 +341,12 @@ func ListBranches(ctx *context.APIContext) { listOptions := utils.GetListOptions(ctx) - if !ctx.Repo.Repository.IsEmpty && ctx.Repo.GitRepo != nil { + if !ctx.Repo.Repository.IsEmpty { + if ctx.Repo.GitRepo == nil { + ctx.Error(http.StatusInternalServerError, "Load git repository failed", nil) + return + } + branchOpts := git_model.FindBranchOptions{ ListOptions: listOptions, RepoID: ctx.Repo.Repository.ID, diff --git a/routers/api/v1/repo/file.go b/routers/api/v1/repo/file.go index 48f890ee552..bf37532fc86 100644 --- a/routers/api/v1/repo/file.go +++ b/routers/api/v1/repo/file.go @@ -219,7 +219,7 @@ func GetRawFileOrLFS(ctx *context.APIContext) { common.ServeContentByReadSeeker(ctx.Base, ctx.Repo.TreePath, lastModified, lfsDataRc) } -func getBlobForEntry(ctx *context.APIContext) (blob *git.Blob, entry *git.TreeEntry, lastModified time.Time) { +func getBlobForEntry(ctx *context.APIContext) (blob *git.Blob, entry *git.TreeEntry, lastModified *time.Time) { entry, err := ctx.Repo.Commit.GetTreeEntryByPath(ctx.Repo.TreePath) if err != nil { if git.IsErrNotExist(err) { @@ -227,23 +227,23 @@ func getBlobForEntry(ctx *context.APIContext) (blob *git.Blob, entry *git.TreeEn } else { ctx.Error(http.StatusInternalServerError, "GetTreeEntryByPath", err) } - return + return nil, nil, nil } if entry.IsDir() || entry.IsSubModule() { ctx.NotFound("getBlobForEntry", nil) - return + return nil, nil, nil } info, _, err := git.Entries([]*git.TreeEntry{entry}).GetCommitsInfo(ctx, ctx.Repo.Commit, path.Dir("/" + ctx.Repo.TreePath)[1:]) if err != nil { ctx.Error(http.StatusInternalServerError, "GetCommitsInfo", err) - return + return nil, nil, nil } if len(info) == 1 { // Not Modified - lastModified = info[0].Commit.Committer.When + lastModified = &info[0].Commit.Committer.When } blob = entry.Blob() diff --git a/routers/api/v1/repo/issue.go b/routers/api/v1/repo/issue.go index 49252f7a4b4..e76775ae822 100644 --- a/routers/api/v1/repo/issue.go +++ b/routers/api/v1/repo/issue.go @@ -195,7 +195,7 @@ func SearchIssues(ctx *context.APIContext) { } var issueIDs []int64 if len(keyword) > 0 && len(repoIDs) > 0 { - if issueIDs, err = issue_indexer.SearchIssuesByKeyword(ctx, repoIDs, keyword); err != nil { + if issueIDs, err = issue_indexer.SearchIssuesByKeyword(ctx, repoIDs, keyword, ctx.FormString("state")); err != nil { ctx.Error(http.StatusInternalServerError, "SearchIssuesByKeyword", err) return } @@ -394,7 +394,7 @@ func ListIssues(ctx *context.APIContext) { var issueIDs []int64 var labelIDs []int64 if len(keyword) > 0 { - issueIDs, err = issue_indexer.SearchIssuesByKeyword(ctx, []int64{ctx.Repo.Repository.ID}, keyword) + issueIDs, err = issue_indexer.SearchIssuesByKeyword(ctx, []int64{ctx.Repo.Repository.ID}, keyword, ctx.FormString("state")) if err != nil { ctx.Error(http.StatusInternalServerError, "SearchIssuesByKeyword", err) return diff --git a/routers/api/v1/repo/issue_attachment.go b/routers/api/v1/repo/issue_attachment.go index 92e11386882..779da9fd7f8 100644 --- a/routers/api/v1/repo/issue_attachment.go +++ b/routers/api/v1/repo/issue_attachment.go @@ -64,7 +64,7 @@ func GetIssueAttachment(ctx *context.APIContext) { return } - ctx.JSON(http.StatusOK, convert.ToAttachment(attach)) + ctx.JSON(http.StatusOK, convert.ToAPIAttachment(ctx.Repo.Repository, attach)) } // ListIssueAttachments lists all attachments of the issue @@ -194,7 +194,7 @@ func CreateIssueAttachment(ctx *context.APIContext) { return } - ctx.JSON(http.StatusCreated, convert.ToAttachment(attachment)) + ctx.JSON(http.StatusCreated, convert.ToAPIAttachment(ctx.Repo.Repository, attachment)) } // EditIssueAttachment updates the given attachment @@ -254,7 +254,7 @@ func EditIssueAttachment(ctx *context.APIContext) { ctx.Error(http.StatusInternalServerError, "UpdateAttachment", err) } - ctx.JSON(http.StatusCreated, convert.ToAttachment(attachment)) + ctx.JSON(http.StatusCreated, convert.ToAPIAttachment(ctx.Repo.Repository, attachment)) } // DeleteIssueAttachment delete a given attachment @@ -332,7 +332,7 @@ func getIssueAttachmentSafeWrite(ctx *context.APIContext) *repo_model.Attachment } func getIssueAttachmentSafeRead(ctx *context.APIContext, issue *issues_model.Issue) *repo_model.Attachment { - attachment, err := repo_model.GetAttachmentByID(ctx, ctx.ParamsInt64("asset")) + attachment, err := repo_model.GetAttachmentByID(ctx, ctx.ParamsInt64("attachment_id")) if err != nil { ctx.NotFoundOrServerError("GetAttachmentByID", repo_model.IsErrAttachmentNotExist, err) return nil diff --git a/routers/api/v1/repo/issue_comment.go b/routers/api/v1/repo/issue_comment.go index 5616e255ad8..c2392126db9 100644 --- a/routers/api/v1/repo/issue_comment.go +++ b/routers/api/v1/repo/issue_comment.go @@ -103,7 +103,7 @@ func ListIssueComments(ctx *context.APIContext) { apiComments := make([]*api.Comment, len(comments)) for i, comment := range comments { comment.Issue = issue - apiComments[i] = convert.ToComment(ctx, comments[i]) + apiComments[i] = convert.ToAPIComment(ctx, ctx.Repo.Repository, comments[i]) } ctx.SetTotalCountHeader(totalCount) @@ -191,7 +191,7 @@ func ListIssueCommentsAndTimeline(ctx *context.APIContext) { for _, comment := range comments { if comment.Type != issues_model.CommentTypeCode && isXRefCommentAccessible(ctx, ctx.Doer, comment, issue.RepoID) { comment.Issue = issue - apiComments = append(apiComments, convert.ToTimelineComment(ctx, comment, ctx.Doer)) + apiComments = append(apiComments, convert.ToTimelineComment(ctx, issue.Repo, comment, ctx.Doer)) } } @@ -308,7 +308,7 @@ func ListRepoIssueComments(ctx *context.APIContext) { return } for i := range comments { - apiComments[i] = convert.ToComment(ctx, comments[i]) + apiComments[i] = convert.ToAPIComment(ctx, ctx.Repo.Repository, comments[i]) } ctx.SetTotalCountHeader(totalCount) @@ -368,7 +368,7 @@ func CreateIssueComment(ctx *context.APIContext) { return } - ctx.JSON(http.StatusCreated, convert.ToComment(ctx, comment)) + ctx.JSON(http.StatusCreated, convert.ToAPIComment(ctx, ctx.Repo.Repository, comment)) } // GetIssueComment Get a comment by ID @@ -436,7 +436,7 @@ func GetIssueComment(ctx *context.APIContext) { return } - ctx.JSON(http.StatusOK, convert.ToComment(ctx, comment)) + ctx.JSON(http.StatusOK, convert.ToAPIComment(ctx, ctx.Repo.Repository, comment)) } // EditIssueComment modify a comment of an issue @@ -561,7 +561,7 @@ func editIssueComment(ctx *context.APIContext, form api.EditIssueCommentOption) return } - ctx.JSON(http.StatusOK, convert.ToComment(ctx, comment)) + ctx.JSON(http.StatusOK, convert.ToAPIComment(ctx, ctx.Repo.Repository, comment)) } // DeleteIssueComment delete a comment from an issue diff --git a/routers/api/v1/repo/issue_comment_attachment.go b/routers/api/v1/repo/issue_comment_attachment.go index 6fe4dbc9771..121e3f10e0d 100644 --- a/routers/api/v1/repo/issue_comment_attachment.go +++ b/routers/api/v1/repo/issue_comment_attachment.go @@ -68,7 +68,7 @@ func GetIssueCommentAttachment(ctx *context.APIContext) { return } - ctx.JSON(http.StatusOK, convert.ToAttachment(attachment)) + ctx.JSON(http.StatusOK, convert.ToAPIAttachment(ctx.Repo.Repository, attachment)) } // ListIssueCommentAttachments lists all attachments of the comment @@ -110,7 +110,7 @@ func ListIssueCommentAttachments(ctx *context.APIContext) { return } - ctx.JSON(http.StatusOK, convert.ToAttachments(comment.Attachments)) + ctx.JSON(http.StatusOK, convert.ToAPIAttachments(ctx.Repo.Repository, comment.Attachments)) } // CreateIssueCommentAttachment creates an attachment and saves the given file @@ -201,7 +201,7 @@ func CreateIssueCommentAttachment(ctx *context.APIContext) { return } - ctx.JSON(http.StatusCreated, convert.ToAttachment(attachment)) + ctx.JSON(http.StatusCreated, convert.ToAPIAttachment(ctx.Repo.Repository, attachment)) } // EditIssueCommentAttachment updates the given attachment @@ -259,7 +259,7 @@ func EditIssueCommentAttachment(ctx *context.APIContext) { if err := repo_model.UpdateAttachment(ctx, attach); err != nil { ctx.Error(http.StatusInternalServerError, "UpdateAttachment", attach) } - ctx.JSON(http.StatusCreated, convert.ToAttachment(attach)) + ctx.JSON(http.StatusCreated, convert.ToAPIAttachment(ctx.Repo.Repository, attach)) } // DeleteIssueCommentAttachment delete a given attachment @@ -352,7 +352,7 @@ func canUserWriteIssueCommentAttachment(ctx *context.APIContext, comment *issues } func getIssueCommentAttachmentSafeRead(ctx *context.APIContext, comment *issues_model.Comment) *repo_model.Attachment { - attachment, err := repo_model.GetAttachmentByID(ctx, ctx.ParamsInt64("asset")) + attachment, err := repo_model.GetAttachmentByID(ctx, ctx.ParamsInt64("attachment_id")) if err != nil { ctx.NotFoundOrServerError("GetAttachmentByID", repo_model.IsErrAttachmentNotExist, err) return nil diff --git a/routers/api/v1/repo/issue_label.go b/routers/api/v1/repo/issue_label.go index 1f0d21141bc..9d43c66d8c9 100644 --- a/routers/api/v1/repo/issue_label.go +++ b/routers/api/v1/repo/issue_label.go @@ -298,26 +298,26 @@ func ClearIssueLabels(ctx *context.APIContext) { ctx.Status(http.StatusNoContent) } -func prepareForReplaceOrAdd(ctx *context.APIContext, form api.IssueLabelsOption) (issue *issues_model.Issue, labels []*issues_model.Label, err error) { - issue, err = issues_model.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index")) +func prepareForReplaceOrAdd(ctx *context.APIContext, form api.IssueLabelsOption) (*issues_model.Issue, []*issues_model.Label, error) { + issue, err := issues_model.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index")) if err != nil { if issues_model.IsErrIssueNotExist(err) { ctx.NotFound() } else { ctx.Error(http.StatusInternalServerError, "GetIssueByIndex", err) } - return + return nil, nil, err } - labels, err = issues_model.GetLabelsByIDs(form.Labels) + labels, err := issues_model.GetLabelsByIDs(form.Labels) if err != nil { ctx.Error(http.StatusInternalServerError, "GetLabelsByIDs", err) - return + return nil, nil, err } if !ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) { ctx.Status(http.StatusForbidden) - return + return nil, nil, nil } return issue, labels, err diff --git a/routers/api/v1/repo/release.go b/routers/api/v1/repo/release.go index e9693dd053b..af7199d1d63 100644 --- a/routers/api/v1/repo/release.go +++ b/routers/api/v1/repo/release.go @@ -64,7 +64,7 @@ func GetRelease(ctx *context.APIContext) { ctx.Error(http.StatusInternalServerError, "LoadAttributes", err) return } - ctx.JSON(http.StatusOK, convert.ToRelease(ctx, release)) + ctx.JSON(http.StatusOK, convert.ToAPIRelease(ctx, ctx.Repo.Repository, release)) } // GetLatestRelease gets the most recent non-prerelease, non-draft release of a repository, sorted by created_at @@ -105,7 +105,7 @@ func GetLatestRelease(ctx *context.APIContext) { ctx.Error(http.StatusInternalServerError, "LoadAttributes", err) return } - ctx.JSON(http.StatusOK, convert.ToRelease(ctx, release)) + ctx.JSON(http.StatusOK, convert.ToAPIRelease(ctx, ctx.Repo.Repository, release)) } // ListReleases list a repository's releases @@ -174,7 +174,7 @@ func ListReleases(ctx *context.APIContext) { ctx.Error(http.StatusInternalServerError, "LoadAttributes", err) return } - rels[i] = convert.ToRelease(ctx, release) + rels[i] = convert.ToAPIRelease(ctx, ctx.Repo.Repository, release) } filteredCount, err := repo_model.CountReleasesByRepoID(ctx.Repo.Repository.ID, opts) @@ -272,7 +272,7 @@ func CreateRelease(ctx *context.APIContext) { return } } - ctx.JSON(http.StatusCreated, convert.ToRelease(ctx, rel)) + ctx.JSON(http.StatusCreated, convert.ToAPIRelease(ctx, ctx.Repo.Repository, rel)) } // EditRelease edit a release @@ -357,7 +357,7 @@ func EditRelease(ctx *context.APIContext) { ctx.Error(http.StatusInternalServerError, "LoadAttributes", err) return } - ctx.JSON(http.StatusOK, convert.ToRelease(ctx, rel)) + ctx.JSON(http.StatusOK, convert.ToAPIRelease(ctx, ctx.Repo.Repository, rel)) } // DeleteRelease delete a release from a repository diff --git a/routers/api/v1/repo/release_attachment.go b/routers/api/v1/repo/release_attachment.go index 305b2808df5..a7d73acceb9 100644 --- a/routers/api/v1/repo/release_attachment.go +++ b/routers/api/v1/repo/release_attachment.go @@ -52,7 +52,7 @@ func GetReleaseAttachment(ctx *context.APIContext) { // "$ref": "#/responses/Attachment" releaseID := ctx.ParamsInt64(":id") - attachID := ctx.ParamsInt64(":asset") + attachID := ctx.ParamsInt64(":attachment_id") attach, err := repo_model.GetAttachmentByID(ctx, attachID) if err != nil { if repo_model.IsErrAttachmentNotExist(err) { @@ -68,7 +68,7 @@ func GetReleaseAttachment(ctx *context.APIContext) { return } // FIXME Should prove the existence of the given repo, but results in unnecessary database requests - ctx.JSON(http.StatusOK, convert.ToAttachment(attach)) + ctx.JSON(http.StatusOK, convert.ToAPIAttachment(ctx.Repo.Repository, attach)) } // ListReleaseAttachments lists all attachments of the release @@ -117,7 +117,7 @@ func ListReleaseAttachments(ctx *context.APIContext) { ctx.Error(http.StatusInternalServerError, "LoadAttributes", err) return } - ctx.JSON(http.StatusOK, convert.ToRelease(ctx, release).Attachments) + ctx.JSON(http.StatusOK, convert.ToAPIRelease(ctx, ctx.Repo.Repository, release).Attachments) } // CreateReleaseAttachment creates an attachment and saves the given file @@ -209,7 +209,7 @@ func CreateReleaseAttachment(ctx *context.APIContext) { return } - ctx.JSON(http.StatusCreated, convert.ToAttachment(attach)) + ctx.JSON(http.StatusCreated, convert.ToAPIAttachment(ctx.Repo.Repository, attach)) } // EditReleaseAttachment updates the given attachment @@ -256,7 +256,7 @@ func EditReleaseAttachment(ctx *context.APIContext) { // Check if release exists an load release releaseID := ctx.ParamsInt64(":id") - attachID := ctx.ParamsInt64(":asset") + attachID := ctx.ParamsInt64(":attachment_id") attach, err := repo_model.GetAttachmentByID(ctx, attachID) if err != nil { if repo_model.IsErrAttachmentNotExist(err) { @@ -279,7 +279,7 @@ func EditReleaseAttachment(ctx *context.APIContext) { if err := repo_model.UpdateAttachment(ctx, attach); err != nil { ctx.Error(http.StatusInternalServerError, "UpdateAttachment", attach) } - ctx.JSON(http.StatusCreated, convert.ToAttachment(attach)) + ctx.JSON(http.StatusCreated, convert.ToAPIAttachment(ctx.Repo.Repository, attach)) } // DeleteReleaseAttachment delete a given attachment @@ -318,7 +318,7 @@ func DeleteReleaseAttachment(ctx *context.APIContext) { // Check if release exists an load release releaseID := ctx.ParamsInt64(":id") - attachID := ctx.ParamsInt64(":asset") + attachID := ctx.ParamsInt64(":attachment_id") attach, err := repo_model.GetAttachmentByID(ctx, attachID) if err != nil { if repo_model.IsErrAttachmentNotExist(err) { diff --git a/routers/api/v1/repo/release_tags.go b/routers/api/v1/repo/release_tags.go index 051ee8f22ec..a03edfafcf5 100644 --- a/routers/api/v1/repo/release_tags.go +++ b/routers/api/v1/repo/release_tags.go @@ -63,7 +63,7 @@ func GetReleaseByTag(ctx *context.APIContext) { ctx.Error(http.StatusInternalServerError, "LoadAttributes", err) return } - ctx.JSON(http.StatusOK, convert.ToRelease(ctx, release)) + ctx.JSON(http.StatusOK, convert.ToAPIRelease(ctx, ctx.Repo.Repository, release)) } // DeleteReleaseByTag delete a release from a repository by tag name diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go index 3abfb58981d..24e42819b00 100644 --- a/routers/api/v1/repo/repo.go +++ b/routers/api/v1/repo/repo.go @@ -379,7 +379,7 @@ func Generate(ctx *context.APIContext) { ctxUser, err = user_model.GetUserByName(ctx, form.Owner) if err != nil { if user_model.IsErrUserNotExist(err) { - ctx.JSON(http.StatusNotFound, map[string]interface{}{ + ctx.JSON(http.StatusNotFound, map[string]any{ "error": "request owner `" + form.Owner + "` does not exist", }) return diff --git a/routers/api/v1/repo/topic.go b/routers/api/v1/repo/topic.go index 7d27fe7d25a..8bf3012d488 100644 --- a/routers/api/v1/repo/topic.go +++ b/routers/api/v1/repo/topic.go @@ -63,7 +63,7 @@ func ListTopics(ctx *context.APIContext) { } ctx.SetTotalCountHeader(total) - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "topics": topicNames, }) } @@ -101,7 +101,7 @@ func UpdateTopics(ctx *context.APIContext) { validTopics, invalidTopics := repo_model.SanitizeAndValidateTopics(topicNames) if len(validTopics) > 25 { - ctx.JSON(http.StatusUnprocessableEntity, map[string]interface{}{ + ctx.JSON(http.StatusUnprocessableEntity, map[string]any{ "invalidTopics": nil, "message": "Exceeding maximum number of topics per repo", }) @@ -109,7 +109,7 @@ func UpdateTopics(ctx *context.APIContext) { } if len(invalidTopics) > 0 { - ctx.JSON(http.StatusUnprocessableEntity, map[string]interface{}{ + ctx.JSON(http.StatusUnprocessableEntity, map[string]any{ "invalidTopics": invalidTopics, "message": "Topic names are invalid", }) @@ -158,7 +158,7 @@ func AddTopic(ctx *context.APIContext) { topicName := strings.TrimSpace(strings.ToLower(ctx.Params(":topic"))) if !repo_model.ValidateTopic(topicName) { - ctx.JSON(http.StatusUnprocessableEntity, map[string]interface{}{ + ctx.JSON(http.StatusUnprocessableEntity, map[string]any{ "invalidTopics": topicName, "message": "Topic name is invalid", }) @@ -175,7 +175,7 @@ func AddTopic(ctx *context.APIContext) { return } if count >= 25 { - ctx.JSON(http.StatusUnprocessableEntity, map[string]interface{}{ + ctx.JSON(http.StatusUnprocessableEntity, map[string]any{ "message": "Exceeding maximum allowed topics per repo.", }) return @@ -223,7 +223,7 @@ func DeleteTopic(ctx *context.APIContext) { topicName := strings.TrimSpace(strings.ToLower(ctx.Params(":topic"))) if !repo_model.ValidateTopic(topicName) { - ctx.JSON(http.StatusUnprocessableEntity, map[string]interface{}{ + ctx.JSON(http.StatusUnprocessableEntity, map[string]any{ "invalidTopics": topicName, "message": "Topic name is invalid", }) @@ -289,7 +289,7 @@ func TopicSearch(ctx *context.APIContext) { } ctx.SetTotalCountHeader(total) - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "topics": topicResponses, }) } diff --git a/routers/api/v1/user/user.go b/routers/api/v1/user/user.go index 314116962b5..2a2361be678 100644 --- a/routers/api/v1/user/user.go +++ b/routers/api/v1/user/user.go @@ -62,7 +62,7 @@ func Search(ctx *context.APIContext) { ListOptions: listOptions, }) if err != nil { - ctx.JSON(http.StatusInternalServerError, map[string]interface{}{ + ctx.JSON(http.StatusInternalServerError, map[string]any{ "ok": false, "error": err.Error(), }) @@ -72,7 +72,7 @@ func Search(ctx *context.APIContext) { ctx.SetLinkHeader(int(maxResults), listOptions.PageSize) ctx.SetTotalCountHeader(maxResults) - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "ok": true, "data": convert.ToUsers(ctx, ctx.Doer, users), }) diff --git a/routers/common/serve.go b/routers/common/serve.go index 3094ee6a6ec..8a7f8b33321 100644 --- a/routers/common/serve.go +++ b/routers/common/serve.go @@ -15,7 +15,7 @@ import ( ) // ServeBlob download a git.Blob -func ServeBlob(ctx *context.Base, filePath string, blob *git.Blob, lastModified time.Time) error { +func ServeBlob(ctx *context.Base, filePath string, blob *git.Blob, lastModified *time.Time) error { if httpcache.HandleGenericETagTimeCache(ctx.Req, ctx.Resp, `"`+blob.ID.String()+`"`, lastModified) { return nil } @@ -38,6 +38,6 @@ func ServeContentByReader(ctx *context.Base, filePath string, size int64, reader httplib.ServeContentByReader(ctx.Req, ctx.Resp, filePath, size, reader) } -func ServeContentByReadSeeker(ctx *context.Base, filePath string, modTime time.Time, reader io.ReadSeeker) { +func ServeContentByReadSeeker(ctx *context.Base, filePath string, modTime *time.Time, reader io.ReadSeeker) { httplib.ServeContentByReadSeeker(ctx.Req, ctx.Resp, filePath, modTime, reader) } diff --git a/routers/install/install.go b/routers/install/install.go index f121f313769..a2e89d3dac4 100644 --- a/routers/install/install.go +++ b/routers/install/install.go @@ -56,6 +56,7 @@ func getSupportedDbTypeNames() (dbTypeNames []map[string]string) { func Contexter() func(next http.Handler) http.Handler { rnd := templates.HTMLRenderer() dbTypeNames := getSupportedDbTypeNames() + envConfigKeys := setting.CollectEnvConfigKeys() return func(next http.Handler) http.Handler { return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { base, baseCleanUp := context.NewBaseContext(resp, req) @@ -70,11 +71,13 @@ func Contexter() func(next http.Handler) http.Handler { ctx.AppendContextValue(context.WebContextKey, ctx) ctx.Data.MergeFrom(middleware.CommonTemplateContextData()) ctx.Data.MergeFrom(middleware.ContextData{ - "locale": ctx.Locale, - "Title": ctx.Locale.Tr("install.install"), - "PageIsInstall": true, - "DbTypeNames": dbTypeNames, - "AllLangs": translation.AllLangs(), + "locale": ctx.Locale, + "Title": ctx.Locale.Tr("install.install"), + "PageIsInstall": true, + "DbTypeNames": dbTypeNames, + "EnvConfigKeys": envConfigKeys, + "CustomConfFile": setting.CustomConf, + "AllLangs": translation.AllLangs(), "PasswordHashAlgorithms": hash.RecommendedHashAlgorithms, }) @@ -218,7 +221,7 @@ func checkDatabase(ctx *context.Context, form *forms.InstallForm) bool { return false } - log.Info("User confirmed reinstallation of Gitea into a pre-existing database") + log.Info("User confirmed re-installation of Gitea into a pre-existing database") } if hasPostInstallationUser || dbMigrationVersion > 0 { @@ -502,6 +505,8 @@ func SubmitInstall(ctx *context.Context) { return } + setting.EnvironmentToConfig(cfg, os.Environ()) + if err = cfg.SaveTo(setting.CustomConf); err != nil { ctx.RenderWithErr(ctx.Tr("install.save_config_failed", err), tplInstall, &form) return @@ -568,6 +573,7 @@ func SubmitInstall(ctx *context.Context) { } } + setting.ClearEnvConfigKeys() log.Info("First-time run install finished!") InstallDone(ctx) diff --git a/routers/private/manager_process.go b/routers/private/manager_process.go index a5993bf3718..68e4a21805b 100644 --- a/routers/private/manager_process.go +++ b/routers/private/manager_process.go @@ -49,7 +49,7 @@ func Processes(ctx *context.PrivateContext) { } if json { - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "TotalNumberOfGoroutines": goroutineCount, "TotalNumberOfProcesses": processCount, "Processes": processes, diff --git a/routers/web/admin/auths.go b/routers/web/admin/auths.go index b6ea3ff4030..adde26f0b56 100644 --- a/routers/web/admin/auths.go +++ b/routers/web/admin/auths.go @@ -60,7 +60,7 @@ func Authentications(ctx *context.Context) { type dropdownItem struct { Name string - Type interface{} + Type any } var ( @@ -454,7 +454,7 @@ func DeleteAuthSource(ctx *context.Context) { } else { ctx.Flash.Error(fmt.Sprintf("auth_service.DeleteSource: %v", err)) } - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "redirect": setting.AppSubURL + "/admin/auths/" + url.PathEscape(ctx.Params(":authid")), }) return @@ -462,7 +462,7 @@ func DeleteAuthSource(ctx *context.Context) { log.Trace("Authentication deleted by admin(%s): %d", ctx.Doer.Name, source.ID) ctx.Flash.Success(ctx.Tr("admin.auths.deletion_success")) - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "redirect": setting.AppSubURL + "/admin/auths", }) } diff --git a/routers/web/admin/config.go b/routers/web/admin/config.go index 2c6989a71db..1c233ae852a 100644 --- a/routers/web/admin/config.go +++ b/routers/web/admin/config.go @@ -207,7 +207,7 @@ func ChangeConfig(ctx *context.Context) { return } - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "version": version + 1, }) } diff --git a/routers/web/admin/hooks.go b/routers/web/admin/hooks.go index 46dc734d252..2e4122c9048 100644 --- a/routers/web/admin/hooks.go +++ b/routers/web/admin/hooks.go @@ -26,8 +26,8 @@ func DefaultOrSystemWebhooks(ctx *context.Context) { ctx.Data["PageIsAdminSystemHooks"] = true ctx.Data["PageIsAdminDefaultHooks"] = true - def := make(map[string]interface{}, len(ctx.Data)) - sys := make(map[string]interface{}, len(ctx.Data)) + def := make(map[string]any, len(ctx.Data)) + sys := make(map[string]any, len(ctx.Data)) for k, v := range ctx.Data { def[k] = v sys[k] = v @@ -67,7 +67,7 @@ func DeleteDefaultOrSystemWebhook(ctx *context.Context) { ctx.Flash.Success(ctx.Tr("repo.settings.webhook_deletion_success")) } - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "redirect": setting.AppSubURL + "/admin/hooks", }) } diff --git a/routers/web/admin/packages.go b/routers/web/admin/packages.go index 4d5987e780b..ace54fc0d80 100644 --- a/routers/web/admin/packages.go +++ b/routers/web/admin/packages.go @@ -97,7 +97,7 @@ func DeletePackageVersion(ctx *context.Context) { } ctx.Flash.Success(ctx.Tr("packages.settings.delete.success")) - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "redirect": setting.AppSubURL + "/admin/packages?page=" + url.QueryEscape(ctx.FormString("page")) + "&q=" + url.QueryEscape(ctx.FormString("q")) + "&type=" + url.QueryEscape(ctx.FormString("type")), }) } diff --git a/routers/web/admin/repos.go b/routers/web/admin/repos.go index 360d3166040..3384ac78c26 100644 --- a/routers/web/admin/repos.go +++ b/routers/web/admin/repos.go @@ -112,7 +112,7 @@ func DeleteRepo(ctx *context.Context) { log.Trace("Repository deleted: %s", repo.FullName()) ctx.Flash.Success(ctx.Tr("repo.settings.deletion_success")) - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "redirect": setting.AppSubURL + "/admin/repos?page=" + url.QueryEscape(ctx.FormString("page")) + "&sort=" + url.QueryEscape(ctx.FormString("sort")), }) } diff --git a/routers/web/admin/stacktrace.go b/routers/web/admin/stacktrace.go index 4b225c2c8a4..f2d2be481a6 100644 --- a/routers/web/admin/stacktrace.go +++ b/routers/web/admin/stacktrace.go @@ -42,7 +42,7 @@ func Stacktrace(ctx *context.Context) { func StacktraceCancel(ctx *context.Context) { pid := ctx.Params("pid") process.GetManager().Cancel(process.IDType(pid)) - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "redirect": setting.AppSubURL + "/admin/monitor/stacktrace", }) } diff --git a/routers/web/admin/users.go b/routers/web/admin/users.go index 3895bcfdb92..08a40764188 100644 --- a/routers/web/admin/users.go +++ b/routers/web/admin/users.go @@ -56,7 +56,7 @@ func Users(ctx *context.Context) { sortType = explore.UserSearchDefaultAdminSort ctx.SetFormString("sort", sortType) } - ctx.PageData["adminUserListSearchForm"] = map[string]interface{}{ + ctx.PageData["adminUserListSearchForm"] = map[string]any{ "StatusFilterMap": statusFilterMap, "SortType": sortType, } diff --git a/routers/web/auth/auth.go b/routers/web/auth/auth.go index bc8f6d58c92..3bf133f5622 100644 --- a/routers/web/auth/auth.go +++ b/routers/web/auth/auth.go @@ -78,7 +78,7 @@ func AutoSignIn(ctx *context.Context) (bool, error) { isSucceed = true - if err := updateSession(ctx, nil, map[string]interface{}{ + if err := updateSession(ctx, nil, map[string]any{ // Set session IDs "uid": u.ID, "uname": u.Name, @@ -201,7 +201,7 @@ func SignInPost(ctx *context.Context) { u, source, err := auth_service.UserSignIn(form.UserName, form.Password) if err != nil { - if user_model.IsErrUserNotExist(err) || user_model.IsErrEmailAddressNotExist(err) { + if errors.Is(err, util.ErrNotExist) || errors.Is(err, util.ErrInvalidArgument) { ctx.RenderWithErr(ctx.Tr("form.username_password_incorrect"), tplSignIn, &form) log.Info("Failed authentication attempt for %s from %s: %v", form.UserName, ctx.RemoteAddr(), err) } else if user_model.IsErrEmailAlreadyUsed(err) { @@ -255,7 +255,7 @@ func SignInPost(ctx *context.Context) { return } - updates := map[string]interface{}{ + updates := map[string]any{ // User will need to use 2FA TOTP or WebAuthn, save data "twofaUid": u.ID, "twofaRemember": form.Remember, @@ -305,7 +305,7 @@ func handleSignInFull(ctx *context.Context, u *user_model.User, remember, obeyRe "twofaUid", "twofaRemember", "linkAccount", - }, map[string]interface{}{ + }, map[string]any{ "uid": u.ID, "uname": u.Name, }); err != nil { @@ -476,7 +476,7 @@ func SignUpPost(ctx *context.Context) { // createAndHandleCreatedUser calls createUserInContext and // then handleUserCreated. -func createAndHandleCreatedUser(ctx *context.Context, tpl base.TplName, form interface{}, u *user_model.User, overwrites *user_model.CreateUserOverwriteOptions, gothUser *goth.User, allowLink bool) bool { +func createAndHandleCreatedUser(ctx *context.Context, tpl base.TplName, form any, u *user_model.User, overwrites *user_model.CreateUserOverwriteOptions, gothUser *goth.User, allowLink bool) bool { if !createUserInContext(ctx, tpl, form, u, overwrites, gothUser, allowLink) { return false } @@ -485,7 +485,7 @@ func createAndHandleCreatedUser(ctx *context.Context, tpl base.TplName, form int // createUserInContext creates a user and handles errors within a given context. // Optionally a template can be specified. -func createUserInContext(ctx *context.Context, tpl base.TplName, form interface{}, u *user_model.User, overwrites *user_model.CreateUserOverwriteOptions, gothUser *goth.User, allowLink bool) (ok bool) { +func createUserInContext(ctx *context.Context, tpl base.TplName, form any, u *user_model.User, overwrites *user_model.CreateUserOverwriteOptions, gothUser *goth.User, allowLink bool) (ok bool) { if err := user_model.CreateUser(u, overwrites); err != nil { if allowLink && (user_model.IsErrUserAlreadyExist(err) || user_model.IsErrEmailAlreadyUsed(err)) { if setting.OAuth2Client.AccountLinking == setting.OAuth2AccountLinkingAuto { @@ -497,23 +497,23 @@ func createUserInContext(ctx *context.Context, tpl base.TplName, form interface{ hasUser, err = user_model.GetUser(user) if !hasUser || err != nil { ctx.ServerError("UserLinkAccount", err) - return + return false } } // TODO: probably we should respect 'remember' user's choice... linkAccount(ctx, user, *gothUser, true) - return // user is already created here, all redirects are handled + return false // user is already created here, all redirects are handled } else if setting.OAuth2Client.AccountLinking == setting.OAuth2AccountLinkingLogin { showLinkingLogin(ctx, *gothUser) - return // user will be created only after linking login + return false // user will be created only after linking login } } // handle error without template if len(tpl) == 0 { ctx.ServerError("CreateUser", err) - return + return false } // handle error with template @@ -542,7 +542,7 @@ func createUserInContext(ctx *context.Context, tpl base.TplName, form interface{ default: ctx.ServerError("CreateUser", err) } - return + return false } log.Trace("Account created: %s", u.Name) return true @@ -559,7 +559,7 @@ func handleUserCreated(ctx *context.Context, u *user_model.User, gothUser *goth. u.SetLastLogin() if err := user_model.UpdateUserCols(ctx, u, "is_admin", "is_active", "last_login_unix"); err != nil { ctx.ServerError("UpdateUser", err) - return + return false } } @@ -577,7 +577,7 @@ func handleUserCreated(ctx *context.Context, u *user_model.User, gothUser *goth. if setting.Service.RegisterManualConfirm { ctx.Data["ManualActivationOnly"] = true ctx.HTML(http.StatusOK, TplActivate) - return + return false } mailer.SendActivateAccountMail(ctx.Locale, u) @@ -592,7 +592,7 @@ func handleUserCreated(ctx *context.Context, u *user_model.User, gothUser *goth. log.Error("Set cache(MailResendLimit) fail: %v", err) } } - return + return false } return true @@ -707,7 +707,7 @@ func handleAccountActivation(ctx *context.Context, user *user_model.User) { log.Trace("User activated: %s", user.Name) - if err := updateSession(ctx, nil, map[string]interface{}{ + if err := updateSession(ctx, nil, map[string]any{ "uid": user.ID, "uname": user.Name, }); err != nil { @@ -760,7 +760,7 @@ func ActivateEmail(ctx *context.Context) { ctx.Redirect(setting.AppSubURL + "/user/settings/account") } -func updateSession(ctx *context.Context, deletes []string, updates map[string]interface{}) error { +func updateSession(ctx *context.Context, deletes []string, updates map[string]any) error { if _, err := session.RegenerateSession(ctx.Resp, ctx.Req); err != nil { return fmt.Errorf("regenerate session: %w", err) } diff --git a/routers/web/auth/linkaccount.go b/routers/web/auth/linkaccount.go index 865bcca1524..0f7ecf1af49 100644 --- a/routers/web/auth/linkaccount.go +++ b/routers/web/auth/linkaccount.go @@ -13,7 +13,9 @@ import ( user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" auth_service "code.gitea.io/gitea/services/auth" "code.gitea.io/gitea/services/auth/source/oauth2" @@ -81,6 +83,32 @@ func LinkAccount(ctx *context.Context) { ctx.HTML(http.StatusOK, tplLinkAccount) } +func handleSignInError(ctx *context.Context, userName string, ptrForm any, tmpl base.TplName, invoker string, err error) { + if errors.Is(err, util.ErrNotExist) { + ctx.RenderWithErr(ctx.Tr("form.username_password_incorrect"), tmpl, ptrForm) + } else if errors.Is(err, util.ErrInvalidArgument) { + ctx.Data["user_exists"] = true + ctx.RenderWithErr(ctx.Tr("form.username_password_incorrect"), tmpl, ptrForm) + } else if user_model.IsErrUserProhibitLogin(err) { + ctx.Data["user_exists"] = true + log.Info("Failed authentication attempt for %s from %s: %v", userName, ctx.RemoteAddr(), err) + ctx.Data["Title"] = ctx.Tr("auth.prohibit_login") + ctx.HTML(http.StatusOK, "user/auth/prohibit_login") + } else if user_model.IsErrUserInactive(err) { + ctx.Data["user_exists"] = true + if setting.Service.RegisterEmailConfirm { + ctx.Data["Title"] = ctx.Tr("auth.active_your_account") + ctx.HTML(http.StatusOK, TplActivate) + } else { + log.Info("Failed authentication attempt for %s from %s: %v", userName, ctx.RemoteAddr(), err) + ctx.Data["Title"] = ctx.Tr("auth.prohibit_login") + ctx.HTML(http.StatusOK, "user/auth/prohibit_login") + } + } else { + ctx.ServerError(invoker, err) + } +} + // LinkAccountPostSignIn handle the coupling of external account with another account using signIn func LinkAccountPostSignIn(ctx *context.Context) { signInForm := web.GetForm(ctx).(*forms.SignInForm) @@ -116,12 +144,7 @@ func LinkAccountPostSignIn(ctx *context.Context) { u, _, err := auth_service.UserSignIn(signInForm.UserName, signInForm.Password) if err != nil { - if user_model.IsErrUserNotExist(err) { - ctx.Data["user_exists"] = true - ctx.RenderWithErr(ctx.Tr("form.username_password_incorrect"), tplLinkAccount, &signInForm) - } else { - ctx.ServerError("UserLinkAccount", err) - } + handleSignInError(ctx, signInForm.UserName, &signInForm, tplLinkAccount, "UserLinkAccount", err) return } @@ -151,7 +174,7 @@ func linkAccount(ctx *context.Context, u *user_model.User, gothUser goth.User, r return } - if err := updateSession(ctx, nil, map[string]interface{}{ + if err := updateSession(ctx, nil, map[string]any{ // User needs to use 2FA, save data and redirect to 2FA page. "twofaUid": u.ID, "twofaRemember": remember, diff --git a/routers/web/auth/oauth.go b/routers/web/auth/oauth.go index 0ce3bbde003..db15bf2e3d1 100644 --- a/routers/web/auth/oauth.go +++ b/routers/web/auth/oauth.go @@ -1008,13 +1008,13 @@ func SignInOAuthCallback(ctx *context.Context) { handleOAuth2SignIn(ctx, authSource, u, gothUser) } -func claimValueToStringSet(claimValue interface{}) container.Set[string] { +func claimValueToStringSet(claimValue any) container.Set[string] { var groups []string switch rawGroup := claimValue.(type) { case []string: groups = rawGroup - case []interface{}: + case []any: for _, group := range rawGroup { groups = append(groups, fmt.Sprintf("%s", group)) } @@ -1067,7 +1067,7 @@ func setUserAdminAndRestrictedFromGroupClaims(source *oauth2.Source, u *user_mod } func showLinkingLogin(ctx *context.Context, gothUser goth.User) { - if err := updateSession(ctx, nil, map[string]interface{}{ + if err := updateSession(ctx, nil, map[string]any{ "linkAccountGothUser": gothUser, }); err != nil { ctx.ServerError("updateSession", err) @@ -1119,7 +1119,7 @@ func handleOAuth2SignIn(ctx *context.Context, source *auth.Source, u *user_model // If this user is enrolled in 2FA and this source doesn't override it, // we can't sign the user in just yet. Instead, redirect them to the 2FA authentication page. if !needs2FA { - if err := updateSession(ctx, nil, map[string]interface{}{ + if err := updateSession(ctx, nil, map[string]any{ "uid": u.ID, "uname": u.Name, }); err != nil { @@ -1189,7 +1189,7 @@ func handleOAuth2SignIn(ctx *context.Context, source *auth.Source, u *user_model } } - if err := updateSession(ctx, nil, map[string]interface{}{ + if err := updateSession(ctx, nil, map[string]any{ // User needs to use 2FA, save data and redirect to 2FA page. "twofaUid": u.ID, "twofaRemember": false, diff --git a/routers/web/auth/oauth_test.go b/routers/web/auth/oauth_test.go index 62f723600d3..adf933fd239 100644 --- a/routers/web/auth/oauth_test.go +++ b/routers/web/auth/oauth_test.go @@ -26,7 +26,7 @@ func createAndParseToken(t *testing.T, grant *auth.OAuth2Grant) *oauth2.OIDCToke assert.Nil(t, terr) assert.NotNil(t, response) - parsedToken, err := jwt.ParseWithClaims(response.IDToken, &oauth2.OIDCToken{}, func(token *jwt.Token) (interface{}, error) { + parsedToken, err := jwt.ParseWithClaims(response.IDToken, &oauth2.OIDCToken{}, func(token *jwt.Token) (any, error) { assert.NotNil(t, token.Method) assert.Equal(t, signingKey.SigningMethod().Alg(), token.Method.Alg()) return signingKey.VerifyKey(), nil diff --git a/routers/web/auth/openid.go b/routers/web/auth/openid.go index 5e0e7b258f6..00fc17f0980 100644 --- a/routers/web/auth/openid.go +++ b/routers/web/auth/openid.go @@ -230,7 +230,7 @@ func signInOpenIDVerify(ctx *context.Context) { if u != nil { nickname = u.LowerName } - if err := updateSession(ctx, nil, map[string]interface{}{ + if err := updateSession(ctx, nil, map[string]any{ "openid_verified_uri": id, "openid_determined_email": email, "openid_determined_username": nickname, @@ -282,11 +282,7 @@ func ConnectOpenIDPost(ctx *context.Context) { u, _, err := auth.UserSignIn(form.UserName, form.Password) if err != nil { - if user_model.IsErrUserNotExist(err) { - ctx.RenderWithErr(ctx.Tr("form.username_password_incorrect"), tplConnectOID, &form) - } else { - ctx.ServerError("ConnectOpenIDPost", err) - } + handleSignInError(ctx, form.UserName, &form, tplConnectOID, "ConnectOpenIDPost", err) return } diff --git a/routers/web/devtest/devtest.go b/routers/web/devtest/devtest.go index 64b732c0356..525ca9be53c 100644 --- a/routers/web/devtest/devtest.go +++ b/routers/web/devtest/devtest.go @@ -34,7 +34,7 @@ func List(ctx *context.Context) { func FetchActionTest(ctx *context.Context) { _ = ctx.Req.ParseForm() - ctx.Flash.Info(ctx.Req.Method + " " + ctx.Req.RequestURI + "
" + + ctx.Flash.Info("fetch-action: " + ctx.Req.Method + " " + ctx.Req.RequestURI + "
" + "Form: " + ctx.Req.Form.Encode() + "
" + "PostForm: " + ctx.Req.PostForm.Encode(), ) @@ -52,5 +52,15 @@ func Tmpl(ctx *context.Context) { ctx.Data["TimePast1y"] = now.Add(-1 * 366 * 86400 * time.Second) ctx.Data["TimeFuture1y"] = now.Add(1 * 366 * 86400 * time.Second) + if ctx.Req.Method == "POST" { + _ = ctx.Req.ParseForm() + ctx.Flash.Info("form: "+ctx.Req.Method+" "+ctx.Req.RequestURI+"
"+ + "Form: "+ctx.Req.Form.Encode()+"
"+ + "PostForm: "+ctx.Req.PostForm.Encode(), + true, + ) + time.Sleep(2 * time.Second) + } + ctx.HTML(http.StatusOK, base.TplName("devtest"+path.Clean("/"+ctx.Params("sub")))) } diff --git a/routers/web/explore/topic.go b/routers/web/explore/topic.go index e172d9e04d0..132ef23fa75 100644 --- a/routers/web/explore/topic.go +++ b/routers/web/explore/topic.go @@ -35,7 +35,7 @@ func TopicSearch(ctx *context.Context) { } ctx.SetTotalCountHeader(total) - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "topics": topicResponses, }) } diff --git a/routers/web/org/members.go b/routers/web/org/members.go index 1953a8b2849..8da0f0b9fd0 100644 --- a/routers/web/org/members.go +++ b/routers/web/org/members.go @@ -101,7 +101,7 @@ func MembersAction(ctx *context.Context) { err = models.RemoveOrgUser(org.ID, uid) if organization.IsErrLastOrgOwner(err) { ctx.Flash.Error(ctx.Tr("form.last_org_owner")) - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "redirect": ctx.Org.OrgLink + "/members", }) return @@ -110,12 +110,12 @@ func MembersAction(ctx *context.Context) { err = models.RemoveOrgUser(org.ID, ctx.Doer.ID) if err == nil { ctx.Flash.Success(ctx.Tr("form.organization_leave_success", org.DisplayName())) - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "redirect": "", // keep the user stay on current page, in case they want to do other operations. }) } else if organization.IsErrLastOrgOwner(err) { ctx.Flash.Error(ctx.Tr("form.last_org_owner")) - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "redirect": ctx.Org.OrgLink + "/members", }) } else { @@ -126,7 +126,7 @@ func MembersAction(ctx *context.Context) { if err != nil { log.Error("Action(%s): %v", ctx.Params(":action"), err) - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "ok": false, "err": err.Error(), }) @@ -138,7 +138,7 @@ func MembersAction(ctx *context.Context) { redirect = setting.AppSubURL + "/" } - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "redirect": redirect, }) } diff --git a/routers/web/org/org_labels.go b/routers/web/org/org_labels.go index 9ce05680d7b..08566637a87 100644 --- a/routers/web/org/org_labels.go +++ b/routers/web/org/org_labels.go @@ -90,7 +90,7 @@ func DeleteLabel(ctx *context.Context) { ctx.Flash.Success(ctx.Tr("repo.issues.label_deletion_success")) } - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "redirect": ctx.Org.OrgLink + "/settings/labels", }) } diff --git a/routers/web/org/projects.go b/routers/web/org/projects.go index e525f2c43f3..21cb23000d2 100644 --- a/routers/web/org/projects.go +++ b/routers/web/org/projects.go @@ -41,6 +41,7 @@ func MustEnableProjects(ctx *context.Context) { // Projects renders the home page of projects func Projects(ctx *context.Context) { + shared_user.PrepareContextForProfileBigAvatar(ctx) ctx.Data["Title"] = ctx.Tr("repo.project_board") sortType := ctx.FormTrim("sort") @@ -218,7 +219,7 @@ func DeleteProject(ctx *context.Context) { ctx.Flash.Success(ctx.Tr("repo.projects.deletion_success")) } - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "redirect": ctx.ContextUser.HomeLink() + "/-/projects", }) } @@ -436,9 +437,11 @@ func UpdateIssueProject(ctx *context.Context) { projectID := ctx.FormInt64("id") for _, issue := range issues { - oldProjectID := issue.Project.ID - if oldProjectID == projectID { - continue + if issue.Project != nil { + oldProjectID := issue.Project.ID + if oldProjectID == projectID { + continue + } } if err := issues_model.ChangeProjectAssign(issue, ctx.Doer, projectID); err != nil { @@ -447,7 +450,7 @@ func UpdateIssueProject(ctx *context.Context) { } } - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "ok": true, }) } @@ -495,7 +498,7 @@ func DeleteProjectBoard(ctx *context.Context) { return } - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "ok": true, }) } @@ -524,7 +527,7 @@ func AddBoardToProjectPost(ctx *context.Context) { return } - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "ok": true, }) } @@ -592,7 +595,7 @@ func EditProjectBoard(ctx *context.Context) { return } - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "ok": true, }) } @@ -609,7 +612,7 @@ func SetDefaultProjectBoard(ctx *context.Context) { return } - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "ok": true, }) } @@ -626,7 +629,7 @@ func UnsetDefaultProjectBoard(ctx *context.Context) { return } - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "ok": true, }) } @@ -728,7 +731,7 @@ func MoveIssues(ctx *context.Context) { return } - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "ok": true, }) } diff --git a/routers/web/org/setting.go b/routers/web/org/setting.go index 2c4a6b93e39..b5653160a26 100644 --- a/routers/web/org/setting.go +++ b/routers/web/org/setting.go @@ -224,7 +224,7 @@ func DeleteWebhook(ctx *context.Context) { ctx.Flash.Success(ctx.Tr("repo.settings.webhook_deletion_success")) } - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "redirect": ctx.Org.OrgLink + "/settings/hooks", }) } diff --git a/routers/web/org/teams.go b/routers/web/org/teams.go index 2ce4bf53227..aefadaf809e 100644 --- a/routers/web/org/teams.go +++ b/routers/web/org/teams.go @@ -79,7 +79,7 @@ func TeamsAction(ctx *context.Context) { ctx.Flash.Error(ctx.Tr("form.last_org_owner")) } else { log.Error("Action(%s): %v", ctx.Params(":action"), err) - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "ok": false, "err": err.Error(), }) @@ -95,7 +95,7 @@ func TeamsAction(ctx *context.Context) { redirect = setting.AppSubURL + "/" } ctx.JSON(http.StatusOK, - map[string]interface{}{ + map[string]any{ "redirect": redirect, }) return @@ -117,7 +117,7 @@ func TeamsAction(ctx *context.Context) { ctx.Flash.Error(ctx.Tr("form.last_org_owner")) } else { log.Error("Action(%s): %v", ctx.Params(":action"), err) - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "ok": false, "err": err.Error(), }) @@ -125,7 +125,7 @@ func TeamsAction(ctx *context.Context) { } } ctx.JSON(http.StatusOK, - map[string]interface{}{ + map[string]any{ "redirect": ctx.Org.OrgLink + "/teams/" + url.PathEscape(ctx.Org.Team.LowerName), }) return @@ -199,7 +199,7 @@ func TeamsAction(ctx *context.Context) { ctx.Flash.Error(ctx.Tr("form.last_org_owner")) } else { log.Error("Action(%s): %v", ctx.Params(":action"), err) - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "ok": false, "err": err.Error(), }) @@ -256,7 +256,7 @@ func TeamsRepoAction(ctx *context.Context) { } if action == "addall" || action == "removeall" { - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "redirect": ctx.Org.OrgLink + "/teams/" + url.PathEscape(ctx.Org.Team.LowerName) + "/repositories", }) return @@ -414,7 +414,7 @@ func SearchTeam(ctx *context.Context) { teams, maxResults, err := org_model.SearchTeam(opts) if err != nil { log.Error("SearchTeam failed: %v", err) - ctx.JSON(http.StatusInternalServerError, map[string]interface{}{ + ctx.JSON(http.StatusInternalServerError, map[string]any{ "ok": false, "error": "SearchTeam internal failure", }) @@ -424,7 +424,7 @@ func SearchTeam(ctx *context.Context) { apiTeams, err := convert.ToTeams(ctx, teams, false) if err != nil { log.Error("convert ToTeams failed: %v", err) - ctx.JSON(http.StatusInternalServerError, map[string]interface{}{ + ctx.JSON(http.StatusInternalServerError, map[string]any{ "ok": false, "error": "SearchTeam failed to get units", }) @@ -432,7 +432,7 @@ func SearchTeam(ctx *context.Context) { } ctx.SetTotalCountHeader(maxResults) - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "ok": true, "data": apiTeams, }) @@ -530,7 +530,7 @@ func DeleteTeam(ctx *context.Context) { ctx.Flash.Success(ctx.Tr("org.teams.delete_team_success")) } - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "redirect": ctx.Org.OrgLink + "/teams", }) } diff --git a/routers/web/repo/attachment.go b/routers/web/repo/attachment.go index af1842ad109..b7be77914f0 100644 --- a/routers/web/repo/attachment.go +++ b/routers/web/repo/attachment.go @@ -15,6 +15,7 @@ import ( "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/storage" "code.gitea.io/gitea/modules/upload" + "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/routers/common" "code.gitea.io/gitea/services/attachment" repo_service "code.gitea.io/gitea/services/repository" @@ -148,7 +149,7 @@ func ServeAttachment(ctx *context.Context, uuid string) { } defer fr.Close() - common.ServeContentByReadSeeker(ctx.Base, attach.Name, attach.CreatedUnix.AsTime(), fr) + common.ServeContentByReadSeeker(ctx.Base, attach.Name, util.ToPointer(attach.CreatedUnix.AsTime()), fr) } // GetAttachment serve attachments diff --git a/routers/web/repo/branch.go b/routers/web/repo/branch.go index f0282a71b87..999104d7872 100644 --- a/routers/web/repo/branch.go +++ b/routers/web/repo/branch.go @@ -57,7 +57,25 @@ func Branches(ctx *context.Context) { return } + commitIDs := []string{defaultBranch.DBBranch.CommitID} + for _, branch := range branches { + commitIDs = append(commitIDs, branch.DBBranch.CommitID) + } + + commitStatuses, err := git_model.GetLatestCommitStatusForRepoCommitIDs(ctx, ctx.Repo.Repository.ID, commitIDs) + if err != nil { + ctx.ServerError("LoadBranches", err) + return + } + + commitStatus := make(map[string]*git_model.CommitStatus) + for commitID, cs := range commitStatuses { + commitStatus[commitID] = git_model.CalcCommitStatus(cs) + } + ctx.Data["Branches"] = branches + ctx.Data["CommitStatus"] = commitStatus + ctx.Data["CommitStatuses"] = commitStatuses ctx.Data["DefaultBranchBranch"] = defaultBranch pager := context.NewPagination(int(branchesCount), pageSize, page, 5) pager.SetDefaultParams(ctx) @@ -144,7 +162,7 @@ func RestoreBranchPost(ctx *context.Context) { } func redirect(ctx *context.Context) { - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "redirect": ctx.Repo.RepoLink + "/branches?page=" + url.QueryEscape(ctx.FormString("page")), }) } @@ -205,7 +223,7 @@ func CreateBranch(ctx *context.Context) { if len(e.Message) == 0 { ctx.Flash.Error(ctx.Tr("repo.editor.push_rejected_no_message")) } else { - flashError, err := ctx.RenderToString(tplAlertDetails, map[string]interface{}{ + flashError, err := ctx.RenderToString(tplAlertDetails, map[string]any{ "Message": ctx.Tr("repo.editor.push_rejected"), "Summary": ctx.Tr("repo.editor.push_rejected_summary"), "Details": utils.SanitizeFlashErrorString(e.Message), diff --git a/routers/web/repo/download.go b/routers/web/repo/download.go index fd67b82ef27..a9e2e2b2fad 100644 --- a/routers/web/repo/download.go +++ b/routers/web/repo/download.go @@ -20,7 +20,7 @@ import ( ) // ServeBlobOrLFS download a git.Blob redirecting to LFS if necessary -func ServeBlobOrLFS(ctx *context.Context, blob *git.Blob, lastModified time.Time) error { +func ServeBlobOrLFS(ctx *context.Context, blob *git.Blob, lastModified *time.Time) error { if httpcache.HandleGenericETagTimeCache(ctx.Req, ctx.Resp, `"`+blob.ID.String()+`"`, lastModified) { return nil } @@ -82,7 +82,7 @@ func ServeBlobOrLFS(ctx *context.Context, blob *git.Blob, lastModified time.Time return common.ServeBlob(ctx.Base, ctx.Repo.TreePath, blob, lastModified) } -func getBlobForEntry(ctx *context.Context) (blob *git.Blob, lastModified time.Time) { +func getBlobForEntry(ctx *context.Context) (blob *git.Blob, lastModified *time.Time) { entry, err := ctx.Repo.Commit.GetTreeEntryByPath(ctx.Repo.TreePath) if err != nil { if git.IsErrNotExist(err) { @@ -90,23 +90,23 @@ func getBlobForEntry(ctx *context.Context) (blob *git.Blob, lastModified time.Ti } else { ctx.ServerError("GetTreeEntryByPath", err) } - return + return nil, nil } if entry.IsDir() || entry.IsSubModule() { ctx.NotFound("getBlobForEntry", nil) - return + return nil, nil } info, _, err := git.Entries([]*git.TreeEntry{entry}).GetCommitsInfo(ctx, ctx.Repo.Commit, path.Dir("/" + ctx.Repo.TreePath)[1:]) if err != nil { ctx.ServerError("GetCommitsInfo", err) - return + return nil, nil } if len(info) == 1 { // Not Modified - lastModified = info[0].Commit.Committer.When + lastModified = &info[0].Commit.Committer.When } blob = entry.Blob() @@ -148,7 +148,7 @@ func DownloadByID(ctx *context.Context) { } return } - if err = common.ServeBlob(ctx.Base, ctx.Repo.TreePath, blob, time.Time{}); err != nil { + if err = common.ServeBlob(ctx.Base, ctx.Repo.TreePath, blob, nil); err != nil { ctx.ServerError("ServeBlob", err) } } @@ -164,7 +164,7 @@ func DownloadByIDOrLFS(ctx *context.Context) { } return } - if err = ServeBlobOrLFS(ctx, blob, time.Time{}); err != nil { + if err = ServeBlobOrLFS(ctx, blob, nil); err != nil { ctx.ServerError("ServeBlob", err) } } diff --git a/routers/web/repo/editor.go b/routers/web/repo/editor.go index a63b08126c9..88f9e42f3bf 100644 --- a/routers/web/repo/editor.go +++ b/routers/web/repo/editor.go @@ -344,7 +344,7 @@ func editFilePost(ctx *context.Context, form forms.EditRepoFileForm, isNewFile b if len(errPushRej.Message) == 0 { ctx.RenderWithErr(ctx.Tr("repo.editor.push_rejected_no_message"), tplEditFile, &form) } else { - flashError, err := ctx.RenderToString(tplAlertDetails, map[string]interface{}{ + flashError, err := ctx.RenderToString(tplAlertDetails, map[string]any{ "Message": ctx.Tr("repo.editor.push_rejected"), "Summary": ctx.Tr("repo.editor.push_rejected_summary"), "Details": utils.SanitizeFlashErrorString(errPushRej.Message), @@ -356,7 +356,7 @@ func editFilePost(ctx *context.Context, form forms.EditRepoFileForm, isNewFile b ctx.RenderWithErr(flashError, tplEditFile, &form) } } else { - flashError, err := ctx.RenderToString(tplAlertDetails, map[string]interface{}{ + flashError, err := ctx.RenderToString(tplAlertDetails, map[string]any{ "Message": ctx.Tr("repo.editor.fail_to_update_file", form.TreePath), "Summary": ctx.Tr("repo.editor.fail_to_update_file_summary"), "Details": utils.SanitizeFlashErrorString(err.Error()), @@ -543,7 +543,7 @@ func DeleteFilePost(ctx *context.Context) { if len(errPushRej.Message) == 0 { ctx.RenderWithErr(ctx.Tr("repo.editor.push_rejected_no_message"), tplDeleteFile, &form) } else { - flashError, err := ctx.RenderToString(tplAlertDetails, map[string]interface{}{ + flashError, err := ctx.RenderToString(tplAlertDetails, map[string]any{ "Message": ctx.Tr("repo.editor.push_rejected"), "Summary": ctx.Tr("repo.editor.push_rejected_summary"), "Details": utils.SanitizeFlashErrorString(errPushRej.Message), @@ -743,7 +743,7 @@ func UploadFilePost(ctx *context.Context) { if len(errPushRej.Message) == 0 { ctx.RenderWithErr(ctx.Tr("repo.editor.push_rejected_no_message"), tplUploadFile, &form) } else { - flashError, err := ctx.RenderToString(tplAlertDetails, map[string]interface{}{ + flashError, err := ctx.RenderToString(tplAlertDetails, map[string]any{ "Message": ctx.Tr("repo.editor.push_rejected"), "Summary": ctx.Tr("repo.editor.push_rejected_summary"), "Details": utils.SanitizeFlashErrorString(errPushRej.Message), diff --git a/routers/web/repo/http.go b/routers/web/repo/http.go index f4e9ac86a1b..0cae9aeda45 100644 --- a/routers/web/repo/http.go +++ b/routers/web/repo/http.go @@ -56,13 +56,13 @@ func CorsHandler() func(next http.Handler) http.Handler { } // httpBase implementation git smart HTTP protocol -func httpBase(ctx *context.Context) (h *serviceHandler) { +func httpBase(ctx *context.Context) *serviceHandler { username := ctx.Params(":username") reponame := strings.TrimSuffix(ctx.Params(":reponame"), ".git") if ctx.FormString("go-get") == "1" { context.EarlyResponseForGoGetMeta(ctx) - return + return nil } var isPull, receivePack bool @@ -101,7 +101,7 @@ func httpBase(ctx *context.Context) (h *serviceHandler) { owner := ctx.ContextUser if !owner.IsOrganization() && !owner.IsActive { ctx.PlainText(http.StatusForbidden, "Repository cannot be accessed. You cannot push or open issues/pull-requests.") - return + return nil } repoExist := true @@ -110,19 +110,19 @@ func httpBase(ctx *context.Context) (h *serviceHandler) { if repo_model.IsErrRepoNotExist(err) { if redirectRepoID, err := repo_model.LookupRedirect(owner.ID, reponame); err == nil { context.RedirectToRepo(ctx.Base, redirectRepoID) - return + return nil } repoExist = false } else { ctx.ServerError("GetRepositoryByName", err) - return + return nil } } // Don't allow pushing if the repo is archived if repoExist && repo.IsArchived && !isPull { ctx.PlainText(http.StatusForbidden, "This repo is archived. You can view files and clone it, but cannot push or open issues/pull-requests.") - return + return nil } // Only public pull don't need auth. @@ -136,7 +136,7 @@ func httpBase(ctx *context.Context) (h *serviceHandler) { if isPublicPull { if err := repo.LoadOwner(ctx); err != nil { ctx.ServerError("LoadOwner", err) - return + return nil } askAuth = askAuth || (repo.Owner.Visibility != structs.VisibleTypePublic) @@ -149,12 +149,12 @@ func httpBase(ctx *context.Context) (h *serviceHandler) { // TODO: support digit auth - which would be Authorization header with digit ctx.Resp.Header().Set("WWW-Authenticate", "Basic realm=\".\"") ctx.Error(http.StatusUnauthorized) - return + return nil } context.CheckRepoScopedToken(ctx, repo, auth_model.GetScopeLevelFromAccessMode(accessMode)) if ctx.Written() { - return + return nil } if ctx.IsBasicAuth && ctx.Data["IsApiToken"] != true && ctx.Data["IsActionsToken"] != true { @@ -162,16 +162,16 @@ func httpBase(ctx *context.Context) (h *serviceHandler) { if err == nil { // TODO: This response should be changed to "invalid credentials" for security reasons once the expectation behind it (creating an app token to authenticate) is properly documented ctx.PlainText(http.StatusUnauthorized, "Users with two-factor authentication enabled cannot perform HTTP/HTTPS operations via plain username and password. Please create and use a personal access token on the user settings page") - return + return nil } else if !auth_model.IsErrTwoFactorNotEnrolled(err) { ctx.ServerError("IsErrTwoFactorNotEnrolled", err) - return + return nil } } if !ctx.Doer.IsActive || ctx.Doer.ProhibitLogin { ctx.PlainText(http.StatusForbidden, "Your account is disabled.") - return + return nil } environ = []string{ @@ -193,23 +193,23 @@ func httpBase(ctx *context.Context) (h *serviceHandler) { task, err := actions_model.GetTaskByID(ctx, taskID) if err != nil { ctx.ServerError("GetTaskByID", err) - return + return nil } if task.RepoID != repo.ID { ctx.PlainText(http.StatusForbidden, "User permission denied") - return + return nil } if task.IsForkPullRequest { if accessMode > perm.AccessModeRead { ctx.PlainText(http.StatusForbidden, "User permission denied") - return + return nil } environ = append(environ, fmt.Sprintf("%s=%d", repo_module.EnvActionPerm, perm.AccessModeRead)) } else { if accessMode > perm.AccessModeWrite { ctx.PlainText(http.StatusForbidden, "User permission denied") - return + return nil } environ = append(environ, fmt.Sprintf("%s=%d", repo_module.EnvActionPerm, perm.AccessModeWrite)) } @@ -217,18 +217,18 @@ func httpBase(ctx *context.Context) (h *serviceHandler) { p, err := access_model.GetUserRepoPermission(ctx, repo, ctx.Doer) if err != nil { ctx.ServerError("GetUserRepoPermission", err) - return + return nil } if !p.CanAccess(accessMode, unitType) { ctx.PlainText(http.StatusNotFound, "Repository not found") - return + return nil } } if !isPull && repo.IsMirror { ctx.PlainText(http.StatusForbidden, "mirror repository is read-only") - return + return nil } } @@ -246,34 +246,34 @@ func httpBase(ctx *context.Context) (h *serviceHandler) { if !repoExist { if !receivePack { ctx.PlainText(http.StatusNotFound, "Repository not found") - return + return nil } if isWiki { // you cannot send wiki operation before create the repository ctx.PlainText(http.StatusNotFound, "Repository not found") - return + return nil } if owner.IsOrganization() && !setting.Repository.EnablePushCreateOrg { ctx.PlainText(http.StatusForbidden, "Push to create is not enabled for organizations.") - return + return nil } if !owner.IsOrganization() && !setting.Repository.EnablePushCreateUser { ctx.PlainText(http.StatusForbidden, "Push to create is not enabled for users.") - return + return nil } // Return dummy payload if GET receive-pack if ctx.Req.Method == http.MethodGet { dummyInfoRefs(ctx) - return + return nil } repo, err = repo_service.PushCreateRepo(ctx, ctx.Doer, owner, reponame) if err != nil { log.Error("pushCreateRepo: %v", err) ctx.Status(http.StatusNotFound) - return + return nil } } @@ -282,11 +282,11 @@ func httpBase(ctx *context.Context) (h *serviceHandler) { if _, err := repo.GetUnit(ctx, unit.TypeWiki); err != nil { if repo_model.IsErrUnitTypeNotExist(err) { ctx.PlainText(http.StatusForbidden, "repository wiki is disabled") - return + return nil } log.Error("Failed to get the wiki unit in %-v Error: %v", repo, err) ctx.ServerError("GetUnit(UnitTypeWiki) for "+repo.FullName(), err) - return + return nil } } diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index 4f14cc381f9..31bcbd7c21c 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -189,7 +189,7 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption uti var issueIDs []int64 if len(keyword) > 0 { - issueIDs, err = issue_indexer.SearchIssuesByKeyword(ctx, []int64{repo.ID}, keyword) + issueIDs, err = issue_indexer.SearchIssuesByKeyword(ctx, []int64{repo.ID}, keyword, ctx.FormString("state")) if err != nil { if issue_indexer.IsAvailable(ctx) { ctx.ServerError("issueIndexer.Search", err) @@ -952,7 +952,7 @@ func renderErrorOfTemplates(ctx *context.Context, errs map[string]error) string lines = append(lines, fmt.Sprintf("%s: %v", file, errs[file])) } - flashError, err := ctx.RenderToString(tplAlertDetails, map[string]interface{}{ + flashError, err := ctx.RenderToString(tplAlertDetails, map[string]any{ "Message": ctx.Tr("repo.issues.choose.ignore_invalid_templates"), "Summary": ctx.Tr("repo.issues.choose.invalid_templates", len(errs)), "Details": utils.SanitizeFlashErrorString(strings.Join(lines, "\n")), @@ -1922,9 +1922,12 @@ func ViewIssue(ctx *context.Context) { ctx.HTML(http.StatusOK, tplIssueView) } +// checkBlockedByIssues return canRead and notPermitted func checkBlockedByIssues(ctx *context.Context, blockers []*issues_model.DependencyInfo) (canRead, notPermitted []*issues_model.DependencyInfo) { - var lastRepoID int64 - var lastPerm access_model.Permission + var ( + lastRepoID int64 + lastPerm access_model.Permission + ) for i, blocker := range blockers { // Get the permissions for this repository perm := lastPerm @@ -1936,7 +1939,7 @@ func checkBlockedByIssues(ctx *context.Context, blockers []*issues_model.Depende perm, err = access_model.GetUserRepoPermission(ctx, &blocker.Repository, ctx.Doer) if err != nil { ctx.ServerError("GetUserRepoPermission", err) - return + return nil, nil } } lastRepoID = blocker.Repository.ID @@ -1977,7 +1980,7 @@ func GetActionIssue(ctx *context.Context) *issues_model.Issue { return nil } if err = issue.LoadAttributes(ctx); err != nil { - ctx.ServerError("LoadAttributes", nil) + ctx.ServerError("LoadAttributes", err) return nil } return issue @@ -2055,7 +2058,7 @@ func GetIssueInfo(ctx *context.Context) { } } - ctx.JSON(http.StatusOK, convert.ToAPIIssue(ctx, issue)) + ctx.JSON(http.StatusOK, convert.ToIssue(ctx, issue)) } // UpdateIssueTitle change issue's title @@ -2081,7 +2084,7 @@ func UpdateIssueTitle(ctx *context.Context) { return } - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "title": issue.Title, }) } @@ -2105,7 +2108,7 @@ func UpdateIssueRef(ctx *context.Context) { return } - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "ref": ref, }) } @@ -2146,7 +2149,7 @@ func UpdateIssueContent(ctx *context.Context) { return } - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "content": content, "attachments": attachmentsHTML(ctx, issue.Attachments, issue.Content), }) @@ -2206,7 +2209,7 @@ func UpdateIssueMilestone(ctx *context.Context) { } } - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "ok": true, }) } @@ -2252,7 +2255,7 @@ func UpdateIssueAssignee(ctx *context.Context) { } } } - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "ok": true, }) } @@ -2377,7 +2380,7 @@ func UpdatePullReviewRequest(ctx *context.Context) { } } - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "ok": true, }) } @@ -2463,7 +2466,7 @@ func SearchIssues(ctx *context.Context) { } var issueIDs []int64 if len(keyword) > 0 && len(repoIDs) > 0 { - if issueIDs, err = issue_indexer.SearchIssuesByKeyword(ctx, repoIDs, keyword); err != nil { + if issueIDs, err = issue_indexer.SearchIssuesByKeyword(ctx, repoIDs, keyword, ctx.FormString("state")); err != nil { ctx.Error(http.StatusInternalServerError, "SearchIssuesByKeyword", err.Error()) return } @@ -2560,7 +2563,7 @@ func SearchIssues(ctx *context.Context) { } ctx.SetTotalCountHeader(filteredCount) - ctx.JSON(http.StatusOK, convert.ToAPIIssueList(ctx, issues)) + ctx.JSON(http.StatusOK, convert.ToIssueList(ctx, issues)) } func getUserIDForFilter(ctx *context.Context, queryName string) int64 { @@ -2611,7 +2614,7 @@ func ListIssues(ctx *context.Context) { var issueIDs []int64 var labelIDs []int64 if len(keyword) > 0 { - issueIDs, err = issue_indexer.SearchIssuesByKeyword(ctx, []int64{ctx.Repo.Repository.ID}, keyword) + issueIDs, err = issue_indexer.SearchIssuesByKeyword(ctx, []int64{ctx.Repo.Repository.ID}, keyword, ctx.FormString("state")) if err != nil { ctx.Error(http.StatusInternalServerError, err.Error()) return @@ -2721,7 +2724,7 @@ func ListIssues(ctx *context.Context) { } ctx.SetTotalCountHeader(filteredCount) - ctx.JSON(http.StatusOK, convert.ToAPIIssueList(ctx, issues)) + ctx.JSON(http.StatusOK, convert.ToIssueList(ctx, issues)) } func BatchDeleteIssues(ctx *context.Context) { @@ -2763,7 +2766,7 @@ func UpdateIssueStatus(ctx *context.Context) { if issue.IsClosed != isClosed { if err := issue_service.ChangeStatus(issue, ctx.Doer, "", isClosed); err != nil { if issues_model.IsErrDependenciesLeft(err) { - ctx.JSON(http.StatusPreconditionFailed, map[string]interface{}{ + ctx.JSON(http.StatusPreconditionFailed, map[string]any{ "error": ctx.Tr("repo.issues.dependency.issue_batch_close_blocked", issue.Index), }) return @@ -2978,7 +2981,7 @@ func UpdateCommentContent(ctx *context.Context) { oldContent := comment.Content comment.Content = ctx.FormString("content") if len(comment.Content) == 0 { - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "content": "", }) return @@ -3012,7 +3015,7 @@ func UpdateCommentContent(ctx *context.Context) { return } - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "content": content, "attachments": attachmentsHTML(ctx, comment.Attachments, comment.Content), }) @@ -3122,14 +3125,14 @@ func ChangeIssueReaction(ctx *context.Context) { } if len(issue.Reactions) == 0 { - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "empty": true, "html": "", }) return } - html, err := ctx.RenderToString(tplReactions, map[string]interface{}{ + html, err := ctx.RenderToString(tplReactions, map[string]any{ "ctxData": ctx.Data, "ActionURL": fmt.Sprintf("%s/issues/%d/reactions", ctx.Repo.RepoLink, issue.Index), "Reactions": issue.Reactions.GroupByType(), @@ -3138,7 +3141,7 @@ func ChangeIssueReaction(ctx *context.Context) { ctx.ServerError("ChangeIssueReaction.HTMLString", err) return } - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "html": html, }) } @@ -3224,14 +3227,14 @@ func ChangeCommentReaction(ctx *context.Context) { } if len(comment.Reactions) == 0 { - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "empty": true, "html": "", }) return } - html, err := ctx.RenderToString(tplReactions, map[string]interface{}{ + html, err := ctx.RenderToString(tplReactions, map[string]any{ "ctxData": ctx.Data, "ActionURL": fmt.Sprintf("%s/comments/%d/reactions", ctx.Repo.RepoLink, comment.ID), "Reactions": comment.Reactions.GroupByType(), @@ -3240,7 +3243,7 @@ func ChangeCommentReaction(ctx *context.Context) { ctx.ServerError("ChangeCommentReaction.HTMLString", err) return } - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "html": html, }) } @@ -3282,9 +3285,12 @@ func filterXRefComments(ctx *context.Context, issue *issues_model.Issue) error { // GetIssueAttachments returns attachments for the issue func GetIssueAttachments(ctx *context.Context) { issue := GetActionIssue(ctx) + if ctx.Written() { + return + } attachments := make([]*api.Attachment, len(issue.Attachments)) for i := 0; i < len(issue.Attachments); i++ { - attachments[i] = convert.ToAttachment(issue.Attachments[i]) + attachments[i] = convert.ToAttachment(ctx.Repo.Repository, issue.Attachments[i]) } ctx.JSON(http.StatusOK, attachments) } @@ -3308,12 +3314,12 @@ func GetCommentAttachments(ctx *context.Context) { return } for i := 0; i < len(comment.Attachments); i++ { - attachments = append(attachments, convert.ToAttachment(comment.Attachments[i])) + attachments = append(attachments, convert.ToAttachment(ctx.Repo.Repository, comment.Attachments[i])) } ctx.JSON(http.StatusOK, attachments) } -func updateAttachments(ctx *context.Context, item interface{}, files []string) error { +func updateAttachments(ctx *context.Context, item any, files []string) error { var attachments []*repo_model.Attachment switch content := item.(type) { case *issues_model.Issue: @@ -3357,7 +3363,7 @@ func updateAttachments(ctx *context.Context, item interface{}, files []string) e } func attachmentsHTML(ctx *context.Context, attachments []*repo_model.Attachment, content string) string { - attachHTML, err := ctx.RenderToString(tplAttachment, map[string]interface{}{ + attachHTML, err := ctx.RenderToString(tplAttachment, map[string]any{ "ctxData": ctx.Data, "Attachments": attachments, "Content": content, diff --git a/routers/web/repo/issue_content_history.go b/routers/web/repo/issue_content_history.go index 7e5295e757b..3dd7725c215 100644 --- a/routers/web/repo/issue_content_history.go +++ b/routers/web/repo/issue_content_history.go @@ -24,13 +24,13 @@ import ( // GetContentHistoryOverview get overview func GetContentHistoryOverview(ctx *context.Context) { issue := GetActionIssue(ctx) - if issue == nil { + if ctx.Written() { return } editedHistoryCountMap, _ := issues_model.QueryIssueContentHistoryEditedCountMap(ctx, issue.ID) - ctx.JSON(http.StatusOK, map[string]interface{}{ - "i18n": map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ + "i18n": map[string]any{ "textEdited": ctx.Tr("repo.issues.content_history.edited"), "textDeleteFromHistory": ctx.Tr("repo.issues.content_history.delete_from_history"), "textDeleteFromHistoryConfirm": ctx.Tr("repo.issues.content_history.delete_from_history_confirm"), @@ -43,17 +43,17 @@ func GetContentHistoryOverview(ctx *context.Context) { // GetContentHistoryList get list func GetContentHistoryList(ctx *context.Context) { issue := GetActionIssue(ctx) - commentID := ctx.FormInt64("comment_id") - if issue == nil { + if ctx.Written() { return } + commentID := ctx.FormInt64("comment_id") items, _ := issues_model.FetchIssueContentHistoryList(ctx, issue.ID, commentID) // render history list to HTML for frontend dropdown items: (name, value) // name is HTML of "avatar + userName + userAction + timeSince" // value is historyId - var results []map[string]interface{} + var results []map[string]any for _, item := range items { var actionText string if item.IsDeleted { @@ -76,13 +76,13 @@ func GetContentHistoryList(ctx *context.Context) { avatarHTML := string(templates.AvatarHTML(src, 28, class, username)) timeSinceText := string(timeutil.TimeSinceUnix(item.EditedUnix, ctx.Locale)) - results = append(results, map[string]interface{}{ + results = append(results, map[string]any{ "name": avatarHTML + "" + name + " " + actionText + " " + timeSinceText, "value": item.HistoryID, }) } - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "results": results, }) } @@ -113,14 +113,14 @@ func canSoftDeleteContentHistory(ctx *context.Context, issue *issues_model.Issue // GetContentHistoryDetail get detail func GetContentHistoryDetail(ctx *context.Context) { issue := GetActionIssue(ctx) - if issue == nil { + if ctx.Written() { return } historyID := ctx.FormInt64("history_id") history, prevHistory, err := issues_model.GetIssueContentHistoryAndPrev(ctx, historyID) if err != nil { - ctx.JSON(http.StatusNotFound, map[string]interface{}{ + ctx.JSON(http.StatusNotFound, map[string]any{ "message": "Can not find the content history", }) return @@ -168,7 +168,7 @@ func GetContentHistoryDetail(ctx *context.Context) { } diffHTMLBuf.WriteString("") - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "canSoftDelete": canSoftDeleteContentHistory(ctx, issue, comment, history), "historyId": historyID, "prevHistoryId": prevHistoryID, @@ -179,7 +179,7 @@ func GetContentHistoryDetail(ctx *context.Context) { // SoftDeleteContentHistory soft delete func SoftDeleteContentHistory(ctx *context.Context) { issue := GetActionIssue(ctx) - if issue == nil { + if ctx.Written() { return } @@ -202,7 +202,7 @@ func SoftDeleteContentHistory(ctx *context.Context) { canSoftDelete := canSoftDeleteContentHistory(ctx, issue, comment, history) if !canSoftDelete { - ctx.JSON(http.StatusForbidden, map[string]interface{}{ + ctx.JSON(http.StatusForbidden, map[string]any{ "message": "Can not delete the content history", }) return @@ -210,7 +210,7 @@ func SoftDeleteContentHistory(ctx *context.Context) { err = issues_model.SoftDeleteIssueContentHistory(ctx, historyID) log.Debug("soft delete issue content history. issue=%d, comment=%d, history=%d", issue.ID, commentID, historyID) - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "ok": err == nil, }) } diff --git a/routers/web/repo/issue_label.go b/routers/web/repo/issue_label.go index 002acbf1d3c..af5db83bd5d 100644 --- a/routers/web/repo/issue_label.go +++ b/routers/web/repo/issue_label.go @@ -157,7 +157,7 @@ func DeleteLabel(ctx *context.Context) { ctx.Flash.Success(ctx.Tr("repo.issues.label_deletion_success")) } - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "redirect": ctx.Repo.RepoLink + "/labels", }) } @@ -226,7 +226,7 @@ func UpdateIssueLabel(ctx *context.Context) { return } - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "ok": true, }) } diff --git a/routers/web/repo/issue_pin.go b/routers/web/repo/issue_pin.go index 7c1a306e6ce..871ca3b333a 100644 --- a/routers/web/repo/issue_pin.go +++ b/routers/web/repo/issue_pin.go @@ -15,6 +15,9 @@ import ( // IssuePinOrUnpin pin or unpin a Issue func IssuePinOrUnpin(ctx *context.Context) { issue := GetActionIssue(ctx) + if ctx.Written() { + return + } // If we don't do this, it will crash when trying to add the pin event to the comment history err := issue.LoadRepo(ctx) diff --git a/routers/web/repo/milestone.go b/routers/web/repo/milestone.go index 4b33fbcb16b..38ef6939676 100644 --- a/routers/web/repo/milestone.go +++ b/routers/web/repo/milestone.go @@ -256,7 +256,7 @@ func DeleteMilestone(ctx *context.Context) { ctx.Flash.Success(ctx.Tr("repo.milestones.deletion_success")) } - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "redirect": ctx.Repo.RepoLink + "/milestones", }) } diff --git a/routers/web/repo/projects.go b/routers/web/repo/projects.go index 6da9edfd0b8..066cdbc5fd8 100644 --- a/routers/web/repo/projects.go +++ b/routers/web/repo/projects.go @@ -203,7 +203,7 @@ func DeleteProject(ctx *context.Context) { ctx.Flash.Success(ctx.Tr("repo.projects.deletion_success")) } - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "redirect": ctx.Repo.RepoLink + "/projects", }) } @@ -385,9 +385,11 @@ func UpdateIssueProject(ctx *context.Context) { projectID := ctx.FormInt64("id") for _, issue := range issues { - oldProjectID := issue.Project.ID - if oldProjectID == projectID { - continue + if issue.Project != nil { + oldProjectID := issue.Project.ID + if oldProjectID == projectID { + continue + } } if err := issues_model.ChangeProjectAssign(issue, ctx.Doer, projectID); err != nil { @@ -396,7 +398,7 @@ func UpdateIssueProject(ctx *context.Context) { } } - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "ok": true, }) } @@ -451,7 +453,7 @@ func DeleteProjectBoard(ctx *context.Context) { return } - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "ok": true, }) } @@ -486,7 +488,7 @@ func AddBoardToProjectPost(ctx *context.Context) { return } - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "ok": true, }) } @@ -560,7 +562,7 @@ func EditProjectBoard(ctx *context.Context) { return } - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "ok": true, }) } @@ -577,7 +579,7 @@ func SetDefaultProjectBoard(ctx *context.Context) { return } - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "ok": true, }) } @@ -594,7 +596,7 @@ func UnSetDefaultProjectBoard(ctx *context.Context) { return } - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "ok": true, }) } @@ -698,7 +700,7 @@ func MoveIssues(ctx *context.Context) { return } - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "ok": true, }) } diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go index 950979a6ed4..1b68ef352a4 100644 --- a/routers/web/repo/pull.go +++ b/routers/web/repo/pull.go @@ -356,12 +356,46 @@ func setMergeTarget(ctx *context.Context, pull *issues_model.PullRequest) { ctx.Data["BaseBranchLink"] = pull.GetBaseBranchLink() } -// PrepareMergedViewPullInfo show meta information for a merged pull request view page -func PrepareMergedViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *git.CompareInfo { +// GetPullDiffStats get Pull Requests diff stats +func GetPullDiffStats(ctx *context.Context) { + issue := checkPullInfo(ctx) pull := issue.PullRequest - setMergeTarget(ctx, pull) - ctx.Data["HasMerged"] = true + mergeBaseCommitID := GetMergedBaseCommitID(ctx, issue) + + if ctx.Written() { + return + } else if mergeBaseCommitID == "" { + ctx.NotFound("PullFiles", nil) + return + } + + headCommitID, err := ctx.Repo.GitRepo.GetRefCommitID(pull.GetGitRefName()) + if err != nil { + ctx.ServerError("GetRefCommitID", err) + return + } + + diffOptions := &gitdiff.DiffOptions{ + BeforeCommitID: mergeBaseCommitID, + AfterCommitID: headCommitID, + MaxLines: setting.Git.MaxGitDiffLines, + MaxLineCharacters: setting.Git.MaxGitDiffLineCharacters, + MaxFiles: setting.Git.MaxGitDiffFiles, + WhitespaceBehavior: gitdiff.GetWhitespaceFlag(ctx.Data["WhitespaceBehavior"].(string)), + } + + diff, err := gitdiff.GetPullDiffStats(ctx.Repo.GitRepo, diffOptions) + if err != nil { + ctx.ServerError("GetPullDiffStats", err) + return + } + + ctx.Data["Diff"] = diff +} + +func GetMergedBaseCommitID(ctx *context.Context, issue *issues_model.Issue) string { + pull := issue.PullRequest var baseCommit string // Some migrated PR won't have any Base SHA and lose history, try to get one @@ -401,6 +435,18 @@ func PrepareMergedViewPullInfo(ctx *context.Context, issue *issues_model.Issue) baseCommit = pull.MergeBase } + return baseCommit +} + +// PrepareMergedViewPullInfo show meta information for a merged pull request view page +func PrepareMergedViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *git.CompareInfo { + pull := issue.PullRequest + + setMergeTarget(ctx, pull) + ctx.Data["HasMerged"] = true + + baseCommit := GetMergedBaseCommitID(ctx, issue) + compareInfo, err := ctx.Repo.GitRepo.GetCompareInfo(ctx.Repo.Repository.RepoPath(), baseCommit, pull.GetGitRefName(), false, false) if err != nil { @@ -677,6 +723,9 @@ func ViewPullCommits(ctx *context.Context) { ctx.Data["Commits"] = commits ctx.Data["CommitCount"] = len(commits) + ctx.Data["HasIssuesOrPullsWritePermission"] = ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) + ctx.Data["IsIssuePoster"] = ctx.IsSigned && issue.IsPoster(ctx.Doer.ID) + getBranchData(ctx, issue) ctx.HTML(http.StatusOK, tplPullCommits) } @@ -756,7 +805,7 @@ func ViewPullFiles(ctx *context.Context) { return } - ctx.PageData["prReview"] = map[string]interface{}{ + ctx.PageData["prReview"] = map[string]any{ "numberOfFiles": diff.NumFiles, "numberOfViewedFiles": diff.NumViewedFiles, } @@ -891,7 +940,7 @@ func UpdatePullRequest(ctx *context.Context) { if err = pull_service.Update(ctx, issue.PullRequest, ctx.Doer, message, rebase); err != nil { if models.IsErrMergeConflicts(err) { conflictError := err.(models.ErrMergeConflicts) - flashError, err := ctx.RenderToString(tplAlertDetails, map[string]interface{}{ + flashError, err := ctx.RenderToString(tplAlertDetails, map[string]any{ "Message": ctx.Tr("repo.pulls.merge_conflict"), "Summary": ctx.Tr("repo.pulls.merge_conflict_summary"), "Details": utils.SanitizeFlashErrorString(conflictError.StdErr) + "
" + utils.SanitizeFlashErrorString(conflictError.StdOut), @@ -905,7 +954,7 @@ func UpdatePullRequest(ctx *context.Context) { return } else if models.IsErrRebaseConflicts(err) { conflictError := err.(models.ErrRebaseConflicts) - flashError, err := ctx.RenderToString(tplAlertDetails, map[string]interface{}{ + flashError, err := ctx.RenderToString(tplAlertDetails, map[string]any{ "Message": ctx.Tr("repo.pulls.rebase_conflict", utils.SanitizeFlashErrorString(conflictError.CommitSHA)), "Summary": ctx.Tr("repo.pulls.rebase_conflict_summary"), "Details": utils.SanitizeFlashErrorString(conflictError.StdErr) + "
" + utils.SanitizeFlashErrorString(conflictError.StdOut), @@ -1040,7 +1089,7 @@ func MergePullRequest(ctx *context.Context) { ctx.Redirect(issue.Link()) } else if models.IsErrMergeConflicts(err) { conflictError := err.(models.ErrMergeConflicts) - flashError, err := ctx.RenderToString(tplAlertDetails, map[string]interface{}{ + flashError, err := ctx.RenderToString(tplAlertDetails, map[string]any{ "Message": ctx.Tr("repo.editor.merge_conflict"), "Summary": ctx.Tr("repo.editor.merge_conflict_summary"), "Details": utils.SanitizeFlashErrorString(conflictError.StdErr) + "
" + utils.SanitizeFlashErrorString(conflictError.StdOut), @@ -1053,7 +1102,7 @@ func MergePullRequest(ctx *context.Context) { ctx.Redirect(issue.Link()) } else if models.IsErrRebaseConflicts(err) { conflictError := err.(models.ErrRebaseConflicts) - flashError, err := ctx.RenderToString(tplAlertDetails, map[string]interface{}{ + flashError, err := ctx.RenderToString(tplAlertDetails, map[string]any{ "Message": ctx.Tr("repo.pulls.rebase_conflict", utils.SanitizeFlashErrorString(conflictError.CommitSHA)), "Summary": ctx.Tr("repo.pulls.rebase_conflict_summary"), "Details": utils.SanitizeFlashErrorString(conflictError.StdErr) + "
" + utils.SanitizeFlashErrorString(conflictError.StdOut), @@ -1083,7 +1132,7 @@ func MergePullRequest(ctx *context.Context) { if len(message) == 0 { ctx.Flash.Error(ctx.Tr("repo.pulls.push_rejected_no_message")) } else { - flashError, err := ctx.RenderToString(tplAlertDetails, map[string]interface{}{ + flashError, err := ctx.RenderToString(tplAlertDetails, map[string]any{ "Message": ctx.Tr("repo.pulls.push_rejected"), "Summary": ctx.Tr("repo.pulls.push_rejected_summary"), "Details": utils.SanitizeFlashErrorString(pushrejErr.Message), @@ -1256,7 +1305,7 @@ func CompareAndPullRequestPost(ctx *context.Context) { ctx.JSONError(ctx.Tr("repo.pulls.push_rejected_no_message")) return } - flashError, err := ctx.RenderToString(tplAlertDetails, map[string]interface{}{ + flashError, err := ctx.RenderToString(tplAlertDetails, map[string]any{ "Message": ctx.Tr("repo.pulls.push_rejected"), "Summary": ctx.Tr("repo.pulls.push_rejected_summary"), "Details": utils.SanitizeFlashErrorString(pushrejErr.Message), @@ -1361,7 +1410,7 @@ func CleanUpPullRequest(ctx *context.Context) { } defer func() { - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "redirect": issue.Link(), }) }() @@ -1445,10 +1494,10 @@ func DownloadPullDiffOrPatch(ctx *context.Context, patch bool) { // UpdatePullRequestTarget change pull request's target branch func UpdatePullRequestTarget(ctx *context.Context) { issue := GetActionIssue(ctx) - pr := issue.PullRequest if ctx.Written() { return } + pr := issue.PullRequest if !issue.IsPull { ctx.Error(http.StatusNotFound) return @@ -1473,7 +1522,7 @@ func UpdatePullRequestTarget(ctx *context.Context) { errorMessage := ctx.Tr("repo.pulls.has_pull_request", html.EscapeString(ctx.Repo.RepoLink+"/pulls/"+strconv.FormatInt(err.IssueID, 10)), html.EscapeString(RepoRelPath), err.IssueID) // FIXME: Creates url inside locale string ctx.Flash.Error(errorMessage) - ctx.JSON(http.StatusConflict, map[string]interface{}{ + ctx.JSON(http.StatusConflict, map[string]any{ "error": err.Error(), "user_error": errorMessage, }) @@ -1481,7 +1530,7 @@ func UpdatePullRequestTarget(ctx *context.Context) { errorMessage := ctx.Tr("repo.pulls.is_closed") ctx.Flash.Error(errorMessage) - ctx.JSON(http.StatusConflict, map[string]interface{}{ + ctx.JSON(http.StatusConflict, map[string]any{ "error": err.Error(), "user_error": errorMessage, }) @@ -1489,7 +1538,7 @@ func UpdatePullRequestTarget(ctx *context.Context) { errorMessage := ctx.Tr("repo.pulls.has_merged") ctx.Flash.Error(errorMessage) - ctx.JSON(http.StatusConflict, map[string]interface{}{ + ctx.JSON(http.StatusConflict, map[string]any{ "error": err.Error(), "user_error": errorMessage, }) @@ -1497,7 +1546,7 @@ func UpdatePullRequestTarget(ctx *context.Context) { errorMessage := ctx.Tr("repo.pulls.nothing_to_compare") ctx.Flash.Error(errorMessage) - ctx.JSON(http.StatusBadRequest, map[string]interface{}{ + ctx.JSON(http.StatusBadRequest, map[string]any{ "error": err.Error(), "user_error": errorMessage, }) @@ -1508,7 +1557,7 @@ func UpdatePullRequestTarget(ctx *context.Context) { } notification.NotifyPullRequestChangeTargetBranch(ctx, ctx.Doer, pr, targetBranch) - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "base_branch": pr.BaseBranch, }) } @@ -1536,7 +1585,7 @@ func SetAllowEdits(ctx *context.Context) { return } - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "allow_maintainer_edit": pr.AllowMaintainerEdit, }) } diff --git a/routers/web/repo/pull_review.go b/routers/web/repo/pull_review.go index 5aa58113670..f7c962d1ae8 100644 --- a/routers/web/repo/pull_review.go +++ b/routers/web/repo/pull_review.go @@ -28,6 +28,9 @@ const ( // RenderNewCodeCommentForm will render the form for creating a new review comment func RenderNewCodeCommentForm(ctx *context.Context) { issue := GetActionIssue(ctx) + if ctx.Written() { + return + } if !issue.IsPull { return } @@ -52,10 +55,10 @@ func RenderNewCodeCommentForm(ctx *context.Context) { func CreateCodeComment(ctx *context.Context) { form := web.GetForm(ctx).(*forms.CodeCommentForm) issue := GetActionIssue(ctx) - if !issue.IsPull { + if ctx.Written() { return } - if ctx.Written() { + if !issue.IsPull { return } @@ -153,7 +156,7 @@ func UpdateResolveConversation(ctx *context.Context) { renderConversation(ctx, comment) return } - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "ok": true, }) } @@ -185,10 +188,10 @@ func renderConversation(ctx *context.Context, comment *issues_model.Comment) { func SubmitReview(ctx *context.Context) { form := web.GetForm(ctx).(*forms.SubmitReviewForm) issue := GetActionIssue(ctx) - if !issue.IsPull { + if ctx.Written() { return } - if ctx.Written() { + if !issue.IsPull { return } if ctx.HasError() { diff --git a/routers/web/repo/release.go b/routers/web/repo/release.go index 5fddddb3440..c9f12a0f185 100644 --- a/routers/web/repo/release.go +++ b/routers/web/repo/release.go @@ -203,6 +203,7 @@ func releasesOrTags(ctx *context.Context, isTagList bool) { ctx.Data["Page"] = pager if isTagList { + ctx.Data["PageIsViewCode"] = !ctx.Repo.Repository.UnitEnabled(ctx, unit.TypeReleases) ctx.HTML(http.StatusOK, tplTagsList) } else { ctx.HTML(http.StatusOK, tplReleasesList) @@ -607,13 +608,13 @@ func deleteReleaseOrTag(ctx *context.Context, isDelTag bool) { } if isDelTag { - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "redirect": ctx.Repo.RepoLink + "/tags", }) return } - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "redirect": ctx.Repo.RepoLink + "/releases", }) } diff --git a/routers/web/repo/repo.go b/routers/web/repo/repo.go index 1af51c3ed8b..945926e910d 100644 --- a/routers/web/repo/repo.go +++ b/routers/web/repo/repo.go @@ -181,7 +181,7 @@ func Create(ctx *context.Context) { ctx.HTML(http.StatusOK, tplCreate) } -func handleCreateError(ctx *context.Context, owner *user_model.User, err error, name string, tpl base.TplName, form interface{}) { +func handleCreateError(ctx *context.Context, owner *user_model.User, err error, name string, tpl base.TplName, form any) { switch { case repo_model.IsErrReachLimitOfRepo(err): maxCreationLimit := owner.MaxCreationLimit() @@ -483,7 +483,7 @@ func InitiateDownload(ctx *context.Context) { completed = true } - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "complete": completed, }) } @@ -580,13 +580,15 @@ func SearchRepo(ctx *context.Context) { // collect the latest commit of each repo // at most there are dozens of repos (limited by MaxResponseItems), so it's not a big problem at the moment - repoIDsToLatestCommitSHAs := make(map[int64]string, len(repos)) + repoBranchNames := make(map[int64]string, len(repos)) for _, repo := range repos { - commitID, err := repo_service.GetBranchCommitID(ctx, repo, repo.DefaultBranch) - if err != nil { - continue - } - repoIDsToLatestCommitSHAs[repo.ID] = commitID + repoBranchNames[repo.ID] = repo.DefaultBranch + } + + repoIDsToLatestCommitSHAs, err := git_model.FindBranchesByRepoAndBranchName(ctx, repoBranchNames) + if err != nil { + log.Error("FindBranchesByRepoAndBranchName: %v", err) + return } // call the database O(1) times to get the commit statuses for all repos diff --git a/routers/web/repo/setting/avatar.go b/routers/web/repo/setting/avatar.go new file mode 100644 index 00000000000..ec673ca2886 --- /dev/null +++ b/routers/web/repo/setting/avatar.go @@ -0,0 +1,76 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package setting + +import ( + "errors" + "fmt" + "io" + + "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/typesniffer" + "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/services/forms" + repo_service "code.gitea.io/gitea/services/repository" +) + +// UpdateAvatarSetting update repo's avatar +func UpdateAvatarSetting(ctx *context.Context, form forms.AvatarForm) error { + ctxRepo := ctx.Repo.Repository + + if form.Avatar == nil { + // No avatar is uploaded and we not removing it here. + // No random avatar generated here. + // Just exit, no action. + if ctxRepo.CustomAvatarRelativePath() == "" { + log.Trace("No avatar was uploaded for repo: %d. Default icon will appear instead.", ctxRepo.ID) + } + return nil + } + + r, err := form.Avatar.Open() + if err != nil { + return fmt.Errorf("Avatar.Open: %w", err) + } + defer r.Close() + + if form.Avatar.Size > setting.Avatar.MaxFileSize { + return errors.New(ctx.Tr("settings.uploaded_avatar_is_too_big")) + } + + data, err := io.ReadAll(r) + if err != nil { + return fmt.Errorf("io.ReadAll: %w", err) + } + st := typesniffer.DetectContentType(data) + if !(st.IsImage() && !st.IsSvgImage()) { + return errors.New(ctx.Tr("settings.uploaded_avatar_not_a_image")) + } + if err = repo_service.UploadAvatar(ctx, ctxRepo, data); err != nil { + return fmt.Errorf("UploadAvatar: %w", err) + } + return nil +} + +// SettingsAvatar save new POSTed repository avatar +func SettingsAvatar(ctx *context.Context) { + form := web.GetForm(ctx).(*forms.AvatarForm) + form.Source = forms.AvatarLocal + if err := UpdateAvatarSetting(ctx, *form); err != nil { + ctx.Flash.Error(err.Error()) + } else { + ctx.Flash.Success(ctx.Tr("repo.settings.update_avatar_success")) + } + ctx.Redirect(ctx.Repo.RepoLink + "/settings") +} + +// SettingsDeleteAvatar delete repository avatar +func SettingsDeleteAvatar(ctx *context.Context) { + if err := repo_service.DeleteAvatar(ctx, ctx.Repo.Repository); err != nil { + ctx.Flash.Error(fmt.Sprintf("DeleteAvatar: %v", err)) + } + ctx.Redirect(ctx.Repo.RepoLink + "/settings") +} diff --git a/routers/web/repo/setting/collaboration.go b/routers/web/repo/setting/collaboration.go new file mode 100644 index 00000000000..8f2d3068620 --- /dev/null +++ b/routers/web/repo/setting/collaboration.go @@ -0,0 +1,210 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package setting + +import ( + "net/http" + "strings" + + "code.gitea.io/gitea/models" + "code.gitea.io/gitea/models/db" + "code.gitea.io/gitea/models/organization" + "code.gitea.io/gitea/models/perm" + repo_model "code.gitea.io/gitea/models/repo" + unit_model "code.gitea.io/gitea/models/unit" + user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/log" + repo_module "code.gitea.io/gitea/modules/repository" + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/routers/utils" + "code.gitea.io/gitea/services/mailer" + org_service "code.gitea.io/gitea/services/org" +) + +// Collaboration render a repository's collaboration page +func Collaboration(ctx *context.Context) { + ctx.Data["Title"] = ctx.Tr("repo.settings.collaboration") + ctx.Data["PageIsSettingsCollaboration"] = true + + users, err := repo_model.GetCollaborators(ctx, ctx.Repo.Repository.ID, db.ListOptions{}) + if err != nil { + ctx.ServerError("GetCollaborators", err) + return + } + ctx.Data["Collaborators"] = users + + teams, err := organization.GetRepoTeams(ctx, ctx.Repo.Repository) + if err != nil { + ctx.ServerError("GetRepoTeams", err) + return + } + ctx.Data["Teams"] = teams + ctx.Data["Repo"] = ctx.Repo.Repository + ctx.Data["OrgID"] = ctx.Repo.Repository.OwnerID + ctx.Data["OrgName"] = ctx.Repo.Repository.OwnerName + ctx.Data["Org"] = ctx.Repo.Repository.Owner + ctx.Data["Units"] = unit_model.Units + + ctx.HTML(http.StatusOK, tplCollaboration) +} + +// CollaborationPost response for actions for a collaboration of a repository +func CollaborationPost(ctx *context.Context) { + name := utils.RemoveUsernameParameterSuffix(strings.ToLower(ctx.FormString("collaborator"))) + if len(name) == 0 || ctx.Repo.Owner.LowerName == name { + ctx.Redirect(setting.AppSubURL + ctx.Req.URL.EscapedPath()) + return + } + + u, err := user_model.GetUserByName(ctx, name) + if err != nil { + if user_model.IsErrUserNotExist(err) { + ctx.Flash.Error(ctx.Tr("form.user_not_exist")) + ctx.Redirect(setting.AppSubURL + ctx.Req.URL.EscapedPath()) + } else { + ctx.ServerError("GetUserByName", err) + } + return + } + + if !u.IsActive { + ctx.Flash.Error(ctx.Tr("repo.settings.add_collaborator_inactive_user")) + ctx.Redirect(setting.AppSubURL + ctx.Req.URL.EscapedPath()) + return + } + + // Organization is not allowed to be added as a collaborator. + if u.IsOrganization() { + ctx.Flash.Error(ctx.Tr("repo.settings.org_not_allowed_to_be_collaborator")) + ctx.Redirect(setting.AppSubURL + ctx.Req.URL.EscapedPath()) + return + } + + if got, err := repo_model.IsCollaborator(ctx, ctx.Repo.Repository.ID, u.ID); err == nil && got { + ctx.Flash.Error(ctx.Tr("repo.settings.add_collaborator_duplicate")) + ctx.Redirect(ctx.Repo.RepoLink + "/settings/collaboration") + return + } + + // find the owner team of the organization the repo belongs too and + // check if the user we're trying to add is an owner. + if ctx.Repo.Repository.Owner.IsOrganization() { + if isOwner, err := organization.IsOrganizationOwner(ctx, ctx.Repo.Repository.Owner.ID, u.ID); err != nil { + ctx.ServerError("IsOrganizationOwner", err) + return + } else if isOwner { + ctx.Flash.Error(ctx.Tr("repo.settings.add_collaborator_owner")) + ctx.Redirect(setting.AppSubURL + ctx.Req.URL.EscapedPath()) + return + } + } + + if err = repo_module.AddCollaborator(ctx, ctx.Repo.Repository, u); err != nil { + ctx.ServerError("AddCollaborator", err) + return + } + + if setting.Service.EnableNotifyMail { + mailer.SendCollaboratorMail(u, ctx.Doer, ctx.Repo.Repository) + } + + ctx.Flash.Success(ctx.Tr("repo.settings.add_collaborator_success")) + ctx.Redirect(setting.AppSubURL + ctx.Req.URL.EscapedPath()) +} + +// ChangeCollaborationAccessMode response for changing access of a collaboration +func ChangeCollaborationAccessMode(ctx *context.Context) { + if err := repo_model.ChangeCollaborationAccessMode( + ctx, + ctx.Repo.Repository, + ctx.FormInt64("uid"), + perm.AccessMode(ctx.FormInt("mode"))); err != nil { + log.Error("ChangeCollaborationAccessMode: %v", err) + } +} + +// DeleteCollaboration delete a collaboration for a repository +func DeleteCollaboration(ctx *context.Context) { + if err := models.DeleteCollaboration(ctx.Repo.Repository, ctx.FormInt64("id")); err != nil { + ctx.Flash.Error("DeleteCollaboration: " + err.Error()) + } else { + ctx.Flash.Success(ctx.Tr("repo.settings.remove_collaborator_success")) + } + + ctx.JSON(http.StatusOK, map[string]any{ + "redirect": ctx.Repo.RepoLink + "/settings/collaboration", + }) +} + +// AddTeamPost response for adding a team to a repository +func AddTeamPost(ctx *context.Context) { + if !ctx.Repo.Owner.RepoAdminChangeTeamAccess && !ctx.Repo.IsOwner() { + ctx.Flash.Error(ctx.Tr("repo.settings.change_team_access_not_allowed")) + ctx.Redirect(ctx.Repo.RepoLink + "/settings/collaboration") + return + } + + name := utils.RemoveUsernameParameterSuffix(strings.ToLower(ctx.FormString("team"))) + if len(name) == 0 { + ctx.Redirect(ctx.Repo.RepoLink + "/settings/collaboration") + return + } + + team, err := organization.OrgFromUser(ctx.Repo.Owner).GetTeam(ctx, name) + if err != nil { + if organization.IsErrTeamNotExist(err) { + ctx.Flash.Error(ctx.Tr("form.team_not_exist")) + ctx.Redirect(ctx.Repo.RepoLink + "/settings/collaboration") + } else { + ctx.ServerError("GetTeam", err) + } + return + } + + if team.OrgID != ctx.Repo.Repository.OwnerID { + ctx.Flash.Error(ctx.Tr("repo.settings.team_not_in_organization")) + ctx.Redirect(ctx.Repo.RepoLink + "/settings/collaboration") + return + } + + if organization.HasTeamRepo(ctx, ctx.Repo.Repository.OwnerID, team.ID, ctx.Repo.Repository.ID) { + ctx.Flash.Error(ctx.Tr("repo.settings.add_team_duplicate")) + ctx.Redirect(ctx.Repo.RepoLink + "/settings/collaboration") + return + } + + if err = org_service.TeamAddRepository(team, ctx.Repo.Repository); err != nil { + ctx.ServerError("TeamAddRepository", err) + return + } + + ctx.Flash.Success(ctx.Tr("repo.settings.add_team_success")) + ctx.Redirect(ctx.Repo.RepoLink + "/settings/collaboration") +} + +// DeleteTeam response for deleting a team from a repository +func DeleteTeam(ctx *context.Context) { + if !ctx.Repo.Owner.RepoAdminChangeTeamAccess && !ctx.Repo.IsOwner() { + ctx.Flash.Error(ctx.Tr("repo.settings.change_team_access_not_allowed")) + ctx.Redirect(ctx.Repo.RepoLink + "/settings/collaboration") + return + } + + team, err := organization.GetTeamByID(ctx, ctx.FormInt64("id")) + if err != nil { + ctx.ServerError("GetTeamByID", err) + return + } + + if err = models.RemoveRepository(team, ctx.Repo.Repository.ID); err != nil { + ctx.ServerError("team.RemoveRepositorys", err) + return + } + + ctx.Flash.Success(ctx.Tr("repo.settings.remove_team_success")) + ctx.JSON(http.StatusOK, map[string]any{ + "redirect": ctx.Repo.RepoLink + "/settings/collaboration", + }) +} diff --git a/routers/web/repo/setting/deploy_key.go b/routers/web/repo/setting/deploy_key.go new file mode 100644 index 00000000000..d08c51f5e5c --- /dev/null +++ b/routers/web/repo/setting/deploy_key.go @@ -0,0 +1,111 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package setting + +import ( + "net/http" + + asymkey_model "code.gitea.io/gitea/models/asymkey" + "code.gitea.io/gitea/models/db" + "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/web" + asymkey_service "code.gitea.io/gitea/services/asymkey" + "code.gitea.io/gitea/services/forms" +) + +// DeployKeys render the deploy keys list of a repository page +func DeployKeys(ctx *context.Context) { + ctx.Data["Title"] = ctx.Tr("repo.settings.deploy_keys") + " / " + ctx.Tr("secrets.secrets") + ctx.Data["PageIsSettingsKeys"] = true + ctx.Data["DisableSSH"] = setting.SSH.Disabled + + keys, err := asymkey_model.ListDeployKeys(ctx, &asymkey_model.ListDeployKeysOptions{RepoID: ctx.Repo.Repository.ID}) + if err != nil { + ctx.ServerError("ListDeployKeys", err) + return + } + ctx.Data["Deploykeys"] = keys + + ctx.HTML(http.StatusOK, tplDeployKeys) +} + +// DeployKeysPost response for adding a deploy key of a repository +func DeployKeysPost(ctx *context.Context) { + form := web.GetForm(ctx).(*forms.AddKeyForm) + ctx.Data["Title"] = ctx.Tr("repo.settings.deploy_keys") + ctx.Data["PageIsSettingsKeys"] = true + ctx.Data["DisableSSH"] = setting.SSH.Disabled + + keys, err := asymkey_model.ListDeployKeys(ctx, &asymkey_model.ListDeployKeysOptions{RepoID: ctx.Repo.Repository.ID}) + if err != nil { + ctx.ServerError("ListDeployKeys", err) + return + } + ctx.Data["Deploykeys"] = keys + + if ctx.HasError() { + ctx.HTML(http.StatusOK, tplDeployKeys) + return + } + + content, err := asymkey_model.CheckPublicKeyString(form.Content) + if err != nil { + if db.IsErrSSHDisabled(err) { + ctx.Flash.Info(ctx.Tr("settings.ssh_disabled")) + } else if asymkey_model.IsErrKeyUnableVerify(err) { + ctx.Flash.Info(ctx.Tr("form.unable_verify_ssh_key")) + } else if err == asymkey_model.ErrKeyIsPrivate { + ctx.Data["HasError"] = true + ctx.Data["Err_Content"] = true + ctx.Flash.Error(ctx.Tr("form.must_use_public_key")) + } else { + ctx.Data["HasError"] = true + ctx.Data["Err_Content"] = true + ctx.Flash.Error(ctx.Tr("form.invalid_ssh_key", err.Error())) + } + ctx.Redirect(ctx.Repo.RepoLink + "/settings/keys") + return + } + + key, err := asymkey_model.AddDeployKey(ctx.Repo.Repository.ID, form.Title, content, !form.IsWritable) + if err != nil { + ctx.Data["HasError"] = true + switch { + case asymkey_model.IsErrDeployKeyAlreadyExist(err): + ctx.Data["Err_Content"] = true + ctx.RenderWithErr(ctx.Tr("repo.settings.key_been_used"), tplDeployKeys, &form) + case asymkey_model.IsErrKeyAlreadyExist(err): + ctx.Data["Err_Content"] = true + ctx.RenderWithErr(ctx.Tr("settings.ssh_key_been_used"), tplDeployKeys, &form) + case asymkey_model.IsErrKeyNameAlreadyUsed(err): + ctx.Data["Err_Title"] = true + ctx.RenderWithErr(ctx.Tr("repo.settings.key_name_used"), tplDeployKeys, &form) + case asymkey_model.IsErrDeployKeyNameAlreadyUsed(err): + ctx.Data["Err_Title"] = true + ctx.RenderWithErr(ctx.Tr("repo.settings.key_name_used"), tplDeployKeys, &form) + default: + ctx.ServerError("AddDeployKey", err) + } + return + } + + log.Trace("Deploy key added: %d", ctx.Repo.Repository.ID) + ctx.Flash.Success(ctx.Tr("repo.settings.add_key_success", key.Name)) + ctx.Redirect(ctx.Repo.RepoLink + "/settings/keys") +} + +// DeleteDeployKey response for deleting a deploy key +func DeleteDeployKey(ctx *context.Context) { + if err := asymkey_service.DeleteDeployKey(ctx.Doer, ctx.FormInt64("id")); err != nil { + ctx.Flash.Error("DeleteDeployKey: " + err.Error()) + } else { + ctx.Flash.Success(ctx.Tr("repo.settings.deploy_key_deletion_success")) + } + + ctx.JSON(http.StatusOK, map[string]any{ + "redirect": ctx.Repo.RepoLink + "/settings/keys", + }) +} diff --git a/routers/web/repo/setting/git_hooks.go b/routers/web/repo/setting/git_hooks.go new file mode 100644 index 00000000000..551327d44b4 --- /dev/null +++ b/routers/web/repo/setting/git_hooks.go @@ -0,0 +1,65 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package setting + +import ( + "net/http" + + "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/git" +) + +// GitHooks hooks of a repository +func GitHooks(ctx *context.Context) { + ctx.Data["Title"] = ctx.Tr("repo.settings.githooks") + ctx.Data["PageIsSettingsGitHooks"] = true + + hooks, err := ctx.Repo.GitRepo.Hooks() + if err != nil { + ctx.ServerError("Hooks", err) + return + } + ctx.Data["Hooks"] = hooks + + ctx.HTML(http.StatusOK, tplGithooks) +} + +// GitHooksEdit render for editing a hook of repository page +func GitHooksEdit(ctx *context.Context) { + ctx.Data["Title"] = ctx.Tr("repo.settings.githooks") + ctx.Data["PageIsSettingsGitHooks"] = true + + name := ctx.Params(":name") + hook, err := ctx.Repo.GitRepo.GetHook(name) + if err != nil { + if err == git.ErrNotValidHook { + ctx.NotFound("GetHook", err) + } else { + ctx.ServerError("GetHook", err) + } + return + } + ctx.Data["Hook"] = hook + ctx.HTML(http.StatusOK, tplGithookEdit) +} + +// GitHooksEditPost response for editing a git hook of a repository +func GitHooksEditPost(ctx *context.Context) { + name := ctx.Params(":name") + hook, err := ctx.Repo.GitRepo.GetHook(name) + if err != nil { + if err == git.ErrNotValidHook { + ctx.NotFound("GetHook", err) + } else { + ctx.ServerError("GetHook", err) + } + return + } + hook.Content = ctx.FormString("content") + if err = hook.Update(); err != nil { + ctx.ServerError("hook.Update", err) + return + } + ctx.Redirect(ctx.Repo.RepoLink + "/settings/hooks/git") +} diff --git a/routers/web/repo/lfs.go b/routers/web/repo/setting/lfs.go similarity index 99% rename from routers/web/repo/lfs.go rename to routers/web/repo/setting/lfs.go index 9957869c999..d478acdde0d 100644 --- a/routers/web/repo/lfs.go +++ b/routers/web/repo/setting/lfs.go @@ -1,7 +1,7 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package repo +package setting import ( "bytes" diff --git a/routers/web/repo/setting/main_test.go b/routers/web/repo/setting/main_test.go new file mode 100644 index 00000000000..5a6fa562177 --- /dev/null +++ b/routers/web/repo/setting/main_test.go @@ -0,0 +1,17 @@ +// Copyright 2017 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package setting + +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/web/repo/setting_protected_branch.go b/routers/web/repo/setting/protected_branch.go similarity index 98% rename from routers/web/repo/setting_protected_branch.go rename to routers/web/repo/setting/protected_branch.go index ae8a799f068..777d52c8580 100644 --- a/routers/web/repo/setting_protected_branch.go +++ b/routers/web/repo/setting/protected_branch.go @@ -1,7 +1,7 @@ // Copyright 2017 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package repo +package setting import ( "fmt" @@ -307,7 +307,7 @@ func DeleteProtectedBranchRulePost(ctx *context.Context) { ruleID := ctx.ParamsInt64("id") if ruleID <= 0 { ctx.Flash.Error(ctx.Tr("repo.settings.remove_protected_branch_failed", fmt.Sprintf("%d", ruleID))) - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "redirect": fmt.Sprintf("%s/settings/branches", ctx.Repo.RepoLink), }) return @@ -316,7 +316,7 @@ func DeleteProtectedBranchRulePost(ctx *context.Context) { rule, err := git_model.GetProtectedBranchRuleByID(ctx, ctx.Repo.Repository.ID, ruleID) if err != nil { ctx.Flash.Error(ctx.Tr("repo.settings.remove_protected_branch_failed", fmt.Sprintf("%d", ruleID))) - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "redirect": fmt.Sprintf("%s/settings/branches", ctx.Repo.RepoLink), }) return @@ -324,7 +324,7 @@ func DeleteProtectedBranchRulePost(ctx *context.Context) { if rule == nil { ctx.Flash.Error(ctx.Tr("repo.settings.remove_protected_branch_failed", fmt.Sprintf("%d", ruleID))) - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "redirect": fmt.Sprintf("%s/settings/branches", ctx.Repo.RepoLink), }) return @@ -332,14 +332,14 @@ func DeleteProtectedBranchRulePost(ctx *context.Context) { if err := git_model.DeleteProtectedBranch(ctx, ctx.Repo.Repository.ID, ruleID); err != nil { ctx.Flash.Error(ctx.Tr("repo.settings.remove_protected_branch_failed", rule.RuleName)) - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "redirect": fmt.Sprintf("%s/settings/branches", ctx.Repo.RepoLink), }) return } ctx.Flash.Success(ctx.Tr("repo.settings.remove_protected_branch_success", rule.RuleName)) - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "redirect": fmt.Sprintf("%s/settings/branches", ctx.Repo.RepoLink), }) } diff --git a/routers/web/repo/tag.go b/routers/web/repo/setting/protected_tag.go similarity index 97% rename from routers/web/repo/tag.go rename to routers/web/repo/setting/protected_tag.go index 95bc6dfce7f..aafbd19e80e 100644 --- a/routers/web/repo/tag.go +++ b/routers/web/repo/setting/protected_tag.go @@ -1,7 +1,7 @@ // Copyright 2021 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package repo +package setting import ( "fmt" @@ -19,8 +19,12 @@ import ( "code.gitea.io/gitea/services/forms" ) +const ( + tplTags base.TplName = "repo/settings/tags" +) + // Tags render the page to protect tags -func Tags(ctx *context.Context) { +func ProtectedTags(ctx *context.Context) { if setTagsContext(ctx) != nil { return } diff --git a/routers/web/repo/setting.go b/routers/web/repo/setting/setting.go similarity index 70% rename from routers/web/repo/setting.go rename to routers/web/repo/setting/setting.go index 354879de68c..cfc939f3e38 100644 --- a/routers/web/repo/setting.go +++ b/routers/web/repo/setting/setting.go @@ -2,22 +2,18 @@ // Copyright 2018 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package repo +package setting import ( - "errors" "fmt" - "io" "net/http" "strconv" "strings" "time" "code.gitea.io/gitea/models" - asymkey_model "code.gitea.io/gitea/models/asymkey" "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/organization" - "code.gitea.io/gitea/models/perm" repo_model "code.gitea.io/gitea/models/repo" unit_model "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" @@ -32,17 +28,13 @@ import ( 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/typesniffer" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/validation" "code.gitea.io/gitea/modules/web" - "code.gitea.io/gitea/routers/utils" asymkey_service "code.gitea.io/gitea/services/asymkey" "code.gitea.io/gitea/services/forms" - "code.gitea.io/gitea/services/mailer" "code.gitea.io/gitea/services/migrations" mirror_service "code.gitea.io/gitea/services/mirror" - org_service "code.gitea.io/gitea/services/org" repo_service "code.gitea.io/gitea/services/repository" wiki_service "code.gitea.io/gitea/services/wiki" ) @@ -51,7 +43,6 @@ const ( tplSettingsOptions base.TplName = "repo/settings/options" tplCollaboration base.TplName = "repo/settings/collaboration" tplBranches base.TplName = "repo/settings/branches" - tplTags base.TplName = "repo/settings/tags" tplGithooks base.TplName = "repo/settings/githooks" tplGithookEdit base.TplName = "repo/settings/githook_edit" tplDeployKeys base.TplName = "repo/settings/deploy_keys" @@ -912,398 +903,6 @@ func handleSettingRemoteAddrError(ctx *context.Context, err error, form *forms.R ctx.RenderWithErr(ctx.Tr("repo.mirror_address_url_invalid"), tplSettingsOptions, form) } -// Collaboration render a repository's collaboration page -func Collaboration(ctx *context.Context) { - ctx.Data["Title"] = ctx.Tr("repo.settings.collaboration") - ctx.Data["PageIsSettingsCollaboration"] = true - - users, err := repo_model.GetCollaborators(ctx, ctx.Repo.Repository.ID, db.ListOptions{}) - if err != nil { - ctx.ServerError("GetCollaborators", err) - return - } - ctx.Data["Collaborators"] = users - - teams, err := organization.GetRepoTeams(ctx, ctx.Repo.Repository) - if err != nil { - ctx.ServerError("GetRepoTeams", err) - return - } - ctx.Data["Teams"] = teams - ctx.Data["Repo"] = ctx.Repo.Repository - ctx.Data["OrgID"] = ctx.Repo.Repository.OwnerID - ctx.Data["OrgName"] = ctx.Repo.Repository.OwnerName - ctx.Data["Org"] = ctx.Repo.Repository.Owner - ctx.Data["Units"] = unit_model.Units - - ctx.HTML(http.StatusOK, tplCollaboration) -} - -// CollaborationPost response for actions for a collaboration of a repository -func CollaborationPost(ctx *context.Context) { - name := utils.RemoveUsernameParameterSuffix(strings.ToLower(ctx.FormString("collaborator"))) - if len(name) == 0 || ctx.Repo.Owner.LowerName == name { - ctx.Redirect(setting.AppSubURL + ctx.Req.URL.EscapedPath()) - return - } - - u, err := user_model.GetUserByName(ctx, name) - if err != nil { - if user_model.IsErrUserNotExist(err) { - ctx.Flash.Error(ctx.Tr("form.user_not_exist")) - ctx.Redirect(setting.AppSubURL + ctx.Req.URL.EscapedPath()) - } else { - ctx.ServerError("GetUserByName", err) - } - return - } - - if !u.IsActive { - ctx.Flash.Error(ctx.Tr("repo.settings.add_collaborator_inactive_user")) - ctx.Redirect(setting.AppSubURL + ctx.Req.URL.EscapedPath()) - return - } - - // Organization is not allowed to be added as a collaborator. - if u.IsOrganization() { - ctx.Flash.Error(ctx.Tr("repo.settings.org_not_allowed_to_be_collaborator")) - ctx.Redirect(setting.AppSubURL + ctx.Req.URL.EscapedPath()) - return - } - - if got, err := repo_model.IsCollaborator(ctx, ctx.Repo.Repository.ID, u.ID); err == nil && got { - ctx.Flash.Error(ctx.Tr("repo.settings.add_collaborator_duplicate")) - ctx.Redirect(ctx.Repo.RepoLink + "/settings/collaboration") - return - } - - // find the owner team of the organization the repo belongs too and - // check if the user we're trying to add is an owner. - if ctx.Repo.Repository.Owner.IsOrganization() { - if isOwner, err := organization.IsOrganizationOwner(ctx, ctx.Repo.Repository.Owner.ID, u.ID); err != nil { - ctx.ServerError("IsOrganizationOwner", err) - return - } else if isOwner { - ctx.Flash.Error(ctx.Tr("repo.settings.add_collaborator_owner")) - ctx.Redirect(setting.AppSubURL + ctx.Req.URL.EscapedPath()) - return - } - } - - if err = repo_module.AddCollaborator(ctx, ctx.Repo.Repository, u); err != nil { - ctx.ServerError("AddCollaborator", err) - return - } - - if setting.Service.EnableNotifyMail { - mailer.SendCollaboratorMail(u, ctx.Doer, ctx.Repo.Repository) - } - - ctx.Flash.Success(ctx.Tr("repo.settings.add_collaborator_success")) - ctx.Redirect(setting.AppSubURL + ctx.Req.URL.EscapedPath()) -} - -// ChangeCollaborationAccessMode response for changing access of a collaboration -func ChangeCollaborationAccessMode(ctx *context.Context) { - if err := repo_model.ChangeCollaborationAccessMode( - ctx, - ctx.Repo.Repository, - ctx.FormInt64("uid"), - perm.AccessMode(ctx.FormInt("mode"))); err != nil { - log.Error("ChangeCollaborationAccessMode: %v", err) - } -} - -// DeleteCollaboration delete a collaboration for a repository -func DeleteCollaboration(ctx *context.Context) { - if err := models.DeleteCollaboration(ctx.Repo.Repository, ctx.FormInt64("id")); err != nil { - ctx.Flash.Error("DeleteCollaboration: " + err.Error()) - } else { - ctx.Flash.Success(ctx.Tr("repo.settings.remove_collaborator_success")) - } - - ctx.JSON(http.StatusOK, map[string]interface{}{ - "redirect": ctx.Repo.RepoLink + "/settings/collaboration", - }) -} - -// AddTeamPost response for adding a team to a repository -func AddTeamPost(ctx *context.Context) { - if !ctx.Repo.Owner.RepoAdminChangeTeamAccess && !ctx.Repo.IsOwner() { - ctx.Flash.Error(ctx.Tr("repo.settings.change_team_access_not_allowed")) - ctx.Redirect(ctx.Repo.RepoLink + "/settings/collaboration") - return - } - - name := utils.RemoveUsernameParameterSuffix(strings.ToLower(ctx.FormString("team"))) - if len(name) == 0 { - ctx.Redirect(ctx.Repo.RepoLink + "/settings/collaboration") - return - } - - team, err := organization.OrgFromUser(ctx.Repo.Owner).GetTeam(ctx, name) - if err != nil { - if organization.IsErrTeamNotExist(err) { - ctx.Flash.Error(ctx.Tr("form.team_not_exist")) - ctx.Redirect(ctx.Repo.RepoLink + "/settings/collaboration") - } else { - ctx.ServerError("GetTeam", err) - } - return - } - - if team.OrgID != ctx.Repo.Repository.OwnerID { - ctx.Flash.Error(ctx.Tr("repo.settings.team_not_in_organization")) - ctx.Redirect(ctx.Repo.RepoLink + "/settings/collaboration") - return - } - - if organization.HasTeamRepo(ctx, ctx.Repo.Repository.OwnerID, team.ID, ctx.Repo.Repository.ID) { - ctx.Flash.Error(ctx.Tr("repo.settings.add_team_duplicate")) - ctx.Redirect(ctx.Repo.RepoLink + "/settings/collaboration") - return - } - - if err = org_service.TeamAddRepository(team, ctx.Repo.Repository); err != nil { - ctx.ServerError("TeamAddRepository", err) - return - } - - ctx.Flash.Success(ctx.Tr("repo.settings.add_team_success")) - ctx.Redirect(ctx.Repo.RepoLink + "/settings/collaboration") -} - -// DeleteTeam response for deleting a team from a repository -func DeleteTeam(ctx *context.Context) { - if !ctx.Repo.Owner.RepoAdminChangeTeamAccess && !ctx.Repo.IsOwner() { - ctx.Flash.Error(ctx.Tr("repo.settings.change_team_access_not_allowed")) - ctx.Redirect(ctx.Repo.RepoLink + "/settings/collaboration") - return - } - - team, err := organization.GetTeamByID(ctx, ctx.FormInt64("id")) - if err != nil { - ctx.ServerError("GetTeamByID", err) - return - } - - if err = models.RemoveRepository(team, ctx.Repo.Repository.ID); err != nil { - ctx.ServerError("team.RemoveRepositorys", err) - return - } - - ctx.Flash.Success(ctx.Tr("repo.settings.remove_team_success")) - ctx.JSON(http.StatusOK, map[string]interface{}{ - "redirect": ctx.Repo.RepoLink + "/settings/collaboration", - }) -} - -// GitHooks hooks of a repository -func GitHooks(ctx *context.Context) { - ctx.Data["Title"] = ctx.Tr("repo.settings.githooks") - ctx.Data["PageIsSettingsGitHooks"] = true - - hooks, err := ctx.Repo.GitRepo.Hooks() - if err != nil { - ctx.ServerError("Hooks", err) - return - } - ctx.Data["Hooks"] = hooks - - ctx.HTML(http.StatusOK, tplGithooks) -} - -// GitHooksEdit render for editing a hook of repository page -func GitHooksEdit(ctx *context.Context) { - ctx.Data["Title"] = ctx.Tr("repo.settings.githooks") - ctx.Data["PageIsSettingsGitHooks"] = true - - name := ctx.Params(":name") - hook, err := ctx.Repo.GitRepo.GetHook(name) - if err != nil { - if err == git.ErrNotValidHook { - ctx.NotFound("GetHook", err) - } else { - ctx.ServerError("GetHook", err) - } - return - } - ctx.Data["Hook"] = hook - ctx.HTML(http.StatusOK, tplGithookEdit) -} - -// GitHooksEditPost response for editing a git hook of a repository -func GitHooksEditPost(ctx *context.Context) { - name := ctx.Params(":name") - hook, err := ctx.Repo.GitRepo.GetHook(name) - if err != nil { - if err == git.ErrNotValidHook { - ctx.NotFound("GetHook", err) - } else { - ctx.ServerError("GetHook", err) - } - return - } - hook.Content = ctx.FormString("content") - if err = hook.Update(); err != nil { - ctx.ServerError("hook.Update", err) - return - } - ctx.Redirect(ctx.Repo.RepoLink + "/settings/hooks/git") -} - -// DeployKeys render the deploy keys list of a repository page -func DeployKeys(ctx *context.Context) { - ctx.Data["Title"] = ctx.Tr("repo.settings.deploy_keys") + " / " + ctx.Tr("secrets.secrets") - ctx.Data["PageIsSettingsKeys"] = true - ctx.Data["DisableSSH"] = setting.SSH.Disabled - - keys, err := asymkey_model.ListDeployKeys(ctx, &asymkey_model.ListDeployKeysOptions{RepoID: ctx.Repo.Repository.ID}) - if err != nil { - ctx.ServerError("ListDeployKeys", err) - return - } - ctx.Data["Deploykeys"] = keys - - ctx.HTML(http.StatusOK, tplDeployKeys) -} - -// DeployKeysPost response for adding a deploy key of a repository -func DeployKeysPost(ctx *context.Context) { - form := web.GetForm(ctx).(*forms.AddKeyForm) - ctx.Data["Title"] = ctx.Tr("repo.settings.deploy_keys") - ctx.Data["PageIsSettingsKeys"] = true - ctx.Data["DisableSSH"] = setting.SSH.Disabled - - keys, err := asymkey_model.ListDeployKeys(ctx, &asymkey_model.ListDeployKeysOptions{RepoID: ctx.Repo.Repository.ID}) - if err != nil { - ctx.ServerError("ListDeployKeys", err) - return - } - ctx.Data["Deploykeys"] = keys - - if ctx.HasError() { - ctx.HTML(http.StatusOK, tplDeployKeys) - return - } - - content, err := asymkey_model.CheckPublicKeyString(form.Content) - if err != nil { - if db.IsErrSSHDisabled(err) { - ctx.Flash.Info(ctx.Tr("settings.ssh_disabled")) - } else if asymkey_model.IsErrKeyUnableVerify(err) { - ctx.Flash.Info(ctx.Tr("form.unable_verify_ssh_key")) - } else if err == asymkey_model.ErrKeyIsPrivate { - ctx.Data["HasError"] = true - ctx.Data["Err_Content"] = true - ctx.Flash.Error(ctx.Tr("form.must_use_public_key")) - } else { - ctx.Data["HasError"] = true - ctx.Data["Err_Content"] = true - ctx.Flash.Error(ctx.Tr("form.invalid_ssh_key", err.Error())) - } - ctx.Redirect(ctx.Repo.RepoLink + "/settings/keys") - return - } - - key, err := asymkey_model.AddDeployKey(ctx.Repo.Repository.ID, form.Title, content, !form.IsWritable) - if err != nil { - ctx.Data["HasError"] = true - switch { - case asymkey_model.IsErrDeployKeyAlreadyExist(err): - ctx.Data["Err_Content"] = true - ctx.RenderWithErr(ctx.Tr("repo.settings.key_been_used"), tplDeployKeys, &form) - case asymkey_model.IsErrKeyAlreadyExist(err): - ctx.Data["Err_Content"] = true - ctx.RenderWithErr(ctx.Tr("settings.ssh_key_been_used"), tplDeployKeys, &form) - case asymkey_model.IsErrKeyNameAlreadyUsed(err): - ctx.Data["Err_Title"] = true - ctx.RenderWithErr(ctx.Tr("repo.settings.key_name_used"), tplDeployKeys, &form) - case asymkey_model.IsErrDeployKeyNameAlreadyUsed(err): - ctx.Data["Err_Title"] = true - ctx.RenderWithErr(ctx.Tr("repo.settings.key_name_used"), tplDeployKeys, &form) - default: - ctx.ServerError("AddDeployKey", err) - } - return - } - - log.Trace("Deploy key added: %d", ctx.Repo.Repository.ID) - ctx.Flash.Success(ctx.Tr("repo.settings.add_key_success", key.Name)) - ctx.Redirect(ctx.Repo.RepoLink + "/settings/keys") -} - -// DeleteDeployKey response for deleting a deploy key -func DeleteDeployKey(ctx *context.Context) { - if err := asymkey_service.DeleteDeployKey(ctx.Doer, ctx.FormInt64("id")); err != nil { - ctx.Flash.Error("DeleteDeployKey: " + err.Error()) - } else { - ctx.Flash.Success(ctx.Tr("repo.settings.deploy_key_deletion_success")) - } - - ctx.JSON(http.StatusOK, map[string]interface{}{ - "redirect": ctx.Repo.RepoLink + "/settings/keys", - }) -} - -// UpdateAvatarSetting update repo's avatar -func UpdateAvatarSetting(ctx *context.Context, form forms.AvatarForm) error { - ctxRepo := ctx.Repo.Repository - - if form.Avatar == nil { - // No avatar is uploaded and we not removing it here. - // No random avatar generated here. - // Just exit, no action. - if ctxRepo.CustomAvatarRelativePath() == "" { - log.Trace("No avatar was uploaded for repo: %d. Default icon will appear instead.", ctxRepo.ID) - } - return nil - } - - r, err := form.Avatar.Open() - if err != nil { - return fmt.Errorf("Avatar.Open: %w", err) - } - defer r.Close() - - if form.Avatar.Size > setting.Avatar.MaxFileSize { - return errors.New(ctx.Tr("settings.uploaded_avatar_is_too_big")) - } - - data, err := io.ReadAll(r) - if err != nil { - return fmt.Errorf("io.ReadAll: %w", err) - } - st := typesniffer.DetectContentType(data) - if !(st.IsImage() && !st.IsSvgImage()) { - return errors.New(ctx.Tr("settings.uploaded_avatar_not_a_image")) - } - if err = repo_service.UploadAvatar(ctx, ctxRepo, data); err != nil { - return fmt.Errorf("UploadAvatar: %w", err) - } - return nil -} - -// SettingsAvatar save new POSTed repository avatar -func SettingsAvatar(ctx *context.Context) { - form := web.GetForm(ctx).(*forms.AvatarForm) - form.Source = forms.AvatarLocal - if err := UpdateAvatarSetting(ctx, *form); err != nil { - ctx.Flash.Error(err.Error()) - } else { - ctx.Flash.Success(ctx.Tr("repo.settings.update_avatar_success")) - } - ctx.Redirect(ctx.Repo.RepoLink + "/settings") -} - -// SettingsDeleteAvatar delete repository avatar -func SettingsDeleteAvatar(ctx *context.Context) { - if err := repo_service.DeleteAvatar(ctx, ctx.Repo.Repository); err != nil { - ctx.Flash.Error(fmt.Sprintf("DeleteAvatar: %v", err)) - } - ctx.Redirect(ctx.Repo.RepoLink + "/settings") -} - func selectPushMirrorByForm(ctx *context.Context, form *forms.RepoSettingForm, repo *repo_model.Repository) (*repo_model.PushMirror, error) { id, err := strconv.ParseInt(form.PushMirrorID, 10, 64) if err != nil { diff --git a/routers/web/repo/settings_test.go b/routers/web/repo/setting/settings_test.go similarity index 99% rename from routers/web/repo/settings_test.go rename to routers/web/repo/setting/settings_test.go index a33e92c8211..6f7c844ce7a 100644 --- a/routers/web/repo/settings_test.go +++ b/routers/web/repo/setting/settings_test.go @@ -1,7 +1,7 @@ // Copyright 2017 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package repo +package setting import ( "net/http" diff --git a/routers/web/repo/webhook.go b/routers/web/repo/setting/webhook.go similarity index 99% rename from routers/web/repo/webhook.go rename to routers/web/repo/setting/webhook.go index b358c7260da..d85d5c8b07f 100644 --- a/routers/web/repo/webhook.go +++ b/routers/web/repo/setting/webhook.go @@ -2,7 +2,7 @@ // Copyright 2017 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package repo +package setting import ( "errors" @@ -145,7 +145,7 @@ func WebhooksNew(ctx *context.Context) { return } if hookType == "discord" { - ctx.Data["DiscordHook"] = map[string]interface{}{ + ctx.Data["DiscordHook"] = map[string]any{ "Username": "Gitea", } } @@ -196,7 +196,7 @@ type webhookParams struct { Secret string HTTPMethod string WebhookForm forms.WebhookForm - Meta interface{} + Meta any } func createWebhook(ctx *context.Context, params webhookParams) { @@ -729,7 +729,7 @@ func DeleteWebhook(ctx *context.Context) { ctx.Flash.Success(ctx.Tr("repo.settings.webhook_deletion_success")) } - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "redirect": ctx.Repo.RepoLink + "/settings/hooks", }) } diff --git a/routers/web/repo/topic.go b/routers/web/repo/topic.go index 4c0b38bd910..d22c3c6aa37 100644 --- a/routers/web/repo/topic.go +++ b/routers/web/repo/topic.go @@ -15,7 +15,7 @@ import ( // TopicsPost response for creating repository func TopicsPost(ctx *context.Context) { if ctx.Doer == nil { - ctx.JSON(http.StatusForbidden, map[string]interface{}{ + ctx.JSON(http.StatusForbidden, map[string]any{ "message": "Only owners could change the topics.", }) return @@ -30,7 +30,7 @@ func TopicsPost(ctx *context.Context) { validTopics, invalidTopics := repo_model.SanitizeAndValidateTopics(topics) if len(validTopics) > 25 { - ctx.JSON(http.StatusUnprocessableEntity, map[string]interface{}{ + ctx.JSON(http.StatusUnprocessableEntity, map[string]any{ "invalidTopics": nil, "message": ctx.Tr("repo.topic.count_prompt"), }) @@ -38,7 +38,7 @@ func TopicsPost(ctx *context.Context) { } if len(invalidTopics) > 0 { - ctx.JSON(http.StatusUnprocessableEntity, map[string]interface{}{ + ctx.JSON(http.StatusUnprocessableEntity, map[string]any{ "invalidTopics": invalidTopics, "message": ctx.Tr("repo.topic.format_prompt"), }) @@ -48,13 +48,13 @@ func TopicsPost(ctx *context.Context) { err := repo_model.SaveTopics(ctx.Repo.Repository.ID, validTopics...) if err != nil { log.Error("SaveTopics failed: %v", err) - ctx.JSON(http.StatusInternalServerError, map[string]interface{}{ + ctx.JSON(http.StatusInternalServerError, map[string]any{ "message": "Save topics failed.", }) return } - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "status": "ok", }) } diff --git a/routers/web/repo/view.go b/routers/web/repo/view.go index ad87bae9b8f..ece2ec5416a 100644 --- a/routers/web/repo/view.go +++ b/routers/web/repo/view.go @@ -977,6 +977,18 @@ func renderCode(ctx *context.Context) { return } + if ctx.Doer != nil { + if err := ctx.Repo.Repository.GetBaseRepo(ctx); err != nil { + ctx.ServerError("GetBaseRepo", err) + return + } + ctx.Data["RecentlyPushedNewBranches"], err = git_model.FindRecentlyPushedNewBranches(ctx, ctx.Repo.Repository.ID, ctx.Doer.ID, ctx.Repo.Repository.DefaultBranch) + if err != nil { + ctx.ServerError("GetRecentlyPushedBranches", err) + return + } + } + var treeNames []string paths := make([]string, 0, 5) if len(ctx.Repo.TreePath) > 0 { diff --git a/routers/web/repo/wiki.go b/routers/web/repo/wiki.go index 22cfe6dd258..4773e25c706 100644 --- a/routers/web/repo/wiki.go +++ b/routers/web/repo/wiki.go @@ -12,7 +12,6 @@ import ( "net/url" "path/filepath" "strings" - "time" git_model "code.gitea.io/gitea/models/git" repo_model "code.gitea.io/gitea/models/repo" @@ -671,7 +670,7 @@ func WikiRaw(ctx *context.Context) { } if entry != nil { - if err = common.ServeBlob(ctx.Base, ctx.Repo.TreePath, entry.Blob(), time.Time{}); err != nil { + if err = common.ServeBlob(ctx.Base, ctx.Repo.TreePath, entry.Blob(), nil); err != nil { ctx.ServerError("ServeBlob", err) } return @@ -791,7 +790,7 @@ func DeleteWikiPagePost(ctx *context.Context) { notification.NotifyDeleteWikiPage(ctx, ctx.Doer, ctx.Repo.Repository, string(wikiName)) - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "redirect": ctx.Repo.RepoLink + "/wiki/", }) } diff --git a/routers/web/repo/wiki_test.go b/routers/web/repo/wiki_test.go index d85879d1e5c..e1284fad670 100644 --- a/routers/web/repo/wiki_test.go +++ b/routers/web/repo/wiki_test.go @@ -62,7 +62,7 @@ func assertWikiNotExists(t *testing.T, repo *repo_model.Repository, wikiName wik assert.Nil(t, wikiEntry(t, repo, wikiName)) } -func assertPagesMetas(t *testing.T, expectedNames []string, metas interface{}) { +func assertPagesMetas(t *testing.T, expectedNames []string, metas any) { pageMetas, ok := metas.([]PageMeta) if !assert.True(t, ok) { return diff --git a/routers/web/shared/actions/runners.go b/routers/web/shared/actions/runners.go index c212c4ff25d..21e5a90d8f2 100644 --- a/routers/web/shared/actions/runners.go +++ b/routers/web/shared/actions/runners.go @@ -160,7 +160,7 @@ func RunnerDeletePost(ctx *context.Context, runnerID int64, log.Warn("DeleteRunnerPost.UpdateRunner failed: %v, url: %s", err, ctx.Req.URL) ctx.Flash.Warning(ctx.Tr("actions.runners.delete_runner_failed")) - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "redirect": failedRedirectTo, }) return @@ -170,7 +170,7 @@ func RunnerDeletePost(ctx *context.Context, runnerID int64, ctx.Flash.Success(ctx.Tr("actions.runners.delete_runner_success")) - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "redirect": successRedirectTo, }) } diff --git a/routers/web/shared/user/header.go b/routers/web/shared/user/header.go index 9594e6975a8..516c853b02e 100644 --- a/routers/web/shared/user/header.go +++ b/routers/web/shared/user/header.go @@ -4,35 +4,109 @@ package user import ( + "code.gitea.io/gitea/models/db" + "code.gitea.io/gitea/models/organization" repo_model "code.gitea.io/gitea/models/repo" + user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/markup" + "code.gitea.io/gitea/modules/markup/markdown" "code.gitea.io/gitea/modules/setting" ) -func RenderUserHeader(ctx *context.Context) { - ctx.Data["IsProjectEnabled"] = true +// prepareContextForCommonProfile store some common data into context data for user's profile related pages (including the nav menu) +// It is designed to be fast and safe to be called multiple times in one request +func prepareContextForCommonProfile(ctx *context.Context) { ctx.Data["IsPackageEnabled"] = setting.Packages.Enabled ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled ctx.Data["ContextUser"] = ctx.ContextUser - tab := ctx.FormString("tab") - ctx.Data["TabName"] = tab - repo, err := repo_model.GetRepositoryByName(ctx.ContextUser.ID, ".profile") - if err == nil && !repo.IsEmpty { - gitRepo, err := git.OpenRepository(ctx, repo.RepoPath()) + ctx.Data["EnableFeed"] = setting.Other.EnableFeed + ctx.Data["FeedURL"] = ctx.ContextUser.HomeLink() +} + +// PrepareContextForProfileBigAvatar set the context for big avatar view on the profile page +func PrepareContextForProfileBigAvatar(ctx *context.Context) { + prepareContextForCommonProfile(ctx) + + ctx.Data["IsFollowing"] = ctx.Doer != nil && user_model.IsFollowing(ctx.Doer.ID, ctx.ContextUser.ID) + ctx.Data["ShowUserEmail"] = setting.UI.ShowUserEmail && ctx.ContextUser.Email != "" && ctx.IsSigned && !ctx.ContextUser.KeepEmailPrivate + + // Show OpenID URIs + openIDs, err := user_model.GetUserOpenIDs(ctx.ContextUser.ID) + if err != nil { + ctx.ServerError("GetUserOpenIDs", err) + return + } + ctx.Data["OpenIDs"] = openIDs + + if len(ctx.ContextUser.Description) != 0 { + content, err := markdown.RenderString(&markup.RenderContext{ + URLPrefix: ctx.Repo.RepoLink, + Metas: map[string]string{"mode": "document"}, + GitRepo: ctx.Repo.GitRepo, + Ctx: ctx, + }, ctx.ContextUser.Description) if err != nil { - ctx.ServerError("OpenRepository", err) + ctx.ServerError("RenderString", err) return } - defer gitRepo.Close() - commit, err := gitRepo.GetBranchCommit(repo.DefaultBranch) - if err != nil { - ctx.ServerError("GetBranchCommit", err) - return + ctx.Data["RenderedDescription"] = content + } + + showPrivate := ctx.IsSigned && (ctx.Doer.IsAdmin || ctx.Doer.ID == ctx.ContextUser.ID) + orgs, err := organization.FindOrgs(organization.FindOrgOptions{ + UserID: ctx.ContextUser.ID, + IncludePrivate: showPrivate, + }) + if err != nil { + ctx.ServerError("FindOrgs", err) + return + } + ctx.Data["Orgs"] = orgs + ctx.Data["HasOrgsVisible"] = organization.HasOrgsVisible(orgs, ctx.Doer) + + badges, _, err := user_model.GetUserBadges(ctx, ctx.ContextUser) + if err != nil { + ctx.ServerError("GetUserBadges", err) + return + } + ctx.Data["Badges"] = badges + + // in case the numbers are already provided by other functions, no need to query again (which is slow) + if _, ok := ctx.Data["NumFollowers"]; !ok { + _, ctx.Data["NumFollowers"], _ = user_model.GetUserFollowers(ctx, ctx.ContextUser, ctx.Doer, db.ListOptions{PageSize: 1, Page: 1}) + } + if _, ok := ctx.Data["NumFollowing"]; !ok { + _, ctx.Data["NumFollowing"], _ = user_model.GetUserFollowing(ctx, ctx.ContextUser, ctx.Doer, db.ListOptions{PageSize: 1, Page: 1}) + } +} + +func FindUserProfileReadme(ctx *context.Context) (profileGitRepo *git.Repository, profileReadmeBlob *git.Blob, profileClose func()) { + profileDbRepo, err := repo_model.GetRepositoryByName(ctx.ContextUser.ID, ".profile") + if err == nil && !profileDbRepo.IsEmpty { + if profileGitRepo, err = git.OpenRepository(ctx, profileDbRepo.RepoPath()); err != nil { + log.Error("FindUserProfileReadme failed to OpenRepository: %v", err) + } else { + if commit, err := profileGitRepo.GetBranchCommit(profileDbRepo.DefaultBranch); err != nil { + log.Error("FindUserProfileReadme failed to GetBranchCommit: %v", err) + } else { + profileReadmeBlob, _ = commit.GetBlobByPath("README.md") + } } - blob, err := commit.GetBlobByPath("README.md") - if err == nil && blob != nil { - ctx.Data["ProfileReadme"] = true + } + return profileGitRepo, profileReadmeBlob, func() { + if profileGitRepo != nil { + _ = profileGitRepo.Close() } } } + +func RenderUserHeader(ctx *context.Context) { + prepareContextForCommonProfile(ctx) + + _, profileReadmeBlob, profileClose := FindUserProfileReadme(ctx) + defer profileClose() + ctx.Data["HasProfileReadme"] = profileReadmeBlob != nil +} diff --git a/routers/web/user/code.go b/routers/web/user/code.go index 15524de7d65..033f65c9c06 100644 --- a/routers/web/user/code.go +++ b/routers/web/user/code.go @@ -11,6 +11,7 @@ import ( "code.gitea.io/gitea/modules/context" code_indexer "code.gitea.io/gitea/modules/indexer/code" "code.gitea.io/gitea/modules/setting" + shared_user "code.gitea.io/gitea/routers/web/shared/user" ) const ( @@ -23,8 +24,9 @@ func CodeSearch(ctx *context.Context) { ctx.Redirect(ctx.ContextUser.HomeLink()) return } + shared_user.PrepareContextForProfileBigAvatar(ctx) + shared_user.RenderUserHeader(ctx) - ctx.Data["IsProjectEnabled"] = true ctx.Data["IsPackageEnabled"] = setting.Packages.Enabled ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled ctx.Data["Title"] = ctx.Tr("explore.code") diff --git a/routers/web/user/home.go b/routers/web/user/home.go index 2513fc9a983..5f1e0eb4277 100644 --- a/routers/web/user/home.go +++ b/routers/web/user/home.go @@ -97,7 +97,7 @@ func Dashboard(ctx *context.Context) { uid = ctxUser.ID } - ctx.PageData["dashboardRepoList"] = map[string]interface{}{ + ctx.PageData["dashboardRepoList"] = map[string]any{ "searchLimit": setting.UI.User.RepoPagingNum, "uid": uid, } @@ -725,7 +725,7 @@ func issueIDsFromSearch(ctx *context.Context, ctxUser *user_model.User, keyword if err != nil { return nil, fmt.Errorf("GetRepoIDsForIssuesOptions: %w", err) } - issueIDsFromSearch, err := issue_indexer.SearchIssuesByKeyword(ctx, searchRepoIDs, keyword) + issueIDsFromSearch, err := issue_indexer.SearchIssuesByKeyword(ctx, searchRepoIDs, keyword, ctx.FormString("state")) if err != nil { return nil, fmt.Errorf("SearchIssuesByKeyword: %w", err) } @@ -857,7 +857,7 @@ func UsernameSubRoute(ctx *context.Context) { context_service.UserAssignmentWeb()(ctx) if !ctx.Written() { ctx.Data["EnableFeed"] = setting.Other.EnableFeed - Profile(ctx) + OwnerProfile(ctx) } } } diff --git a/routers/web/user/notification.go b/routers/web/user/notification.go index e0aa92879fc..cae12f41267 100644 --- a/routers/web/user/notification.go +++ b/routers/web/user/notification.go @@ -186,7 +186,7 @@ func NotificationStatusPost(ctx *context.Context) { if ctx.Written() { return } - ctx.Data["Link"] = setting.AppURL + "notifications" + ctx.Data["Link"] = setting.AppSubURL + "/notifications" ctx.Data["SequenceNumber"] = ctx.Req.PostFormValue("sequence-number") ctx.HTML(http.StatusOK, tplNotificationDiv) diff --git a/routers/web/user/package.go b/routers/web/user/package.go index 20141914b65..2e2c2a6e1f9 100644 --- a/routers/web/user/package.go +++ b/routers/web/user/package.go @@ -22,6 +22,7 @@ import ( "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" + packages_helper "code.gitea.io/gitea/routers/api/packages/helper" shared_user "code.gitea.io/gitea/routers/web/shared/user" "code.gitea.io/gitea/services/forms" packages_service "code.gitea.io/gitea/services/packages" @@ -36,6 +37,7 @@ const ( // ListPackages displays a list of all packages of the context user func ListPackages(ctx *context.Context) { + shared_user.PrepareContextForProfileBigAvatar(ctx) page := ctx.FormInt("page") if page <= 1 { page = 1 @@ -258,6 +260,7 @@ func ViewPackageVersion(ctx *context.Context) { // ListPackageVersions lists all versions of a package func ListPackageVersions(ctx *context.Context) { + shared_user.PrepareContextForProfileBigAvatar(ctx) p, err := packages_model.GetPackageByName(ctx, ctx.Package.Owner.ID, packages_model.Type(ctx.Params("type")), ctx.Params("name")) if err != nil { if err == packages_model.ErrPackageNotExist { @@ -443,18 +446,11 @@ func DownloadPackageFile(ctx *context.Context) { return } - s, _, err := packages_service.GetPackageFileStream( - ctx, - pf, - ) + s, u, _, err := packages_service.GetPackageFileStream(ctx, pf) if err != nil { ctx.ServerError("GetPackageFileStream", err) return } - defer s.Close() - ctx.ServeContent(s, &context.ServeHeaderOptions{ - Filename: pf.Name, - LastModified: pf.CreatedUnix.AsLocalTime(), - }) + packages_helper.ServePackageFile(ctx, s, u, pf) } diff --git a/routers/web/user/profile.go b/routers/web/user/profile.go index 6f9f84d60db..07a2261c967 100644 --- a/routers/web/user/profile.go +++ b/routers/web/user/profile.go @@ -11,22 +11,22 @@ import ( activities_model "code.gitea.io/gitea/models/activities" "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/models/organization" - project_model "code.gitea.io/gitea/models/project" repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/markup" "code.gitea.io/gitea/modules/markup/markdown" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/routers/web/feed" "code.gitea.io/gitea/routers/web/org" + shared_user "code.gitea.io/gitea/routers/web/shared/user" ) -// Profile render user's profile page -func Profile(ctx *context.Context) { +// OwnerProfile render profile page for a user or a organization (aka, repo owner) +func OwnerProfile(ctx *context.Context) { if strings.Contains(ctx.Req.Header.Get("Accept"), "application/rss+xml") { feed.ShowUserFeedRSS(ctx) return @@ -38,36 +38,22 @@ func Profile(ctx *context.Context) { if ctx.ContextUser.IsOrganization() { org.Home(ctx) - return + } else { + userProfile(ctx) } +} +func userProfile(ctx *context.Context) { // check view permissions if !user_model.IsUserVisibleToViewer(ctx, ctx.ContextUser, ctx.Doer) { ctx.NotFound("user", fmt.Errorf(ctx.ContextUser.Name)) return } - // advertise feed via meta tag - ctx.Data["FeedURL"] = ctx.ContextUser.HomeLink() - - // Show OpenID URIs - openIDs, err := user_model.GetUserOpenIDs(ctx.ContextUser.ID) - if err != nil { - ctx.ServerError("GetUserOpenIDs", err) - return - } - - var isFollowing bool - if ctx.Doer != nil { - isFollowing = user_model.IsFollowing(ctx.Doer.ID, ctx.ContextUser.ID) - } - ctx.Data["Title"] = ctx.ContextUser.DisplayName() ctx.Data["PageIsUserProfile"] = true - ctx.Data["ContextUser"] = ctx.ContextUser - ctx.Data["OpenIDs"] = openIDs - ctx.Data["IsFollowing"] = isFollowing + // prepare heatmap data if setting.Service.EnableUserHeatmap { data, err := activities_model.GetUserHeatmapDataByUser(ctx.ContextUser, ctx.Doer) if err != nil { @@ -78,75 +64,28 @@ func Profile(ctx *context.Context) { ctx.Data["HeatmapTotalContributions"] = activities_model.GetTotalContributionsInHeatmap(data) } - if len(ctx.ContextUser.Description) != 0 { - content, err := markdown.RenderString(&markup.RenderContext{ - URLPrefix: ctx.Repo.RepoLink, - Metas: map[string]string{"mode": "document"}, - GitRepo: ctx.Repo.GitRepo, - Ctx: ctx, - }, ctx.ContextUser.Description) - if err != nil { - ctx.ServerError("RenderString", err) - return - } - ctx.Data["RenderedDescription"] = content - } - - repo, err := repo_model.GetRepositoryByName(ctx.ContextUser.ID, ".profile") - if err == nil && !repo.IsEmpty { - gitRepo, err := git.OpenRepository(ctx, repo.RepoPath()) - if err != nil { - ctx.ServerError("OpenRepository", err) - return - } - defer gitRepo.Close() - commit, err := gitRepo.GetBranchCommit(repo.DefaultBranch) - if err != nil { - ctx.ServerError("GetBranchCommit", err) - return - } - blob, err := commit.GetBlobByPath("README.md") - if err == nil { - bytes, err := blob.GetBlobContent(setting.UI.MaxDisplayFileSize) - if err != nil { - ctx.ServerError("GetBlobContent", err) - return - } - profileContent, err := markdown.RenderString(&markup.RenderContext{ - Ctx: ctx, - GitRepo: gitRepo, - }, bytes) - if err != nil { - ctx.ServerError("RenderString", err) - return - } - ctx.Data["ProfileReadme"] = profileContent - } - } + profileGitRepo, profileReadmeBlob, profileClose := shared_user.FindUserProfileReadme(ctx) + defer profileClose() showPrivate := ctx.IsSigned && (ctx.Doer.IsAdmin || ctx.Doer.ID == ctx.ContextUser.ID) + prepareUserProfileTabData(ctx, showPrivate, profileGitRepo, profileReadmeBlob) + // call PrepareContextForProfileBigAvatar later to avoid re-querying the NumFollowers & NumFollowing + shared_user.PrepareContextForProfileBigAvatar(ctx) + ctx.HTML(http.StatusOK, tplProfile) +} - orgs, err := organization.FindOrgs(organization.FindOrgOptions{ - UserID: ctx.ContextUser.ID, - IncludePrivate: showPrivate, - }) - if err != nil { - ctx.ServerError("FindOrgs", err) - return - } - - ctx.Data["Orgs"] = orgs - ctx.Data["HasOrgsVisible"] = organization.HasOrgsVisible(orgs, ctx.Doer) - - badges, _, err := user_model.GetUserBadges(ctx, ctx.ContextUser) - if err != nil { - ctx.ServerError("GetUserBadges", err) - return - } - ctx.Data["Badges"] = badges - +func prepareUserProfileTabData(ctx *context.Context, showPrivate bool, profileGitRepo *git.Repository, profileReadme *git.Blob) { + // if there is a profile readme, default to "overview" page, otherwise, default to "repositories" page tab := ctx.FormString("tab") + if tab == "" { + if profileReadme != nil { + tab = "overview" + } else { + tab = "repositories" + } + } ctx.Data["TabName"] = tab + ctx.Data["HasProfileReadme"] = profileReadme != nil page := ctx.FormInt("page") if page <= 0 { @@ -154,12 +93,7 @@ func Profile(ctx *context.Context) { } pagingNum := setting.UI.User.RepoPagingNum - if tab == "activity" { - pagingNum = setting.UI.FeedPagingNum - } - topicOnly := ctx.FormBool("topic") - var ( repos []*repo_model.Repository count int64 @@ -228,6 +162,7 @@ func Profile(ctx *context.Context) { total = int(count) case "activity": date := ctx.FormString("date") + pagingNum = setting.UI.FeedPagingNum items, count, err := activities_model.GetFeeds(ctx, activities_model.GetFeedsOptions{ RequestedUser: ctx.ContextUser, Actor: ctx.Doer, @@ -271,16 +206,6 @@ func Profile(ctx *context.Context) { } total = int(count) - case "projects": - ctx.Data["OpenProjects"], _, err = project_model.FindProjects(ctx, project_model.SearchOptions{ - Page: -1, - IsClosed: util.OptionalBoolFalse, - Type: project_model.TypeIndividual, - }) - if err != nil { - ctx.ServerError("GetProjects", err) - return - } case "watching": repos, count, err = repo_model.SearchRepository(ctx, &repo_model.SearchRepoOptions{ ListOptions: db.ListOptions{ @@ -303,7 +228,17 @@ func Profile(ctx *context.Context) { } total = int(count) - default: + case "overview": + if bytes, err := profileReadme.GetBlobContent(setting.UI.MaxDisplayFileSize); err != nil { + log.Error("failed to GetBlobContent: %v", err) + } else { + if profileContent, err := markdown.RenderString(&markup.RenderContext{Ctx: ctx, GitRepo: profileGitRepo}, bytes); err != nil { + log.Error("failed to RenderString: %v", err) + } else { + ctx.Data["ProfileReadme"] = profileContent + } + } + default: // default to "repositories" repos, count, err = repo_model.SearchRepository(ctx, &repo_model.SearchRepoOptions{ ListOptions: db.ListOptions{ PageSize: pagingNum, @@ -339,13 +274,6 @@ func Profile(ctx *context.Context) { pager.AddParam(ctx, "date", "Date") } ctx.Data["Page"] = pager - ctx.Data["IsProjectEnabled"] = true - ctx.Data["IsPackageEnabled"] = setting.Packages.Enabled - ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled - - ctx.Data["ShowUserEmail"] = setting.UI.ShowUserEmail && ctx.ContextUser.Email != "" && ctx.IsSigned && !ctx.ContextUser.KeepEmailPrivate - - ctx.HTML(http.StatusOK, tplProfile) } // Action response for follow/unfollow user request @@ -359,9 +287,9 @@ func Action(ctx *context.Context) { } if err != nil { - ctx.ServerError(fmt.Sprintf("Action (%s)", ctx.FormString("action")), err) + log.Error("Failed to apply action %q: %v", ctx.FormString("action"), err) + ctx.JSONError(fmt.Sprintf("Action %q failed", ctx.FormString("action"))) return } - // FIXME: We should check this URL and make sure that it's a valid Gitea URL - ctx.RedirectToFirst(ctx.FormString("redirect_to"), ctx.ContextUser.HomeLink()) + ctx.JSONOK() } diff --git a/routers/web/user/search.go b/routers/web/user/search.go index bdc4116e377..fa2e52dd419 100644 --- a/routers/web/user/search.go +++ b/routers/web/user/search.go @@ -28,7 +28,7 @@ func Search(ctx *context.Context) { ListOptions: listOptions, }) if err != nil { - ctx.JSON(http.StatusInternalServerError, map[string]interface{}{ + ctx.JSON(http.StatusInternalServerError, map[string]any{ "ok": false, "error": err.Error(), }) @@ -37,7 +37,7 @@ func Search(ctx *context.Context) { ctx.SetTotalCountHeader(maxResults) - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "ok": true, "data": convert.ToUsers(ctx, ctx.Doer, users), }) diff --git a/routers/web/user/setting/account.go b/routers/web/user/setting/account.go index a67c2398fb0..532f0d8e391 100644 --- a/routers/web/user/setting/account.go +++ b/routers/web/user/setting/account.go @@ -227,7 +227,7 @@ func DeleteEmail(ctx *context.Context) { log.Trace("Email address deleted: %s", ctx.Doer.Name) ctx.Flash.Success(ctx.Tr("settings.email_deletion_success")) - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "redirect": setting.AppSubURL + "/user/settings/account", }) } diff --git a/routers/web/user/setting/applications.go b/routers/web/user/setting/applications.go index f9e9ca5e52d..81209376960 100644 --- a/routers/web/user/setting/applications.go +++ b/routers/web/user/setting/applications.go @@ -83,7 +83,7 @@ func DeleteApplication(ctx *context.Context) { ctx.Flash.Success(ctx.Tr("settings.delete_token_success")) } - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "redirect": setting.AppSubURL + "/user/settings/applications", }) } diff --git a/routers/web/user/setting/keys.go b/routers/web/user/setting/keys.go index 6debf95bbce..d9412cae7ca 100644 --- a/routers/web/user/setting/keys.go +++ b/routers/web/user/setting/keys.go @@ -256,7 +256,7 @@ func DeleteKey(ctx *context.Context) { ctx.Flash.Warning("Function not implemented") ctx.Redirect(setting.AppSubURL + "/user/settings/keys") } - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "redirect": setting.AppSubURL + "/user/settings/keys", }) } diff --git a/routers/web/user/setting/oauth2_common.go b/routers/web/user/setting/oauth2_common.go index 5de0f0e22f4..923ce4b4367 100644 --- a/routers/web/user/setting/oauth2_common.go +++ b/routers/web/user/setting/oauth2_common.go @@ -138,7 +138,7 @@ func (oa *OAuth2CommonHandlers) DeleteApp(ctx *context.Context) { } ctx.Flash.Success(ctx.Tr("settings.remove_oauth2_application_success")) - ctx.JSON(http.StatusOK, map[string]interface{}{"redirect": oa.BasePathList}) + ctx.JSON(http.StatusOK, map[string]any{"redirect": oa.BasePathList}) } // RevokeGrant revokes the grant @@ -149,5 +149,5 @@ func (oa *OAuth2CommonHandlers) RevokeGrant(ctx *context.Context) { } ctx.Flash.Success(ctx.Tr("settings.revoke_oauth2_grant_success")) - ctx.JSON(http.StatusOK, map[string]interface{}{"redirect": oa.BasePathList}) + ctx.JSON(http.StatusOK, map[string]any{"redirect": oa.BasePathList}) } diff --git a/routers/web/user/setting/security/openid.go b/routers/web/user/setting/security/openid.go index 08fcb6b6234..f4133f39160 100644 --- a/routers/web/user/setting/security/openid.go +++ b/routers/web/user/setting/security/openid.go @@ -112,7 +112,7 @@ func DeleteOpenID(ctx *context.Context) { log.Trace("OpenID address deleted: %s", ctx.Doer.Name) ctx.Flash.Success(ctx.Tr("settings.openid_deletion_success")) - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "redirect": setting.AppSubURL + "/user/settings/security", }) } diff --git a/routers/web/user/setting/security/security.go b/routers/web/user/setting/security/security.go index 6e6e7efb0b2..cc5f817a9d4 100644 --- a/routers/web/user/setting/security/security.go +++ b/routers/web/user/setting/security/security.go @@ -48,7 +48,7 @@ func DeleteAccountLink(ctx *context.Context) { } } - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "redirect": setting.AppSubURL + "/user/settings/security", }) } diff --git a/routers/web/user/setting/security/webauthn.go b/routers/web/user/setting/security/webauthn.go index 826562f1574..89ac184a47e 100644 --- a/routers/web/user/setting/security/webauthn.go +++ b/routers/web/user/setting/security/webauthn.go @@ -116,7 +116,7 @@ func WebauthnDelete(ctx *context.Context) { ctx.ServerError("GetWebAuthnCredentialByID", err) return } - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "redirect": setting.AppSubURL + "/user/settings/security", }) } diff --git a/routers/web/user/setting/webhooks.go b/routers/web/user/setting/webhooks.go index 9b0b0c9611c..db03d7b1ed2 100644 --- a/routers/web/user/setting/webhooks.go +++ b/routers/web/user/setting/webhooks.go @@ -42,7 +42,7 @@ func DeleteWebhook(ctx *context.Context) { ctx.Flash.Success(ctx.Tr("repo.settings.webhook_deletion_success")) } - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "redirect": setting.AppSubURL + "/user/settings/hooks", }) } diff --git a/routers/web/user/task.go b/routers/web/user/task.go index 3818682403d..d92bf64af0f 100644 --- a/routers/web/user/task.go +++ b/routers/web/user/task.go @@ -17,12 +17,12 @@ func TaskStatus(ctx *context.Context) { task, opts, err := admin_model.GetMigratingTaskByID(ctx.ParamsInt64("task"), ctx.Doer.ID) if err != nil { if admin_model.IsErrTaskDoesNotExist(err) { - ctx.JSON(http.StatusNotFound, map[string]interface{}{ + ctx.JSON(http.StatusNotFound, map[string]any{ "error": "task `" + strconv.FormatInt(ctx.ParamsInt64("task"), 10) + "` does not exist", }) return } - ctx.JSON(http.StatusInternalServerError, map[string]interface{}{ + ctx.JSON(http.StatusInternalServerError, map[string]any{ "err": err, }) return @@ -36,13 +36,13 @@ func TaskStatus(ctx *context.Context) { if err := json.Unmarshal([]byte(message), &translatableMessage); err != nil { translatableMessage = admin_model.TranslatableMessage{ Format: "migrate.migrating_failed.error", - Args: []interface{}{task.Message}, + Args: []any{task.Message}, } } message = ctx.Tr(translatableMessage.Format, translatableMessage.Args...) } - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "status": task.Status, "message": message, "repo-id": task.RepoID, diff --git a/routers/web/web.go b/routers/web/web.go index 9190c40166e..6f34c3cdfe6 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -279,32 +279,32 @@ func registerRoutes(m *web.Route) { } addWebhookAddRoutes := func() { - m.Get("/{type}/new", repo.WebhooksNew) - m.Post("/gitea/new", web.Bind(forms.NewWebhookForm{}), repo.GiteaHooksNewPost) - m.Post("/gogs/new", web.Bind(forms.NewGogshookForm{}), repo.GogsHooksNewPost) - m.Post("/slack/new", web.Bind(forms.NewSlackHookForm{}), repo.SlackHooksNewPost) - m.Post("/discord/new", web.Bind(forms.NewDiscordHookForm{}), repo.DiscordHooksNewPost) - m.Post("/dingtalk/new", web.Bind(forms.NewDingtalkHookForm{}), repo.DingtalkHooksNewPost) - m.Post("/telegram/new", web.Bind(forms.NewTelegramHookForm{}), repo.TelegramHooksNewPost) - m.Post("/matrix/new", web.Bind(forms.NewMatrixHookForm{}), repo.MatrixHooksNewPost) - m.Post("/msteams/new", web.Bind(forms.NewMSTeamsHookForm{}), repo.MSTeamsHooksNewPost) - m.Post("/feishu/new", web.Bind(forms.NewFeishuHookForm{}), repo.FeishuHooksNewPost) - m.Post("/wechatwork/new", web.Bind(forms.NewWechatWorkHookForm{}), repo.WechatworkHooksNewPost) - m.Post("/packagist/new", web.Bind(forms.NewPackagistHookForm{}), repo.PackagistHooksNewPost) + m.Get("/{type}/new", repo_setting.WebhooksNew) + m.Post("/gitea/new", web.Bind(forms.NewWebhookForm{}), repo_setting.GiteaHooksNewPost) + m.Post("/gogs/new", web.Bind(forms.NewGogshookForm{}), repo_setting.GogsHooksNewPost) + m.Post("/slack/new", web.Bind(forms.NewSlackHookForm{}), repo_setting.SlackHooksNewPost) + m.Post("/discord/new", web.Bind(forms.NewDiscordHookForm{}), repo_setting.DiscordHooksNewPost) + m.Post("/dingtalk/new", web.Bind(forms.NewDingtalkHookForm{}), repo_setting.DingtalkHooksNewPost) + m.Post("/telegram/new", web.Bind(forms.NewTelegramHookForm{}), repo_setting.TelegramHooksNewPost) + m.Post("/matrix/new", web.Bind(forms.NewMatrixHookForm{}), repo_setting.MatrixHooksNewPost) + m.Post("/msteams/new", web.Bind(forms.NewMSTeamsHookForm{}), repo_setting.MSTeamsHooksNewPost) + m.Post("/feishu/new", web.Bind(forms.NewFeishuHookForm{}), repo_setting.FeishuHooksNewPost) + m.Post("/wechatwork/new", web.Bind(forms.NewWechatWorkHookForm{}), repo_setting.WechatworkHooksNewPost) + m.Post("/packagist/new", web.Bind(forms.NewPackagistHookForm{}), repo_setting.PackagistHooksNewPost) } addWebhookEditRoutes := func() { - m.Post("/gitea/{id}", web.Bind(forms.NewWebhookForm{}), repo.GiteaHooksEditPost) - m.Post("/gogs/{id}", web.Bind(forms.NewGogshookForm{}), repo.GogsHooksEditPost) - m.Post("/slack/{id}", web.Bind(forms.NewSlackHookForm{}), repo.SlackHooksEditPost) - m.Post("/discord/{id}", web.Bind(forms.NewDiscordHookForm{}), repo.DiscordHooksEditPost) - m.Post("/dingtalk/{id}", web.Bind(forms.NewDingtalkHookForm{}), repo.DingtalkHooksEditPost) - m.Post("/telegram/{id}", web.Bind(forms.NewTelegramHookForm{}), repo.TelegramHooksEditPost) - m.Post("/matrix/{id}", web.Bind(forms.NewMatrixHookForm{}), repo.MatrixHooksEditPost) - m.Post("/msteams/{id}", web.Bind(forms.NewMSTeamsHookForm{}), repo.MSTeamsHooksEditPost) - m.Post("/feishu/{id}", web.Bind(forms.NewFeishuHookForm{}), repo.FeishuHooksEditPost) - m.Post("/wechatwork/{id}", web.Bind(forms.NewWechatWorkHookForm{}), repo.WechatworkHooksEditPost) - m.Post("/packagist/{id}", web.Bind(forms.NewPackagistHookForm{}), repo.PackagistHooksEditPost) + m.Post("/gitea/{id}", web.Bind(forms.NewWebhookForm{}), repo_setting.GiteaHooksEditPost) + m.Post("/gogs/{id}", web.Bind(forms.NewGogshookForm{}), repo_setting.GogsHooksEditPost) + m.Post("/slack/{id}", web.Bind(forms.NewSlackHookForm{}), repo_setting.SlackHooksEditPost) + m.Post("/discord/{id}", web.Bind(forms.NewDiscordHookForm{}), repo_setting.DiscordHooksEditPost) + m.Post("/dingtalk/{id}", web.Bind(forms.NewDingtalkHookForm{}), repo_setting.DingtalkHooksEditPost) + m.Post("/telegram/{id}", web.Bind(forms.NewTelegramHookForm{}), repo_setting.TelegramHooksEditPost) + m.Post("/matrix/{id}", web.Bind(forms.NewMatrixHookForm{}), repo_setting.MatrixHooksEditPost) + m.Post("/msteams/{id}", web.Bind(forms.NewMSTeamsHookForm{}), repo_setting.MSTeamsHooksEditPost) + m.Post("/feishu/{id}", web.Bind(forms.NewFeishuHookForm{}), repo_setting.FeishuHooksEditPost) + m.Post("/wechatwork/{id}", web.Bind(forms.NewWechatWorkHookForm{}), repo_setting.WechatworkHooksEditPost) + m.Post("/packagist/{id}", web.Bind(forms.NewPackagistHookForm{}), repo_setting.PackagistHooksEditPost) } addSettingVariablesRoutes := func() { @@ -515,8 +515,8 @@ func registerRoutes(m *web.Route) { m.Post("/delete", user_setting.DeleteWebhook) addWebhookAddRoutes() m.Group("/{id}", func() { - m.Get("", repo.WebHooksEdit) - m.Post("/replay/{uuid}", repo.ReplayWebhook) + m.Get("", repo_setting.WebHooksEdit) + m.Post("/replay/{uuid}", repo_setting.ReplayWebhook) }) addWebhookEditRoutes() }, webhooksEnabled) @@ -605,8 +605,8 @@ func registerRoutes(m *web.Route) { m.Get("", admin.DefaultOrSystemWebhooks) m.Post("/delete", admin.DeleteDefaultOrSystemWebhook) m.Group("/{id}", func() { - m.Get("", repo.WebHooksEdit) - m.Post("/replay/{uuid}", repo.ReplayWebhook) + m.Get("", repo_setting.WebHooksEdit) + m.Post("/replay/{uuid}", repo_setting.ReplayWebhook) }) addWebhookEditRoutes() }, webhooksEnabled) @@ -753,8 +753,8 @@ func registerRoutes(m *web.Route) { m.Post("/delete", org.DeleteWebhook) addWebhookAddRoutes() m.Group("/{id}", func() { - m.Get("", repo.WebHooksEdit) - m.Post("/replay/{uuid}", repo.ReplayWebhook) + m.Get("", repo_setting.WebHooksEdit) + m.Post("/replay/{uuid}", repo_setting.ReplayWebhook) }) addWebhookEditRoutes() }, webhooksEnabled) @@ -875,78 +875,78 @@ func registerRoutes(m *web.Route) { m.Group("/{username}/{reponame}", func() { m.Group("/settings", func() { m.Group("", func() { - m.Combo("").Get(repo.Settings). - Post(web.Bind(forms.RepoSettingForm{}), repo.SettingsPost) - }, repo.SettingsCtxData) - m.Post("/avatar", web.Bind(forms.AvatarForm{}), repo.SettingsAvatar) - m.Post("/avatar/delete", repo.SettingsDeleteAvatar) + m.Combo("").Get(repo_setting.Settings). + Post(web.Bind(forms.RepoSettingForm{}), repo_setting.SettingsPost) + }, repo_setting.SettingsCtxData) + m.Post("/avatar", web.Bind(forms.AvatarForm{}), repo_setting.SettingsAvatar) + m.Post("/avatar/delete", repo_setting.SettingsDeleteAvatar) m.Group("/collaboration", func() { - m.Combo("").Get(repo.Collaboration).Post(repo.CollaborationPost) - m.Post("/access_mode", repo.ChangeCollaborationAccessMode) - m.Post("/delete", repo.DeleteCollaboration) + m.Combo("").Get(repo_setting.Collaboration).Post(repo_setting.CollaborationPost) + m.Post("/access_mode", repo_setting.ChangeCollaborationAccessMode) + m.Post("/delete", repo_setting.DeleteCollaboration) m.Group("/team", func() { - m.Post("", repo.AddTeamPost) - m.Post("/delete", repo.DeleteTeam) + m.Post("", repo_setting.AddTeamPost) + m.Post("/delete", repo_setting.DeleteTeam) }) }) m.Group("/branches", func() { - m.Post("/", repo.SetDefaultBranchPost) + m.Post("/", repo_setting.SetDefaultBranchPost) }, repo.MustBeNotEmpty) m.Group("/branches", func() { - m.Get("/", repo.ProtectedBranchRules) - m.Combo("/edit").Get(repo.SettingsProtectedBranch). - Post(web.Bind(forms.ProtectBranchForm{}), context.RepoMustNotBeArchived(), repo.SettingsProtectedBranchPost) - m.Post("/{id}/delete", repo.DeleteProtectedBranchRulePost) + m.Get("/", repo_setting.ProtectedBranchRules) + m.Combo("/edit").Get(repo_setting.SettingsProtectedBranch). + Post(web.Bind(forms.ProtectBranchForm{}), context.RepoMustNotBeArchived(), repo_setting.SettingsProtectedBranchPost) + m.Post("/{id}/delete", repo_setting.DeleteProtectedBranchRulePost) }, repo.MustBeNotEmpty) - m.Post("/rename_branch", web.Bind(forms.RenameBranchForm{}), context.RepoMustNotBeArchived(), repo.RenameBranchPost) + m.Post("/rename_branch", web.Bind(forms.RenameBranchForm{}), context.RepoMustNotBeArchived(), repo_setting.RenameBranchPost) m.Group("/tags", func() { - m.Get("", repo.Tags) - m.Post("", web.Bind(forms.ProtectTagForm{}), context.RepoMustNotBeArchived(), repo.NewProtectedTagPost) - m.Post("/delete", context.RepoMustNotBeArchived(), repo.DeleteProtectedTagPost) - m.Get("/{id}", repo.EditProtectedTag) - m.Post("/{id}", web.Bind(forms.ProtectTagForm{}), context.RepoMustNotBeArchived(), repo.EditProtectedTagPost) + m.Get("", repo_setting.ProtectedTags) + m.Post("", web.Bind(forms.ProtectTagForm{}), context.RepoMustNotBeArchived(), repo_setting.NewProtectedTagPost) + m.Post("/delete", context.RepoMustNotBeArchived(), repo_setting.DeleteProtectedTagPost) + m.Get("/{id}", repo_setting.EditProtectedTag) + m.Post("/{id}", web.Bind(forms.ProtectTagForm{}), context.RepoMustNotBeArchived(), repo_setting.EditProtectedTagPost) }) m.Group("/hooks/git", func() { - m.Get("", repo.GitHooks) - m.Combo("/{name}").Get(repo.GitHooksEdit). - Post(repo.GitHooksEditPost) + m.Get("", repo_setting.GitHooks) + m.Combo("/{name}").Get(repo_setting.GitHooksEdit). + Post(repo_setting.GitHooksEditPost) }, context.GitHookService()) m.Group("/hooks", func() { - m.Get("", repo.Webhooks) - m.Post("/delete", repo.DeleteWebhook) + m.Get("", repo_setting.Webhooks) + m.Post("/delete", repo_setting.DeleteWebhook) addWebhookAddRoutes() m.Group("/{id}", func() { - m.Get("", repo.WebHooksEdit) - m.Post("/test", repo.TestWebhook) - m.Post("/replay/{uuid}", repo.ReplayWebhook) + m.Get("", repo_setting.WebHooksEdit) + m.Post("/test", repo_setting.TestWebhook) + m.Post("/replay/{uuid}", repo_setting.ReplayWebhook) }) addWebhookEditRoutes() }, webhooksEnabled) m.Group("/keys", func() { - m.Combo("").Get(repo.DeployKeys). - Post(web.Bind(forms.AddKeyForm{}), repo.DeployKeysPost) - m.Post("/delete", repo.DeleteDeployKey) + m.Combo("").Get(repo_setting.DeployKeys). + Post(web.Bind(forms.AddKeyForm{}), repo_setting.DeployKeysPost) + m.Post("/delete", repo_setting.DeleteDeployKey) }) m.Group("/lfs", func() { - m.Get("/", repo.LFSFiles) - m.Get("/show/{oid}", repo.LFSFileGet) - m.Post("/delete/{oid}", repo.LFSDelete) - m.Get("/pointers", repo.LFSPointerFiles) - m.Post("/pointers/associate", repo.LFSAutoAssociate) - m.Get("/find", repo.LFSFileFind) + m.Get("/", repo_setting.LFSFiles) + m.Get("/show/{oid}", repo_setting.LFSFileGet) + m.Post("/delete/{oid}", repo_setting.LFSDelete) + m.Get("/pointers", repo_setting.LFSPointerFiles) + m.Post("/pointers/associate", repo_setting.LFSAutoAssociate) + m.Get("/find", repo_setting.LFSFileFind) m.Group("/locks", func() { - m.Get("/", repo.LFSLocks) - m.Post("/", repo.LFSLockFile) - m.Post("/{lid}/unlock", repo.LFSUnlock) + m.Get("/", repo_setting.LFSLocks) + m.Post("/", repo_setting.LFSLockFile) + m.Post("/{lid}/unlock", repo_setting.LFSUnlock) }) }) m.Group("/actions", func() { @@ -1257,20 +1257,20 @@ func registerRoutes(m *web.Route) { m.Group("/blob_excerpt", func() { m.Get("/{sha}", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.ExcerptBlob) - }, func(ctx *context.Context) (cancel gocontext.CancelFunc) { + }, func(ctx *context.Context) gocontext.CancelFunc { if ctx.FormBool("wiki") { ctx.Data["PageIsWiki"] = true repo.MustEnableWiki(ctx) - return + return nil } reqRepoCodeReader(ctx) if ctx.Written() { - return + return nil } - cancel = context.RepoRef()(ctx) + cancel := context.RepoRef()(ctx) if ctx.Written() { - return + return cancel } repo.MustBeNotEmpty(ctx) @@ -1278,9 +1278,10 @@ func registerRoutes(m *web.Route) { }) m.Group("/pulls/{index}", func() { + m.Get("", repo.SetWhitespaceBehavior, repo.GetPullDiffStats, repo.ViewIssue) m.Get(".diff", repo.DownloadPullDiff) m.Get(".patch", repo.DownloadPullPatch) - m.Get("/commits", context.RepoRef(), repo.ViewPullCommits) + m.Get("/commits", context.RepoRef(), repo.SetWhitespaceBehavior, repo.GetPullDiffStats, repo.ViewPullCommits) m.Post("/merge", context.RepoMustNotBeArchived(), web.Bind(forms.MergePullRequestForm{}), repo.MergePullRequest) m.Post("/cancel_auto_merge", context.RepoMustNotBeArchived(), repo.CancelAutoMergePullRequest) m.Post("/update", repo.UpdatePullRequest) diff --git a/routers/web/webfinger.go b/routers/web/webfinger.go index 1442e09a314..0966b04951a 100644 --- a/routers/web/webfinger.go +++ b/routers/web/webfinger.go @@ -18,18 +18,18 @@ import ( // https://datatracker.ietf.org/doc/html/draft-ietf-appsawg-webfinger-14#section-4.4 type webfingerJRD struct { - Subject string `json:"subject,omitempty"` - Aliases []string `json:"aliases,omitempty"` - Properties map[string]interface{} `json:"properties,omitempty"` - Links []*webfingerLink `json:"links,omitempty"` + Subject string `json:"subject,omitempty"` + Aliases []string `json:"aliases,omitempty"` + Properties map[string]any `json:"properties,omitempty"` + Links []*webfingerLink `json:"links,omitempty"` } type webfingerLink struct { - Rel string `json:"rel,omitempty"` - Type string `json:"type,omitempty"` - Href string `json:"href,omitempty"` - Titles map[string]string `json:"titles,omitempty"` - Properties map[string]interface{} `json:"properties,omitempty"` + Rel string `json:"rel,omitempty"` + Type string `json:"type,omitempty"` + Href string `json:"href,omitempty"` + Titles map[string]string `json:"titles,omitempty"` + Properties map[string]any `json:"properties,omitempty"` } // WebfingerQuery returns information about a resource diff --git a/services/actions/notifier.go b/services/actions/notifier.go index 507eeaacf6c..cfe2e284dac 100644 --- a/services/actions/notifier.go +++ b/services/actions/notifier.go @@ -171,7 +171,7 @@ func (n *actionsNotifier) NotifyCreateIssueComment(ctx context.Context, doer *us WithPayload(&api.IssueCommentPayload{ Action: api.HookIssueCommentCreated, Issue: convert.ToAPIIssue(ctx, issue), - Comment: convert.ToComment(ctx, comment), + Comment: convert.ToAPIComment(ctx, repo, comment), Repository: convert.ToRepo(ctx, repo, permission), Sender: convert.ToUser(ctx, doer, nil), IsPull: true, @@ -185,7 +185,7 @@ func (n *actionsNotifier) NotifyCreateIssueComment(ctx context.Context, doer *us WithPayload(&api.IssueCommentPayload{ Action: api.HookIssueCommentCreated, Issue: convert.ToAPIIssue(ctx, issue), - Comment: convert.ToComment(ctx, comment), + Comment: convert.ToAPIComment(ctx, repo, comment), Repository: convert.ToRepo(ctx, repo, permission), Sender: convert.ToUser(ctx, doer, nil), IsPull: false, diff --git a/services/actions/notifier_helper.go b/services/actions/notifier_helper.go index 8e6cdcf680d..90ad3001bad 100644 --- a/services/actions/notifier_helper.go +++ b/services/actions/notifier_helper.go @@ -152,7 +152,6 @@ func notify(ctx context.Context, input *notifyInput) error { } else { for _, wf := range workflows { if wf.TriggerEvent != actions_module.GithubEventPullRequestTarget { - wf.Ref = ref detectedWorkflows = append(detectedWorkflows, wf) } } @@ -174,7 +173,6 @@ func notify(ctx context.Context, input *notifyInput) error { } else { for _, wf := range baseWorkflows { if wf.TriggerEvent == actions_module.GithubEventPullRequestTarget { - wf.Ref = baseRef detectedWorkflows = append(detectedWorkflows, wf) } } @@ -212,8 +210,8 @@ func notify(ctx context.Context, input *notifyInput) error { OwnerID: input.Repo.OwnerID, WorkflowID: dwf.EntryName, TriggerUserID: input.Doer.ID, - Ref: dwf.Ref, - CommitSHA: dwf.Commit.ID.String(), + Ref: ref, + CommitSHA: commit.ID.String(), IsForkPullRequest: isForkPullRequest, Event: input.Event, EventPayload: string(p), @@ -262,7 +260,7 @@ func notifyRelease(ctx context.Context, doer *user_model.User, rel *repo_model.R WithRef(git.RefNameFromTag(rel.TagName).String()). WithPayload(&api.ReleasePayload{ Action: action, - Release: convert.ToRelease(ctx, rel), + Release: convert.ToAPIRelease(ctx, rel.Repo, rel), Repository: convert.ToRepo(ctx, rel.Repo, permission), Sender: convert.ToUser(ctx, doer, nil), }). diff --git a/services/auth/group.go b/services/auth/group.go index a1ff65f2038..7193dfcf49e 100644 --- a/services/auth/group.go +++ b/services/auth/group.go @@ -49,12 +49,22 @@ func (b *Group) Name() string { // Verify extracts and validates func (b *Group) Verify(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) (*user_model.User, error) { // Try to sign in with each of the enabled plugins + var retErr error for _, ssoMethod := range b.methods { user, err := ssoMethod.Verify(req, w, store, sess) if err != nil { - return nil, err + if retErr == nil { + retErr = err + } + // Try other methods if this one failed. + // Some methods may share the same protocol to detect if they are matched. + // For example, OAuth2 and conan.Auth both read token from "Authorization: Bearer " header, + // If OAuth2 returns error, we should give conan.Auth a chance to try. + continue } + // If any method returns a user, we can stop trying. + // Return the user and ignore any error returned by previous methods. if user != nil { if store.GetData()["AuthedMethod"] == nil { if named, ok := ssoMethod.(Named); ok { @@ -65,5 +75,6 @@ func (b *Group) Verify(req *http.Request, w http.ResponseWriter, store DataStore } } - return nil, nil + // If no method returns a user, return the error returned by the first method. + return nil, retErr } diff --git a/services/auth/oauth2.go b/services/auth/oauth2.go index b70f84da9b6..0dd7a12d2c4 100644 --- a/services/auth/oauth2.go +++ b/services/auth/oauth2.go @@ -59,31 +59,32 @@ func (o *OAuth2) Name() string { return "oauth2" } +// parseToken returns the token from request, and a boolean value +// representing whether the token exists or not +func parseToken(req *http.Request) (string, bool) { + _ = req.ParseForm() + // Check token. + if token := req.Form.Get("token"); token != "" { + return token, true + } + // Check access token. + if token := req.Form.Get("access_token"); token != "" { + return token, true + } + // check header token + if auHead := req.Header.Get("Authorization"); auHead != "" { + auths := strings.Fields(auHead) + if len(auths) == 2 && (auths[0] == "token" || strings.ToLower(auths[0]) == "bearer") { + return auths[1], true + } + } + return "", false +} + // userIDFromToken returns the user id corresponding to the OAuth token. // It will set 'IsApiToken' to true if the token is an API token and // set 'ApiTokenScope' to the scope of the access token -func (o *OAuth2) userIDFromToken(req *http.Request, store DataStore) int64 { - _ = req.ParseForm() - - // Check access token. - tokenSHA := req.Form.Get("token") - if len(tokenSHA) == 0 { - tokenSHA = req.Form.Get("access_token") - } - if len(tokenSHA) == 0 { - // Well, check with header again. - auHead := req.Header.Get("Authorization") - if len(auHead) > 0 { - auths := strings.Fields(auHead) - if len(auths) == 2 && (auths[0] == "token" || strings.ToLower(auths[0]) == "bearer") { - tokenSHA = auths[1] - } - } - } - if len(tokenSHA) == 0 { - return 0 - } - +func (o *OAuth2) userIDFromToken(tokenSHA string, store DataStore) int64 { // Let's see if token is valid. if strings.Contains(tokenSHA, ".") { uid := CheckOAuthAccessToken(tokenSHA) @@ -129,10 +130,15 @@ func (o *OAuth2) Verify(req *http.Request, w http.ResponseWriter, store DataStor return nil, nil } - id := o.userIDFromToken(req, store) + token, ok := parseToken(req) + if !ok { + return nil, nil + } + + id := o.userIDFromToken(token, store) if id <= 0 && id != -2 { // -2 means actions, so we need to allow it. - return nil, nil + return nil, user_model.ErrUserNotExist{} } log.Trace("OAuth2 Authorization: Found token for user[%d]", id) diff --git a/services/auth/source/db/authenticate.go b/services/auth/source/db/authenticate.go index 773ec601ba3..34a0459149a 100644 --- a/services/auth/source/db/authenticate.go +++ b/services/auth/source/db/authenticate.go @@ -4,19 +4,54 @@ package db import ( + "fmt" + "code.gitea.io/gitea/models/db" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/util" ) +// ErrUserPasswordNotSet represents a "ErrUserPasswordNotSet" kind of error. +type ErrUserPasswordNotSet struct { + UID int64 + Name string +} + +func (err ErrUserPasswordNotSet) Error() string { + return fmt.Sprintf("user's password isn't set [uid: %d, name: %s]", err.UID, err.Name) +} + +// Unwrap unwraps this error as a ErrInvalidArgument error +func (err ErrUserPasswordNotSet) Unwrap() error { + return util.ErrInvalidArgument +} + +// ErrUserPasswordInvalid represents a "ErrUserPasswordInvalid" kind of error. +type ErrUserPasswordInvalid struct { + UID int64 + Name string +} + +func (err ErrUserPasswordInvalid) Error() string { + return fmt.Sprintf("user's password is invalid [uid: %d, name: %s]", err.UID, err.Name) +} + +// Unwrap unwraps this error as a ErrInvalidArgument error +func (err ErrUserPasswordInvalid) Unwrap() error { + return util.ErrInvalidArgument +} + // Authenticate authenticates the provided user against the DB func Authenticate(user *user_model.User, login, password string) (*user_model.User, error) { if user == nil { return nil, user_model.ErrUserNotExist{Name: login} } - if !user.IsPasswordSet() || !user.ValidatePassword(password) { - return nil, user_model.ErrUserNotExist{UID: user.ID, Name: user.Name} + if !user.IsPasswordSet() { + return nil, ErrUserPasswordNotSet{UID: user.ID, Name: user.Name} + } else if !user.ValidatePassword(password) { + return nil, ErrUserPasswordInvalid{UID: user.ID, Name: user.Name} } // Update password hash if server password hash algorithm have changed diff --git a/services/auth/source/oauth2/jwtsigningkey.go b/services/auth/source/oauth2/jwtsigningkey.go index ed0fc67ca0f..33bd3648e71 100644 --- a/services/auth/source/oauth2/jwtsigningkey.go +++ b/services/auth/source/oauth2/jwtsigningkey.go @@ -38,8 +38,8 @@ func (err ErrInvalidAlgorithmType) Error() string { type JWTSigningKey interface { IsSymmetric() bool SigningMethod() jwt.SigningMethod - SignKey() interface{} - VerifyKey() interface{} + SignKey() any + VerifyKey() any ToJWK() (map[string]string, error) PreProcessToken(*jwt.Token) } @@ -57,11 +57,11 @@ func (key hmacSigningKey) SigningMethod() jwt.SigningMethod { return key.signingMethod } -func (key hmacSigningKey) SignKey() interface{} { +func (key hmacSigningKey) SignKey() any { return key.secret } -func (key hmacSigningKey) VerifyKey() interface{} { +func (key hmacSigningKey) VerifyKey() any { return key.secret } @@ -101,11 +101,11 @@ func (key rsaSingingKey) SigningMethod() jwt.SigningMethod { return key.signingMethod } -func (key rsaSingingKey) SignKey() interface{} { +func (key rsaSingingKey) SignKey() any { return key.key } -func (key rsaSingingKey) VerifyKey() interface{} { +func (key rsaSingingKey) VerifyKey() any { return key.key.Public() } @@ -152,11 +152,11 @@ func (key eddsaSigningKey) SigningMethod() jwt.SigningMethod { return key.signingMethod } -func (key eddsaSigningKey) SignKey() interface{} { +func (key eddsaSigningKey) SignKey() any { return key.key } -func (key eddsaSigningKey) VerifyKey() interface{} { +func (key eddsaSigningKey) VerifyKey() any { return key.key.Public() } @@ -203,11 +203,11 @@ func (key ecdsaSingingKey) SigningMethod() jwt.SigningMethod { return key.signingMethod } -func (key ecdsaSingingKey) SignKey() interface{} { +func (key ecdsaSingingKey) SignKey() any { return key.key } -func (key ecdsaSingingKey) VerifyKey() interface{} { +func (key ecdsaSingingKey) VerifyKey() any { return key.key.Public() } @@ -229,7 +229,7 @@ func (key ecdsaSingingKey) PreProcessToken(token *jwt.Token) { } // CreateJWTSigningKey creates a signing key from an algorithm / key pair. -func CreateJWTSigningKey(algorithm string, key interface{}) (JWTSigningKey, error) { +func CreateJWTSigningKey(algorithm string, key any) (JWTSigningKey, error) { var signingMethod jwt.SigningMethod switch algorithm { case "HS256": @@ -292,7 +292,7 @@ var DefaultSigningKey JWTSigningKey // InitSigningKey creates the default signing key from settings or creates a random key. func InitSigningKey() error { var err error - var key interface{} + var key any switch setting.OAuth2.JWTSigningAlgorithm { case "HS256": @@ -335,7 +335,7 @@ func InitSigningKey() error { // loadSymmetricKey checks if the configured secret is valid. // If it is not valid, it will return an error. -func loadSymmetricKey() (interface{}, error) { +func loadSymmetricKey() (any, error) { key := make([]byte, 32) n, err := base64.RawURLEncoding.Decode(key, []byte(setting.OAuth2.JWTSecretBase64)) if err != nil { @@ -350,7 +350,7 @@ func loadSymmetricKey() (interface{}, error) { // loadOrCreateAsymmetricKey checks if the configured private key exists. // If it does not exist a new random key gets generated and saved on the configured path. -func loadOrCreateAsymmetricKey() (interface{}, error) { +func loadOrCreateAsymmetricKey() (any, error) { keyPath := setting.OAuth2.JWTSigningPrivateKeyFile isExist, err := util.IsExist(keyPath) @@ -359,7 +359,7 @@ func loadOrCreateAsymmetricKey() (interface{}, error) { } if !isExist { err := func() error { - key, err := func() (interface{}, error) { + key, err := func() (any, error) { switch { case strings.HasPrefix(setting.OAuth2.JWTSigningAlgorithm, "RS"): return rsa.GenerateKey(rand.Reader, 4096) diff --git a/services/auth/source/oauth2/token.go b/services/auth/source/oauth2/token.go index c5a064054e5..612c9db8cf5 100644 --- a/services/auth/source/oauth2/token.go +++ b/services/auth/source/oauth2/token.go @@ -41,7 +41,7 @@ type Token struct { // ParseToken parses a signed jwt string func ParseToken(jwtToken string, signingKey JWTSigningKey) (*Token, error) { - parsedToken, err := jwt.ParseWithClaims(jwtToken, &Token{}, func(token *jwt.Token) (interface{}, error) { + parsedToken, err := jwt.ParseWithClaims(jwtToken, &Token{}, func(token *jwt.Token) (any, error) { if token.Method == nil || token.Method.Alg() != signingKey.SigningMethod().Alg() { return nil, fmt.Errorf("unexpected signing algo: %v", token.Header["alg"]) } diff --git a/services/context/user.go b/services/context/user.go index 4e74aa50bd1..62d2dc0aa23 100644 --- a/services/context/user.go +++ b/services/context/user.go @@ -15,7 +15,7 @@ import ( // UserAssignmentWeb returns a middleware to handle context-user assignment for web routes func UserAssignmentWeb() func(ctx *context.Context) { return func(ctx *context.Context) { - errorFn := func(status int, title string, obj interface{}) { + errorFn := func(status int, title string, obj any) { err, ok := obj.(error) if !ok { err = fmt.Errorf("%s", obj) @@ -58,7 +58,7 @@ func UserAssignmentAPI() func(ctx *context.APIContext) { } } -func userAssignment(ctx *context.Base, doer *user_model.User, errCb func(int, string, interface{})) (contextUser *user_model.User) { +func userAssignment(ctx *context.Base, doer *user_model.User, errCb func(int, string, any)) (contextUser *user_model.User) { username := ctx.Params(":username") if doer != nil && doer.LowerName == strings.ToLower(username) { diff --git a/services/convert/activity.go b/services/convert/activity.go index 71a2722a493..01fef73e589 100644 --- a/services/convert/activity.go +++ b/services/convert/activity.go @@ -37,7 +37,7 @@ func ToActivity(ctx context.Context, ac *activities_model.Action, doer *user_mod if ac.Comment != nil { result.CommentID = ac.CommentID - result.Comment = ToComment(ctx, ac.Comment) + result.Comment = ToAPIComment(ctx, ac.Repo, ac.Comment) } return result diff --git a/services/convert/attachment.go b/services/convert/attachment.go index ddba181a120..ab36a1c5778 100644 --- a/services/convert/attachment.go +++ b/services/convert/attachment.go @@ -4,12 +4,38 @@ package convert import ( + "strconv" + repo_model "code.gitea.io/gitea/models/repo" + "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" ) -// ToAttachment converts models.Attachment to api.Attachment -func ToAttachment(a *repo_model.Attachment) *api.Attachment { +func WebAssetDownloadURL(repo *repo_model.Repository, attach *repo_model.Attachment) string { + return attach.DownloadURL() +} + +func APIAssetDownloadURL(repo *repo_model.Repository, attach *repo_model.Attachment) string { + if attach.CustomDownloadURL != "" { + return attach.CustomDownloadURL + } + + // /repos/{owner}/{repo}/releases/{id}/assets/{attachment_id} + return setting.AppURL + "api/repos/" + repo.FullName() + "/releases/" + strconv.FormatInt(attach.ReleaseID, 10) + "/assets/" + strconv.FormatInt(attach.ID, 10) +} + +// ToAttachment converts models.Attachment to api.Attachment for API usage +func ToAttachment(repo *repo_model.Repository, a *repo_model.Attachment) *api.Attachment { + return toAttachment(repo, a, WebAssetDownloadURL) +} + +// ToAPIAttachment converts models.Attachment to api.Attachment for API usage +func ToAPIAttachment(repo *repo_model.Repository, a *repo_model.Attachment) *api.Attachment { + return toAttachment(repo, a, APIAssetDownloadURL) +} + +// toAttachment converts models.Attachment to api.Attachment for API usage +func toAttachment(repo *repo_model.Repository, a *repo_model.Attachment, getDownloadURL func(repo *repo_model.Repository, attach *repo_model.Attachment) string) *api.Attachment { return &api.Attachment{ ID: a.ID, Name: a.Name, @@ -17,14 +43,18 @@ func ToAttachment(a *repo_model.Attachment) *api.Attachment { DownloadCount: a.DownloadCount, Size: a.Size, UUID: a.UUID, - DownloadURL: a.DownloadURL(), + DownloadURL: getDownloadURL(repo, a), // for web request json and api request json, return different download urls } } -func ToAttachments(attachments []*repo_model.Attachment) []*api.Attachment { +func ToAPIAttachments(repo *repo_model.Repository, attachments []*repo_model.Attachment) []*api.Attachment { + return toAttachments(repo, attachments, APIAssetDownloadURL) +} + +func toAttachments(repo *repo_model.Repository, attachments []*repo_model.Attachment, getDownloadURL func(repo *repo_model.Repository, attach *repo_model.Attachment) string) []*api.Attachment { converted := make([]*api.Attachment, 0, len(attachments)) for _, attachment := range attachments { - converted = append(converted, ToAttachment(attachment)) + converted = append(converted, toAttachment(repo, attachment, getDownloadURL)) } return converted } diff --git a/services/convert/issue.go b/services/convert/issue.go index bcb1618e8fa..d81840f0252 100644 --- a/services/convert/issue.go +++ b/services/convert/issue.go @@ -19,11 +19,19 @@ import ( api "code.gitea.io/gitea/modules/structs" ) +func ToIssue(ctx context.Context, issue *issues_model.Issue) *api.Issue { + return toIssue(ctx, issue, WebAssetDownloadURL) +} + // ToAPIIssue converts an Issue to API format // it assumes some fields assigned with values: // Required - Poster, Labels, // Optional - Milestone, Assignee, PullRequest func ToAPIIssue(ctx context.Context, issue *issues_model.Issue) *api.Issue { + return toIssue(ctx, issue, APIAssetDownloadURL) +} + +func toIssue(ctx context.Context, issue *issues_model.Issue, getDownloadURL func(repo *repo_model.Repository, attach *repo_model.Attachment) string) *api.Issue { if err := issue.LoadLabels(ctx); err != nil { return &api.Issue{} } @@ -40,7 +48,7 @@ func ToAPIIssue(ctx context.Context, issue *issues_model.Issue) *api.Issue { Poster: ToUser(ctx, issue.Poster, nil), Title: issue.Title, Body: issue.Content, - Attachments: ToAttachments(issue.Attachments), + Attachments: toAttachments(issue.Repo, issue.Attachments, getDownloadURL), Ref: issue.Ref, State: issue.State(), IsLocked: issue.IsLocked, @@ -105,6 +113,15 @@ func ToAPIIssue(ctx context.Context, issue *issues_model.Issue) *api.Issue { return apiIssue } +// ToIssueList converts an IssueList to API format +func ToIssueList(ctx context.Context, il issues_model.IssueList) []*api.Issue { + result := make([]*api.Issue, len(il)) + for i := range il { + result[i] = ToIssue(ctx, il[i]) + } + return result +} + // ToAPIIssueList converts an IssueList to API format func ToAPIIssueList(ctx context.Context, il issues_model.IssueList) []*api.Issue { result := make([]*api.Issue, len(il)) diff --git a/services/convert/issue_comment.go b/services/convert/issue_comment.go index db48faa69e6..b0145c38e63 100644 --- a/services/convert/issue_comment.go +++ b/services/convert/issue_comment.go @@ -14,8 +14,8 @@ import ( "code.gitea.io/gitea/modules/util" ) -// ToComment converts a issues_model.Comment to the api.Comment format -func ToComment(ctx context.Context, c *issues_model.Comment) *api.Comment { +// ToAPIComment converts a issues_model.Comment to the api.Comment format for API usage +func ToAPIComment(ctx context.Context, repo *repo_model.Repository, c *issues_model.Comment) *api.Comment { return &api.Comment{ ID: c.ID, Poster: ToUser(ctx, c.Poster, nil), @@ -23,14 +23,14 @@ func ToComment(ctx context.Context, c *issues_model.Comment) *api.Comment { IssueURL: c.IssueURL(), PRURL: c.PRURL(), Body: c.Content, - Attachments: ToAttachments(c.Attachments), + Attachments: ToAPIAttachments(repo, c.Attachments), Created: c.CreatedUnix.AsTime(), Updated: c.UpdatedUnix.AsTime(), } } // ToTimelineComment converts a issues_model.Comment to the api.TimelineComment format -func ToTimelineComment(ctx context.Context, c *issues_model.Comment, doer *user_model.User) *api.TimelineComment { +func ToTimelineComment(ctx context.Context, repo *repo_model.Repository, c *issues_model.Comment, doer *user_model.User) *api.TimelineComment { err := c.LoadMilestone(ctx) if err != nil { log.Error("LoadMilestone: %v", err) @@ -143,7 +143,7 @@ func ToTimelineComment(ctx context.Context, c *issues_model.Comment, doer *user_ log.Error("LoadPoster: %v", err) return nil } - comment.RefComment = ToComment(ctx, com) + comment.RefComment = ToAPIComment(ctx, repo, com) } if c.Label != nil { diff --git a/services/convert/release.go b/services/convert/release.go index ca28aa0d6b0..d8aa46d4326 100644 --- a/services/convert/release.go +++ b/services/convert/release.go @@ -10,8 +10,8 @@ import ( api "code.gitea.io/gitea/modules/structs" ) -// ToRelease convert a repo_model.Release to api.Release -func ToRelease(ctx context.Context, r *repo_model.Release) *api.Release { +// ToAPIRelease convert a repo_model.Release to api.Release +func ToAPIRelease(ctx context.Context, repo *repo_model.Repository, r *repo_model.Release) *api.Release { return &api.Release{ ID: r.ID, TagName: r.TagName, @@ -27,6 +27,6 @@ func ToRelease(ctx context.Context, r *repo_model.Release) *api.Release { CreatedAt: r.CreatedUnix.AsTime(), PublishedAt: r.CreatedUnix.AsTime(), Publisher: ToUser(ctx, r.Publisher, nil), - Attachments: ToAttachments(r.Attachments), + Attachments: ToAPIAttachments(repo, r.Attachments), } } diff --git a/services/cron/setting.go b/services/cron/setting.go index 952a4d17ab6..0656307cba0 100644 --- a/services/cron/setting.go +++ b/services/cron/setting.go @@ -14,7 +14,7 @@ type Config interface { IsEnabled() bool DoRunAtStart() bool GetSchedule() string - FormatMessage(locale translation.Locale, name, status, doer string, args ...interface{}) string + FormatMessage(locale translation.Locale, name, status, doer string, args ...any) string DoNoticeOnSuccess() bool } @@ -68,8 +68,8 @@ func (b *BaseConfig) DoNoticeOnSuccess() bool { // FormatMessage returns a message for the task // Please note the `status` string will be concatenated with `admin.dashboard.cron.` and `admin.dashboard.task.` to provide locale messages. Similarly `name` will be composed with `admin.dashboard.` to provide the locale name for the task. -func (b *BaseConfig) FormatMessage(locale translation.Locale, name, status, doer string, args ...interface{}) string { - realArgs := make([]interface{}, 0, len(args)+2) +func (b *BaseConfig) FormatMessage(locale translation.Locale, name, status, doer string, args ...any) string { + realArgs := make([]any, 0, len(args)+2) realArgs = append(realArgs, locale.Tr("admin.dashboard."+name)) if doer == "" { realArgs = append(realArgs, "(Cron)") diff --git a/services/externalaccount/link.go b/services/externalaccount/link.go index dcdc57ee489..a19d4c5ab3e 100644 --- a/services/externalaccount/link.go +++ b/services/externalaccount/link.go @@ -13,8 +13,8 @@ import ( // Store represents a thing that stores things type Store interface { - Get(interface{}) interface{} - Set(interface{}, interface{}) error + Get(any) any + Set(any, any) error Release() error } diff --git a/services/forms/user_form_hidden_comments.go b/services/forms/user_form_hidden_comments.go index 7eb800a0201..03e629a553a 100644 --- a/services/forms/user_form_hidden_comments.go +++ b/services/forms/user_form_hidden_comments.go @@ -90,7 +90,7 @@ func IsUserHiddenCommentTypeGroupChecked(group string, hiddenCommentTypes *big.I commentTypes, ok := hiddenCommentTypeGroups[group] if !ok { log.Critical("the group map for hidden comment types is out of sync, unknown group: %v", group) - return + return false } if hiddenCommentTypes == nil { return false diff --git a/services/gitdiff/gitdiff.go b/services/gitdiff/gitdiff.go index 9adf3b94009..9e1db6fd435 100644 --- a/services/gitdiff/gitdiff.go +++ b/services/gitdiff/gitdiff.go @@ -779,7 +779,7 @@ func skipToNextDiffHead(input *bufio.Reader) (line string, err error) { for { lineBytes, isFragment, err = input.ReadLine() if err != nil { - return + return "", err } if wasFragment { wasFragment = isFragment @@ -795,7 +795,7 @@ func skipToNextDiffHead(input *bufio.Reader) (line string, err error) { var tail string tail, err = input.ReadString('\n') if err != nil { - return + return "", err } line += tail } @@ -821,22 +821,21 @@ func parseHunks(curFile *DiffFile, maxLines, maxLineCharacters int, input *bufio _, isFragment, err = input.ReadLine() if err != nil { // Now by the definition of ReadLine this cannot be io.EOF - err = fmt.Errorf("unable to ReadLine: %w", err) - return + return nil, false, fmt.Errorf("unable to ReadLine: %w", err) } } sb.Reset() lineBytes, isFragment, err = input.ReadLine() if err != nil { if err == io.EOF { - return + return lineBytes, isFragment, err } err = fmt.Errorf("unable to ReadLine: %w", err) - return + return nil, false, err } if lineBytes[0] == 'd' { // End of hunks - return + return lineBytes, isFragment, err } switch lineBytes[0] { @@ -853,8 +852,7 @@ func parseHunks(curFile *DiffFile, maxLines, maxLineCharacters int, input *bufio lineBytes, isFragment, err = input.ReadLine() if err != nil { // Now by the definition of ReadLine this cannot be io.EOF - err = fmt.Errorf("unable to ReadLine: %w", err) - return + return nil, false, fmt.Errorf("unable to ReadLine: %w", err) } _, _ = sb.Write(lineBytes) } @@ -884,8 +882,7 @@ func parseHunks(curFile *DiffFile, maxLines, maxLineCharacters int, input *bufio } // This is used only to indicate that the current file does not have a terminal newline if !bytes.Equal(lineBytes, []byte("\\ No newline at end of file")) { - err = fmt.Errorf("unexpected line in hunk: %s", string(lineBytes)) - return + return nil, false, fmt.Errorf("unexpected line in hunk: %s", string(lineBytes)) } // Technically this should be the end the file! // FIXME: we should be putting a marker at the end of the file if there is no terminal new line @@ -953,8 +950,7 @@ func parseHunks(curFile *DiffFile, maxLines, maxLineCharacters int, input *bufio curSection.Lines = append(curSection.Lines, diffLine) default: // This is unexpected - err = fmt.Errorf("unexpected line in hunk: %s", string(lineBytes)) - return + return nil, false, fmt.Errorf("unexpected line in hunk: %s", string(lineBytes)) } line := string(lineBytes) @@ -965,8 +961,7 @@ func parseHunks(curFile *DiffFile, maxLines, maxLineCharacters int, input *bufio lineBytes, isFragment, err = input.ReadLine() if err != nil { // Now by the definition of ReadLine this cannot be io.EOF - err = fmt.Errorf("unable to ReadLine: %w", err) - return + return lineBytes, isFragment, fmt.Errorf("unable to ReadLine: %w", err) } } } @@ -1229,6 +1224,42 @@ func GetDiff(gitRepo *git.Repository, opts *DiffOptions, files ...string) (*Diff return diff, nil } +type PullDiffStats struct { + TotalAddition, TotalDeletion int +} + +// GetPullDiffStats +func GetPullDiffStats(gitRepo *git.Repository, opts *DiffOptions) (*PullDiffStats, error) { + repoPath := gitRepo.Path + + diff := &PullDiffStats{} + + separator := "..." + if opts.DirectComparison { + separator = ".." + } + + diffPaths := []string{opts.BeforeCommitID + separator + opts.AfterCommitID} + if len(opts.BeforeCommitID) == 0 || opts.BeforeCommitID == git.EmptySHA { + diffPaths = []string{git.EmptyTreeSHA, opts.AfterCommitID} + } + + var err error + + _, diff.TotalAddition, diff.TotalDeletion, err = git.GetDiffShortStat(gitRepo.Ctx, repoPath, nil, diffPaths...) + if err != nil && strings.Contains(err.Error(), "no merge base") { + // git >= 2.28 now returns an error if base and head have become unrelated. + // previously it would return the results of git diff --shortstat base head so let's try that... + diffPaths = []string{opts.BeforeCommitID, opts.AfterCommitID} + _, diff.TotalAddition, diff.TotalDeletion, err = git.GetDiffShortStat(gitRepo.Ctx, repoPath, nil, diffPaths...) + } + if err != nil { + return nil, err + } + + return diff, nil +} + // SyncAndGetUserSpecificDiff is like GetDiff, except that user specific data such as which files the given user has already viewed on the given PR will also be set // Additionally, the database asynchronously is updated if files have changed since the last review func SyncAndGetUserSpecificDiff(ctx context.Context, userID int64, pull *issues_model.PullRequest, gitRepo *git.Repository, opts *DiffOptions, files ...string) (*Diff, error) { diff --git a/services/issue/assignee.go b/services/issue/assignee.go index 943a761e282..8fe35b56020 100644 --- a/services/issue/assignee.go +++ b/services/issue/assignee.go @@ -46,13 +46,12 @@ func DeleteNotPassedAssignee(ctx context.Context, issue *issues_model.Issue, doe func ToggleAssignee(ctx context.Context, issue *issues_model.Issue, doer *user_model.User, assigneeID int64) (removed bool, comment *issues_model.Comment, err error) { removed, comment, err = issues_model.ToggleIssueAssignee(ctx, issue, doer, assigneeID) if err != nil { - return + return false, nil, err } - assignee, err1 := user_model.GetUserByID(ctx, assigneeID) - if err1 != nil { - err = err1 - return + assignee, err := user_model.GetUserByID(ctx, assigneeID) + if err != nil { + return false, nil, err } notification.NotifyIssueChangeAssignee(ctx, doer, issue, assignee, removed, comment) @@ -236,23 +235,23 @@ func TeamReviewRequest(ctx context.Context, issue *issues_model.Issue, doer *use } if err != nil { - return + return nil, err } if comment == nil || !isAdd { - return + return nil, nil } // notify all user in this team - if err = comment.LoadIssue(ctx); err != nil { - return + if err := comment.LoadIssue(ctx); err != nil { + return nil, err } members, err := organization.GetTeamMembers(ctx, &organization.SearchMembersOptions{ TeamID: reviewer.ID, }) if err != nil { - return + return nil, err } for _, member := range members { diff --git a/services/issue/issue.go b/services/issue/issue.go index 61890c75def..b6c6a26cbdc 100644 --- a/services/issue/issue.go +++ b/services/issue/issue.go @@ -49,17 +49,17 @@ func NewIssue(ctx context.Context, repo *repo_model.Repository, issue *issues_mo } // ChangeTitle changes the title of this issue, as the given user. -func ChangeTitle(ctx context.Context, issue *issues_model.Issue, doer *user_model.User, title string) (err error) { +func ChangeTitle(ctx context.Context, issue *issues_model.Issue, doer *user_model.User, title string) error { oldTitle := issue.Title issue.Title = title - if err = issues_model.ChangeIssueTitle(ctx, issue, doer, oldTitle); err != nil { - return + if err := issues_model.ChangeIssueTitle(ctx, issue, doer, oldTitle); err != nil { + return err } if issue.IsPull && issues_model.HasWorkInProgressPrefix(oldTitle) && !issues_model.HasWorkInProgressPrefix(title) { - if err = issues_model.PullRequestCodeOwnersReview(ctx, issue, issue.PullRequest); err != nil { - return + if err := issues_model.PullRequestCodeOwnersReview(ctx, issue, issue.PullRequest); err != nil { + return err } } diff --git a/services/issue/label.go b/services/issue/label.go index c18abbfcda3..ee821a49c94 100644 --- a/services/issue/label.go +++ b/services/issue/label.go @@ -12,9 +12,9 @@ import ( ) // ClearLabels clears all of an issue's labels -func ClearLabels(issue *issues_model.Issue, doer *user_model.User) (err error) { - if err = issues_model.ClearIssueLabels(issue, doer); err != nil { - return +func ClearLabels(issue *issues_model.Issue, doer *user_model.User) error { + if err := issues_model.ClearIssueLabels(issue, doer); err != nil { + return err } notification.NotifyIssueClearLabels(db.DefaultContext, doer, issue) diff --git a/services/lfs/server.go b/services/lfs/server.go index b32f218785e..cc0ec73105b 100644 --- a/services/lfs/server.go +++ b/services/lfs/server.go @@ -375,7 +375,7 @@ func VerifyHandler(ctx *context.Context) { writeStatus(ctx, status) } -func decodeJSON(req *http.Request, v interface{}) error { +func decodeJSON(req *http.Request, v any) error { defer req.Body.Close() dec := json.NewDecoder(req.Body) @@ -552,7 +552,7 @@ func handleLFSToken(ctx stdCtx.Context, tokenSHA string, target *repo_model.Repo if !strings.Contains(tokenSHA, ".") { return nil, nil } - token, err := jwt.ParseWithClaims(tokenSHA, &Claims{}, func(t *jwt.Token) (interface{}, error) { + token, err := jwt.ParseWithClaims(tokenSHA, &Claims{}, func(t *jwt.Token) (any, error) { if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok { return nil, fmt.Errorf("unexpected signing method: %v", t.Header["alg"]) } diff --git a/services/mailer/incoming/incoming.go b/services/mailer/incoming/incoming.go index 2653e80586d..eade0cf271b 100644 --- a/services/mailer/incoming/incoming.go +++ b/services/mailer/incoming/incoming.go @@ -205,7 +205,7 @@ loop: if err := c.Store( handledSet, imap.FormatFlagsOp(imap.AddFlags, true), - []interface{}{imap.DeletedFlag}, + []any{imap.DeletedFlag}, nil, ); err != nil { return fmt.Errorf("imap store failed: %w", err) diff --git a/services/mailer/incoming/payload/payload.go b/services/mailer/incoming/payload/payload.go index eb82f5c3ed3..00ada7826bd 100644 --- a/services/mailer/incoming/payload/payload.go +++ b/services/mailer/incoming/payload/payload.go @@ -20,7 +20,7 @@ const ( ) // CreateReferencePayload creates data which GetReferenceFromPayload resolves to the reference again. -func CreateReferencePayload(reference interface{}) ([]byte, error) { +func CreateReferencePayload(reference any) ([]byte, error) { var refType payloadReferenceType var refID int64 @@ -44,7 +44,7 @@ func CreateReferencePayload(reference interface{}) ([]byte, error) { } // GetReferenceFromPayload resolves the reference from the payload -func GetReferenceFromPayload(ctx context.Context, payload []byte) (interface{}, error) { +func GetReferenceFromPayload(ctx context.Context, payload []byte) (any, error) { if len(payload) < 1 { return nil, util.NewInvalidArgumentErrorf("payload to small") } diff --git a/services/mailer/mail.go b/services/mailer/mail.go index 351b79b5df2..50d59a44527 100644 --- a/services/mailer/mail.go +++ b/services/mailer/mail.go @@ -67,7 +67,7 @@ func SendTestMail(email string) error { // sendUserMail sends a mail to the user func sendUserMail(language string, u *user_model.User, tpl base.TplName, code, subject, info string) { locale := translation.NewLocale(language) - data := map[string]interface{}{ + data := map[string]any{ "DisplayName": u.DisplayName(), "ActiveCodeLives": timeutil.MinutesToFriendly(setting.Service.ActiveCodeLives, locale), "ResetPwdCodeLives": timeutil.MinutesToFriendly(setting.Service.ResetPwdCodeLives, locale), @@ -118,7 +118,7 @@ func SendActivateEmailMail(u *user_model.User, email *user_model.EmailAddress) { return } locale := translation.NewLocale(u.Language) - data := map[string]interface{}{ + data := map[string]any{ "DisplayName": u.DisplayName(), "ActiveCodeLives": timeutil.MinutesToFriendly(setting.Service.ActiveCodeLives, locale), "Code": u.GenerateEmailActivateCode(email.Email), @@ -151,7 +151,7 @@ func SendRegisterNotifyMail(u *user_model.User) { } locale := translation.NewLocale(u.Language) - data := map[string]interface{}{ + data := map[string]any{ "DisplayName": u.DisplayName(), "Username": u.Name, "Language": locale.Language(), @@ -184,7 +184,7 @@ func SendCollaboratorMail(u, doer *user_model.User, repo *repo_model.Repository) repoName := repo.FullName() subject := locale.Tr("mail.repo.collaborator.added.subject", doer.DisplayName(), repoName) - data := map[string]interface{}{ + data := map[string]any{ "Subject": subject, "RepoName": repoName, "Link": repo.HTMLURL(), @@ -258,7 +258,7 @@ func composeIssueCommentMessages(ctx *mailCommentContext, lang string, recipient } locale := translation.NewLocale(lang) - mailMeta := map[string]interface{}{ + mailMeta := map[string]any{ "FallbackSubject": fallback, "Body": body, "Link": link, diff --git a/services/mailer/mail_release.go b/services/mailer/mail_release.go index ebf9285b0a2..fb638ebd42c 100644 --- a/services/mailer/mail_release.go +++ b/services/mailer/mail_release.go @@ -68,7 +68,7 @@ func mailNewRelease(ctx context.Context, lang string, tos []string, rel *repo_mo } subject := locale.Tr("mail.release.new.subject", rel.TagName, rel.Repo.FullName()) - mailMeta := map[string]interface{}{ + mailMeta := map[string]any{ "Release": rel, "Subject": subject, "Language": locale.Language(), diff --git a/services/mailer/mail_repo.go b/services/mailer/mail_repo.go index 9b2f24faa81..e9c1991b5b8 100644 --- a/services/mailer/mail_repo.go +++ b/services/mailer/mail_repo.go @@ -64,7 +64,7 @@ func sendRepoTransferNotifyMailPerLang(lang string, newOwner, doer *user_model.U subject = locale.Tr("mail.repo.transfer.subject_to", doer.DisplayName(), repo.FullName(), destination) } - data := map[string]interface{}{ + data := map[string]any{ "Doer": doer, "User": repo.Owner, "Repo": repo.FullName(), diff --git a/services/mailer/mail_team_invite.go b/services/mailer/mail_team_invite.go index 917e1844356..b6f47ee9215 100644 --- a/services/mailer/mail_team_invite.go +++ b/services/mailer/mail_team_invite.go @@ -34,7 +34,7 @@ func MailTeamInvite(ctx context.Context, inviter *user_model.User, team *org_mod locale := translation.NewLocale(inviter.Language) subject := locale.Tr("mail.team_invite.subject", inviter.DisplayName(), org.DisplayName()) - mailMeta := map[string]interface{}{ + mailMeta := map[string]any{ "Inviter": inviter, "Organization": org, "Team": team, diff --git a/services/migrations/codebase.go b/services/migrations/codebase.go index 22bf0f73de7..492fc908e90 100644 --- a/services/migrations/codebase.go +++ b/services/migrations/codebase.go @@ -127,7 +127,7 @@ func (d *CodebaseDownloader) FormatCloneURL(opts base.MigrateOptions, remoteAddr return opts.CloneAddr, nil } -func (d *CodebaseDownloader) callAPI(endpoint string, parameter map[string]string, result interface{}) error { +func (d *CodebaseDownloader) callAPI(endpoint string, parameter map[string]string, result any) error { u, err := d.baseURL.Parse(endpoint) if err != nil { return err diff --git a/services/migrations/common.go b/services/migrations/common.go index 34d7c93ddf0..4f9837472d9 100644 --- a/services/migrations/common.go +++ b/services/migrations/common.go @@ -14,7 +14,7 @@ import ( ) // WarnAndNotice will log the provided message and send a repository notice -func WarnAndNotice(fmtStr string, args ...interface{}) { +func WarnAndNotice(fmtStr string, args ...any) { log.Warn(fmtStr, args...) if err := system_model.CreateRepositoryNotice(fmt.Sprintf(fmtStr, args...)); err != nil { log.Error("create repository notice failed: ", err) diff --git a/services/migrations/dump.go b/services/migrations/dump.go index 729112bcd23..603954810cf 100644 --- a/services/migrations/dump.go +++ b/services/migrations/dump.go @@ -112,7 +112,7 @@ func (g *RepositoryDumper) CreateRepo(repo *base.Repository, opts base.MigrateOp } defer f.Close() - bs, err := yaml.Marshal(map[string]interface{}{ + bs, err := yaml.Marshal(map[string]any{ "name": repo.Name, "owner": repo.Owner, "description": repo.Description, @@ -227,7 +227,7 @@ func (g *RepositoryDumper) CreateTopics(topics ...string) error { } defer f.Close() - bs, err := yaml.Marshal(map[string]interface{}{ + bs, err := yaml.Marshal(map[string]any{ "topics": topics, }) if err != nil { @@ -380,7 +380,7 @@ func (g *RepositoryDumper) CreateIssues(issues ...*base.Issue) error { return nil } -func (g *RepositoryDumper) createItems(dir string, itemFiles map[int64]*os.File, itemsMap map[int64][]interface{}) error { +func (g *RepositoryDumper) createItems(dir string, itemFiles map[int64]*os.File, itemsMap map[int64][]any) error { if err := os.MkdirAll(dir, os.ModePerm); err != nil { return err } @@ -394,7 +394,7 @@ func (g *RepositoryDumper) createItems(dir string, itemFiles map[int64]*os.File, return nil } -func (g *RepositoryDumper) encodeItems(number int64, items []interface{}, dir string, itemFiles map[int64]*os.File) error { +func (g *RepositoryDumper) encodeItems(number int64, items []any, dir string, itemFiles map[int64]*os.File) error { itemFile := itemFiles[number] if itemFile == nil { var err error @@ -413,7 +413,7 @@ func (g *RepositoryDumper) encodeItems(number int64, items []interface{}, dir st // CreateComments creates comments of issues func (g *RepositoryDumper) CreateComments(comments ...*base.Comment) error { - commentsMap := make(map[int64][]interface{}, len(comments)) + commentsMap := make(map[int64][]any, len(comments)) for _, comment := range comments { commentsMap[comment.IssueIndex] = append(commentsMap[comment.IssueIndex], comment) } @@ -621,7 +621,7 @@ func (g *RepositoryDumper) CreatePullRequests(prs ...*base.PullRequest) error { // CreateReviews create pull request reviews func (g *RepositoryDumper) CreateReviews(reviews ...*base.Review) error { - reviewsMap := make(map[int64][]interface{}, len(reviews)) + reviewsMap := make(map[int64][]any, len(reviews)) for _, review := range reviews { reviewsMap[review.IssueIndex] = append(reviewsMap[review.IssueIndex], review) } diff --git a/services/migrations/onedev.go b/services/migrations/onedev.go index 33fc43c3496..e2f7b771f36 100644 --- a/services/migrations/onedev.go +++ b/services/migrations/onedev.go @@ -121,7 +121,7 @@ func (d *OneDevDownloader) LogString() string { return fmt.Sprintf("", d.baseURL, d.repoID, d.repoName) } -func (d *OneDevDownloader) callAPI(endpoint string, parameter map[string]string, result interface{}) error { +func (d *OneDevDownloader) callAPI(endpoint string, parameter map[string]string, result any) error { u, err := d.baseURL.Parse(endpoint) if err != nil { return err @@ -400,9 +400,9 @@ func (d *OneDevDownloader) GetComments(commentable base.Commentable) ([]*base.Co } rawChanges := make([]struct { - Date time.Time `json:"date"` - UserID int64 `json:"userId"` - Data map[string]interface{} `json:"data"` + Date time.Time `json:"date"` + UserID int64 `json:"userId"` + Data map[string]any `json:"data"` }, 0, 100) if context.IsPullRequest { diff --git a/services/mirror/mirror.go b/services/mirror/mirror.go index 35ba09521b6..abce1d3c2d4 100644 --- a/services/mirror/mirror.go +++ b/services/mirror/mirror.go @@ -41,7 +41,7 @@ func Update(ctx context.Context, pullLimit, pushLimit int) error { } log.Trace("Doing: Update") - handler := func(idx int, bean interface{}) error { + handler := func(idx int, bean any) error { var repo *repo_model.Repository var mirrorType mirror_module.SyncType var referenceID int64 @@ -91,7 +91,7 @@ func Update(ctx context.Context, pullLimit, pushLimit int) error { pullMirrorsRequested := 0 if pullLimit != 0 { - if err := repo_model.MirrorsIterate(pullLimit, func(idx int, bean interface{}) error { + if err := repo_model.MirrorsIterate(pullLimit, func(idx int, bean any) error { if err := handler(idx, bean); err != nil { return err } @@ -105,7 +105,7 @@ func Update(ctx context.Context, pullLimit, pushLimit int) error { pushMirrorsRequested := 0 if pushLimit != 0 { - if err := repo_model.PushMirrorsIterate(ctx, pushLimit, func(idx int, bean interface{}) error { + if err := repo_model.PushMirrorsIterate(ctx, pushLimit, func(idx int, bean any) error { if err := handler(idx, bean); err != nil { return err } diff --git a/services/mirror/mirror_pull.go b/services/mirror/mirror_pull.go index 53ab632b01c..51c7de58b64 100644 --- a/services/mirror/mirror_pull.go +++ b/services/mirror/mirror_pull.go @@ -307,6 +307,11 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo return nil, false } + log.Trace("SyncMirrors [repo: %-v]: syncing branches...", m.Repo) + if _, err = repo_module.SyncRepoBranchesWithRepo(ctx, m.Repo, gitRepo, 0); err != nil { + log.Error("SyncMirrors [repo: %-v]: failed to synchronize branches: %v", m.Repo, err) + } + log.Trace("SyncMirrors [repo: %-v]: syncing releases with tags...", m.Repo) if err = repo_module.SyncReleasesWithTags(m.Repo, gitRepo); err != nil { log.Error("SyncMirrors [repo: %-v]: failed to synchronize tags to releases: %v", m.Repo, err) diff --git a/services/packages/auth.go b/services/packages/auth.go index a7acdaf1c3a..41d3a0a8251 100644 --- a/services/packages/auth.go +++ b/services/packages/auth.go @@ -53,7 +53,7 @@ func ParseAuthorizationToken(req *http.Request) (int64, error) { return 0, fmt.Errorf("split token failed") } - token, err := jwt.ParseWithClaims(parts[1], &packageClaims{}, func(t *jwt.Token) (interface{}, error) { + token, err := jwt.ParseWithClaims(parts[1], &packageClaims{}, func(t *jwt.Token) (any, error) { if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok { return nil, fmt.Errorf("unexpected signing method: %v", t.Header["alg"]) } diff --git a/services/packages/packages.go b/services/packages/packages.go index 23aa8a5c315..bdc56efeefa 100644 --- a/services/packages/packages.go +++ b/services/packages/packages.go @@ -9,6 +9,7 @@ import ( "errors" "fmt" "io" + "net/url" "strings" "code.gitea.io/gitea/models/db" @@ -20,6 +21,7 @@ import ( "code.gitea.io/gitea/modules/notification" packages_module "code.gitea.io/gitea/modules/packages" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/storage" "code.gitea.io/gitea/modules/util" ) @@ -42,7 +44,7 @@ type PackageCreationInfo struct { PackageInfo SemverCompatible bool Creator *user_model.User - Metadata interface{} + Metadata any PackageProperties map[string]string VersionProperties map[string]string } @@ -562,70 +564,62 @@ func DeletePackageFile(ctx context.Context, pf *packages_model.PackageFile) erro } // GetFileStreamByPackageNameAndVersion returns the content of the specific package file -func GetFileStreamByPackageNameAndVersion(ctx context.Context, pvi *PackageInfo, pfi *PackageFileInfo) (io.ReadSeekCloser, *packages_model.PackageFile, error) { +func GetFileStreamByPackageNameAndVersion(ctx context.Context, pvi *PackageInfo, pfi *PackageFileInfo) (io.ReadSeekCloser, *url.URL, *packages_model.PackageFile, error) { log.Trace("Getting package file stream: %v, %v, %s, %s, %s, %s", pvi.Owner.ID, pvi.PackageType, pvi.Name, pvi.Version, pfi.Filename, pfi.CompositeKey) pv, err := packages_model.GetVersionByNameAndVersion(ctx, pvi.Owner.ID, pvi.PackageType, pvi.Name, pvi.Version) if err != nil { if err == packages_model.ErrPackageNotExist { - return nil, nil, err + return nil, nil, nil, err } log.Error("Error getting package: %v", err) - return nil, nil, err + return nil, nil, nil, err } return GetFileStreamByPackageVersion(ctx, pv, pfi) } -// GetFileStreamByPackageVersionAndFileID returns the content of the specific package file -func GetFileStreamByPackageVersionAndFileID(ctx context.Context, owner *user_model.User, versionID, fileID int64) (io.ReadSeekCloser, *packages_model.PackageFile, error) { - log.Trace("Getting package file stream: %v, %v, %v", owner.ID, versionID, fileID) - - pv, err := packages_model.GetVersionByID(ctx, versionID) - if err != nil { - if err != packages_model.ErrPackageNotExist { - log.Error("Error getting package version: %v", err) - } - return nil, nil, err - } - - p, err := packages_model.GetPackageByID(ctx, pv.PackageID) - if err != nil { - log.Error("Error getting package: %v", err) - return nil, nil, err - } - - if p.OwnerID != owner.ID { - return nil, nil, packages_model.ErrPackageNotExist - } - - pf, err := packages_model.GetFileForVersionByID(ctx, versionID, fileID) - if err != nil { - log.Error("Error getting file: %v", err) - return nil, nil, err - } - - return GetPackageFileStream(ctx, pf) -} - // GetFileStreamByPackageVersion returns the content of the specific package file -func GetFileStreamByPackageVersion(ctx context.Context, pv *packages_model.PackageVersion, pfi *PackageFileInfo) (io.ReadSeekCloser, *packages_model.PackageFile, error) { +func GetFileStreamByPackageVersion(ctx context.Context, pv *packages_model.PackageVersion, pfi *PackageFileInfo) (io.ReadSeekCloser, *url.URL, *packages_model.PackageFile, error) { pf, err := packages_model.GetFileForVersionByName(ctx, pv.ID, pfi.Filename, pfi.CompositeKey) if err != nil { - return nil, nil, err + return nil, nil, nil, err } return GetPackageFileStream(ctx, pf) } // GetPackageFileStream returns the content of the specific package file -func GetPackageFileStream(ctx context.Context, pf *packages_model.PackageFile) (io.ReadSeekCloser, *packages_model.PackageFile, error) { +func GetPackageFileStream(ctx context.Context, pf *packages_model.PackageFile) (io.ReadSeekCloser, *url.URL, *packages_model.PackageFile, error) { pb, err := packages_model.GetBlobByID(ctx, pf.BlobID) if err != nil { - return nil, nil, err + return nil, nil, nil, err + } + + return GetPackageBlobStream(ctx, pf, pb) +} + +// GetPackageBlobStream returns the content of the specific package blob +// If the storage supports direct serving and it's enabled, only the direct serving url is returned. +func GetPackageBlobStream(ctx context.Context, pf *packages_model.PackageFile, pb *packages_model.PackageBlob) (io.ReadSeekCloser, *url.URL, *packages_model.PackageFile, error) { + key := packages_module.BlobHash256Key(pb.HashSHA256) + + cs := packages_module.NewContentStore() + + var s io.ReadSeekCloser + var u *url.URL + var err error + + if cs.ShouldServeDirect() { + u, err = cs.GetServeDirectURL(key, pf.Name) + if err != nil && !errors.Is(err, storage.ErrURLNotSupported) { + log.Error("Error getting serve direct url: %v", err) + } + } + if u == nil { + s, err = cs.Get(key) } - s, err := packages_module.NewContentStore().Get(packages_module.BlobHash256Key(pb.HashSHA256)) if err == nil { if pf.IsLead { if err := packages_model.IncrementDownloadCounter(ctx, pf.VersionID); err != nil { @@ -633,7 +627,7 @@ func GetPackageFileStream(ctx context.Context, pf *packages_model.PackageFile) ( } } } - return s, pf, err + return s, u, pf, err } // RemoveAllPackages for User diff --git a/services/pull/merge.go b/services/pull/merge.go index 85bb90b8538..416c7444635 100644 --- a/services/pull/merge.go +++ b/services/pull/merge.go @@ -110,6 +110,11 @@ func getMergeMessage(ctx context.Context, baseGitRepo *git.Repository, pr *issue } } + if mergeStyle == repo_model.MergeStyleRebase { + // for fast-forward rebase, do not amend the last commit if there is no template + return "", "", nil + } + // Squash merge has a different from other styles. if mergeStyle == repo_model.MergeStyleSquash { return fmt.Sprintf("%s (%s%d)", pr.Issue.Title, issueReference, pr.Issue.Index), "", nil diff --git a/services/pull/review.go b/services/pull/review.go index 6feffe4ec4e..e920bc8c165 100644 --- a/services/pull/review.go +++ b/services/pull/review.go @@ -320,7 +320,7 @@ func SubmitReview(ctx context.Context, doer *user_model.User, gitRepo *git.Repos func DismissReview(ctx context.Context, reviewID, repoID int64, message string, doer *user_model.User, isDismiss, dismissPriors bool) (comment *issues_model.Comment, err error) { review, err := issues_model.GetReviewByID(ctx, reviewID) if err != nil { - return + return nil, err } if review.Type != issues_model.ReviewTypeApprove && review.Type != issues_model.ReviewTypeReject { @@ -328,7 +328,7 @@ func DismissReview(ctx context.Context, reviewID, repoID int64, message string, } // load data for notify - if err = review.LoadAttributes(ctx); err != nil { + if err := review.LoadAttributes(ctx); err != nil { return nil, err } @@ -337,8 +337,8 @@ func DismissReview(ctx context.Context, reviewID, repoID int64, message string, return nil, fmt.Errorf("reviews's repository is not the same as the one we expect") } - if err = issues_model.DismissReview(review, isDismiss); err != nil { - return + if err := issues_model.DismissReview(review, isDismiss); err != nil { + return nil, err } if dismissPriors { @@ -361,11 +361,11 @@ func DismissReview(ctx context.Context, reviewID, repoID int64, message string, return nil, nil } - if err = review.Issue.LoadPullRequest(ctx); err != nil { - return + if err := review.Issue.LoadPullRequest(ctx); err != nil { + return nil, err } - if err = review.Issue.LoadAttributes(ctx); err != nil { - return + if err := review.Issue.LoadAttributes(ctx); err != nil { + return nil, err } comment, err = issue_service.CreateComment(ctx, &issues_model.CreateCommentOptions{ @@ -377,7 +377,7 @@ func DismissReview(ctx context.Context, reviewID, repoID int64, message string, Repo: review.Issue.Repo, }) if err != nil { - return + return nil, err } comment.Review = review @@ -386,5 +386,5 @@ func DismissReview(ctx context.Context, reviewID, repoID int64, message string, notification.NotifyPullReviewDismiss(ctx, doer, review, comment) - return comment, err + return comment, nil } diff --git a/services/release/release.go b/services/release/release.go index c1190305b66..1ccbd9c8116 100644 --- a/services/release/release.go +++ b/services/release/release.go @@ -187,7 +187,7 @@ func CreateNewTag(ctx context.Context, doer *user_model.User, repo *repo_model.R // editAttachments accept a map of attachment uuid to new attachment name which will be updated with attachments. func UpdateRelease(doer *user_model.User, gitRepo *git.Repository, rel *repo_model.Release, addAttachmentUUIDs, delAttachmentUUIDs []string, editAttachments map[string]string, -) (err error) { +) error { if rel.ID == 0 { return errors.New("UpdateRelease only accepts an exist release") } @@ -264,8 +264,8 @@ func UpdateRelease(doer *user_model.User, gitRepo *git.Repository, rel *repo_mod } } - if err = committer.Commit(); err != nil { - return + if err := committer.Commit(); err != nil { + return err } for _, uuid := range delAttachmentUUIDs { @@ -280,14 +280,14 @@ func UpdateRelease(doer *user_model.User, gitRepo *git.Repository, rel *repo_mod if !isCreated { notification.NotifyUpdateRelease(gitRepo.Ctx, doer, rel) - return + return nil } if !rel.IsDraft { notification.NotifyNewRelease(gitRepo.Ctx, rel) } - return err + return nil } // DeleteReleaseByID deletes a release and corresponding Git tag by given ID. diff --git a/services/repository/files/content.go b/services/repository/files/content.go index c701431d678..30d62fbcdf7 100644 --- a/services/repository/files/content.go +++ b/services/repository/files/content.go @@ -40,9 +40,9 @@ func (ct *ContentType) String() string { // GetContentsOrList gets the meta data of a file's contents (*ContentsResponse) if treePath not a tree // directory, otherwise a listing of file contents ([]*ContentsResponse). Ref can be a branch, commit or tag -func GetContentsOrList(ctx context.Context, repo *repo_model.Repository, treePath, ref string) (interface{}, error) { +func GetContentsOrList(ctx context.Context, repo *repo_model.Repository, treePath, ref string) (any, error) { if repo.IsEmpty { - return make([]interface{}, 0), nil + return make([]any, 0), nil } if ref == "" { ref = repo.DefaultBranch diff --git a/services/repository/lfs.go b/services/repository/lfs.go index aeb808a72f3..0bd4d53a5c4 100644 --- a/services/repository/lfs.go +++ b/services/repository/lfs.go @@ -19,7 +19,7 @@ import ( // GarbageCollectLFSMetaObjectsOptions provides options for GarbageCollectLFSMetaObjects function type GarbageCollectLFSMetaObjectsOptions struct { - Logger log.Logger + LogDetail func(format string, v ...any) AutoFix bool OlderThan time.Time UpdatedLessRecentlyThan time.Time @@ -32,10 +32,12 @@ func GarbageCollectLFSMetaObjects(ctx context.Context, opts GarbageCollectLFSMet log.Trace("Doing: GarbageCollectLFSMetaObjects") defer log.Trace("Finished: GarbageCollectLFSMetaObjects") + if opts.LogDetail == nil { + opts.LogDetail = log.Debug + } + if !setting.LFS.StartServer { - if opts.Logger != nil { - opts.Logger.Info("LFS support is disabled") - } + opts.LogDetail("LFS support is disabled") return nil } @@ -54,21 +56,17 @@ func GarbageCollectLFSMetaObjects(ctx context.Context, opts GarbageCollectLFSMet // GarbageCollectLFSMetaObjectsForRepo garbage collects LFS objects for a specific repository func GarbageCollectLFSMetaObjectsForRepo(ctx context.Context, repo *repo_model.Repository, opts GarbageCollectLFSMetaObjectsOptions) error { - if opts.Logger != nil { - opts.Logger.Info("Checking %-v", repo) - } + opts.LogDetail("Checking %-v", repo) total, orphaned, collected, deleted := int64(0), 0, 0, 0 - if opts.Logger != nil { - defer func() { - if orphaned == 0 { - opts.Logger.Info("Found %d total LFSMetaObjects in %-v", total, repo) - } else if !opts.AutoFix { - opts.Logger.Info("Found %d/%d orphaned LFSMetaObjects in %-v", orphaned, total, repo) - } else { - opts.Logger.Info("Collected %d/%d orphaned/%d total LFSMetaObjects in %-v. %d removed from storage.", collected, orphaned, total, repo, deleted) - } - }() - } + defer func() { + if orphaned == 0 { + opts.LogDetail("Found %d total LFSMetaObjects in %-v", total, repo) + } else if !opts.AutoFix { + opts.LogDetail("Found %d/%d orphaned LFSMetaObjects in %-v", orphaned, total, repo) + } else { + opts.LogDetail("Collected %d/%d orphaned/%d total LFSMetaObjects in %-v. %d removed from storage.", collected, orphaned, total, repo, deleted) + } + }() gitRepo, err := git.OpenRepository(ctx, repo.RepoPath()) if err != nil { @@ -129,9 +127,7 @@ func GarbageCollectLFSMetaObjectsForRepo(ctx context.Context, repo *repo_model.R }) if err == errStop { - if opts.Logger != nil { - opts.Logger.Info("Processing stopped at %d total LFSMetaObjects in %-v", total, repo) - } + opts.LogDetail("Processing stopped at %d total LFSMetaObjects in %-v", total, repo) return nil } else if err != nil { return err diff --git a/services/repository/push.go b/services/repository/push.go index 7e7069f580b..8e4bab15629 100644 --- a/services/repository/push.go +++ b/services/repository/push.go @@ -259,7 +259,7 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error { notification.NotifyPushCommits(ctx, pusher, repo, opts, commits) - if err = git_model.UpdateBranch(ctx, repo.ID, branch, newCommit.ID.String(), newCommit.CommitMessage, opts.PusherID, newCommit.Committer.When); err != nil { + if err = git_model.UpdateBranch(ctx, repo.ID, opts.PusherID, branch, newCommit); err != nil { return fmt.Errorf("git_model.UpdateBranch %s:%s failed: %v", repo.FullName(), branch, err) } diff --git a/services/task/migrate.go b/services/task/migrate.go index 98454482b5a..52b6220a048 100644 --- a/services/task/migrate.go +++ b/services/task/migrate.go @@ -71,7 +71,7 @@ func runMigrateTask(t *admin_model.Task) (err error) { }() if err = t.LoadRepo(); err != nil { - return + return err } // if repository is ready, then just finish the task @@ -80,16 +80,16 @@ func runMigrateTask(t *admin_model.Task) (err error) { } if err = t.LoadDoer(); err != nil { - return + return err } if err = t.LoadOwner(); err != nil { - return + return err } var opts *migration.MigrateOptions opts, err = t.MigrateConfig() if err != nil { - return + return err } opts.MigrateToRepoID = t.RepoID @@ -101,7 +101,7 @@ func runMigrateTask(t *admin_model.Task) (err error) { t.StartTime = timeutil.TimeStampNow() t.Status = structs.TaskStatusRunning if err = t.UpdateCols("start_time", "status"); err != nil { - return + return err } // check whether the task should be canceled, this goroutine is also managed by process manager @@ -121,7 +121,7 @@ func runMigrateTask(t *admin_model.Task) (err error) { } }() - t.Repo, err = migrations.MigrateRepository(ctx, t.Doer, t.Owner.Name, *opts, func(format string, args ...interface{}) { + t.Repo, err = migrations.MigrateRepository(ctx, t.Doer, t.Owner.Name, *opts, func(format string, args ...any) { message := admin_model.TranslatableMessage{ Format: format, Args: args, @@ -133,12 +133,11 @@ func runMigrateTask(t *admin_model.Task) (err error) { if err == nil { log.Trace("Repository migrated [%d]: %s/%s", t.Repo.ID, t.Owner.Name, t.Repo.Name) - return + return nil } if repo_model.IsErrRepoAlreadyExist(err) { - err = errors.New("the repository name is already used") - return + return errors.New("the repository name is already used") } // remoteAddr may contain credentials, so we sanitize it diff --git a/services/webhook/notifier.go b/services/webhook/notifier.go index 3332d5d4aa1..23080a5a351 100644 --- a/services/webhook/notifier.go +++ b/services/webhook/notifier.go @@ -386,7 +386,7 @@ func (m *webhookNotifier) NotifyUpdateComment(ctx context.Context, doer *user_mo if err := PrepareWebhooks(ctx, EventSource{Repository: c.Issue.Repo}, eventType, &api.IssueCommentPayload{ Action: api.HookIssueCommentEdited, Issue: convert.ToAPIIssue(ctx, c.Issue), - Comment: convert.ToComment(ctx, c), + Comment: convert.ToAPIComment(ctx, c.Issue.Repo, c), Changes: &api.ChangesPayload{ Body: &api.ChangesFromPayload{ From: oldContent, @@ -414,7 +414,7 @@ func (m *webhookNotifier) NotifyCreateIssueComment(ctx context.Context, doer *us if err := PrepareWebhooks(ctx, EventSource{Repository: issue.Repo}, eventType, &api.IssueCommentPayload{ Action: api.HookIssueCommentCreated, Issue: convert.ToAPIIssue(ctx, issue), - Comment: convert.ToComment(ctx, comment), + Comment: convert.ToAPIComment(ctx, repo, comment), Repository: convert.ToRepo(ctx, repo, permission), Sender: convert.ToUser(ctx, doer, nil), IsPull: issue.IsPull, @@ -451,7 +451,7 @@ func (m *webhookNotifier) NotifyDeleteComment(ctx context.Context, doer *user_mo if err := PrepareWebhooks(ctx, EventSource{Repository: comment.Issue.Repo}, eventType, &api.IssueCommentPayload{ Action: api.HookIssueCommentDeleted, Issue: convert.ToAPIIssue(ctx, comment.Issue), - Comment: convert.ToComment(ctx, comment), + Comment: convert.ToAPIComment(ctx, comment.Issue.Repo, comment), Repository: convert.ToRepo(ctx, comment.Issue.Repo, permission), Sender: convert.ToUser(ctx, doer, nil), IsPull: comment.Issue.IsPull, @@ -808,7 +808,7 @@ func sendReleaseHook(ctx context.Context, doer *user_model.User, rel *repo_model permission, _ := access_model.GetUserRepoPermission(ctx, rel.Repo, doer) if err := PrepareWebhooks(ctx, EventSource{Repository: rel.Repo}, webhook_module.HookEventRelease, &api.ReleasePayload{ Action: action, - Release: convert.ToRelease(ctx, rel), + Release: convert.ToAPIRelease(ctx, rel.Repo, rel), Repository: convert.ToRepo(ctx, rel.Repo, permission), Sender: convert.ToUser(ctx, doer, nil), }); err != nil { diff --git a/templates/admin/auth/edit.tmpl b/templates/admin/auth/edit.tmpl index 801cf9e7b45..6dfa86d9dd1 100644 --- a/templates/admin/auth/edit.tmpl +++ b/templates/admin/auth/edit.tmpl @@ -106,7 +106,6 @@ -
diff --git a/templates/base/head_navbar.tmpl b/templates/base/head_navbar.tmpl index 3898c98ac9b..35aff8e5382 100644 --- a/templates/base/head_navbar.tmpl +++ b/templates/base/head_navbar.tmpl @@ -23,7 +23,6 @@
- {{if and .IsSigned .MustChangePassword}} {{/* No links */}} diff --git a/templates/code/searchcombo.tmpl b/templates/code/searchcombo.tmpl new file mode 100644 index 00000000000..e495b3b454c --- /dev/null +++ b/templates/code/searchcombo.tmpl @@ -0,0 +1,17 @@ +{{template "code/searchform" .}} +
+
+ {{if .CodeIndexerUnavailable}} +
+

{{$.locale.Tr "explore.code_search_unavailable"}}

+
+ {{else if .SearchResults}} +

+ {{.locale.Tr "explore.code_search_results" (.Keyword|Escape) | Str2html}} +

+ {{template "code/searchresults" .}} + {{else if .Keyword}} +
{{$.locale.Tr "explore.code_no_results"}}
+ {{end}} +
+{{template "base/paginate" .}} diff --git a/templates/devtest/fomantic-modal.tmpl b/templates/devtest/fomantic-modal.tmpl index aaa4c2c12d1..799bab7594e 100644 --- a/templates/devtest/fomantic-modal.tmpl +++ b/templates/devtest/fomantic-modal.tmpl @@ -1,5 +1,18 @@ {{template "base/head" .}}
+ {{template "base/alert" .}} + + + + +
+
- +
diff --git a/templates/package/content/chef.tmpl b/templates/package/content/chef.tmpl index 77f1f379981..331b1fb49f6 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 d0f88dadc69..c2a8bc96259 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 07ad6b644d6..86f1c8ede3a 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 71c2c98d3f3..627e637e1ae 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 d928b36e604..5aba2b60611 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 dcabdbce93b..38a58bc4e78 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 8b60b33bcc8..ea146d170f9 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 556e31971c5..d3e4c7a62ae 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 2343d945b3a..616698c4147 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 9b4f57538a2..11c05a12b6e 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 f18f12091bc..d4c991c4664 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 a8d3f403169..e376a45f8b8 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 f3013adc76e..e95d9176b85 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 28aa29abe9a..840bedbd9db 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 07c387ecac7..460a06fcd11 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 63bb8b07920..42965c3271a 100644 --- a/templates/package/content/rpm.tmpl +++ b/templates/package/content/rpm.tmpl @@ -13,7 +13,7 @@
- +
diff --git a/templates/package/content/rubygems.tmpl b/templates/package/content/rubygems.tmpl index 6e6ee7105fc..2f618392e4e 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 ea87599fcc7..c227e30c343 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 b6ec2eaf326..e23acf58a6d 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/settings.tmpl b/templates/package/settings.tmpl index 205d875c3ee..abcdd7ec1c8 100644 --- a/templates/package/settings.tmpl +++ b/templates/package/settings.tmpl @@ -1,7 +1,8 @@ {{template "base/head" .}}
- {{template "user/overview/header" .}} + {{template "shared/user/org_profile_avatar" .}}
+ {{template "user/overview/header" .}} {{template "base/alert" .}}

{{.PackageDescriptor.Package.Name}} ({{.PackageDescriptor.Version.Version}}) / {{.locale.Tr "repo.settings"}}

diff --git a/templates/package/shared/cargo.tmpl b/templates/package/shared/cargo.tmpl index 687fc8492d8..4e26df29097 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 3d14eda3efa..79a2d885fb3 100644 --- a/templates/package/shared/list.tmpl +++ b/templates/package/shared/list.tmpl @@ -1,55 +1,53 @@ -
- {{template "base/alert" .}} -
-
- {{template "shared/searchinput" dict "locale" .locale "Value" .Query "AutoFocus" true}} - - -
-
-
- {{range .PackageDescriptors}} -
  • -
    -
    - {{.Package.Name}} - {{svg .Package.Type.SVGName 16}} {{.Package.Type.Name}} -
    -
    - {{$timeStr := TimeSinceUnix .Version.CreatedUnix $.locale}} - {{$hasRepositoryAccess := false}} - {{if .Repository}} - {{$hasRepositoryAccess = index $.RepositoryAccessMap .Repository.ID}} - {{end}} - {{if $hasRepositoryAccess}} - {{$.locale.Tr "packages.published_by_in" $timeStr .Creator.HomeLink (.Creator.GetDisplayName | Escape) .Repository.Link (.Repository.FullName | Escape) | Safe}} - {{else}} - {{$.locale.Tr "packages.published_by" $timeStr .Creator.HomeLink (.Creator.GetDisplayName | Escape) | Safe}} - {{end}} -
    -
    -
  • - {{else}} - {{if not .HasPackages}} -
    - {{svg "octicon-package" 48}} -

    {{.locale.Tr "packages.empty"}}

    - {{if and .Repository .CanWritePackages}} - {{$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}}

    -
    - {{else}} -

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

    +{{template "base/alert" .}} +
    +
    + {{template "shared/searchinput" dict "locale" .locale "Value" .Query "AutoFocus" true}} + +
    +
    +
    + {{range .PackageDescriptors}} +
  • +
    +
    + {{.Package.Name}} + {{svg .Package.Type.SVGName 16}} {{.Package.Type.Name}} +
    +
    + {{$timeStr := TimeSinceUnix .Version.CreatedUnix $.locale}} + {{$hasRepositoryAccess := false}} + {{if .Repository}} + {{$hasRepositoryAccess = index $.RepositoryAccessMap .Repository.ID}} + {{end}} + {{if $hasRepositoryAccess}} + {{$.locale.Tr "packages.published_by_in" $timeStr .Creator.HomeLink (.Creator.GetDisplayName | Escape) .Repository.Link (.Repository.FullName | Escape) | Safe}} + {{else}} + {{$.locale.Tr "packages.published_by" $timeStr .Creator.HomeLink (.Creator.GetDisplayName | Escape) | Safe}} + {{end}} +
    +
    +
  • + {{else}} + {{if not .HasPackages}} +
    + {{svg "octicon-package" 48}} +

    {{.locale.Tr "packages.empty"}}

    + {{if and .Repository .CanWritePackages}} + {{$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}}

    +
    + {{else}} +

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

    + {{end}} + {{end}} + {{template "base/paginate" .}}
    diff --git a/templates/package/shared/versionlist.tmpl b/templates/package/shared/versionlist.tmpl index 23cd0bfcc0d..feba8ef1457 100644 --- a/templates/package/shared/versionlist.tmpl +++ b/templates/package/shared/versionlist.tmpl @@ -1,39 +1,37 @@ -
    -

    {{.PackageDescriptor.Package.Name}} / {{.locale.Tr "packages.versions"}}

    -
    -
    - {{template "shared/searchinput" dict "locale" .locale "Value" .Query "AutoFocus" true}} - - {{if eq .PackageDescriptor.Package.Type "container"}} - - {{end}} - -
    -
    -
    - {{range .PackageDescriptors}} -
  • -
    - -
    - {{$.locale.Tr "packages.published_by" (TimeSinceUnix .Version.CreatedUnix $.locale) .Creator.HomeLink (.Creator.GetDisplayName | Escape) | Safe}} -
    -
    -
  • - {{else}} -

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

    +

    {{.PackageDescriptor.Package.Name}} / {{.locale.Tr "packages.versions"}}

    +
    +
    + {{template "shared/searchinput" dict "locale" .locale "Value" .Query "AutoFocus" true}} + + {{if eq .PackageDescriptor.Package.Type "container"}} + {{end}} - {{template "base/paginate" .}} +
    +
    +
    + {{range .PackageDescriptors}} +
  • +
    + +
    + {{$.locale.Tr "packages.published_by" (TimeSinceUnix .Version.CreatedUnix $.locale) .Creator.HomeLink (.Creator.GetDisplayName | Escape) | Safe}} +
    +
    +
  • + {{else}} +

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

    + {{end}} + {{template "base/paginate" .}}
    diff --git a/templates/package/view.tmpl b/templates/package/view.tmpl index 6064db1b4c0..fe4214cbd23 100644 --- a/templates/package/view.tmpl +++ b/templates/package/view.tmpl @@ -1,7 +1,8 @@ {{template "base/head" .}}
    - {{template "user/overview/header" .}} + {{template "shared/user/org_profile_avatar" .}}
    + {{template "user/overview/header" .}}

    {{.PackageDescriptor.Package.Name}} ({{.PackageDescriptor.Version.Version}})

    diff --git a/templates/projects/list.tmpl b/templates/projects/list.tmpl index 9725c1e63b3..fc8bf608110 100644 --- a/templates/projects/list.tmpl +++ b/templates/projects/list.tmpl @@ -1,82 +1,76 @@ -
    -
    - {{if .CanWriteProjects}} - -
    - {{end}} +{{if .CanWriteProjects}} + +
    +{{end}} - {{template "base/alert" .}} - +{{template "base/alert" .}} + - -
    - {{range .Projects}} -
  • -

    - {{svg .IconName 16}} - {{.Title}} -

    -
    -
    -
    - {{svg "octicon-issue-opened" 14}} - {{$.locale.PrettyNumber .NumOpenIssues}} {{$.locale.Tr "repo.issues.open_title"}} -
    -
    - {{svg "octicon-check" 14}} - {{$.locale.PrettyNumber .NumClosedIssues}} {{$.locale.Tr "repo.issues.closed_title"}} -
    -
    - {{if and $.CanWriteProjects (not $.Repository.IsArchived)}} - - {{end}} -
    - {{if .Description}} -
    - {{.RenderedContent|Str2html}} -
    - {{end}} -
  • - {{end}} - - {{template "base/paginate" .}} + +
    + {{range .Projects}} +
  • +

    + {{svg .IconName 16}} + {{.Title}} +

    +
    +
    +
    + {{svg "octicon-issue-opened" 14}} + {{$.locale.PrettyNumber .NumOpenIssues}} {{$.locale.Tr "repo.issues.open_title"}} +
    +
    + {{svg "octicon-check" 14}} + {{$.locale.PrettyNumber .NumClosedIssues}} {{$.locale.Tr "repo.issues.closed_title"}} +
    +
    + {{if and $.CanWriteProjects (not $.Repository.IsArchived)}} + + {{end}} +
    + {{if .Description}} +
    + {{.RenderedContent|Str2html}} +
    + {{end}} +
  • + {{end}} + + {{template "base/paginate" .}} +
    {{if $.CanWriteProjects}}