Merge branch 'rebase-forgejo-dependency' into forgejo

This commit is contained in:
Earl Warren 2023-12-04 15:21:55 +01:00
commit adabda9378
No known key found for this signature in database
GPG key ID: 0579CB2928A78A00
153 changed files with 2932 additions and 256 deletions

View file

@ -117,6 +117,8 @@ package "code.gitea.io/gitea/models/unittest"
func LoadFixtures func LoadFixtures
func Copy func Copy
func CopyDir func CopyDir
func NewMockWebServer
func NormalizedFullPath
func FixturesDir func FixturesDir
func fatalTestError func fatalTestError
func InitSettings func InitSettings

File diff suppressed because one or more lines are too long

View file

@ -410,6 +410,10 @@ USER = root
;; ;;
;; Whether execute database models migrations automatically ;; Whether execute database models migrations automatically
;AUTO_MIGRATION = true ;AUTO_MIGRATION = true
;;
;; Threshold value (in seconds) beyond which query execution time is logged as a warning in the xorm logger
;;
;SLOW_QUERY_TRESHOLD = 5s
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -810,6 +814,11 @@ LEVEL = Info
;; Every new user will have restricted permissions depending on this setting ;; Every new user will have restricted permissions depending on this setting
;DEFAULT_USER_IS_RESTRICTED = false ;DEFAULT_USER_IS_RESTRICTED = false
;; ;;
;; Users will be able to use dots when choosing their username. Disabling this is
;; helpful if your usersare having issues with e.g. RSS feeds or advanced third-party
;; extensions that use strange regex patterns.
; ALLOW_DOTS_IN_USERNAMES = true
;;
;; Either "public", "limited" or "private", default is "public" ;; Either "public", "limited" or "private", default is "public"
;; Limited is for users visible only to signed users ;; Limited is for users visible only to signed users
;; Private is for users visible only to members of their organizations ;; Private is for users visible only to members of their organizations
@ -1459,6 +1468,8 @@ LEVEL = Info
;; ;;
;; Default configuration for email notifications for users (user configurable). Options: enabled, onmention, disabled ;; Default configuration for email notifications for users (user configurable). Options: enabled, onmention, disabled
;DEFAULT_EMAIL_NOTIFICATIONS = enabled ;DEFAULT_EMAIL_NOTIFICATIONS = enabled
;; Send an email to all admins when a new user signs up to inform the admins about this act. Options: true, false
;SEND_NOTIFICATION_EMAIL_ON_NEW_USER = false
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -1773,9 +1784,6 @@ LEVEL = Info
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;AVATAR_UPLOAD_PATH = data/avatars
;REPOSITORY_AVATAR_UPLOAD_PATH = data/repo-avatars
;;
;; How Gitea deals with missing repository avatars ;; How Gitea deals with missing repository avatars
;; none = no avatar will be displayed; random = random avatar will be displayed; image = default image will be used ;; none = no avatar will be displayed; random = random avatar will be displayed; image = default image will be used
;REPOSITORY_AVATAR_FALLBACK = none ;REPOSITORY_AVATAR_FALLBACK = none

View file

@ -456,6 +456,7 @@ The following configuration set `Content-Type: application/vnd.android.package-a
- `MAX_IDLE_CONNS` **2**: Max idle database connections on connection pool, default is 2 - this will be capped to `MAX_OPEN_CONNS`. - `MAX_IDLE_CONNS` **2**: Max idle database connections on connection pool, default is 2 - this will be capped to `MAX_OPEN_CONNS`.
- `CONN_MAX_LIFETIME` **0 or 3s**: Sets the maximum amount of time a DB connection may be reused - default is 0, meaning there is no limit (except on MySQL where it is 3s - see #6804 & #7071). - `CONN_MAX_LIFETIME` **0 or 3s**: Sets the maximum amount of time a DB connection may be reused - default is 0, meaning there is no limit (except on MySQL where it is 3s - see #6804 & #7071).
- `AUTO_MIGRATION` **true**: Whether execute database models migrations automatically. - `AUTO_MIGRATION` **true**: Whether execute database models migrations automatically.
- `SLOW_QUERY_TRESHOLD` **5s**: Threshold value in seconds beyond which query execution time is logged as a warning in the xorm logger.
[^1]: It may be necessary to specify a hostport even when listening on a unix socket, as the port is part of the socket name. see [#24552](https://github.com/go-gitea/gitea/issues/24552#issuecomment-1681649367) for additional details. [^1]: It may be necessary to specify a hostport even when listening on a unix socket, as the port is part of the socket name. see [#24552](https://github.com/go-gitea/gitea/issues/24552#issuecomment-1681649367) for additional details.
@ -515,6 +516,7 @@ And the following unique queues:
- `DEFAULT_EMAIL_NOTIFICATIONS`: **enabled**: Default configuration for email notifications for users (user configurable). Options: enabled, onmention, disabled - `DEFAULT_EMAIL_NOTIFICATIONS`: **enabled**: Default configuration for email notifications for users (user configurable). Options: enabled, onmention, disabled
- `DISABLE_REGULAR_ORG_CREATION`: **false**: Disallow regular (non-admin) users from creating organizations. - `DISABLE_REGULAR_ORG_CREATION`: **false**: Disallow regular (non-admin) users from creating organizations.
- `SEND_NOTIFICATION_EMAIL_ON_NEW_USER`: **false**: Send an email to all admins when a new user signs up to inform the admins about this act.
## Security (`security`) ## Security (`security`)

8
go.mod
View file

@ -15,7 +15,6 @@ require (
gitea.com/lunny/levelqueue v0.4.2-0.20230414023320-3c0159fe0fe4 gitea.com/lunny/levelqueue v0.4.2-0.20230414023320-3c0159fe0fe4
github.com/42wim/sshsig v0.0.0-20211121163825-841cf5bbc121 github.com/42wim/sshsig v0.0.0-20211121163825-841cf5bbc121
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358
github.com/NYTimes/gziphandler v1.1.1
github.com/PuerkitoBio/goquery v1.8.1 github.com/PuerkitoBio/goquery v1.8.1
github.com/alecthomas/chroma/v2 v2.10.0 github.com/alecthomas/chroma/v2 v2.10.0
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb
@ -78,14 +77,12 @@ require (
github.com/mholt/archiver/v3 v3.5.1 github.com/mholt/archiver/v3 v3.5.1
github.com/microcosm-cc/bluemonday v1.0.26 github.com/microcosm-cc/bluemonday v1.0.26
github.com/minio/minio-go/v7 v7.0.63 github.com/minio/minio-go/v7 v7.0.63
github.com/minio/sha256-simd v1.0.1
github.com/msteinert/pam v1.2.0 github.com/msteinert/pam v1.2.0
github.com/nektos/act v0.2.52 github.com/nektos/act v0.2.52
github.com/niklasfasching/go-org v1.7.0 github.com/niklasfasching/go-org v1.7.0
github.com/olivere/elastic/v7 v7.0.32 github.com/olivere/elastic/v7 v7.0.32
github.com/opencontainers/go-digest v1.0.0 github.com/opencontainers/go-digest v1.0.0
github.com/opencontainers/image-spec v1.1.0-rc5 github.com/opencontainers/image-spec v1.1.0-rc5
github.com/pkg/errors v0.9.1
github.com/pquerna/otp v1.4.0 github.com/pquerna/otp v1.4.0
github.com/prometheus/client_golang v1.17.0 github.com/prometheus/client_golang v1.17.0
github.com/quasoft/websspi v1.1.2 github.com/quasoft/websspi v1.1.2
@ -101,7 +98,6 @@ require (
github.com/ulikunitz/xz v0.5.11 github.com/ulikunitz/xz v0.5.11
github.com/urfave/cli/v2 v2.25.7 github.com/urfave/cli/v2 v2.25.7
github.com/xanzy/go-gitlab v0.93.1 github.com/xanzy/go-gitlab v0.93.1
github.com/xeipuuv/gojsonschema v1.2.0
github.com/yohcop/openid-go v1.0.1 github.com/yohcop/openid-go v1.0.1
github.com/yuin/goldmark v1.5.6 github.com/yuin/goldmark v1.5.6
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc
@ -232,6 +228,7 @@ require (
github.com/mholt/acmez v1.2.0 // indirect github.com/mholt/acmez v1.2.0 // indirect
github.com/miekg/dns v1.1.56 // indirect github.com/miekg/dns v1.1.56 // indirect
github.com/minio/md5-simd v1.1.2 // indirect github.com/minio/md5-simd v1.1.2 // indirect
github.com/minio/sha256-simd v1.0.1 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect
@ -247,6 +244,7 @@ require (
github.com/pelletier/go-toml/v2 v2.1.0 // indirect github.com/pelletier/go-toml/v2 v2.1.0 // indirect
github.com/pierrec/lz4/v4 v4.1.18 // indirect github.com/pierrec/lz4/v4 v4.1.18 // indirect
github.com/pjbgf/sha1cd v0.3.0 // indirect github.com/pjbgf/sha1cd v0.3.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/prometheus/client_model v0.5.0 // indirect github.com/prometheus/client_model v0.5.0 // indirect
github.com/prometheus/common v0.44.0 // indirect github.com/prometheus/common v0.44.0 // indirect
@ -277,8 +275,6 @@ require (
github.com/valyala/fastjson v1.6.4 // indirect github.com/valyala/fastjson v1.6.4 // indirect
github.com/x448/float16 v0.8.4 // indirect github.com/x448/float16 v0.8.4 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
github.com/zeebo/blake3 v0.2.3 // indirect github.com/zeebo/blake3 v0.2.3 // indirect

9
go.sum
View file

@ -101,8 +101,6 @@ github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBa
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I=
github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=
github.com/ProtonMail/go-crypto v0.0.0-20230923063757-afb1ddc0824c h1:kMFnB0vCcX7IL/m9Y5LO+KQYv+t1CQOiFe6+SV2J7bE= github.com/ProtonMail/go-crypto v0.0.0-20230923063757-afb1ddc0824c h1:kMFnB0vCcX7IL/m9Y5LO+KQYv+t1CQOiFe6+SV2J7bE=
github.com/ProtonMail/go-crypto v0.0.0-20230923063757-afb1ddc0824c/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= github.com/ProtonMail/go-crypto v0.0.0-20230923063757-afb1ddc0824c/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0=
github.com/PuerkitoBio/goquery v1.8.1 h1:uQxhNlArOIdbrH1tr0UXwdVFgDcZDrZVdcpygAcwmWM= github.com/PuerkitoBio/goquery v1.8.1 h1:uQxhNlArOIdbrH1tr0UXwdVFgDcZDrZVdcpygAcwmWM=
@ -1047,13 +1045,6 @@ github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3k
github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM= github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM=
github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8= github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8=
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM= github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo= github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo=
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos= github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=

View file

@ -14,6 +14,7 @@ func TestMain(m *testing.M) {
FixtureFiles: []string{ FixtureFiles: []string{
"gpg_key.yml", "gpg_key.yml",
"public_key.yml", "public_key.yml",
"TestParseCommitWithSSHSignature/public_key.yml",
"deploy_key.yml", "deploy_key.yml",
"gpg_key_import.yml", "gpg_key_import.yml",
"user.yml", "user.yml",

View file

@ -169,7 +169,12 @@ func RewriteAllPublicKeys(ctx context.Context) error {
return err return err
} }
t.Close() if err := t.Sync(); err != nil {
return err
}
if err := t.Close(); err != nil {
return err
}
return util.Rename(tmpPath, fPath) return util.Rename(tmpPath, fPath)
} }

View file

@ -92,7 +92,12 @@ func RewriteAllPrincipalKeys(ctx context.Context) error {
return err return err
} }
t.Close() if err := t.Sync(); err != nil {
return err
}
if err := t.Close(); err != nil {
return err
}
return util.Rename(tmpPath, fPath) return util.Rename(tmpPath, fPath)
} }

View file

@ -39,6 +39,12 @@ func ParseCommitWithSSHSignature(ctx context.Context, c *git.Commit, committer *
log.Error("GetEmailAddresses: %v", err) log.Error("GetEmailAddresses: %v", err)
} }
// Add the noreply email address as verified address.
committerEmailAddresses = append(committerEmailAddresses, &user_model.EmailAddress{
IsActivated: true,
Email: committer.GetPlaceholderEmail(),
})
activated := false activated := false
for _, e := range committerEmailAddresses { for _, e := range committerEmailAddresses {
if e.IsActivated && strings.EqualFold(e.Email, c.Committer.Email) { if e.IsActivated && strings.EqualFold(e.Email, c.Committer.Email) {

View file

@ -0,0 +1,146 @@
// Copyright 2023 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package asymkey
import (
"testing"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/test"
"github.com/stretchr/testify/assert"
)
func TestParseCommitWithSSHSignature(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
sshKey := unittest.AssertExistsAndLoadBean(t, &PublicKey{ID: 1000, OwnerID: 2})
t.Run("No commiter", func(t *testing.T) {
commitVerification := ParseCommitWithSSHSignature(db.DefaultContext, &git.Commit{}, &user_model.User{})
assert.False(t, commitVerification.Verified)
assert.Equal(t, NoKeyFound, commitVerification.Reason)
})
t.Run("Commiter without keys", func(t *testing.T) {
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
commitVerification := ParseCommitWithSSHSignature(db.DefaultContext, &git.Commit{Committer: &git.Signature{Email: user.Email}}, user)
assert.False(t, commitVerification.Verified)
assert.Equal(t, NoKeyFound, commitVerification.Reason)
})
t.Run("Correct signature with wrong email", func(t *testing.T) {
gitCommit := &git.Commit{
Committer: &git.Signature{
Email: "non-existent",
},
Signature: &git.CommitGPGSignature{
Payload: `tree 2d491b2985a7ff848d5c02748e7ea9f9f7619f9f
parent 45b03601635a1f463b81963a4022c7f87ce96ef9
author user2 <non-existent> 1699710556 +0100
committer user2 <non-existent> 1699710556 +0100
Using email that isn't known to Forgejo
`,
Signature: `-----BEGIN SSH SIGNATURE-----
U1NIU0lHAAAAAQAAADMAAAALc3NoLWVkMjU1MTkAAAAgoGSe9Zy7Ez9bSJcaTNjh/Y7p95
f5DujjqkpzFRtw6CEAAAADZ2l0AAAAAAAAAAZzaGE1MTIAAABTAAAAC3NzaC1lZDI1NTE5
AAAAQIMufOuSjZeDUujrkVK4sl7ICa0WwEftas8UAYxx0Thdkiw2qWjR1U1PKfTLm16/w8
/bS1LX1lZNuzm2LR2qEgw=
-----END SSH SIGNATURE-----
`,
},
}
commitVerification := ParseCommitWithSSHSignature(db.DefaultContext, gitCommit, user2)
assert.False(t, commitVerification.Verified)
assert.Equal(t, NoKeyFound, commitVerification.Reason)
})
t.Run("Incorrect signature with correct email", func(t *testing.T) {
gitCommit := &git.Commit{
Committer: &git.Signature{
Email: "user2@example.com",
},
Signature: &git.CommitGPGSignature{
Payload: `tree 853694aae8816094a0d875fee7ea26278dbf5d0f
parent c2780d5c313da2a947eae22efd7dacf4213f4e7f
author user2 <user2@example.com> 1699707877 +0100
committer user2 <user2@example.com> 1699707877 +0100
Add content
`,
Signature: `-----BEGIN SSH SIGNATURE-----`,
},
}
commitVerification := ParseCommitWithSSHSignature(db.DefaultContext, gitCommit, user2)
assert.False(t, commitVerification.Verified)
assert.Equal(t, NoKeyFound, commitVerification.Reason)
})
t.Run("Valid signature with correct email", func(t *testing.T) {
gitCommit := &git.Commit{
Committer: &git.Signature{
Email: "user2@example.com",
},
Signature: &git.CommitGPGSignature{
Payload: `tree 853694aae8816094a0d875fee7ea26278dbf5d0f
parent c2780d5c313da2a947eae22efd7dacf4213f4e7f
author user2 <user2@example.com> 1699707877 +0100
committer user2 <user2@example.com> 1699707877 +0100
Add content
`,
Signature: `-----BEGIN SSH SIGNATURE-----
U1NIU0lHAAAAAQAAADMAAAALc3NoLWVkMjU1MTkAAAAgoGSe9Zy7Ez9bSJcaTNjh/Y7p95
f5DujjqkpzFRtw6CEAAAADZ2l0AAAAAAAAAAZzaGE1MTIAAABTAAAAC3NzaC1lZDI1NTE5
AAAAQBe2Fwk/FKY3SBCnG6jSYcO6ucyahp2SpQ/0P+otslzIHpWNW8cQ0fGLdhhaFynJXQ
fs9cMpZVM9BfIKNUSO8QY=
-----END SSH SIGNATURE-----
`,
},
}
commitVerification := ParseCommitWithSSHSignature(db.DefaultContext, gitCommit, user2)
assert.True(t, commitVerification.Verified)
assert.Equal(t, "user2 / SHA256:TKfwbZMR7e9OnlV2l1prfah1TXH8CmqR0PvFEXVCXA4", commitVerification.Reason)
assert.Equal(t, sshKey, commitVerification.SigningSSHKey)
})
t.Run("Valid signature with noreply email", func(t *testing.T) {
defer test.MockVariableValue(&setting.Service.NoReplyAddress, "noreply.example.com")()
gitCommit := &git.Commit{
Committer: &git.Signature{
Email: "user2@noreply.example.com",
},
Signature: &git.CommitGPGSignature{
Payload: `tree 4836c7f639f37388bab4050ef5c97bbbd54272fc
parent 795be1b0117ea5c65456050bb9fd84744d4fd9c6
author user2 <user2@noreply.example.com> 1699709594 +0100
committer user2 <user2@noreply.example.com> 1699709594 +0100
Commit with noreply
`,
Signature: `-----BEGIN SSH SIGNATURE-----
U1NIU0lHAAAAAQAAADMAAAALc3NoLWVkMjU1MTkAAAAgoGSe9Zy7Ez9bSJcaTNjh/Y7p95
f5DujjqkpzFRtw6CEAAAADZ2l0AAAAAAAAAAZzaGE1MTIAAABTAAAAC3NzaC1lZDI1NTE5
AAAAQJz83KKxD6Bz/ZvNpqkA3RPOSQ4LQ5FfEItbtoONkbwV9wAWMnmBqgggo/lnXCJ3oq
muPLbvEduU+Ze/1Ol1pgk=
-----END SSH SIGNATURE-----
`,
},
}
commitVerification := ParseCommitWithSSHSignature(db.DefaultContext, gitCommit, user2)
assert.True(t, commitVerification.Verified)
assert.Equal(t, "user2 / SHA256:TKfwbZMR7e9OnlV2l1prfah1TXH8CmqR0PvFEXVCXA4", commitVerification.Reason)
assert.Equal(t, sshKey, commitVerification.SigningSSHKey)
})
}

View file

@ -250,7 +250,7 @@ func (s AccessTokenScope) parse() (accessTokenScopeBitmap, error) {
remainingScopes = remainingScopes[i+1:] remainingScopes = remainingScopes[i+1:]
} }
singleScope := AccessTokenScope(v) singleScope := AccessTokenScope(v)
if singleScope == "" { if singleScope == "" || singleScope == "sudo" {
continue continue
} }
if singleScope == AccessTokenScopeAll { if singleScope == AccessTokenScopeAll {

View file

@ -20,7 +20,7 @@ func TestAccessTokenScope_Normalize(t *testing.T) {
tests := []scopeTestNormalize{ tests := []scopeTestNormalize{
{"", "", nil}, {"", "", nil},
{"write:misc,write:notification,read:package,write:notification,public-only", "public-only,write:misc,write:notification,read:package", nil}, {"write:misc,write:notification,read:package,write:notification,public-only", "public-only,write:misc,write:notification,read:package", nil},
{"all", "all", nil}, {"all,sudo", "all", nil},
{"write:activitypub,write:admin,write:misc,write:notification,write:organization,write:package,write:issue,write:repository,write:user", "all", nil}, {"write:activitypub,write:admin,write:misc,write:notification,write:organization,write:package,write:issue,write:repository,write:user", "all", nil},
{"write:activitypub,write:admin,write:misc,write:notification,write:organization,write:package,write:issue,write:repository,write:user,public-only", "public-only,all", nil}, {"write:activitypub,write:admin,write:misc,write:notification,write:organization,write:package,write:issue,write:repository,write:user,public-only", "public-only,all", nil},
} }

View file

@ -6,6 +6,7 @@ package auth
import ( import (
"context" "context"
"crypto/md5" "crypto/md5"
"crypto/sha256"
"crypto/subtle" "crypto/subtle"
"encoding/base32" "encoding/base32"
"encoding/base64" "encoding/base64"
@ -18,7 +19,6 @@ import (
"code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/util"
"github.com/minio/sha256-simd"
"github.com/pquerna/otp/totp" "github.com/pquerna/otp/totp"
"golang.org/x/crypto/pbkdf2" "golang.org/x/crypto/pbkdf2"
) )

View file

@ -11,10 +11,13 @@ import (
"io" "io"
"reflect" "reflect"
"strings" "strings"
"time"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"xorm.io/xorm" "xorm.io/xorm"
"xorm.io/xorm/contexts"
"xorm.io/xorm/names" "xorm.io/xorm/names"
"xorm.io/xorm/schemas" "xorm.io/xorm/schemas"
@ -147,6 +150,13 @@ func InitEngine(ctx context.Context) error {
xormEngine.SetConnMaxLifetime(setting.Database.ConnMaxLifetime) xormEngine.SetConnMaxLifetime(setting.Database.ConnMaxLifetime)
xormEngine.SetDefaultContext(ctx) xormEngine.SetDefaultContext(ctx)
if setting.Database.SlowQueryTreshold > 0 {
xormEngine.AddHook(&SlowQueryHook{
Treshold: setting.Database.SlowQueryTreshold,
Logger: log.GetLogger("xorm"),
})
}
SetDefaultEngine(ctx, xormEngine) SetDefaultEngine(ctx, xormEngine)
return nil return nil
} }
@ -300,3 +310,21 @@ func SetLogSQL(ctx context.Context, on bool) {
sess.Engine().ShowSQL(on) sess.Engine().ShowSQL(on)
} }
} }
type SlowQueryHook struct {
Treshold time.Duration
Logger log.Logger
}
var _ contexts.Hook = &SlowQueryHook{}
func (SlowQueryHook) BeforeProcess(c *contexts.ContextHook) (context.Context, error) {
return c.Ctx, nil
}
func (h *SlowQueryHook) AfterProcess(c *contexts.ContextHook) error {
if c.ExecuteTime >= h.Treshold {
h.Logger.Log(8, log.WARN, "[Slow SQL Query] %s %v - %v", c.SQL, c.Args, c.ExecuteTime)
}
return nil
}

View file

@ -6,15 +6,19 @@ package db_test
import ( import (
"path/filepath" "path/filepath"
"testing" "testing"
"time"
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues" issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/models/unittest"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/test"
_ "code.gitea.io/gitea/cmd" // for TestPrimaryKeys _ "code.gitea.io/gitea/cmd" // for TestPrimaryKeys
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"xorm.io/xorm"
) )
func TestDumpDatabase(t *testing.T) { func TestDumpDatabase(t *testing.T) {
@ -85,3 +89,37 @@ func TestPrimaryKeys(t *testing.T) {
} }
} }
} }
func TestSlowQuery(t *testing.T) {
lc, cleanup := test.NewLogChecker("slow-query")
lc.StopMark("[Slow SQL Query]")
defer cleanup()
e := db.GetEngine(db.DefaultContext)
engine, ok := e.(*xorm.Engine)
assert.True(t, ok)
// It's not possible to clean this up with XORM, but it's luckily not harmful
// to leave around.
engine.AddHook(&db.SlowQueryHook{
Treshold: time.Second * 10,
Logger: log.GetLogger("slow-query"),
})
// NOOP query.
e.Exec("SELECT 1 WHERE false;")
_, stopped := lc.Check(100 * time.Millisecond)
assert.False(t, stopped)
engine.AddHook(&db.SlowQueryHook{
Treshold: 0, // Every query should be logged.
Logger: log.GetLogger("slow-query"),
})
// NOOP query.
e.Exec("SELECT 1 WHERE false;")
_, stopped = lc.Check(100 * time.Millisecond)
assert.True(t, stopped)
}

View file

@ -0,0 +1,13 @@
-
id: 1000
owner_id: 2
name: user2@localhost
fingerprint: "SHA256:TKfwbZMR7e9OnlV2l1prfah1TXH8CmqR0PvFEXVCXA4"
content: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKBknvWcuxM/W0iXGkzY4f2O6feX+Q7o46pKcxUbcOgh user2@localhost"
# private key (base64-ed) LS0tLS1CRUdJTiBPUEVOU1NIIFBSSVZBVEUgS0VZLS0tLS0KYjNCbGJuTnphQzFyWlhrdGRqRUFBQUFBQkc1dmJtVUFBQUFFYm05dVpRQUFBQUFBQUFBQkFBQUFNd0FBQUF0emMyZ3RaVwpReU5UVXhPUUFBQUNDZ1pKNzFuTHNUUDF0SWx4cE0yT0g5anVuM2wva082T09xU25NVkczRG9JUUFBQUpocG43YTZhWisyCnVnQUFBQXR6YzJndFpXUXlOVFV4T1FBQUFDQ2daSjcxbkxzVFAxdElseHBNMk9IOWp1bjNsL2tPNk9PcVNuTVZHM0RvSVEKQUFBRUFxVm12bmo1LzZ5TW12ck9Ub29xa3F5MmUrc21aK0tBcEtKR0crRnY5MlA2QmtudldjdXhNL1cwaVhHa3pZNGYyTwo2ZmVYK1E3bzQ2cEtjeFViY09naEFBQUFFMmQxYzNSbFpFQm5kWE4wWldRdFltVmhjM1FCQWc9PQotLS0tLUVORCBPUEVOU1NIIFBSSVZBVEUgS0VZLS0tLS0=
mode: 2
type: 1
verified: true
created_unix: 1559593109
updated_unix: 1565224552
login_source_id: 0

View file

@ -150,3 +150,17 @@
is_prerelease: false is_prerelease: false
is_tag: false is_tag: false
created_unix: 946684803 created_unix: 946684803
- id: 12
repo_id: 59
publisher_id: 2
tag_name: "v1.0"
lower_tag_name: "v1.0"
target: "main"
title: "v1.0"
sha1: "d8f53dfb33f6ccf4169c34970b5e747511c18beb"
num_commits: 1
is_draft: false
is_prerelease: false
is_tag: false
created_unix: 946684803

View file

@ -608,6 +608,38 @@
type: 1 type: 1
created_unix: 946684810 created_unix: 946684810
# BEGIN Forgejo [GITEA] Improve HTML title on repositories
-
id: 1093
repo_id: 59
type: 1
created_unix: 946684810
-
id: 1094
repo_id: 59
type: 2
created_unix: 946684810
-
id: 1095
repo_id: 59
type: 3
created_unix: 946684810
-
id: 1096
repo_id: 59
type: 4
created_unix: 946684810
-
id: 1097
repo_id: 59
type: 5
created_unix: 946684810
# END Forgejo [GITEA] Improve HTML title on repositories
- -
id: 91 id: 91
repo_id: 58 repo_id: 58

View file

@ -1467,6 +1467,7 @@
owner_name: user27 owner_name: user27
lower_name: repo49 lower_name: repo49
name: repo49 name: repo49
description: A wonderful repository with more than just a README.md
default_branch: master default_branch: master
num_watches: 0 num_watches: 0
num_stars: 0 num_stars: 0
@ -1693,3 +1694,16 @@
size: 0 size: 0
is_fsck_enabled: true is_fsck_enabled: true
close_issues_via_commit_in_any_branch: false close_issues_via_commit_in_any_branch: false
-
id: 59
owner_id: 2
owner_name: user2
lower_name: repo59
name: repo59
default_branch: master
is_empty: false
is_archived: false
is_private: false
status: 0
num_issues: 0

View file

@ -66,7 +66,7 @@
num_followers: 2 num_followers: 2
num_following: 1 num_following: 1
num_stars: 2 num_stars: 2
num_repos: 14 num_repos: 15
num_teams: 0 num_teams: 0
num_members: 0 num_members: 0
visibility: 0 visibility: 0

View file

@ -4,9 +4,9 @@
package base package base
import ( import (
"crypto/sha256"
"encoding/hex" "encoding/hex"
"github.com/minio/sha256-simd"
"golang.org/x/crypto/pbkdf2" "golang.org/x/crypto/pbkdf2"
) )

View file

@ -4,9 +4,9 @@
package v1_14 //nolint package v1_14 //nolint
import ( import (
"crypto/sha256"
"encoding/hex" "encoding/hex"
"github.com/minio/sha256-simd"
"golang.org/x/crypto/argon2" "golang.org/x/crypto/argon2"
"golang.org/x/crypto/bcrypt" "golang.org/x/crypto/bcrypt"
"golang.org/x/crypto/pbkdf2" "golang.org/x/crypto/pbkdf2"

View file

@ -4,13 +4,7 @@
package v1_21 //nolint package v1_21 //nolint
import ( import (
"context" repo_model "code.gitea.io/gitea/models/repo"
"fmt"
"path/filepath"
"strings"
"code.gitea.io/gitea/modules/git"
giturl "code.gitea.io/gitea/modules/git/url"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"xorm.io/xorm" "xorm.io/xorm"
@ -73,7 +67,7 @@ func migratePullMirrors(x *xorm.Engine) error {
start += len(mirrors) start += len(mirrors)
for _, m := range mirrors { for _, m := range mirrors {
remoteAddress, err := getRemoteAddress(m.RepoOwner, m.RepoName, "origin") remoteAddress, err := repo_model.GetPushMirrorRemoteAddress(m.RepoOwner, m.RepoName, "origin")
if err != nil { if err != nil {
return err return err
} }
@ -136,7 +130,7 @@ func migratePushMirrors(x *xorm.Engine) error {
start += len(mirrors) start += len(mirrors)
for _, m := range mirrors { for _, m := range mirrors {
remoteAddress, err := getRemoteAddress(m.RepoOwner, m.RepoName, m.RemoteName) remoteAddress, err := repo_model.GetPushMirrorRemoteAddress(m.RepoOwner, m.RepoName, m.RemoteName)
if err != nil { if err != nil {
return err return err
} }
@ -160,20 +154,3 @@ func migratePushMirrors(x *xorm.Engine) error {
return sess.Commit() return sess.Commit()
} }
func getRemoteAddress(ownerName, repoName, remoteName string) (string, error) {
repoPath := filepath.Join(setting.RepoRootPath, strings.ToLower(ownerName), strings.ToLower(repoName)+".git")
remoteURL, err := git.GetRemoteAddress(context.Background(), repoPath, remoteName)
if err != nil {
return "", fmt.Errorf("get remote %s's address of %s/%s failed: %v", remoteName, ownerName, repoName, err)
}
u, err := giturl.Parse(remoteURL)
if err != nil {
return "", err
}
u.User = nil
return u.String(), nil
}

View file

@ -5,10 +5,16 @@ package repo
import ( import (
"context" "context"
"fmt"
"path/filepath"
"strings"
"time" "time"
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/git"
giturl "code.gitea.io/gitea/modules/git/url"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/util"
@ -145,3 +151,21 @@ func PushMirrorsIterate(ctx context.Context, limit int, f func(idx int, bean any
} }
return sess.Iterate(new(PushMirror), f) return sess.Iterate(new(PushMirror), f)
} }
// GetPushMirrorRemoteAddress returns the address of associated with a repository's given remote.
func GetPushMirrorRemoteAddress(ownerName, repoName, remoteName string) (string, error) {
repoPath := filepath.Join(setting.RepoRootPath, strings.ToLower(ownerName), strings.ToLower(repoName)+".git")
remoteURL, err := git.GetRemoteAddress(context.Background(), repoPath, remoteName)
if err != nil {
return "", fmt.Errorf("get remote %s's address of %s/%s failed: %v", remoteName, ownerName, repoName, err)
}
u, err := giturl.Parse(remoteURL)
if err != nil {
return "", err
}
u.User = nil
return u.String(), nil
}

View file

@ -138,12 +138,12 @@ func getTestCases() []struct {
{ {
name: "AllPublic/PublicRepositoriesOfUserIncludingCollaborative", name: "AllPublic/PublicRepositoriesOfUserIncludingCollaborative",
opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, AllPublic: true, Template: util.OptionalBoolFalse}, opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, AllPublic: true, Template: util.OptionalBoolFalse},
count: 31, count: 32,
}, },
{ {
name: "AllPublic/PublicAndPrivateRepositoriesOfUserIncludingCollaborative", name: "AllPublic/PublicAndPrivateRepositoriesOfUserIncludingCollaborative",
opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, Private: true, AllPublic: true, AllLimited: true, Template: util.OptionalBoolFalse}, opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, Private: true, AllPublic: true, AllLimited: true, Template: util.OptionalBoolFalse},
count: 36, count: 37,
}, },
{ {
name: "AllPublic/PublicAndPrivateRepositoriesOfUserIncludingCollaborativeByName", name: "AllPublic/PublicAndPrivateRepositoriesOfUserIncludingCollaborativeByName",
@ -158,7 +158,7 @@ func getTestCases() []struct {
{ {
name: "AllPublic/PublicRepositoriesOfOrganization", name: "AllPublic/PublicRepositoriesOfOrganization",
opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 17, AllPublic: true, Collaborate: util.OptionalBoolFalse, Template: util.OptionalBoolFalse}, opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 17, AllPublic: true, Collaborate: util.OptionalBoolFalse, Template: util.OptionalBoolFalse},
count: 31, count: 32,
}, },
{ {
name: "AllTemplates", name: "AllTemplates",

View file

@ -0,0 +1,113 @@
// Copyright 2017 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package unittest
import (
"bufio"
"fmt"
"io"
"net/http"
"net/http/httptest"
"net/url"
"os"
"slices"
"strings"
"testing"
"code.gitea.io/gitea/modules/log"
"github.com/stretchr/testify/assert"
)
// Mocks HTTP responses of a third-party service (such as GitHub, GitLab…)
// This has two modes:
// - live mode: the requests made to the mock HTTP server are transmitted to the live
// service, and responses are saved as test data files
// - test mode: the responses to requests to the mock HTTP server are read from the
// test data files
func NewMockWebServer(t *testing.T, liveServerBaseURL, testDataDir string, liveMode bool) *httptest.Server {
mockServerBaseURL := ""
ignoredHeaders := []string{"cf-ray", "server", "date", "report-to", "nel", "x-request-id"}
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
path := NormalizedFullPath(r.URL)
log.Info("Mock HTTP Server: got request for path %s", r.URL.Path)
// TODO check request method (support POST?)
fixturePath := fmt.Sprintf("%s/%s", testDataDir, strings.ReplaceAll(path, "/", "_"))
if liveMode {
liveURL := fmt.Sprintf("%s%s", liveServerBaseURL, path)
request, err := http.NewRequest(r.Method, liveURL, nil)
assert.NoError(t, err, "constructing an HTTP request to %s failed", liveURL)
for headerName, headerValues := range r.Header {
// do not pass on the encoding: let the Transport of the HTTP client handle that for us
if strings.ToLower(headerName) != "accept-encoding" {
for _, headerValue := range headerValues {
request.Header.Add(headerName, headerValue)
}
}
}
response, err := http.DefaultClient.Do(request)
assert.NoError(t, err, "HTTP request to %s failed: %s", liveURL)
fixture, err := os.Create(fixturePath)
assert.NoError(t, err, "failed to open the fixture file %s for writing", fixturePath)
defer fixture.Close()
fixtureWriter := bufio.NewWriter(fixture)
for headerName, headerValues := range response.Header {
for _, headerValue := range headerValues {
if !slices.Contains(ignoredHeaders, strings.ToLower(headerName)) {
_, err := fixtureWriter.WriteString(fmt.Sprintf("%s: %s\n", headerName, headerValue))
assert.NoError(t, err, "writing the header of the HTTP response to the fixture file failed")
}
}
}
_, err = fixtureWriter.WriteString("\n")
assert.NoError(t, err, "writing the header of the HTTP response to the fixture file failed")
fixtureWriter.Flush()
log.Info("Mock HTTP Server: writing response to %s", fixturePath)
_, err = io.Copy(fixture, response.Body)
assert.NoError(t, err, "writing the body of the HTTP response to %s failed", liveURL)
err = fixture.Sync()
assert.NoError(t, err, "writing the body of the HTTP response to the fixture file failed")
}
fixture, err := os.ReadFile(fixturePath)
assert.NoError(t, err, "missing mock HTTP response: "+fixturePath)
w.WriteHeader(http.StatusOK)
// replace any mention of the live HTTP service by the mocked host
stringFixture := strings.ReplaceAll(string(fixture), liveServerBaseURL, mockServerBaseURL)
// parse back the fixture file into a series of HTTP headers followed by response body
lines := strings.Split(stringFixture, "\n")
for idx, line := range lines {
colonIndex := strings.Index(line, ": ")
if colonIndex != -1 {
w.Header().Set(line[0:colonIndex], line[colonIndex+2:])
} else {
// we reached the end of the headers (empty line), so what follows is the body
responseBody := strings.Join(lines[idx+1:], "\n")
_, err := w.Write([]byte(responseBody))
assert.NoError(t, err, "writing the body of the HTTP response failed")
break
}
}
}))
mockServerBaseURL = server.URL
return server
}
func NormalizedFullPath(url *url.URL) string {
// TODO normalize path (remove trailing slash?)
// TODO normalize RawQuery (order query parameters?)
if len(url.Query()) == 0 {
return url.EscapedPath()
}
return fmt.Sprintf("%s?%s", url.EscapedPath(), url.RawQuery)
}

View file

@ -189,6 +189,25 @@ func GetEmailAddresses(ctx context.Context, uid int64) ([]*EmailAddress, error)
return emails, nil return emails, nil
} }
type ActivatedEmailAddress struct {
ID int64
Email string
}
func GetActivatedEmailAddresses(ctx context.Context, uid int64) ([]*ActivatedEmailAddress, error) {
emails := make([]*ActivatedEmailAddress, 0, 8)
if err := db.GetEngine(ctx).
Table("email_address").
Select("id, email").
Where("uid=?", uid).
And("is_activated=?", true).
Asc("id").
Find(&emails); err != nil {
return nil, err
}
return emails, nil
}
// GetEmailAddressByID gets a user's email address by ID // GetEmailAddressByID gets a user's email address by ID
func GetEmailAddressByID(ctx context.Context, uid, id int64) (*EmailAddress, error) { func GetEmailAddressByID(ctx context.Context, uid, id int64) (*EmailAddress, error) {
// User ID is required for security reasons // User ID is required for security reasons

View file

@ -4,6 +4,7 @@
package user_test package user_test
import ( import (
"fmt"
"testing" "testing"
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
@ -309,3 +310,37 @@ func TestEmailAddressValidate(t *testing.T) {
}) })
} }
} }
func TestGetActivatedEmailAddresses(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
testCases := []struct {
UID int64
expected []*user_model.ActivatedEmailAddress
}{
{
UID: 1,
expected: []*user_model.ActivatedEmailAddress{{ID: 9, Email: "user1@example.com"}, {ID: 33, Email: "user1-2@example.com"}, {ID: 34, Email: "user1-3@example.com"}},
},
{
UID: 2,
expected: []*user_model.ActivatedEmailAddress{{ID: 3, Email: "user2@example.com"}},
},
{
UID: 4,
expected: []*user_model.ActivatedEmailAddress{{ID: 11, Email: "user4@example.com"}},
},
{
UID: 11,
expected: []*user_model.ActivatedEmailAddress{},
},
}
for _, testCase := range testCases {
t.Run(fmt.Sprintf("User %d", testCase.UID), func(t *testing.T) {
emails, err := user_model.GetActivatedEmailAddresses(db.DefaultContext, testCase.UID)
assert.NoError(t, err)
assert.Equal(t, testCase.expected, emails)
})
}
}

View file

@ -223,6 +223,12 @@ func GetAllUsers(ctx context.Context) ([]*User, error) {
return users, db.GetEngine(ctx).OrderBy("id").Where("type = ?", UserTypeIndividual).Find(&users) return users, db.GetEngine(ctx).OrderBy("id").Where("type = ?", UserTypeIndividual).Find(&users)
} }
// GetAllAdmins returns a slice of all adminusers found in DB.
func GetAllAdmins(ctx context.Context) ([]*User, error) {
users := make([]*User, 0)
return users, db.GetEngine(ctx).OrderBy("id").Where("type = ?", UserTypeIndividual).And("is_admin = ?", true).Find(&users)
}
// IsLocal returns true if user login type is LoginPlain. // IsLocal returns true if user login type is LoginPlain.
func (u *User) IsLocal() bool { func (u *User) IsLocal() bool {
return u.LoginType <= auth.Plain return u.LoginType <= auth.Plain

View file

@ -550,3 +550,13 @@ func Test_ValidateUser(t *testing.T) {
assert.EqualValues(t, expected, err == nil, fmt.Sprintf("case: %+v", kase)) assert.EqualValues(t, expected, err == nil, fmt.Sprintf("case: %+v", kase))
} }
} }
func TestGetAllAdmins(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
admins, err := user_model.GetAllAdmins(db.DefaultContext)
assert.NoError(t, err)
assert.Len(t, admins, 1)
assert.Equal(t, int64(1), admins[0].ID)
}

View file

@ -4,12 +4,12 @@
package hash package hash
import ( import (
"crypto/sha256"
"encoding/hex" "encoding/hex"
"strings" "strings"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"github.com/minio/sha256-simd"
"golang.org/x/crypto/pbkdf2" "golang.org/x/crypto/pbkdf2"
) )

View file

@ -4,10 +4,9 @@
package avatar package avatar
import ( import (
"crypto/sha256"
"encoding/hex" "encoding/hex"
"strconv" "strconv"
"github.com/minio/sha256-simd"
) )
// HashAvatar will generate a unique string, which ensures that when there's a // HashAvatar will generate a unique string, which ensures that when there's a

View file

@ -7,11 +7,10 @@
package identicon package identicon
import ( import (
"crypto/sha256"
"fmt" "fmt"
"image" "image"
"image/color" "image/color"
"github.com/minio/sha256-simd"
) )
const minImageSize = 16 const minImageSize = 16

View file

@ -6,6 +6,7 @@ package base
import ( import (
"crypto/md5" "crypto/md5"
"crypto/sha1" "crypto/sha1"
"crypto/sha256"
"encoding/base64" "encoding/base64"
"encoding/hex" "encoding/hex"
"errors" "errors"
@ -24,7 +25,6 @@ import (
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"github.com/dustin/go-humanize" "github.com/dustin/go-humanize"
"github.com/minio/sha256-simd"
) )
// EncodeMD5 encodes string to md5 hex value. // EncodeMD5 encodes string to md5 hex value.

View file

@ -0,0 +1,91 @@
// Copyright 2023 The Forgejo Authors c/o Codeberg e.V.. All rights reserved.
// SPDX-License-Identifier: MIT
package doctor
import (
"context"
"strings"
"code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/log"
"xorm.io/builder"
)
func FixPushMirrorsWithoutGitRemote(ctx context.Context, logger log.Logger, autofix bool) error {
var missingMirrors []*repo_model.PushMirror
err := db.Iterate(ctx, builder.Gt{"id": 0}, func(ctx context.Context, repo *repo_model.Repository) error {
pushMirrors, _, err := repo_model.GetPushMirrorsByRepoID(ctx, repo.ID, db.ListOptions{})
if err != nil {
return err
}
for i := 0; i < len(pushMirrors); i++ {
_, err = repo_model.GetPushMirrorRemoteAddress(repo.OwnerName, repo.Name, pushMirrors[i].RemoteName)
if err != nil {
if strings.Contains(err.Error(), "No such remote") {
missingMirrors = append(missingMirrors, pushMirrors[i])
} else if logger != nil {
logger.Warn("Unable to retrieve the remote address of a mirror: %s", err)
}
}
}
return nil
})
if err != nil {
if logger != nil {
logger.Critical("Unable to iterate across repounits to fix push mirrors without a git remote: Error %v", err)
}
return err
}
count := len(missingMirrors)
if !autofix {
if logger != nil {
if count == 0 {
logger.Info("Found no push mirrors with missing git remotes")
} else {
logger.Warn("Found %d push mirrors with missing git remotes", count)
}
}
return nil
}
for i := 0; i < len(missingMirrors); i++ {
if logger != nil {
logger.Info("Removing push mirror #%d (remote: %s), for repo: %s/%s",
missingMirrors[i].ID,
missingMirrors[i].RemoteName,
missingMirrors[i].GetRepository(ctx).OwnerName,
missingMirrors[i].GetRepository(ctx).Name)
}
err = repo_model.DeletePushMirrors(ctx, repo_model.PushMirrorOptions{
ID: missingMirrors[i].ID,
RepoID: missingMirrors[i].RepoID,
RemoteName: missingMirrors[i].RemoteName,
})
if err != nil {
if logger != nil {
logger.Critical("Error removing a push mirror (repo_id: %d, push_mirror: %d): %s", missingMirrors[i].Repo.ID, missingMirrors[i].ID, err)
}
return err
}
}
return nil
}
func init() {
Register(&Check{
Title: "Check for push mirrors without a git remote configured",
Name: "fix-push-mirrors-without-git-remote",
IsDefault: false,
Run: FixPushMirrorsWithoutGitRemote,
Priority: 7,
})
}

View file

@ -509,6 +509,62 @@ func GetCommitFileStatus(ctx context.Context, repoPath, commitID string) (*Commi
return fileStatus, nil return fileStatus, nil
} }
func parseCommitRenames(renames *[][2]string, stdout io.Reader) {
rd := bufio.NewReader(stdout)
for {
// Skip (R || three digits || NULL byte)
_, err := rd.Discard(5)
if err != nil {
if err != io.EOF {
log.Error("Unexpected error whilst reading from git log --name-status. Error: %v", err)
}
return
}
oldFileName, err := rd.ReadString('\x00')
if err != nil {
if err != io.EOF {
log.Error("Unexpected error whilst reading from git log --name-status. Error: %v", err)
}
return
}
newFileName, err := rd.ReadString('\x00')
if err != nil {
if err != io.EOF {
log.Error("Unexpected error whilst reading from git log --name-status. Error: %v", err)
}
return
}
oldFileName = strings.TrimSuffix(oldFileName, "\x00")
newFileName = strings.TrimSuffix(newFileName, "\x00")
*renames = append(*renames, [2]string{oldFileName, newFileName})
}
}
// GetCommitFileRenames returns the renames that the commit contains.
func GetCommitFileRenames(ctx context.Context, repoPath, commitID string) ([][2]string, error) {
renames := [][2]string{}
stdout, w := io.Pipe()
done := make(chan struct{})
go func() {
parseCommitRenames(&renames, stdout)
close(done)
}()
stderr := new(bytes.Buffer)
err := NewCommand(ctx, "show", "--name-status", "--pretty=format:", "-z", "--diff-filter=R").AddDynamicArguments(commitID).Run(&RunOpts{
Dir: repoPath,
Stdout: w,
Stderr: stderr,
})
w.Close() // Close writer to exit parsing goroutine
if err != nil {
return nil, ConcatenateError(err, stderr.String())
}
<-done
return renames, nil
}
// GetFullCommitID returns full length (40) of commit ID by given short SHA in a repository. // GetFullCommitID returns full length (40) of commit ID by given short SHA in a repository.
func GetFullCommitID(ctx context.Context, repoPath, shortID string) (string, error) { func GetFullCommitID(ctx context.Context, repoPath, shortID string) (string, error) {
commitID, _, err := NewCommand(ctx, "rev-parse").AddDynamicArguments(shortID).RunStdString(&RunOpts{Dir: repoPath}) commitID, _, err := NewCommand(ctx, "rev-parse").AddDynamicArguments(shortID).RunStdString(&RunOpts{Dir: repoPath})

View file

@ -278,3 +278,30 @@ func TestGetCommitFileStatusMerges(t *testing.T) {
assert.Equal(t, commitFileStatus.Removed, expected.Removed) assert.Equal(t, commitFileStatus.Removed, expected.Removed)
assert.Equal(t, commitFileStatus.Modified, expected.Modified) assert.Equal(t, commitFileStatus.Modified, expected.Modified)
} }
func TestParseCommitRenames(t *testing.T) {
testcases := []struct {
output string
renames [][2]string
}{
{
output: "R090\x00renamed.txt\x00history.txt\x00",
renames: [][2]string{{"renamed.txt", "history.txt"}},
},
{
output: "R090\x00renamed.txt\x00history.txt\x00R000\x00corruptedstdouthere",
renames: [][2]string{{"renamed.txt", "history.txt"}},
},
{
output: "R100\x00renamed.txt\x00history.txt\x00R001\x00readme.md\x00README.md\x00",
renames: [][2]string{{"renamed.txt", "history.txt"}, {"readme.md", "README.md"}},
},
}
for _, testcase := range testcases {
renames := [][2]string{}
parseCommitRenames(&renames, strings.NewReader(testcase.output))
assert.Equal(t, testcase.renames, renames)
}
}

View file

@ -4,12 +4,11 @@
package git package git
import ( import (
"crypto/sha256"
"fmt" "fmt"
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"github.com/minio/sha256-simd"
) )
// Cache represents a caching interface // Cache represents a caching interface

View file

@ -4,6 +4,7 @@
package lfs package lfs
import ( import (
"crypto/sha256"
"encoding/hex" "encoding/hex"
"errors" "errors"
"hash" "hash"
@ -12,8 +13,6 @@ import (
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/storage" "code.gitea.io/gitea/modules/storage"
"github.com/minio/sha256-simd"
) )
var ( var (

View file

@ -4,6 +4,7 @@
package lfs package lfs
import ( import (
"crypto/sha256"
"encoding/hex" "encoding/hex"
"errors" "errors"
"fmt" "fmt"
@ -12,8 +13,6 @@ import (
"regexp" "regexp"
"strconv" "strconv"
"strings" "strings"
"github.com/minio/sha256-simd"
) )
const ( const (

View file

@ -510,6 +510,18 @@ func TestMathBlock(t *testing.T) {
"$$a$$", "$$a$$",
`<pre class="code-block is-loading"><code class="chroma language-math display">a</code></pre>` + nl, `<pre class="code-block is-loading"><code class="chroma language-math display">a</code></pre>` + nl,
}, },
{
`\[a b\]`,
`<pre class="code-block is-loading"><code class="chroma language-math display">a b</code></pre>` + nl,
},
{
`\[a b]`,
`<p>[a b]</p>` + nl,
},
{
`$$a`,
`<p>$$a</p>` + nl,
},
} }
for _, test := range testcases { for _, test := range testcases {

View file

@ -55,10 +55,7 @@ func (b *blockParser) Open(parent ast.Node, reader text.Reader, pc parser.Contex
return node, parser.Close | parser.NoChildren return node, parser.Close | parser.NoChildren
} }
reader.Advance(segment.Len() - 1) return nil, parser.NoChildren
segment.Start += 2
node.Lines().Append(segment)
return node, parser.NoChildren
} }
// Continue parses the current line and returns a result of parsing. // Continue parses the current line and returns a result of parsing.

View file

@ -7,13 +7,12 @@ import (
"crypto/aes" "crypto/aes"
"crypto/cipher" "crypto/cipher"
"crypto/rand" "crypto/rand"
"crypto/sha256"
"encoding/base64" "encoding/base64"
"encoding/hex" "encoding/hex"
"errors" "errors"
"fmt" "fmt"
"io" "io"
"github.com/minio/sha256-simd"
) )
// AesEncrypt encrypts text and given key with AES. // AesEncrypt encrypts text and given key with AES.

View file

@ -7,6 +7,7 @@ package setting
var Admin struct { var Admin struct {
DisableRegularOrgCreation bool DisableRegularOrgCreation bool
DefaultEmailNotification string DefaultEmailNotification string
SendNotificationEmailOnNewUser bool
} }
func loadAdminFrom(rootCfg ConfigProvider) { func loadAdminFrom(rootCfg ConfigProvider) {

View file

@ -45,6 +45,7 @@ var (
ConnMaxLifetime time.Duration ConnMaxLifetime time.Duration
IterateBufferSize int IterateBufferSize int
AutoMigration bool AutoMigration bool
SlowQueryTreshold time.Duration
}{ }{
Timeout: 500, Timeout: 500,
IterateBufferSize: 50, IterateBufferSize: 50,
@ -87,6 +88,7 @@ func loadDBSetting(rootCfg ConfigProvider) {
Database.DBConnectRetries = sec.Key("DB_RETRIES").MustInt(10) Database.DBConnectRetries = sec.Key("DB_RETRIES").MustInt(10)
Database.DBConnectBackoff = sec.Key("DB_RETRY_BACKOFF").MustDuration(3 * time.Second) Database.DBConnectBackoff = sec.Key("DB_RETRY_BACKOFF").MustDuration(3 * time.Second)
Database.AutoMigration = sec.Key("AUTO_MIGRATION").MustBool(true) Database.AutoMigration = sec.Key("AUTO_MIGRATION").MustBool(true)
Database.SlowQueryTreshold = sec.Key("SLOW_QUERY_TRESHOLD").MustDuration(5 * time.Second)
} }
// DBConnStr returns database connection string // DBConnStr returns database connection string

View file

@ -68,6 +68,7 @@ var Service = struct {
DefaultKeepEmailPrivate bool DefaultKeepEmailPrivate bool
DefaultAllowCreateOrganization bool DefaultAllowCreateOrganization bool
DefaultUserIsRestricted bool DefaultUserIsRestricted bool
AllowDotsInUsernames bool
EnableTimetracking bool EnableTimetracking bool
DefaultEnableTimetracking bool DefaultEnableTimetracking bool
DefaultEnableDependencies bool DefaultEnableDependencies bool
@ -180,6 +181,7 @@ func loadServiceFrom(rootCfg ConfigProvider) {
Service.DefaultKeepEmailPrivate = sec.Key("DEFAULT_KEEP_EMAIL_PRIVATE").MustBool() Service.DefaultKeepEmailPrivate = sec.Key("DEFAULT_KEEP_EMAIL_PRIVATE").MustBool()
Service.DefaultAllowCreateOrganization = sec.Key("DEFAULT_ALLOW_CREATE_ORGANIZATION").MustBool(true) Service.DefaultAllowCreateOrganization = sec.Key("DEFAULT_ALLOW_CREATE_ORGANIZATION").MustBool(true)
Service.DefaultUserIsRestricted = sec.Key("DEFAULT_USER_IS_RESTRICTED").MustBool(false) Service.DefaultUserIsRestricted = sec.Key("DEFAULT_USER_IS_RESTRICTED").MustBool(false)
Service.AllowDotsInUsernames = sec.Key("ALLOW_DOTS_IN_USERNAMES").MustBool(true)
Service.EnableTimetracking = sec.Key("ENABLE_TIMETRACKING").MustBool(true) Service.EnableTimetracking = sec.Key("ENABLE_TIMETRACKING").MustBool(true)
if Service.EnableTimetracking { if Service.EnableTimetracking {
Service.DefaultEnableTimetracking = sec.Key("DEFAULT_ENABLE_TIMETRACKING").MustBool(true) Service.DefaultEnableTimetracking = sec.Key("DEFAULT_ENABLE_TIMETRACKING").MustBool(true)

View file

@ -7,10 +7,9 @@ import (
"crypto" "crypto"
"crypto/rand" "crypto/rand"
"crypto/rsa" "crypto/rsa"
"crypto/sha256"
"crypto/x509" "crypto/x509"
"encoding/pem" "encoding/pem"
"github.com/minio/sha256-simd"
) )
// GenerateKeyPair generates a public and private keypair // GenerateKeyPair generates a public and private keypair

View file

@ -7,12 +7,12 @@ import (
"crypto" "crypto"
"crypto/rand" "crypto/rand"
"crypto/rsa" "crypto/rsa"
"crypto/sha256"
"crypto/x509" "crypto/x509"
"encoding/pem" "encoding/pem"
"regexp" "regexp"
"testing" "testing"
"github.com/minio/sha256-simd"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )

View file

@ -117,13 +117,20 @@ func IsValidExternalTrackerURLFormat(uri string) bool {
} }
var ( var (
validUsernamePattern = regexp.MustCompile(`^[\da-zA-Z][-.\w]*$`) validUsernamePatternWithDots = regexp.MustCompile(`^[\da-zA-Z][-.\w]*$`)
invalidUsernamePattern = regexp.MustCompile(`[-._]{2,}|[-._]$`) // No consecutive or trailing non-alphanumeric chars validUsernamePatternWithoutDots = regexp.MustCompile(`^[\da-zA-Z][-\w]*$`)
// No consecutive or trailing non-alphanumeric chars, catches both cases
invalidUsernamePattern = regexp.MustCompile(`[-._]{2,}|[-._]$`)
) )
// IsValidUsername checks if username is valid // IsValidUsername checks if username is valid
func IsValidUsername(name string) bool { func IsValidUsername(name string) bool {
// It is difficult to find a single pattern that is both readable and effective, // It is difficult to find a single pattern that is both readable and effective,
// but it's easier to use positive and negative checks. // but it's easier to use positive and negative checks.
return validUsernamePattern.MatchString(name) && !invalidUsernamePattern.MatchString(name) if setting.Service.AllowDotsInUsernames {
return validUsernamePatternWithDots.MatchString(name) && !invalidUsernamePattern.MatchString(name)
}
return validUsernamePatternWithoutDots.MatchString(name) && !invalidUsernamePattern.MatchString(name)
} }

View file

@ -155,7 +155,8 @@ func Test_IsValidExternalTrackerURLFormat(t *testing.T) {
} }
} }
func TestIsValidUsername(t *testing.T) { func TestIsValidUsernameAllowDots(t *testing.T) {
setting.Service.AllowDotsInUsernames = true
tests := []struct { tests := []struct {
arg string arg string
want bool want bool
@ -185,3 +186,31 @@ func TestIsValidUsername(t *testing.T) {
}) })
} }
} }
func TestIsValidUsernameBanDots(t *testing.T) {
setting.Service.AllowDotsInUsernames = false
defer func() {
setting.Service.AllowDotsInUsernames = true
}()
tests := []struct {
arg string
want bool
}{
{arg: "a", want: true},
{arg: "abc", want: true},
{arg: "0.b-c", want: false},
{arg: "a.b-c_d", want: false},
{arg: ".abc", want: false},
{arg: "abc.", want: false},
{arg: "a..bc", want: false},
{arg: "a...bc", want: false},
{arg: "a.-bc", want: false},
{arg: "a._bc", want: false},
}
for _, tt := range tests {
t.Run(tt.arg, func(t *testing.T) {
assert.Equalf(t, tt.want, IsValidUsername(tt.arg), "IsValidUsername[AllowDotsInUsernames=false](%v)", tt.arg)
})
}
}

View file

@ -147,6 +147,16 @@ func toHandlerProvider(handler any) func(next http.Handler) http.Handler {
} }
} }
if hp, ok := handler.(func(next http.Handler) http.HandlerFunc); ok {
return func(next http.Handler) http.Handler {
h := hp(next) // this handle could be dynamically generated, so we can't use it for debug info
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
routing.UpdateFuncInfo(req.Context(), funcInfo)
h.ServeHTTP(resp, req)
})
}
}
provider := func(next http.Handler) http.Handler { provider := func(next http.Handler) http.Handler {
return http.HandlerFunc(func(respOrig http.ResponseWriter, req *http.Request) { return http.HandlerFunc(func(respOrig http.ResponseWriter, req *http.Request) {
// wrap the response writer to check whether the response has been written // wrap the response writer to check whether the response has been written

View file

@ -8,6 +8,7 @@ import (
"reflect" "reflect"
"strings" "strings"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/translation" "code.gitea.io/gitea/modules/translation"
"code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/validation" "code.gitea.io/gitea/modules/validation"
@ -135,7 +136,11 @@ func Validate(errs binding.Errors, data map[string]any, f Form, l translation.Lo
case validation.ErrRegexPattern: case validation.ErrRegexPattern:
data["ErrorMsg"] = trName + l.Tr("form.regex_pattern_error", errs[0].Message) data["ErrorMsg"] = trName + l.Tr("form.regex_pattern_error", errs[0].Message)
case validation.ErrUsername: case validation.ErrUsername:
if setting.Service.AllowDotsInUsernames {
data["ErrorMsg"] = trName + l.Tr("form.username_error") data["ErrorMsg"] = trName + l.Tr("form.username_error")
} else {
data["ErrorMsg"] = trName + l.Tr("form.username_error_no_dots")
}
case validation.ErrInvalidGroupTeamMap: case validation.ErrInvalidGroupTeamMap:
data["ErrorMsg"] = trName + l.Tr("form.invalid_group_team_map_error", errs[0].Message) data["ErrorMsg"] = trName + l.Tr("form.invalid_group_team_map_error", errs[0].Message)
default: default:

View file

@ -295,6 +295,7 @@ default_allow_create_organization = Allow Creation of Organizations by Default
default_allow_create_organization_popup = Allow new user accounts to create organizations by default. default_allow_create_organization_popup = Allow new user accounts to create organizations by default.
default_enable_timetracking = Enable Time Tracking by Default default_enable_timetracking = Enable Time Tracking by Default
default_enable_timetracking_popup = Enable time tracking for new repositories by default. default_enable_timetracking_popup = Enable time tracking for new repositories by default.
allow_dots_in_usernames = Allow users to use dots in their usernames. Doesn't affect existing accounts.
no_reply_address = Hidden Email Domain no_reply_address = Hidden Email Domain
no_reply_address_helper = Domain name for users with a hidden email address. For example, the username 'joe' will be logged in Git as 'joe@noreply.example.org' if the hidden email domain is set to 'noreply.example.org'. no_reply_address_helper = Domain name for users with a hidden email address. For example, the username 'joe' will be logged in Git as 'joe@noreply.example.org' if the hidden email domain is set to 'noreply.example.org'.
password_algorithm = Password Hash Algorithm password_algorithm = Password Hash Algorithm
@ -440,6 +441,10 @@ activate_email = Verify your email address
activate_email.title = %s, please verify your email address activate_email.title = %s, please verify your email address
activate_email.text = Please click the following link to verify your email address within <b>%s</b>: activate_email.text = Please click the following link to verify your email address within <b>%s</b>:
admin.new_user.subject = New user %s just signed up
admin.new_user.user_info = User Information
admin.new_user.text = Please <a href="%s">click here</a> to manage the user from the admin panel.
register_notify = Welcome to Gitea register_notify = Welcome to Gitea
register_notify.title = %[1]s, welcome to %[2]s register_notify.title = %[1]s, welcome to %[2]s
register_notify.text_1 = this is your registration confirmation email for %s! register_notify.text_1 = this is your registration confirmation email for %s!
@ -534,6 +539,7 @@ include_error = ` must contain substring "%s".`
glob_pattern_error = ` glob pattern is invalid: %s.` glob_pattern_error = ` glob pattern is invalid: %s.`
regex_pattern_error = ` regex pattern is invalid: %s.` regex_pattern_error = ` regex pattern is invalid: %s.`
username_error = ` can only contain alphanumeric chars ('0-9','a-z','A-Z'), dash ('-'), underscore ('_') and dot ('.'). It cannot begin or end with non-alphanumeric chars, and consecutive non-alphanumeric chars are also forbidden.` username_error = ` can only contain alphanumeric chars ('0-9','a-z','A-Z'), dash ('-'), underscore ('_') and dot ('.'). It cannot begin or end with non-alphanumeric chars, and consecutive non-alphanumeric chars are also forbidden.`
username_error_no_dots = ` can only contain alphanumeric chars ('0-9','a-z','A-Z'), dash ('-') and underscore ('_'). It cannot begin or end with non-alphanumeric chars, and consecutive non-alphanumeric chars are also forbidden.`
invalid_group_team_map_error = ` mapping is invalid: %s` invalid_group_team_map_error = ` mapping is invalid: %s`
unknown_error = Unknown error: unknown_error = Unknown error:
captcha_incorrect = The CAPTCHA code is incorrect. captcha_incorrect = The CAPTCHA code is incorrect.
@ -1251,6 +1257,7 @@ editor.new_branch_name_desc = New branch name…
editor.cancel = Cancel editor.cancel = Cancel
editor.filename_cannot_be_empty = The filename cannot be empty. editor.filename_cannot_be_empty = The filename cannot be empty.
editor.filename_is_invalid = The filename is invalid: "%s". editor.filename_is_invalid = The filename is invalid: "%s".
editor.invalid_commit_mail = Invalid mail for creating a commit.
editor.branch_does_not_exist = Branch "%s" does not exist in this repository. editor.branch_does_not_exist = Branch "%s" does not exist in this repository.
editor.branch_already_exists = Branch "%s" already exists in this repository. editor.branch_already_exists = Branch "%s" already exists in this repository.
editor.directory_is_a_file = Directory name "%s" is already used as a filename in this repository. editor.directory_is_a_file = Directory name "%s" is already used as a filename in this repository.
@ -1289,6 +1296,8 @@ commits.find = Search
commits.search_all = All Branches commits.search_all = All Branches
commits.author = Author commits.author = Author
commits.message = Message commits.message = Message
commits.browse_further = Browse further
commits.renamed_from = Renamed from %s
commits.date = Date commits.date = Date
commits.older = Older commits.older = Older
commits.newer = Newer commits.newer = Newer
@ -1901,6 +1910,7 @@ wiki.page_title = Page title
wiki.page_content = Page content wiki.page_content = Page content
wiki.default_commit_message = Write a note about this page update (optional). wiki.default_commit_message = Write a note about this page update (optional).
wiki.save_page = Save Page wiki.save_page = Save Page
wiki.cancel = Cancel
wiki.last_commit_info = %s edited this page %s wiki.last_commit_info = %s edited this page %s
wiki.edit_page_button = Edit wiki.edit_page_button = Edit
wiki.new_page_button = New Page wiki.new_page_button = New Page

View file

@ -8,6 +8,7 @@ import (
"crypto" "crypto"
"crypto/rsa" "crypto/rsa"
"crypto/sha1" "crypto/sha1"
"crypto/sha256"
"crypto/x509" "crypto/x509"
"encoding/base64" "encoding/base64"
"encoding/pem" "encoding/pem"
@ -26,8 +27,6 @@ import (
chef_module "code.gitea.io/gitea/modules/packages/chef" chef_module "code.gitea.io/gitea/modules/packages/chef"
"code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/services/auth" "code.gitea.io/gitea/services/auth"
"github.com/minio/sha256-simd"
) )
const ( const (

View file

@ -6,6 +6,7 @@ package maven
import ( import (
"crypto/md5" "crypto/md5"
"crypto/sha1" "crypto/sha1"
"crypto/sha256"
"crypto/sha512" "crypto/sha512"
"encoding/hex" "encoding/hex"
"encoding/xml" "encoding/xml"
@ -26,8 +27,6 @@ import (
maven_module "code.gitea.io/gitea/modules/packages/maven" maven_module "code.gitea.io/gitea/modules/packages/maven"
"code.gitea.io/gitea/routers/api/packages/helper" "code.gitea.io/gitea/routers/api/packages/helper"
packages_service "code.gitea.io/gitea/services/packages" packages_service "code.gitea.io/gitea/services/packages"
"github.com/minio/sha256-simd"
) )
const ( const (

View file

@ -358,6 +358,12 @@ func SubmitInstall(ctx *context.Context) {
ctx.RenderWithErr(ctx.Tr("form.password_not_match"), tplInstall, form) ctx.RenderWithErr(ctx.Tr("form.password_not_match"), tplInstall, form)
return return
} }
if len(form.AdminPasswd) < setting.MinPasswordLength {
ctx.Data["Err_Admin"] = true
ctx.Data["Err_AdminPasswd"] = true
ctx.RenderWithErr(ctx.Tr("auth.password_too_short", setting.MinPasswordLength), tplInstall, form)
return
}
} }
// Init the engine with migration // Init the engine with migration

View file

@ -32,6 +32,7 @@ import (
"code.gitea.io/gitea/services/externalaccount" "code.gitea.io/gitea/services/externalaccount"
"code.gitea.io/gitea/services/forms" "code.gitea.io/gitea/services/forms"
"code.gitea.io/gitea/services/mailer" "code.gitea.io/gitea/services/mailer"
notify_service "code.gitea.io/gitea/services/notify"
"github.com/markbates/goth" "github.com/markbates/goth"
) )
@ -600,6 +601,7 @@ func handleUserCreated(ctx *context.Context, u *user_model.User, gothUser *goth.
} }
} }
notify_service.NewUserSignUp(ctx, u)
// update external user information // update external user information
if gothUser != nil { if gothUser != nil {
if err := externalaccount.UpdateExternalUser(ctx, u, *gothUser); err != nil { if err := externalaccount.UpdateExternalUser(ctx, u, *gothUser); err != nil {
@ -623,7 +625,6 @@ func handleUserCreated(ctx *context.Context, u *user_model.User, gothUser *goth.
ctx.Data["Email"] = u.Email ctx.Data["Email"] = u.Email
ctx.Data["ActiveCodeLives"] = timeutil.MinutesToFriendly(setting.Service.ActiveCodeLives, ctx.Locale) ctx.Data["ActiveCodeLives"] = timeutil.MinutesToFriendly(setting.Service.ActiveCodeLives, ctx.Locale)
ctx.HTML(http.StatusOK, TplActivate) ctx.HTML(http.StatusOK, TplActivate)
if setting.CacheService.Enabled { if setting.CacheService.Enabled {
if err := ctx.Cache.Put("MailResendLimit_"+u.LowerName, u.LowerName, 180); err != nil { if err := ctx.Cache.Put("MailResendLimit_"+u.LowerName, u.LowerName, 180); err != nil {
log.Error("Set cache(MailResendLimit) fail: %v", err) log.Error("Set cache(MailResendLimit) fail: %v", err)

View file

@ -951,10 +951,16 @@ func SignInOAuthCallback(ctx *context.Context) {
return return
} else if !setting.Service.AllowOnlyInternalRegistration && setting.OAuth2Client.EnableAutoRegistration { } else if !setting.Service.AllowOnlyInternalRegistration && setting.OAuth2Client.EnableAutoRegistration {
// create new user with details from oauth2 provider // create new user with details from oauth2 provider
var missingFields []string
if gothUser.UserID == "" { if gothUser.UserID == "" {
missingFields = append(missingFields, "sub") log.Error("OAuth2 Provider %s returned empty or missing field: UserID", authSource.Name)
if authSource.IsOAuth2() && authSource.Cfg.(*oauth2.Source).Provider == "openidConnect" {
log.Error("You may need to change the 'OPENID_CONNECT_SCOPES' setting to request all required fields")
} }
err = fmt.Errorf("OAuth2 Provider %s returned empty or missing field: UserID", authSource.Name)
ctx.ServerError("CreateUser", err)
return
}
var missingFields []string
if gothUser.Email == "" { if gothUser.Email == "" {
missingFields = append(missingFields, "email") missingFields = append(missingFields, "email")
} }
@ -962,12 +968,10 @@ func SignInOAuthCallback(ctx *context.Context) {
missingFields = append(missingFields, "nickname") missingFields = append(missingFields, "nickname")
} }
if len(missingFields) > 0 { if len(missingFields) > 0 {
log.Error("OAuth2 Provider %s returned empty or missing fields: %s", authSource.Name, missingFields) // we don't have enough information to create an account automatically,
if authSource.IsOAuth2() && authSource.Cfg.(*oauth2.Source).Provider == "openidConnect" { // so we prompt the user for the remaining bits
log.Error("You may need to change the 'OPENID_CONNECT_SCOPES' setting to request all required fields") log.Trace("OAuth2 Provider %s returned empty or missing fields: %s, prompting the user for them", authSource.Name, missingFields)
} showLinkingLogin(ctx, gothUser)
err = fmt.Errorf("OAuth2 Provider %s returned empty or missing fields: %s", authSource.Name, missingFields)
ctx.ServerError("CreateUser", err)
return return
} }
u = &user_model.User{ u = &user_model.User{

View file

@ -21,6 +21,7 @@ import (
"code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/util"
"github.com/gorilla/feeds" "github.com/gorilla/feeds"
"github.com/jaytaylor/html2text"
) )
func toBranchLink(ctx *context.Context, act *activities_model.Action) string { func toBranchLink(ctx *context.Context, act *activities_model.Action) string {
@ -239,8 +240,15 @@ func feedActionsToFeedItems(ctx *context.Context, actions activities_model.Actio
content = desc content = desc
} }
// It's a common practice for feed generators to use plain text titles.
// See https://codeberg.org/forgejo/forgejo/pulls/1595
plainTitle, err := html2text.FromString(title, html2text.Options{OmitLinks: true})
if err != nil {
return nil, err
}
items = append(items, &feeds.Item{ items = append(items, &feeds.Item{
Title: title, Title: plainTitle,
Link: link, Link: link,
Description: desc, Description: desc,
Author: &feeds.Author{ Author: &feeds.Author{

View file

@ -8,11 +8,12 @@ import (
) )
// RenderBranchFeed render format for branch or file // RenderBranchFeed render format for branch or file
func RenderBranchFeed(ctx *context.Context) { func RenderBranchFeed(feedType string) func(ctx *context.Context) {
_, _, showFeedType := GetFeedType(ctx.Params(":reponame"), ctx.Req) return func(ctx *context.Context) {
if ctx.Repo.TreePath == "" { if ctx.Repo.TreePath == "" {
ShowBranchFeed(ctx, ctx.Repo.Repository, showFeedType) ShowBranchFeed(ctx, ctx.Repo.Repository, feedType)
} else { } else {
ShowFileFeed(ctx, ctx.Repo.Repository, showFeedType) ShowFileFeed(ctx, ctx.Repo.Repository, feedType)
}
} }
} }

View file

@ -239,6 +239,22 @@ func FileHistory(ctx *context.Context) {
ctx.ServerError("CommitsByFileAndRange", err) ctx.ServerError("CommitsByFileAndRange", err)
return return
} }
oldestCommit := commits[len(commits)-1]
renamedFiles, err := git.GetCommitFileRenames(ctx, ctx.Repo.GitRepo.Path, oldestCommit.ID.String())
if err != nil {
ctx.ServerError("GetCommitFileRenames", err)
return
}
for _, renames := range renamedFiles {
if renames[1] == fileName {
ctx.Data["OldFilename"] = renames[0]
ctx.Data["OldFilenameHistory"] = fmt.Sprintf("%s/commits/commit/%s/%s", ctx.Repo.RepoLink, oldestCommit.ID.String(), renames[0])
break
}
}
ctx.Data["Commits"] = git_model.ConvertFromGitCommit(ctx, commits, ctx.Repo.Repository) ctx.Data["Commits"] = git_model.ConvertFromGitCommit(ctx, commits, ctx.Repo.Repository)
ctx.Data["Username"] = ctx.Repo.Owner.Name ctx.Data["Username"] = ctx.Repo.Owner.Name

View file

@ -14,6 +14,7 @@ import (
git_model "code.gitea.io/gitea/models/git" git_model "code.gitea.io/gitea/models/git"
repo_model "code.gitea.io/gitea/models/repo" repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit" "code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/charset" "code.gitea.io/gitea/modules/charset"
"code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/context"
@ -99,6 +100,27 @@ func getParentTreeFields(treePath string) (treeNames, treePaths []string) {
return treeNames, treePaths return treeNames, treePaths
} }
// getSelectableEmailAddresses returns which emails can be used by the user as
// email for a Git commiter.
func getSelectableEmailAddresses(ctx *context.Context) ([]*user_model.ActivatedEmailAddress, error) {
// Retrieve emails that the user could use for commiter identity.
commitEmails, err := user_model.GetActivatedEmailAddresses(ctx, ctx.Doer.ID)
if err != nil {
return nil, fmt.Errorf("GetActivatedEmailAddresses: %w", err)
}
// Allow for the placeholder mail to be used. Use -1 as ID to identify
// this entry to be the placerholder mail of the user.
placeholderMail := &user_model.ActivatedEmailAddress{ID: -1, Email: ctx.Doer.GetPlaceholderEmail()}
if ctx.Doer.KeepEmailPrivate {
commitEmails = append([]*user_model.ActivatedEmailAddress{placeholderMail}, commitEmails...)
} else {
commitEmails = append(commitEmails, placeholderMail)
}
return commitEmails, nil
}
func editFile(ctx *context.Context, isNewFile bool) { func editFile(ctx *context.Context, isNewFile bool) {
ctx.Data["PageIsEdit"] = true ctx.Data["PageIsEdit"] = true
ctx.Data["IsNewFile"] = isNewFile ctx.Data["IsNewFile"] = isNewFile
@ -177,6 +199,12 @@ func editFile(ctx *context.Context, isNewFile bool) {
treeNames = append(treeNames, fileName) treeNames = append(treeNames, fileName)
} }
commitEmails, err := getSelectableEmailAddresses(ctx)
if err != nil {
ctx.ServerError("getSelectableEmailAddresses", err)
return
}
ctx.Data["TreeNames"] = treeNames ctx.Data["TreeNames"] = treeNames
ctx.Data["TreePaths"] = treePaths ctx.Data["TreePaths"] = treePaths
ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL() ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL()
@ -192,6 +220,8 @@ func editFile(ctx *context.Context, isNewFile bool) {
ctx.Data["PreviewableExtensions"] = strings.Join(markup.PreviewableExtensions(), ",") ctx.Data["PreviewableExtensions"] = strings.Join(markup.PreviewableExtensions(), ",")
ctx.Data["LineWrapExtensions"] = strings.Join(setting.Repository.Editor.LineWrapExtensions, ",") ctx.Data["LineWrapExtensions"] = strings.Join(setting.Repository.Editor.LineWrapExtensions, ",")
ctx.Data["EditorconfigJson"] = GetEditorConfig(ctx, treePath) ctx.Data["EditorconfigJson"] = GetEditorConfig(ctx, treePath)
ctx.Data["CommitMails"] = commitEmails
ctx.Data["DefaultCommitMail"] = ctx.Doer.GetEmail()
ctx.HTML(http.StatusOK, tplEditFile) ctx.HTML(http.StatusOK, tplEditFile)
} }
@ -227,6 +257,12 @@ func editFilePost(ctx *context.Context, form forms.EditRepoFileForm, isNewFile b
branchName = form.NewBranchName branchName = form.NewBranchName
} }
commitEmails, err := getSelectableEmailAddresses(ctx)
if err != nil {
ctx.ServerError("getSelectableEmailAddresses", err)
return
}
ctx.Data["PageIsEdit"] = true ctx.Data["PageIsEdit"] = true
ctx.Data["PageHasPosted"] = true ctx.Data["PageHasPosted"] = true
ctx.Data["IsNewFile"] = isNewFile ctx.Data["IsNewFile"] = isNewFile
@ -243,6 +279,8 @@ func editFilePost(ctx *context.Context, form forms.EditRepoFileForm, isNewFile b
ctx.Data["PreviewableExtensions"] = strings.Join(markup.PreviewableExtensions(), ",") ctx.Data["PreviewableExtensions"] = strings.Join(markup.PreviewableExtensions(), ",")
ctx.Data["LineWrapExtensions"] = strings.Join(setting.Repository.Editor.LineWrapExtensions, ",") ctx.Data["LineWrapExtensions"] = strings.Join(setting.Repository.Editor.LineWrapExtensions, ",")
ctx.Data["EditorconfigJson"] = GetEditorConfig(ctx, form.TreePath) ctx.Data["EditorconfigJson"] = GetEditorConfig(ctx, form.TreePath)
ctx.Data["CommitMails"] = commitEmails
ctx.Data["DefaultCommitMail"] = ctx.Doer.GetEmail()
if ctx.HasError() { if ctx.HasError() {
ctx.HTML(http.StatusOK, tplEditFile) ctx.HTML(http.StatusOK, tplEditFile)
@ -277,6 +315,30 @@ func editFilePost(ctx *context.Context, form forms.EditRepoFileForm, isNewFile b
operation = "create" operation = "create"
} }
gitIdentity := &files_service.IdentityOptions{
Name: ctx.Doer.Name,
}
// -1 is defined as placeholder email.
if form.CommitMailID == -1 {
gitIdentity.Email = ctx.Doer.GetPlaceholderEmail()
} else {
// Check if the given email is activated.
email, err := user_model.GetEmailAddressByID(ctx, ctx.Doer.ID, form.CommitMailID)
if err != nil {
ctx.ServerError("GetEmailAddressByID", err)
return
}
if email == nil || !email.IsActivated {
ctx.Data["Err_CommitMailID"] = true
ctx.RenderWithErr(ctx.Tr("repo.editor.invalid_commit_mail"), tplEditFile, &form)
return
}
gitIdentity.Email = email.Email
}
if _, err := files_service.ChangeRepoFiles(ctx, ctx.Repo.Repository, ctx.Doer, &files_service.ChangeRepoFilesOptions{ if _, err := files_service.ChangeRepoFiles(ctx, ctx.Repo.Repository, ctx.Doer, &files_service.ChangeRepoFilesOptions{
LastCommitID: form.LastCommit, LastCommitID: form.LastCommit,
OldBranch: ctx.Repo.BranchName, OldBranch: ctx.Repo.BranchName,
@ -291,6 +353,8 @@ func editFilePost(ctx *context.Context, form forms.EditRepoFileForm, isNewFile b
}, },
}, },
Signoff: form.Signoff, Signoff: form.Signoff,
Author: gitIdentity,
Committer: gitIdentity,
}); err != nil { }); err != nil {
// This is where we handle all the errors thrown by files_service.ChangeRepoFiles // This is where we handle all the errors thrown by files_service.ChangeRepoFiles
if git.IsErrNotExist(err) { if git.IsErrNotExist(err) {

View file

@ -198,11 +198,19 @@ func SoftDeleteContentHistory(ctx *context.Context) {
log.Error("can not get comment for issue content history %v. err=%v", historyID, err) log.Error("can not get comment for issue content history %v. err=%v", historyID, err)
return return
} }
if comment.IssueID != issue.ID {
ctx.NotFound("CompareRepoID", issues_model.ErrCommentNotExist{})
return
}
} }
if history, err = issues_model.GetIssueContentHistoryByID(ctx, historyID); err != nil { if history, err = issues_model.GetIssueContentHistoryByID(ctx, historyID); err != nil {
log.Error("can not get issue content history %v. err=%v", historyID, err) log.Error("can not get issue content history %v. err=%v", historyID, err)
return return
} }
if history.IssueID != issue.ID {
ctx.NotFound("CompareRepoID", issues_model.ErrCommentNotExist{})
return
}
canSoftDelete := canSoftDeleteContentHistory(ctx, issue, comment, history) canSoftDelete := canSoftDeleteContentHistory(ctx, issue, comment, history)
if !canSoftDelete { if !canSoftDelete {

View file

@ -89,6 +89,10 @@ func IssuePinMove(ctx *context.Context) {
log.Error(err.Error()) log.Error(err.Error())
return return
} }
if issue.RepoID != ctx.Repo.Repository.ID {
ctx.NotFound("CompareRepoID", issues_model.ErrCommentNotExist{})
return
}
err = issue.MovePin(ctx, form.Position) err = issue.MovePin(ctx, form.Position)
if err != nil { if err != nil {

View file

@ -391,7 +391,9 @@ func NewReleasePost(ctx *context.Context) {
return return
} }
if !ctx.Repo.GitRepo.IsBranchExist(form.Target) { // form.Target can be a branch name or a full commitID.
if !ctx.Repo.GitRepo.IsBranchExist(form.Target) &&
len(form.Target) == git.SHAFullLength && !ctx.Repo.GitRepo.IsCommitExist(form.Target) {
ctx.RenderWithErr(ctx.Tr("form.target_branch_not_exist"), tplReleaseNew, &form) ctx.RenderWithErr(ctx.Tr("form.target_branch_not_exist"), tplReleaseNew, &form)
return return
} }

View file

@ -165,7 +165,7 @@ func renderDirectory(ctx *context.Context, treeLink string) {
if ctx.Repo.TreePath != "" { if ctx.Repo.TreePath != "" {
ctx.Data["HideRepoInfo"] = true ctx.Data["HideRepoInfo"] = true
ctx.Data["Title"] = ctx.Tr("repo.file.title", ctx.Repo.Repository.Name+"/"+path.Base(ctx.Repo.TreePath), ctx.Repo.RefName) ctx.Data["Title"] = ctx.Tr("repo.file.title", ctx.Repo.Repository.Name+"/"+ctx.Repo.TreePath, ctx.Repo.RefName)
} }
subfolder, readmeFile, err := findReadmeFileInEntries(ctx, entries, true) subfolder, readmeFile, err := findReadmeFileInEntries(ctx, entries, true)
@ -348,7 +348,7 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st
} }
defer dataRc.Close() defer dataRc.Close()
ctx.Data["Title"] = ctx.Tr("repo.file.title", ctx.Repo.Repository.Name+"/"+path.Base(ctx.Repo.TreePath), ctx.Repo.RefName) ctx.Data["Title"] = ctx.Tr("repo.file.title", ctx.Repo.Repository.Name+"/"+ctx.Repo.TreePath, ctx.Repo.RefName)
ctx.Data["FileIsSymlink"] = entry.IsLink() ctx.Data["FileIsSymlink"] = entry.IsLink()
ctx.Data["FileName"] = blob.Name() ctx.Data["FileName"] = blob.Name()
ctx.Data["RawFileLink"] = rawLink + "/" + util.PathEscapeSegments(ctx.Repo.TreePath) ctx.Data["RawFileLink"] = rawLink + "/" + util.PathEscapeSegments(ctx.Repo.TreePath)
@ -735,12 +735,19 @@ func Home(ctx *context.Context) {
if setting.Other.EnableFeed { if setting.Other.EnableFeed {
isFeed, _, showFeedType := feed.GetFeedType(ctx.Params(":reponame"), ctx.Req) isFeed, _, showFeedType := feed.GetFeedType(ctx.Params(":reponame"), ctx.Req)
if isFeed { if isFeed {
switch { if ctx.Link == fmt.Sprintf("%s.%s", ctx.Repo.RepoLink, showFeedType) {
case ctx.Link == fmt.Sprintf("%s.%s", ctx.Repo.RepoLink, showFeedType):
feed.ShowRepoFeed(ctx, ctx.Repo.Repository, showFeedType) feed.ShowRepoFeed(ctx, ctx.Repo.Repository, showFeedType)
case ctx.Repo.TreePath == "": return
}
if ctx.Repo.Repository.IsEmpty {
ctx.NotFound("MustBeNotEmpty", nil)
return
}
if ctx.Repo.TreePath == "" {
feed.ShowBranchFeed(ctx, ctx.Repo.Repository, showFeedType) feed.ShowBranchFeed(ctx, ctx.Repo.Repository, showFeedType)
case ctx.Repo.TreePath != "": } else {
feed.ShowFileFeed(ctx, ctx.Repo.Repository, showFeedType) feed.ShowFileFeed(ctx, ctx.Repo.Repository, showFeedType)
} }
return return

View file

@ -315,9 +315,18 @@ func Milestones(ctx *context.Context) {
ctx.Data["RepoIDs"] = repoIDs ctx.Data["RepoIDs"] = repoIDs
ctx.Data["IsShowClosed"] = isShowClosed ctx.Data["IsShowClosed"] = isShowClosed
// Convert []int64 to string
reposParam, err := json.Marshal(repoIDs)
if err != nil {
ctx.ServerError("json.Marshal", err)
return
}
ctx.Data["ReposParam"] = string(reposParam)
pager := context.NewPagination(pagerCount, setting.UI.IssuePagingNum, page, 5) pager := context.NewPagination(pagerCount, setting.UI.IssuePagingNum, page, 5)
pager.AddParam(ctx, "q", "Keyword") pager.AddParam(ctx, "q", "Keyword")
pager.AddParam(ctx, "repos", "RepoIDs") pager.AddParam(ctx, "repos", "ReposParam")
pager.AddParam(ctx, "sort", "SortType") pager.AddParam(ctx, "sort", "SortType")
pager.AddParam(ctx, "state", "State") pager.AddParam(ctx, "state", "State")
ctx.Data["Page"] = pager ctx.Data["Page"] = pager
@ -690,7 +699,11 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
} }
// Convert []int64 to string // Convert []int64 to string
reposParam, _ := json.Marshal(opts.RepoIDs) reposParam, err := json.Marshal(selectedRepoIDs)
if err != nil {
ctx.ServerError("json.Marshal", err)
return
}
ctx.Data["ReposParam"] = string(reposParam) ctx.Data["ReposParam"] = string(reposParam)

View file

@ -49,17 +49,12 @@ import (
_ "code.gitea.io/gitea/modules/session" // to registers all internal adapters _ "code.gitea.io/gitea/modules/session" // to registers all internal adapters
"gitea.com/go-chi/captcha" "gitea.com/go-chi/captcha"
"github.com/NYTimes/gziphandler"
chi_middleware "github.com/go-chi/chi/v5/middleware" chi_middleware "github.com/go-chi/chi/v5/middleware"
"github.com/go-chi/cors" "github.com/go-chi/cors"
"github.com/klauspost/compress/gzhttp"
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
) )
const (
// GzipMinSize represents min size to compress for the body size of response
GzipMinSize = 1400
)
// CorsHandler return a http handler who set CORS options if enabled by config // CorsHandler return a http handler who set CORS options if enabled by config
func CorsHandler() func(next http.Handler) http.Handler { func CorsHandler() func(next http.Handler) http.Handler {
if setting.CORSConfig.Enabled { if setting.CORSConfig.Enabled {
@ -230,11 +225,11 @@ func Routes() *web.Route {
var mid []any var mid []any
if setting.EnableGzip { if setting.EnableGzip {
h, err := gziphandler.GzipHandlerWithOpts(gziphandler.MinSize(GzipMinSize)) wrapper, err := gzhttp.NewWrapper(gzhttp.RandomJitter(32, 0, false))
if err != nil { if err != nil {
log.Fatal("GzipHandlerWithOpts failed: %v", err) log.Fatal("gzhttp.NewWrapper failed: %v", err)
} }
mid = append(mid, h) mid = append(mid, wrapper)
} }
if setting.Service.EnableCaptcha { if setting.Service.EnableCaptcha {
@ -1212,7 +1207,7 @@ func registerRoutes(m *web.Route) {
Post(web.Bind(forms.UploadRepoFileForm{}), repo.UploadFilePost) Post(web.Bind(forms.UploadRepoFileForm{}), repo.UploadFilePost)
m.Combo("/_diffpatch/*").Get(repo.NewDiffPatch). m.Combo("/_diffpatch/*").Get(repo.NewDiffPatch).
Post(web.Bind(forms.EditRepoFileForm{}), repo.NewDiffPatchPost) Post(web.Bind(forms.EditRepoFileForm{}), repo.NewDiffPatchPost)
m.Combo("/_cherrypick/{sha:([a-f0-9]{7,40})}/*").Get(repo.CherryPick). m.Combo("/_cherrypick/{sha:([a-f0-9]{4,40})}/*").Get(repo.CherryPick).
Post(web.Bind(forms.CherryPickForm{}), repo.CherryPickPost) Post(web.Bind(forms.CherryPickForm{}), repo.CherryPickPost)
}, repo.MustBeEditable) }, repo.MustBeEditable)
m.Group("", func() { m.Group("", func() {
@ -1354,8 +1349,8 @@ func registerRoutes(m *web.Route) {
m.Combo("/*"). m.Combo("/*").
Get(repo.Wiki). Get(repo.Wiki).
Post(context.RepoMustNotBeArchived(), reqSignIn, reqRepoWikiWriter, web.Bind(forms.NewWikiForm{}), repo.WikiPost) Post(context.RepoMustNotBeArchived(), reqSignIn, reqRepoWikiWriter, web.Bind(forms.NewWikiForm{}), repo.WikiPost)
m.Get("/commit/{sha:[a-f0-9]{7,40}}", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.Diff) m.Get("/commit/{sha:[a-f0-9]{4,40}}", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.Diff)
m.Get("/commit/{sha:[a-f0-9]{7,40}}.{ext:patch|diff}", repo.RawDiff) m.Get("/commit/{sha:[a-f0-9]{4,40}}.{ext:patch|diff}", repo.RawDiff)
}, repo.MustEnableWiki, func(ctx *context.Context) { }, repo.MustEnableWiki, func(ctx *context.Context) {
ctx.Data["PageIsWiki"] = true ctx.Data["PageIsWiki"] = true
ctx.Data["CloneButtonOriginLink"] = ctx.Repo.Repository.WikiCloneLink() ctx.Data["CloneButtonOriginLink"] = ctx.Repo.Repository.WikiCloneLink()
@ -1415,7 +1410,7 @@ func registerRoutes(m *web.Route) {
m.Group("/commits", func() { m.Group("/commits", func() {
m.Get("", context.RepoRef(), repo.SetWhitespaceBehavior, repo.GetPullDiffStats, repo.ViewPullCommits) m.Get("", context.RepoRef(), repo.SetWhitespaceBehavior, repo.GetPullDiffStats, repo.ViewPullCommits)
m.Get("/list", context.RepoRef(), repo.GetPullCommits) m.Get("/list", context.RepoRef(), repo.GetPullCommits)
m.Get("/{sha:[a-f0-9]{7,40}}", context.RepoRef(), repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.SetShowOutdatedComments, repo.ViewPullFilesForSingleCommit) m.Get("/{sha:[a-f0-9]{4,40}}", context.RepoRef(), repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.SetShowOutdatedComments, repo.ViewPullFilesForSingleCommit)
}) })
m.Post("/merge", context.RepoMustNotBeArchived(), web.Bind(forms.MergePullRequestForm{}), repo.MergePullRequest) m.Post("/merge", context.RepoMustNotBeArchived(), web.Bind(forms.MergePullRequestForm{}), repo.MergePullRequest)
m.Post("/cancel_auto_merge", context.RepoMustNotBeArchived(), repo.CancelAutoMergePullRequest) m.Post("/cancel_auto_merge", context.RepoMustNotBeArchived(), repo.CancelAutoMergePullRequest)
@ -1424,8 +1419,8 @@ func registerRoutes(m *web.Route) {
m.Post("/cleanup", context.RepoMustNotBeArchived(), context.RepoRef(), repo.CleanUpPullRequest) m.Post("/cleanup", context.RepoMustNotBeArchived(), context.RepoRef(), repo.CleanUpPullRequest)
m.Group("/files", func() { m.Group("/files", func() {
m.Get("", context.RepoRef(), repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.SetShowOutdatedComments, repo.ViewPullFilesForAllCommitsOfPr) m.Get("", context.RepoRef(), repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.SetShowOutdatedComments, repo.ViewPullFilesForAllCommitsOfPr)
m.Get("/{sha:[a-f0-9]{7,40}}", context.RepoRef(), repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.SetShowOutdatedComments, repo.ViewPullFilesStartingFromCommit) m.Get("/{sha:[a-f0-9]{4,40}}", context.RepoRef(), repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.SetShowOutdatedComments, repo.ViewPullFilesStartingFromCommit)
m.Get("/{shaFrom:[a-f0-9]{7,40}}..{shaTo:[a-f0-9]{7,40}}", context.RepoRef(), repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.SetShowOutdatedComments, repo.ViewPullFilesForRange) m.Get("/{shaFrom:[a-f0-9]{4,40}}..{shaTo:[a-f0-9]{4,40}}", context.RepoRef(), repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.SetShowOutdatedComments, repo.ViewPullFilesForRange)
m.Group("/reviews", func() { m.Group("/reviews", func() {
m.Get("/new_comment", repo.RenderNewCodeCommentForm) m.Get("/new_comment", repo.RenderNewCodeCommentForm)
m.Post("/comments", web.Bind(forms.CodeCommentForm{}), repo.SetShowOutdatedComments, repo.CreateCodeComment) m.Post("/comments", web.Bind(forms.CodeCommentForm{}), repo.SetShowOutdatedComments, repo.CreateCodeComment)
@ -1475,13 +1470,13 @@ func registerRoutes(m *web.Route) {
m.Group("", func() { m.Group("", func() {
m.Get("/graph", repo.Graph) m.Get("/graph", repo.Graph)
m.Get("/commit/{sha:([a-f0-9]{7,40})$}", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.Diff) m.Get("/commit/{sha:([a-f0-9]{4,40})$}", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.Diff)
m.Get("/commit/{sha:([a-f0-9]{7,40})$}/load-branches-and-tags", repo.LoadBranchesAndTags) m.Get("/commit/{sha:([a-f0-9]{4,40})$}/load-branches-and-tags", repo.LoadBranchesAndTags)
m.Get("/cherry-pick/{sha:([a-f0-9]{7,40})$}", repo.SetEditorconfigIfExists, repo.CherryPick) m.Get("/cherry-pick/{sha:([a-f0-9]{4,40})$}", repo.SetEditorconfigIfExists, repo.CherryPick)
}, repo.MustBeNotEmpty, context.RepoRef(), reqRepoCodeReader) }, repo.MustBeNotEmpty, context.RepoRef(), reqRepoCodeReader)
m.Get("/rss/branch/*", context.RepoRefByType(context.RepoRefBranch), feedEnabled, feed.RenderBranchFeed) m.Get("/rss/branch/*", repo.MustBeNotEmpty, context.RepoRefByType(context.RepoRefBranch), feedEnabled, feed.RenderBranchFeed("rss"))
m.Get("/atom/branch/*", context.RepoRefByType(context.RepoRefBranch), feedEnabled, feed.RenderBranchFeed) m.Get("/atom/branch/*", repo.MustBeNotEmpty, context.RepoRefByType(context.RepoRefBranch), feedEnabled, feed.RenderBranchFeed("atom"))
m.Group("/src", func() { m.Group("/src", func() {
m.Get("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.Home) m.Get("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.Home)
@ -1494,7 +1489,7 @@ func registerRoutes(m *web.Route) {
m.Group("", func() { m.Group("", func() {
m.Get("/forks", repo.Forks) m.Get("/forks", repo.Forks)
}, context.RepoRef(), reqRepoCodeReader) }, context.RepoRef(), reqRepoCodeReader)
m.Get("/commit/{sha:([a-f0-9]{7,40})}.{ext:patch|diff}", repo.MustBeNotEmpty, reqRepoCodeReader, repo.RawDiff) m.Get("/commit/{sha:([a-f0-9]{4,40})}.{ext:patch|diff}", repo.MustBeNotEmpty, reqRepoCodeReader, repo.RawDiff)
}, ignSignIn, context.RepoAssignment, context.UnitTypes()) }, ignSignIn, context.RepoAssignment, context.UnitTypes())
m.Post("/{username}/{reponame}/lastcommit/*", ignSignInAndCsrf, context.RepoAssignment, context.UnitTypes(), context.RepoRefByType(context.RepoRefCommit), reqRepoCodeReader, repo.LastCommit) m.Post("/{username}/{reponame}/lastcommit/*", ignSignInAndCsrf, context.RepoAssignment, context.UnitTypes(), context.RepoRefByType(context.RepoRefCommit), reqRepoCodeReader, repo.LastCommit)

View file

@ -766,6 +766,7 @@ type EditRepoFileForm struct {
CommitChoice string `binding:"Required;MaxSize(50)"` CommitChoice string `binding:"Required;MaxSize(50)"`
NewBranchName string `binding:"GitRefName;MaxSize(100)"` NewBranchName string `binding:"GitRefName;MaxSize(100)"`
LastCommit string LastCommit string
CommitMailID int64 `binding:"Required"`
Signoff bool Signoff bool
} }

View file

@ -5,6 +5,7 @@ package lfs
import ( import (
stdCtx "context" stdCtx "context"
"crypto/sha256"
"encoding/base64" "encoding/base64"
"encoding/hex" "encoding/hex"
"errors" "errors"
@ -33,7 +34,6 @@ import (
"code.gitea.io/gitea/modules/storage" "code.gitea.io/gitea/modules/storage"
"github.com/golang-jwt/jwt/v5" "github.com/golang-jwt/jwt/v5"
"github.com/minio/sha256-simd"
) )
// requestContext contain variables from the HTTP request. // requestContext contain variables from the HTTP request.

View file

@ -0,0 +1,80 @@
// Copyright 2023 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package mailer
import (
"bytes"
"context"
"strconv"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/templates"
"code.gitea.io/gitea/modules/translation"
)
const (
tplNewUserMail base.TplName = "notify/admin_new_user"
)
var sa = SendAsync
// MailNewUser sends notification emails on new user registrations to all admins
func MailNewUser(ctx context.Context, u *user_model.User) {
if !setting.Admin.SendNotificationEmailOnNewUser {
return
}
if setting.MailService == nil {
// No mail service configured
return
}
recipients, err := user_model.GetAllAdmins(ctx)
if err != nil {
log.Error("user_model.GetAllAdmins: %v", err)
return
}
langMap := make(map[string][]string)
for _, r := range recipients {
langMap[r.Language] = append(langMap[r.Language], r.Email)
}
for lang, tos := range langMap {
mailNewUser(ctx, u, lang, tos)
}
}
func mailNewUser(ctx context.Context, u *user_model.User, lang string, tos []string) {
locale := translation.NewLocale(lang)
subject := locale.Tr("mail.admin.new_user.subject", u.Name)
manageUserURL := setting.AppSubURL + "/admin/users/" + strconv.FormatInt(u.ID, 10)
body := locale.Tr("mail.admin.new_user.text", manageUserURL)
mailMeta := map[string]any{
"NewUser": u,
"Subject": subject,
"Body": body,
"Language": locale.Language(),
"locale": locale,
"Str2html": templates.Str2html,
}
var mailBody bytes.Buffer
if err := bodyTemplates.ExecuteTemplate(&mailBody, string(tplNewUserMail), mailMeta); err != nil {
log.Error("ExecuteTemplate [%s]: %v", string(tplNewUserMail)+"/body", err)
return
}
msgs := make([]*Message, 0, len(tos))
for _, to := range tos {
msg := NewMessage(to, subject, mailBody.String())
msg.Info = subject
msgs = append(msgs, msg)
}
sa(msgs...)
}

View file

@ -0,0 +1,88 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package mailer
import (
"context"
"strconv"
"strings"
"testing"
"code.gitea.io/gitea/models/db"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/setting"
"github.com/stretchr/testify/assert"
)
func getTestUsers() []*user_model.User {
admin := new(user_model.User)
admin.Name = "admin"
admin.IsAdmin = true
admin.Language = "en_US"
admin.Email = "admin@example.com"
newUser := new(user_model.User)
newUser.Name = "new_user"
newUser.Language = "en_US"
newUser.IsAdmin = false
newUser.Email = "new_user@example.com"
newUser.LastLoginUnix = 1693648327
newUser.CreatedUnix = 1693648027
user_model.CreateUser(db.DefaultContext, admin)
user_model.CreateUser(db.DefaultContext, newUser)
users := make([]*user_model.User, 0)
users = append(users, admin)
users = append(users, newUser)
return users
}
func cleanUpUsers(ctx context.Context, users []*user_model.User) {
for _, u := range users {
db.DeleteByID(ctx, u.ID, new(user_model.User))
}
}
func TestAdminNotificationMail_test(t *testing.T) {
mailService := setting.Mailer{
From: "test@example.com",
Protocol: "dummy",
}
setting.MailService = &mailService
setting.Domain = "localhost"
setting.AppSubURL = "http://localhost"
// test with SEND_NOTIFICATION_EMAIL_ON_NEW_USER enabled
setting.Admin.SendNotificationEmailOnNewUser = true
ctx := context.Background()
NewContext(ctx)
users := getTestUsers()
oldSendAsync := sa
defer func() {
sa = oldSendAsync
cleanUpUsers(ctx, users)
}()
sa = func(msgs ...*Message) {
assert.Equal(t, len(msgs), 1, "Test provides only one admin user, so only one email must be sent")
assert.Equal(t, msgs[0].To, users[0].Email, "checks if the recipient is the admin of the instance")
manageUserURL := "/admin/users/" + strconv.FormatInt(users[1].ID, 10)
assert.True(t, strings.ContainsAny(msgs[0].Body, manageUserURL), "checks if the message contains the link to manage the newly created user from the admin panel")
}
MailNewUser(ctx, users[1])
// test with SEND_NOTIFICATION_EMAIL_ON_NEW_USER disabled; emails shouldn't be sent
setting.Admin.SendNotificationEmailOnNewUser = false
sa = func(msgs ...*Message) {
assert.Equal(t, 1, 0, "this shouldn't execute. MailNewUser must exit early since SEND_NOTIFICATION_EMAIL_ON_NEW_USER is disabled")
}
MailNewUser(ctx, users[1])
}

View file

@ -202,3 +202,7 @@ func (m *mailNotifier) RepoPendingTransfer(ctx context.Context, doer, newOwner *
log.Error("SendRepoTransferNotifyMail: %v", err) log.Error("SendRepoTransferNotifyMail: %v", err)
} }
} }
func (m *mailNotifier) NewUserSignUp(ctx context.Context, newUser *user_model.User) {
MailNewUser(ctx, newUser)
}

View file

@ -6,14 +6,13 @@ package token
import ( import (
"context" "context"
crypto_hmac "crypto/hmac" crypto_hmac "crypto/hmac"
"crypto/sha256"
"encoding/base32" "encoding/base32"
"fmt" "fmt"
"time" "time"
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/util"
"github.com/minio/sha256-simd"
) )
// A token is a verifiable container describing an action. // A token is a verifiable container describing an action.

View file

@ -859,6 +859,11 @@ func (g *GiteaLocalUploader) CreateReviews(reviews ...*base.Review) error {
} }
for _, comment := range review.Comments { for _, comment := range review.Comments {
// Skip code comment if it doesn't have a diff it is commeting on.
if comment.DiffHunk == "" {
continue
}
line := comment.Line line := comment.Line
if line != 0 { if line != 0 {
comment.Position = 1 comment.Position = 1

View file

@ -57,8 +57,9 @@ func (f *GitlabDownloaderFactory) GitServiceType() structs.GitServiceType {
// GitlabDownloader implements a Downloader interface to get repository information // GitlabDownloader implements a Downloader interface to get repository information
// from gitlab via go-gitlab // from gitlab via go-gitlab
// - issueCount is incremented in GetIssues() to ensure PR and Issue numbers do not overlap, // - maxIssueNumber is the maximum issue number seen, updated in GetIssues() to ensure PR and Issue numbers do not overlap,
// because Gitlab has individual Issue and Pull Request numbers. // because Gitlab has individual Issue and Pull Request numbers.
// Note that because some issue numbers may be skipped, this number may be greater than the total number of issues
type GitlabDownloader struct { type GitlabDownloader struct {
base.NullDownloader base.NullDownloader
ctx context.Context ctx context.Context
@ -66,7 +67,7 @@ type GitlabDownloader struct {
baseURL string baseURL string
repoID int repoID int
repoName string repoName string
issueCount int64 maxIssueNumber int64
maxPerPage int maxPerPage int
} }
@ -450,8 +451,8 @@ func (g *GitlabDownloader) GetIssues(page, perPage int) ([]*base.Issue, bool, er
Context: gitlabIssueContext{IsMergeRequest: false}, Context: gitlabIssueContext{IsMergeRequest: false},
}) })
// increment issueCount, to be used in GetPullRequests() // update maxIssueNumber, to be used in GetPullRequests()
g.issueCount++ g.maxIssueNumber = max(g.maxIssueNumber, int64(issue.IID))
} }
return allIssues, len(issues) < perPage, nil return allIssues, len(issues) < perPage, nil
@ -608,7 +609,7 @@ func (g *GitlabDownloader) GetPullRequests(page, perPage int) ([]*base.PullReque
} }
// Add the PR ID to the Issue Count because PR and Issues share ID space in Gitea // Add the PR ID to the Issue Count because PR and Issues share ID space in Gitea
newPRNumber := g.issueCount + int64(pr.IID) newPRNumber := g.maxIssueNumber + int64(pr.IID)
allPRs = append(allPRs, &base.PullRequest{ allPRs = append(allPRs, &base.PullRequest{
Title: pr.Title, Title: pr.Title,

View file

@ -13,6 +13,7 @@ import (
"testing" "testing"
"time" "time"
"code.gitea.io/gitea/models/unittest"
"code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/json"
base "code.gitea.io/gitea/modules/migration" base "code.gitea.io/gitea/modules/migration"
@ -21,18 +22,15 @@ import (
) )
func TestGitlabDownloadRepo(t *testing.T) { func TestGitlabDownloadRepo(t *testing.T) {
// Skip tests if Gitlab token is not found // If a GitLab access token is provided, this test will make HTTP requests to the live gitlab.com instance.
// When doing so, the responses from gitlab.com will be saved as test data files.
// If no access token is available, those cached responses will be used instead.
gitlabPersonalAccessToken := os.Getenv("GITLAB_READ_TOKEN") gitlabPersonalAccessToken := os.Getenv("GITLAB_READ_TOKEN")
if gitlabPersonalAccessToken == "" { fixturePath := "./testdata/gitlab/full_download"
t.Skip("skipped test because GITLAB_READ_TOKEN was not in the environment") server := unittest.NewMockWebServer(t, "https://gitlab.com", fixturePath, gitlabPersonalAccessToken != "")
} defer server.Close()
resp, err := http.Get("https://gitlab.com/gitea/test_repo") downloader, err := NewGitlabDownloader(context.Background(), server.URL, "gitea/test_repo", "", "", gitlabPersonalAccessToken)
if err != nil || resp.StatusCode != http.StatusOK {
t.Skipf("Can't access test repo, skipping %s", t.Name())
}
downloader, err := NewGitlabDownloader(context.Background(), "https://gitlab.com", "gitea/test_repo", "", "", gitlabPersonalAccessToken)
if err != nil { if err != nil {
t.Fatalf("NewGitlabDownloader is nil: %v", err) t.Fatalf("NewGitlabDownloader is nil: %v", err)
} }
@ -43,8 +41,8 @@ func TestGitlabDownloadRepo(t *testing.T) {
Name: "test_repo", Name: "test_repo",
Owner: "", Owner: "",
Description: "Test repository for testing migration from gitlab to gitea", Description: "Test repository for testing migration from gitlab to gitea",
CloneURL: "https://gitlab.com/gitea/test_repo.git", CloneURL: server.URL + "/gitea/test_repo.git",
OriginalURL: "https://gitlab.com/gitea/test_repo", OriginalURL: server.URL + "/gitea/test_repo",
DefaultBranch: "master", DefaultBranch: "master",
}, repo) }, repo)
@ -281,17 +279,17 @@ func TestGitlabDownloadRepo(t *testing.T) {
UserName: "real6543", UserName: "real6543",
Content: "tada", Content: "tada",
}}, }},
PatchURL: "https://gitlab.com/gitea/test_repo/-/merge_requests/2.patch", PatchURL: server.URL + "/gitea/test_repo/-/merge_requests/2.patch",
Head: base.PullRequestBranch{ Head: base.PullRequestBranch{
Ref: "feat/test", Ref: "feat/test",
CloneURL: "https://gitlab.com/gitea/test_repo/-/merge_requests/2", CloneURL: server.URL + "/gitea/test_repo/-/merge_requests/2",
SHA: "9f733b96b98a4175276edf6a2e1231489c3bdd23", SHA: "9f733b96b98a4175276edf6a2e1231489c3bdd23",
RepoName: "test_repo", RepoName: "test_repo",
OwnerName: "lafriks", OwnerName: "lafriks",
}, },
Base: base.PullRequestBranch{ Base: base.PullRequestBranch{
Ref: "master", Ref: "master",
SHA: "", SHA: "c59c9b451acca9d106cc19d61d87afe3fbbb8b83",
OwnerName: "lafriks", OwnerName: "lafriks",
RepoName: "test_repo", RepoName: "test_repo",
}, },
@ -309,16 +307,16 @@ func TestGitlabDownloadRepo(t *testing.T) {
assertReviewsEqual(t, []*base.Review{ assertReviewsEqual(t, []*base.Review{
{ {
IssueIndex: 1, IssueIndex: 1,
ReviewerID: 4102996, ReviewerID: 527793,
ReviewerName: "zeripath", ReviewerName: "axifive",
CreatedAt: time.Date(2019, 11, 28, 16, 2, 8, 377000000, time.UTC), CreatedAt: time.Date(2019, 11, 28, 8, 54, 41, 34000000, time.UTC),
State: "APPROVED", State: "APPROVED",
}, },
{ {
IssueIndex: 1, IssueIndex: 1,
ReviewerID: 527793, ReviewerID: 4102996,
ReviewerName: "axifive", ReviewerName: "zeripath",
CreatedAt: time.Date(2019, 11, 28, 16, 2, 8, 377000000, time.UTC), CreatedAt: time.Date(2019, 11, 28, 8, 54, 41, 34000000, time.UTC),
State: "APPROVED", State: "APPROVED",
}, },
}, rvs) }, rvs)
@ -330,12 +328,55 @@ func TestGitlabDownloadRepo(t *testing.T) {
IssueIndex: 2, IssueIndex: 2,
ReviewerID: 4575606, ReviewerID: 4575606,
ReviewerName: "real6543", ReviewerName: "real6543",
CreatedAt: time.Date(2020, 4, 19, 19, 24, 21, 108000000, time.UTC), CreatedAt: time.Date(2019, 11, 28, 15, 56, 54, 108000000, time.UTC),
State: "APPROVED", State: "APPROVED",
}, },
}, rvs) }, rvs)
} }
func TestGitlabSkippedIssueNumber(t *testing.T) {
// If a GitLab access token is provided, this test will make HTTP requests to the live gitlab.com instance.
// When doing so, the responses from gitlab.com will be saved as test data files.
// If no access token is available, those cached responses will be used instead.
gitlabPersonalAccessToken := os.Getenv("GITLAB_READ_TOKEN")
fixturePath := "./testdata/gitlab/skipped_issue_number"
server := unittest.NewMockWebServer(t, "https://gitlab.com", fixturePath, gitlabPersonalAccessToken != "")
defer server.Close()
downloader, err := NewGitlabDownloader(context.Background(), server.URL, "troyengel/archbuild", "", "", gitlabPersonalAccessToken)
if err != nil {
t.Fatalf("NewGitlabDownloader is nil: %v", err)
}
repo, err := downloader.GetRepoInfo()
assert.NoError(t, err)
assertRepositoryEqual(t, &base.Repository{
Name: "archbuild",
Owner: "troyengel",
Description: "Arch packaging and build files",
CloneURL: server.URL + "/troyengel/archbuild.git",
OriginalURL: server.URL + "/troyengel/archbuild",
DefaultBranch: "master",
}, repo)
issues, isEnd, err := downloader.GetIssues(1, 10)
assert.NoError(t, err)
assert.True(t, isEnd)
// the only issue in this repository has number 2
assert.EqualValues(t, 1, len(issues))
assert.EqualValues(t, 2, issues[0].Number)
assert.EqualValues(t, "vpn unlimited errors", issues[0].Title)
prs, _, err := downloader.GetPullRequests(1, 10)
assert.NoError(t, err)
// the only merge request in this repository has number 1,
// but we offset it by the maximum issue number so it becomes
// pull request 3 in Forgejo
assert.EqualValues(t, 1, len(prs))
assert.EqualValues(t, 3, prs[0].Number)
assert.EqualValues(t, "Review", prs[0].Title)
}
func gitlabClientMockSetup(t *testing.T) (*http.ServeMux, *httptest.Server, *gitlab.Client) { func gitlabClientMockSetup(t *testing.T) (*http.ServeMux, *httptest.Server, *gitlab.Client) {
// mux is the HTTP request multiplexer used with the test server. // mux is the HTTP request multiplexer used with the test server.
mux := http.NewServeMux() mux := http.NewServeMux()

View file

@ -0,0 +1,22 @@
X-Frame-Options: SAMEORIGIN
Ratelimit-Resettime: Thu, 30 Nov 2023 08:45:46 GMT
Cf-Cache-Status: MISS
Set-Cookie: _cfuvid=TkY5Br2q4C67LJ2jZWlgdQaosj3Z4aI81Qb27PNKXfo-1701333886606-0-604800000; path=/; domain=.gitlab.com; HttpOnly; Secure; SameSite=None
Etag: W/"3cacfe29f44a69e84a577337eac55d89"
X-Content-Type-Options: nosniff
Ratelimit-Remaining: 1996
Gitlab-Lb: haproxy-main-29-lb-gprd
Cache-Control: max-age=0, private, must-revalidate
Referrer-Policy: strict-origin-when-cross-origin
X-Gitlab-Meta: {"correlation_id":"291f87cd975a51a7b756806ce7b53e2e","version":"1"}
Ratelimit-Observed: 4
Vary: Origin, Accept-Encoding
Gitlab-Sv: localhost
Content-Security-Policy: default-src 'none'
Ratelimit-Reset: 1701333946
Content-Type: application/json
X-Runtime: 0.101081
Strict-Transport-Security: max-age=31536000
Ratelimit-Limit: 2000
{"id":15578026,"description":"Test repository for testing migration from gitlab to gitea","name":"test_repo","name_with_namespace":"gitea / test_repo","path":"test_repo","path_with_namespace":"gitea/test_repo","created_at":"2019-11-28T08:20:33.019Z","default_branch":"master","tag_list":["migration","test"],"topics":["migration","test"],"ssh_url_to_repo":"git@gitlab.com:gitea/test_repo.git","http_url_to_repo":"https://gitlab.com/gitea/test_repo.git","web_url":"https://gitlab.com/gitea/test_repo","readme_url":"https://gitlab.com/gitea/test_repo/-/blob/master/README.md","forks_count":1,"avatar_url":null,"star_count":0,"last_activity_at":"2020-04-19T19:46:04.527Z","namespace":{"id":3181312,"name":"gitea","path":"gitea","kind":"group","full_path":"gitea","parent_id":null,"avatar_url":"/uploads/-/system/group/avatar/3181312/gitea.png","web_url":"https://gitlab.com/groups/gitea"},"container_registry_image_prefix":"registry.gitlab.com/gitea/test_repo","_links":{"self":"https://gitlab.com/api/v4/projects/15578026","issues":"https://gitlab.com/api/v4/projects/15578026/issues","merge_requests":"https://gitlab.com/api/v4/projects/15578026/merge_requests","repo_branches":"https://gitlab.com/api/v4/projects/15578026/repository/branches","labels":"https://gitlab.com/api/v4/projects/15578026/labels","events":"https://gitlab.com/api/v4/projects/15578026/events","members":"https://gitlab.com/api/v4/projects/15578026/members","cluster_agents":"https://gitlab.com/api/v4/projects/15578026/cluster_agents"},"packages_enabled":true,"empty_repo":false,"archived":false,"visibility":"public","resolve_outdated_diff_discussions":false,"issues_enabled":true,"merge_requests_enabled":true,"wiki_enabled":true,"jobs_enabled":true,"snippets_enabled":true,"container_registry_enabled":true,"service_desk_enabled":true,"can_create_merge_request_in":true,"issues_access_level":"enabled","repository_access_level":"enabled","merge_requests_access_level":"enabled","forking_access_level":"enabled","wiki_access_level":"enabled","builds_access_level":"enabled","snippets_access_level":"enabled","pages_access_level":"enabled","analytics_access_level":"enabled","container_registry_access_level":"enabled","security_and_compliance_access_level":"private","releases_access_level":"enabled","environments_access_level":"enabled","feature_flags_access_level":"enabled","infrastructure_access_level":"enabled","monitor_access_level":"enabled","model_experiments_access_level":"enabled","emails_disabled":false,"emails_enabled":true,"shared_runners_enabled":true,"lfs_enabled":true,"creator_id":1241334,"import_status":"none","open_issues_count":0,"description_html":"\u003cp data-sourcepos=\"1:1-1:58\" dir=\"auto\"\u003eTest repository for testing migration from gitlab to gitea\u003c/p\u003e","updated_at":"2022-08-26T19:41:46.691Z","ci_config_path":null,"public_jobs":true,"shared_with_groups":[],"only_allow_merge_if_pipeline_succeeds":false,"allow_merge_on_skipped_pipeline":null,"request_access_enabled":true,"only_allow_merge_if_all_discussions_are_resolved":false,"remove_source_branch_after_merge":true,"printing_merge_request_link_enabled":true,"merge_method":"ff","squash_option":"default_off","enforce_auth_checks_on_uploads":true,"suggestion_commit_message":null,"merge_commit_template":null,"squash_commit_template":null,"issue_branch_template":null,"autoclose_referenced_issues":true,"external_authorization_classification_label":"","requirements_enabled":false,"requirements_access_level":"enabled","security_and_compliance_enabled":false,"compliance_frameworks":[],"permissions":{"project_access":null,"group_access":null}}

View file

@ -0,0 +1,29 @@
Cache-Control: max-age=0, private, must-revalidate
Vary: Origin, Accept-Encoding
Gitlab-Lb: haproxy-main-24-lb-gprd
Content-Type: application/json
X-Per-Page: 2
X-Prev-Page:
Set-Cookie: _cfuvid=N.nfy5eIdFH5lXhsnMyEbeBkoxabcl1SVeyyP0_NrdE-1701333887790-0-604800000; path=/; domain=.gitlab.com; HttpOnly; Secure; SameSite=None
Content-Security-Policy: default-src 'none'
X-Content-Type-Options: nosniff
X-Next-Page:
X-Total-Pages: 1
Etag: W/"4c0531a3595f741f229f5a105e013b95"
X-Gitlab-Meta: {"correlation_id":"b2eca136986f016d946685fb99287f1c","version":"1"}
Ratelimit-Observed: 8
Ratelimit-Resettime: Thu, 30 Nov 2023 08:45:47 GMT
X-Frame-Options: SAMEORIGIN
Referrer-Policy: strict-origin-when-cross-origin
Ratelimit-Remaining: 1992
Gitlab-Sv: localhost
Cf-Cache-Status: MISS
Strict-Transport-Security: max-age=31536000
Ratelimit-Limit: 2000
X-Total: 2
X-Page: 1
X-Runtime: 0.178587
Ratelimit-Reset: 1701333947
Link: <https://gitlab.com/api/v4/projects/15578026/issues?id=15578026&order_by=created_at&page=1&per_page=2&sort=asc&state=all&with_labels_details=false>; rel="first", <https://gitlab.com/api/v4/projects/15578026/issues?id=15578026&order_by=created_at&page=1&per_page=2&sort=asc&state=all&with_labels_details=false>; rel="last"
[{"id":27687675,"iid":1,"project_id":15578026,"title":"Please add an animated gif icon to the merge button","description":"I just want the merge button to hurt my eyes a little. :stuck_out_tongue_closed_eyes:","state":"closed","created_at":"2019-11-28T08:43:35.459Z","updated_at":"2019-11-28T08:46:23.304Z","closed_at":"2019-11-28T08:46:23.275Z","closed_by":{"id":1241334,"username":"lafriks","name":"Lauris BH","state":"active","locked":false,"avatar_url":"https://gitlab.com/uploads/-/system/user/avatar/1241334/avatar.png","web_url":"https://gitlab.com/lafriks"},"labels":["bug","discussion"],"milestone":{"id":1082926,"iid":1,"project_id":15578026,"title":"1.0.0","description":"","state":"closed","created_at":"2019-11-28T08:42:30.301Z","updated_at":"2019-11-28T15:57:52.401Z","due_date":null,"start_date":null,"expired":false,"web_url":"https://gitlab.com/gitea/test_repo/-/milestones/1"},"assignees":[],"author":{"id":1241334,"username":"lafriks","name":"Lauris BH","state":"active","locked":false,"avatar_url":"https://gitlab.com/uploads/-/system/user/avatar/1241334/avatar.png","web_url":"https://gitlab.com/lafriks"},"type":"ISSUE","assignee":null,"user_notes_count":0,"merge_requests_count":0,"upvotes":1,"downvotes":0,"due_date":null,"confidential":false,"discussion_locked":null,"issue_type":"issue","web_url":"https://gitlab.com/gitea/test_repo/-/issues/1","time_stats":{"time_estimate":0,"total_time_spent":0,"human_time_estimate":null,"human_total_time_spent":null},"task_completion_status":{"count":0,"completed_count":0},"blocking_issues_count":0,"has_tasks":true,"task_status":"0 of 0 checklist items completed","_links":{"self":"https://gitlab.com/api/v4/projects/15578026/issues/1","notes":"https://gitlab.com/api/v4/projects/15578026/issues/1/notes","award_emoji":"https://gitlab.com/api/v4/projects/15578026/issues/1/award_emoji","project":"https://gitlab.com/api/v4/projects/15578026","closed_as_duplicate_of":null},"references":{"short":"#1","relative":"#1","full":"gitea/test_repo#1"},"severity":"UNKNOWN","moved_to_id":null,"service_desk_reply_to":null},{"id":27687706,"iid":2,"project_id":15578026,"title":"Test issue","description":"This is test issue 2, do not touch!","state":"closed","created_at":"2019-11-28T08:44:46.277Z","updated_at":"2019-11-28T08:45:44.987Z","closed_at":"2019-11-28T08:45:44.959Z","closed_by":{"id":1241334,"username":"lafriks","name":"Lauris BH","state":"active","locked":false,"avatar_url":"https://gitlab.com/uploads/-/system/user/avatar/1241334/avatar.png","web_url":"https://gitlab.com/lafriks"},"labels":["duplicate"],"milestone":{"id":1082927,"iid":2,"project_id":15578026,"title":"1.1.0","description":"","state":"active","created_at":"2019-11-28T08:42:44.575Z","updated_at":"2019-11-28T08:42:44.575Z","due_date":null,"start_date":null,"expired":false,"web_url":"https://gitlab.com/gitea/test_repo/-/milestones/2"},"assignees":[],"author":{"id":1241334,"username":"lafriks","name":"Lauris BH","state":"active","locked":false,"avatar_url":"https://gitlab.com/uploads/-/system/user/avatar/1241334/avatar.png","web_url":"https://gitlab.com/lafriks"},"type":"ISSUE","assignee":null,"user_notes_count":2,"merge_requests_count":0,"upvotes":1,"downvotes":1,"due_date":null,"confidential":false,"discussion_locked":null,"issue_type":"issue","web_url":"https://gitlab.com/gitea/test_repo/-/issues/2","time_stats":{"time_estimate":0,"total_time_spent":0,"human_time_estimate":null,"human_total_time_spent":null},"task_completion_status":{"count":0,"completed_count":0},"blocking_issues_count":0,"has_tasks":true,"task_status":"0 of 0 checklist items completed","_links":{"self":"https://gitlab.com/api/v4/projects/15578026/issues/2","notes":"https://gitlab.com/api/v4/projects/15578026/issues/2/notes","award_emoji":"https://gitlab.com/api/v4/projects/15578026/issues/2/award_emoji","project":"https://gitlab.com/api/v4/projects/15578026","closed_as_duplicate_of":null},"references":{"short":"#2","relative":"#2","full":"gitea/test_repo#2"},"severity":"UNKNOWN","moved_to_id":null,"service_desk_reply_to":null}]

View file

@ -0,0 +1,29 @@
X-Total-Pages: 1
Referrer-Policy: strict-origin-when-cross-origin
Cache-Control: max-age=0, private, must-revalidate
X-Frame-Options: SAMEORIGIN
X-Runtime: 0.052748
X-Total: 2
Gitlab-Sv: localhost
Link: <https://gitlab.com/api/v4/projects/15578026/issues/1/award_emoji?id=15578026&issue_iid=1&page=1&per_page=2>; rel="first", <https://gitlab.com/api/v4/projects/15578026/issues/1/award_emoji?id=15578026&issue_iid=1&page=1&per_page=2>; rel="last"
X-Content-Type-Options: nosniff
X-Gitlab-Meta: {"correlation_id":"22fc215ac386644c9cb8736b652cf702","version":"1"}
X-Per-Page: 2
Strict-Transport-Security: max-age=31536000
Ratelimit-Reset: 1701333947
Content-Security-Policy: default-src 'none'
Ratelimit-Remaining: 1991
Ratelimit-Observed: 9
X-Next-Page:
Ratelimit-Resettime: Thu, 30 Nov 2023 08:45:47 GMT
Ratelimit-Limit: 2000
Gitlab-Lb: haproxy-main-43-lb-gprd
Cf-Cache-Status: MISS
Etag: W/"69c922434ed11248c864d157eb8eabfc"
Content-Type: application/json
Vary: Origin, Accept-Encoding
X-Page: 1
X-Prev-Page:
Set-Cookie: _cfuvid=POpffkskz4lvLcv2Fhjp7lF3MsmIOugWDzFGtb3ZUig-1701333887995-0-604800000; path=/; domain=.gitlab.com; HttpOnly; Secure; SameSite=None
[{"id":3009580,"name":"thumbsup","user":{"id":1241334,"username":"lafriks","name":"Lauris BH","state":"active","locked":false,"avatar_url":"https://gitlab.com/uploads/-/system/user/avatar/1241334/avatar.png","web_url":"https://gitlab.com/lafriks"},"created_at":"2019-11-28T08:43:40.322Z","updated_at":"2019-11-28T08:43:40.322Z","awardable_id":27687675,"awardable_type":"Issue","url":null},{"id":3009585,"name":"open_mouth","user":{"id":1241334,"username":"lafriks","name":"Lauris BH","state":"active","locked":false,"avatar_url":"https://gitlab.com/uploads/-/system/user/avatar/1241334/avatar.png","web_url":"https://gitlab.com/lafriks"},"created_at":"2019-11-28T08:44:01.902Z","updated_at":"2019-11-28T08:44:01.902Z","awardable_id":27687675,"awardable_type":"Issue","url":null}]

View file

@ -0,0 +1,31 @@
Content-Type: application/json
Content-Length: 2
X-Next-Page:
X-Gitlab-Meta: {"correlation_id":"6b9bc368e2cdc69a1b8e7d2dff7546c5","version":"1"}
Referrer-Policy: strict-origin-when-cross-origin
Ratelimit-Reset: 1701333948
Strict-Transport-Security: max-age=31536000
X-Per-Page: 2
X-Total: 2
Ratelimit-Limit: 2000
Etag: W/"4f53cda18c2baa0c0354bb5f9a3ecbe5"
Link: <https://gitlab.com/api/v4/projects/15578026/issues/1/award_emoji?id=15578026&issue_iid=1&page=1&per_page=2>; rel="first", <https://gitlab.com/api/v4/projects/15578026/issues/1/award_emoji?id=15578026&issue_iid=1&page=1&per_page=2>; rel="last"
X-Runtime: 0.069944
Ratelimit-Remaining: 1990
Ratelimit-Resettime: Thu, 30 Nov 2023 08:45:48 GMT
Gitlab-Sv: localhost
X-Prev-Page:
X-Total-Pages: 1
Ratelimit-Observed: 10
Gitlab-Lb: haproxy-main-17-lb-gprd
Content-Security-Policy: default-src 'none'
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
Accept-Ranges: bytes
Cache-Control: max-age=0, private, must-revalidate
Vary: Origin, Accept-Encoding
X-Page: 2
Cf-Cache-Status: MISS
Set-Cookie: _cfuvid=vcfsxezcg_2Kdh8xD5coOU_uxQIH1in.6BsRttrSIYg-1701333888217-0-604800000; path=/; domain=.gitlab.com; HttpOnly; Secure; SameSite=None
[]

View file

@ -0,0 +1,29 @@
Cf-Cache-Status: MISS
Etag: W/"5fdbcbf64f34ba0e74ce9dd8d6e0efe3"
X-Content-Type-Options: nosniff
X-Per-Page: 2
Ratelimit-Limit: 2000
Set-Cookie: _cfuvid=NLtUZdQlWvWiXr4L8Zfc555FowZOCxJlA0pAOAEkNvg-1701333888445-0-604800000; path=/; domain=.gitlab.com; HttpOnly; Secure; SameSite=None
X-Runtime: 0.075612
Ratelimit-Reset: 1701333948
Ratelimit-Resettime: Thu, 30 Nov 2023 08:45:48 GMT
X-Page: 1
X-Prev-Page:
X-Total-Pages: 3
X-Frame-Options: SAMEORIGIN
Referrer-Policy: strict-origin-when-cross-origin
Content-Security-Policy: default-src 'none'
Ratelimit-Observed: 11
Gitlab-Lb: haproxy-main-09-lb-gprd
Gitlab-Sv: localhost
Ratelimit-Remaining: 1989
Cache-Control: max-age=0, private, must-revalidate
Link: <https://gitlab.com/api/v4/projects/15578026/issues/2/award_emoji?id=15578026&issue_iid=2&page=2&per_page=2>; rel="next", <https://gitlab.com/api/v4/projects/15578026/issues/2/award_emoji?id=15578026&issue_iid=2&page=1&per_page=2>; rel="first", <https://gitlab.com/api/v4/projects/15578026/issues/2/award_emoji?id=15578026&issue_iid=2&page=3&per_page=2>; rel="last"
X-Gitlab-Meta: {"correlation_id":"a69709cf552479bc5f5f3e4e1f808790","version":"1"}
X-Next-Page: 2
Strict-Transport-Security: max-age=31536000
Content-Type: application/json
Vary: Origin, Accept-Encoding
X-Total: 6
[{"id":3009627,"name":"thumbsup","user":{"id":1241334,"username":"lafriks","name":"Lauris BH","state":"active","locked":false,"avatar_url":"https://gitlab.com/uploads/-/system/user/avatar/1241334/avatar.png","web_url":"https://gitlab.com/lafriks"},"created_at":"2019-11-28T08:46:42.657Z","updated_at":"2019-11-28T08:46:42.657Z","awardable_id":27687706,"awardable_type":"Issue","url":null},{"id":3009628,"name":"thumbsdown","user":{"id":1241334,"username":"lafriks","name":"Lauris BH","state":"active","locked":false,"avatar_url":"https://gitlab.com/uploads/-/system/user/avatar/1241334/avatar.png","web_url":"https://gitlab.com/lafriks"},"created_at":"2019-11-28T08:46:43.471Z","updated_at":"2019-11-28T08:46:43.471Z","awardable_id":27687706,"awardable_type":"Issue","url":null}]

View file

@ -0,0 +1,29 @@
Ratelimit-Limit: 2000
X-Content-Type-Options: nosniff
Ratelimit-Resettime: Thu, 30 Nov 2023 08:45:48 GMT
X-Total: 6
Set-Cookie: _cfuvid=E5GyZy0rB2zrRH0ewMyrJd1wBrt7A2sGNmOHTiWwbYk-1701333888703-0-604800000; path=/; domain=.gitlab.com; HttpOnly; Secure; SameSite=None
X-Next-Page: 3
X-Page: 2
Link: <https://gitlab.com/api/v4/projects/15578026/issues/2/award_emoji?id=15578026&issue_iid=2&page=1&per_page=2>; rel="prev", <https://gitlab.com/api/v4/projects/15578026/issues/2/award_emoji?id=15578026&issue_iid=2&page=3&per_page=2>; rel="next", <https://gitlab.com/api/v4/projects/15578026/issues/2/award_emoji?id=15578026&issue_iid=2&page=1&per_page=2>; rel="first", <https://gitlab.com/api/v4/projects/15578026/issues/2/award_emoji?id=15578026&issue_iid=2&page=3&per_page=2>; rel="last"
X-Gitlab-Meta: {"correlation_id":"41e59fb2e78f5e68518b81bd4ff3bb1b","version":"1"}
X-Prev-Page: 1
Cf-Cache-Status: MISS
Referrer-Policy: strict-origin-when-cross-origin
Gitlab-Lb: haproxy-main-26-lb-gprd
Cache-Control: max-age=0, private, must-revalidate
Etag: W/"d16c513b32212d9286fce6f53340c1cf"
Vary: Origin, Accept-Encoding
X-Per-Page: 2
Strict-Transport-Security: max-age=31536000
Ratelimit-Reset: 1701333948
Content-Type: application/json
X-Runtime: 0.105758
Ratelimit-Remaining: 1988
Content-Security-Policy: default-src 'none'
X-Frame-Options: SAMEORIGIN
Gitlab-Sv: localhost
X-Total-Pages: 3
Ratelimit-Observed: 12
[{"id":3009632,"name":"laughing","user":{"id":1241334,"username":"lafriks","name":"Lauris BH","state":"active","locked":false,"avatar_url":"https://gitlab.com/uploads/-/system/user/avatar/1241334/avatar.png","web_url":"https://gitlab.com/lafriks"},"created_at":"2019-11-28T08:47:14.381Z","updated_at":"2019-11-28T08:47:14.381Z","awardable_id":27687706,"awardable_type":"Issue","url":null},{"id":3009634,"name":"tada","user":{"id":1241334,"username":"lafriks","name":"Lauris BH","state":"active","locked":false,"avatar_url":"https://gitlab.com/uploads/-/system/user/avatar/1241334/avatar.png","web_url":"https://gitlab.com/lafriks"},"created_at":"2019-11-28T08:47:18.254Z","updated_at":"2019-11-28T08:47:18.254Z","awardable_id":27687706,"awardable_type":"Issue","url":null}]

View file

@ -0,0 +1,29 @@
Content-Type: application/json
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
Referrer-Policy: strict-origin-when-cross-origin
Link: <https://gitlab.com/api/v4/projects/15578026/issues/2/award_emoji?id=15578026&issue_iid=2&page=2&per_page=2>; rel="prev", <https://gitlab.com/api/v4/projects/15578026/issues/2/award_emoji?id=15578026&issue_iid=2&page=1&per_page=2>; rel="first", <https://gitlab.com/api/v4/projects/15578026/issues/2/award_emoji?id=15578026&issue_iid=2&page=3&per_page=2>; rel="last"
Cf-Cache-Status: MISS
Etag: W/"165d37bf09a54bb31f4619cca8722cb4"
Ratelimit-Observed: 13
Ratelimit-Limit: 2000
Set-Cookie: _cfuvid=W6F2uJSFkB3Iyl27_xtklVvP4Z_nSsjPqytClHPW9H8-1701333888913-0-604800000; path=/; domain=.gitlab.com; HttpOnly; Secure; SameSite=None
X-Page: 3
X-Total-Pages: 3
Strict-Transport-Security: max-age=31536000
Ratelimit-Resettime: Thu, 30 Nov 2023 08:45:48 GMT
Cache-Control: max-age=0, private, must-revalidate
Vary: Origin, Accept-Encoding
X-Gitlab-Meta: {"correlation_id":"b805751aeb6f562dff684cec34dfdc0b","version":"1"}
X-Per-Page: 2
X-Prev-Page: 2
X-Runtime: 0.061943
Gitlab-Lb: haproxy-main-11-lb-gprd
Gitlab-Sv: localhost
X-Total: 6
Ratelimit-Remaining: 1987
Content-Security-Policy: default-src 'none'
X-Next-Page:
Ratelimit-Reset: 1701333948
[{"id":3009636,"name":"confused","user":{"id":1241334,"username":"lafriks","name":"Lauris BH","state":"active","locked":false,"avatar_url":"https://gitlab.com/uploads/-/system/user/avatar/1241334/avatar.png","web_url":"https://gitlab.com/lafriks"},"created_at":"2019-11-28T08:47:27.248Z","updated_at":"2019-11-28T08:47:27.248Z","awardable_id":27687706,"awardable_type":"Issue","url":null},{"id":3009640,"name":"hearts","user":{"id":1241334,"username":"lafriks","name":"Lauris BH","state":"active","locked":false,"avatar_url":"https://gitlab.com/uploads/-/system/user/avatar/1241334/avatar.png","web_url":"https://gitlab.com/lafriks"},"created_at":"2019-11-28T08:47:33.059Z","updated_at":"2019-11-28T08:47:33.059Z","awardable_id":27687706,"awardable_type":"Issue","url":null}]

View file

@ -0,0 +1,31 @@
Ratelimit-Remaining: 1986
Accept-Ranges: bytes
Set-Cookie: _cfuvid=DZQIMINjFohqKKjkWnojkq2xuUaqb42YEQg3BZXe68w-1701333889148-0-604800000; path=/; domain=.gitlab.com; HttpOnly; Secure; SameSite=None
Strict-Transport-Security: max-age=31536000
X-Prev-Page:
Ratelimit-Reset: 1701333949
Ratelimit-Limit: 2000
Etag: W/"4f53cda18c2baa0c0354bb5f9a3ecbe5"
Cache-Control: max-age=0, private, must-revalidate
X-Next-Page:
X-Page: 4
Referrer-Policy: strict-origin-when-cross-origin
Gitlab-Sv: localhost
Content-Type: application/json
X-Content-Type-Options: nosniff
Link: <https://gitlab.com/api/v4/projects/15578026/issues/2/award_emoji?id=15578026&issue_iid=2&page=1&per_page=2>; rel="first", <https://gitlab.com/api/v4/projects/15578026/issues/2/award_emoji?id=15578026&issue_iid=2&page=3&per_page=2>; rel="last"
Vary: Origin, Accept-Encoding
Content-Security-Policy: default-src 'none'
X-Runtime: 0.081728
X-Total: 6
Content-Length: 2
X-Frame-Options: SAMEORIGIN
X-Gitlab-Meta: {"correlation_id":"588184d2d9ad2c4a7e7c00a51575091c","version":"1"}
Cf-Cache-Status: MISS
X-Total-Pages: 3
Ratelimit-Observed: 14
Ratelimit-Resettime: Thu, 30 Nov 2023 08:45:49 GMT
Gitlab-Lb: haproxy-main-34-lb-gprd
X-Per-Page: 2
[]

View file

@ -0,0 +1,29 @@
X-Runtime: 0.173849
Content-Security-Policy: default-src 'none'
X-Per-Page: 100
X-Prev-Page:
X-Total: 4
Ratelimit-Observed: 15
X-Page: 1
Ratelimit-Remaining: 1985
Cache-Control: max-age=0, private, must-revalidate
X-Gitlab-Meta: {"correlation_id":"1d2b49b1b17e0c2d58c800a1b6c7eca3","version":"1"}
X-Next-Page:
Referrer-Policy: strict-origin-when-cross-origin
Cf-Cache-Status: MISS
Content-Type: application/json
Etag: W/"bcc91e8a7b2eac98b4d96ae791e0649d"
Vary: Origin, Accept-Encoding
X-Frame-Options: SAMEORIGIN
Strict-Transport-Security: max-age=31536000
Ratelimit-Reset: 1701333949
Gitlab-Lb: haproxy-main-08-lb-gprd
Gitlab-Sv: localhost
Link: <https://gitlab.com/api/v4/projects/15578026/issues/2/discussions?id=15578026&noteable_id=2&page=1&per_page=100>; rel="first", <https://gitlab.com/api/v4/projects/15578026/issues/2/discussions?id=15578026&noteable_id=2&page=1&per_page=100>; rel="last"
X-Content-Type-Options: nosniff
X-Total-Pages: 1
Ratelimit-Limit: 2000
Set-Cookie: _cfuvid=yj.PGr9ftsz1kNpgtsmQhAcGpdMnklLE.NQ9h71hm5Q-1701333889475-0-604800000; path=/; domain=.gitlab.com; HttpOnly; Secure; SameSite=None
Ratelimit-Resettime: Thu, 30 Nov 2023 08:45:49 GMT
[{"id":"617967369d98d8b73b6105a40318fe839f931a24","individual_note":true,"notes":[{"id":251637434,"type":null,"body":"This is a comment","attachment":null,"author":{"id":1241334,"username":"lafriks","name":"Lauris BH","state":"active","locked":false,"avatar_url":"https://gitlab.com/uploads/-/system/user/avatar/1241334/avatar.png","web_url":"https://gitlab.com/lafriks"},"created_at":"2019-11-28T08:44:52.501Z","updated_at":"2019-11-28T08:44:52.501Z","system":false,"noteable_id":27687706,"noteable_type":"Issue","project_id":15578026,"resolvable":false,"confidential":false,"internal":false,"noteable_iid":2,"commands_changes":{}}]},{"id":"b92d74daee411a17d844041bcd3c267ade58f680","individual_note":true,"notes":[{"id":251637528,"type":null,"body":"changed milestone to %2","attachment":null,"author":{"id":1241334,"username":"lafriks","name":"Lauris BH","state":"active","locked":false,"avatar_url":"https://gitlab.com/uploads/-/system/user/avatar/1241334/avatar.png","web_url":"https://gitlab.com/lafriks"},"created_at":"2019-11-28T08:45:02.329Z","updated_at":"2019-11-28T08:45:02.335Z","system":true,"noteable_id":27687706,"noteable_type":"Issue","project_id":15578026,"resolvable":false,"confidential":false,"internal":false,"noteable_iid":2,"commands_changes":{}}]},{"id":"6010f567d2b58758ef618070372c97891ac75349","individual_note":true,"notes":[{"id":251637892,"type":null,"body":"closed","attachment":null,"author":{"id":1241334,"username":"lafriks","name":"Lauris BH","state":"active","locked":false,"avatar_url":"https://gitlab.com/uploads/-/system/user/avatar/1241334/avatar.png","web_url":"https://gitlab.com/lafriks"},"created_at":"2019-11-28T08:45:45.007Z","updated_at":"2019-11-28T08:45:45.010Z","system":true,"noteable_id":27687706,"noteable_type":"Issue","project_id":15578026,"resolvable":false,"confidential":false,"internal":false,"noteable_iid":2,"commands_changes":{}}]},{"id":"632d0cbfd6a1a08f38aaf9ef7715116f4b188ebb","individual_note":true,"notes":[{"id":251637999,"type":null,"body":"A second comment","attachment":null,"author":{"id":1241334,"username":"lafriks","name":"Lauris BH","state":"active","locked":false,"avatar_url":"https://gitlab.com/uploads/-/system/user/avatar/1241334/avatar.png","web_url":"https://gitlab.com/lafriks"},"created_at":"2019-11-28T08:45:53.501Z","updated_at":"2019-11-28T08:45:53.501Z","system":false,"noteable_id":27687706,"noteable_type":"Issue","project_id":15578026,"resolvable":false,"confidential":false,"internal":false,"noteable_iid":2,"commands_changes":{}}]}]

View file

@ -0,0 +1,29 @@
Cache-Control: max-age=0, private, must-revalidate
Strict-Transport-Security: max-age=31536000
Gitlab-Lb: haproxy-main-28-lb-gprd
Link: <https://gitlab.com/api/v4/projects/15578026/labels?id=15578026&include_ancestor_groups=true&page=1&per_page=100&with_counts=false>; rel="first", <https://gitlab.com/api/v4/projects/15578026/labels?id=15578026&include_ancestor_groups=true&page=1&per_page=100&with_counts=false>; rel="last"
X-Per-Page: 100
X-Runtime: 0.149464
X-Total: 9
Ratelimit-Observed: 6
Gitlab-Sv: localhost
X-Frame-Options: SAMEORIGIN
X-Gitlab-Meta: {"correlation_id":"2ccddf20767704a98ed6b582db3b103e","version":"1"}
X-Next-Page:
X-Prev-Page:
X-Total-Pages: 1
Ratelimit-Remaining: 1994
Etag: W/"5a3fb9bc7b1018070943f4aa1353f8b6"
Ratelimit-Limit: 2000
X-Page: 1
Content-Type: application/json
Vary: Origin, Accept-Encoding
Set-Cookie: _cfuvid=geNpLvH8Cv5XeYfUVwtpaazw43v9lCcqHE.vyXGk3kU-1701333887126-0-604800000; path=/; domain=.gitlab.com; HttpOnly; Secure; SameSite=None
X-Content-Type-Options: nosniff
Ratelimit-Reset: 1701333947
Ratelimit-Resettime: Thu, 30 Nov 2023 08:45:47 GMT
Content-Security-Policy: default-src 'none'
Referrer-Policy: strict-origin-when-cross-origin
Cf-Cache-Status: MISS
[{"id":12959095,"name":"bug","description":null,"description_html":"","text_color":"#FFFFFF","color":"#d9534f","subscribed":false,"priority":null,"is_project_label":true},{"id":12959097,"name":"confirmed","description":null,"description_html":"","text_color":"#FFFFFF","color":"#d9534f","subscribed":false,"priority":null,"is_project_label":true},{"id":12959096,"name":"critical","description":null,"description_html":"","text_color":"#FFFFFF","color":"#d9534f","subscribed":false,"priority":null,"is_project_label":true},{"id":12959100,"name":"discussion","description":null,"description_html":"","text_color":"#FFFFFF","color":"#428bca","subscribed":false,"priority":null,"is_project_label":true},{"id":12959098,"name":"documentation","description":null,"description_html":"","text_color":"#1F1E24","color":"#f0ad4e","subscribed":false,"priority":null,"is_project_label":true},{"id":12959554,"name":"duplicate","description":null,"description_html":"","text_color":"#FFFFFF","color":"#7F8C8D","subscribed":false,"priority":null,"is_project_label":true},{"id":12959102,"name":"enhancement","description":null,"description_html":"","text_color":"#FFFFFF","color":"#5cb85c","subscribed":false,"priority":null,"is_project_label":true},{"id":12959101,"name":"suggestion","description":null,"description_html":"","text_color":"#FFFFFF","color":"#428bca","subscribed":false,"priority":null,"is_project_label":true},{"id":12959099,"name":"support","description":null,"description_html":"","text_color":"#1F1E24","color":"#f0ad4e","subscribed":false,"priority":null,"is_project_label":true}]

View file

@ -0,0 +1,29 @@
Content-Type: application/json
X-Page: 1
Link: <https://gitlab.com/api/v4/projects/15578026/merge_requests?id=15578026&order_by=created_at&page=2&per_page=1&sort=desc&state=all&view=simple&with_labels_details=false&with_merge_status_recheck=false>; rel="next", <https://gitlab.com/api/v4/projects/15578026/merge_requests?id=15578026&order_by=created_at&page=1&per_page=1&sort=desc&state=all&view=simple&with_labels_details=false&with_merge_status_recheck=false>; rel="first", <https://gitlab.com/api/v4/projects/15578026/merge_requests?id=15578026&order_by=created_at&page=2&per_page=1&sort=desc&state=all&view=simple&with_labels_details=false&with_merge_status_recheck=false>; rel="last"
Vary: Origin, Accept-Encoding
X-Runtime: 0.139912
X-Gitlab-Meta: {"correlation_id":"002c20b78ace441f5585931fed7093ed","version":"1"}
Gitlab-Sv: localhost
Cache-Control: max-age=0, private, must-revalidate
X-Total: 2
X-Total-Pages: 2
Ratelimit-Observed: 16
Ratelimit-Limit: 2000
X-Content-Type-Options: nosniff
Ratelimit-Remaining: 1984
Set-Cookie: _cfuvid=6nsOEFMJm7NgrvYZAMGwiJBdm5A5CU71S33zOdN8Kyo-1701333889768-0-604800000; path=/; domain=.gitlab.com; HttpOnly; Secure; SameSite=None
X-Per-Page: 1
X-Prev-Page:
Strict-Transport-Security: max-age=31536000
Ratelimit-Reset: 1701333949
Gitlab-Lb: haproxy-main-09-lb-gprd
X-Next-Page: 2
Cf-Cache-Status: MISS
Content-Security-Policy: default-src 'none'
Etag: W/"14f72c1f555b0e6348d338190e9e4839"
X-Frame-Options: SAMEORIGIN
Referrer-Policy: strict-origin-when-cross-origin
Ratelimit-Resettime: Thu, 30 Nov 2023 08:45:49 GMT
[{"id":43524600,"iid":2,"project_id":15578026,"title":"Test branch","description":"do not merge this PR","state":"opened","created_at":"2019-11-28T15:56:54.104Z","updated_at":"2020-04-19T19:24:21.108Z","web_url":"https://gitlab.com/gitea/test_repo/-/merge_requests/2"}]

View file

@ -0,0 +1,22 @@
Ratelimit-Resettime: Thu, 30 Nov 2023 08:45:51 GMT
Ratelimit-Limit: 2000
Content-Security-Policy: default-src 'none'
X-Gitlab-Meta: {"correlation_id":"0a1a7339f3527e175a537afe058a6ac7","version":"1"}
Ratelimit-Observed: 21
Cache-Control: max-age=0, private, must-revalidate
Gitlab-Lb: haproxy-main-38-lb-gprd
X-Frame-Options: SAMEORIGIN
Strict-Transport-Security: max-age=31536000
Cf-Cache-Status: MISS
Content-Type: application/json
Etag: W/"19aa54b7d4531bd5ab98282e0b772f20"
X-Content-Type-Options: nosniff
Set-Cookie: _cfuvid=J2edW64FLja6v0MZCh5tVbLYO42.VvIsTqQ.uj1Gr_k-1701333891171-0-604800000; path=/; domain=.gitlab.com; HttpOnly; Secure; SameSite=None
Ratelimit-Reset: 1701333951
Gitlab-Sv: localhost
Vary: Origin, Accept-Encoding
Ratelimit-Remaining: 1979
X-Runtime: 0.155712
Referrer-Policy: strict-origin-when-cross-origin
{"id":43486906,"iid":1,"project_id":15578026,"title":"Update README.md","description":"add warning to readme","state":"merged","created_at":"2019-11-28T08:54:41.034Z","updated_at":"2019-11-28T16:02:08.377Z","merge_status":"can_be_merged","approved":true,"approvals_required":0,"approvals_left":0,"require_password_to_approve":false,"approved_by":[{"user":{"id":527793,"username":"axifive","name":"Alexey Terentyev","state":"active","locked":false,"avatar_url":"https://secure.gravatar.com/avatar/06683cd6b2e2c2ce0ab00fb80cc0729f?s=80\u0026d=identicon","web_url":"https://gitlab.com/axifive"}},{"user":{"id":4102996,"username":"zeripath","name":"zeripath","state":"active","locked":false,"avatar_url":"https://secure.gravatar.com/avatar/1ae18535c2b1aed798da090448997248?s=80\u0026d=identicon","web_url":"https://gitlab.com/zeripath"}}],"suggested_approvers":[],"approvers":[],"approver_groups":[],"user_has_approved":false,"user_can_approve":false,"approval_rules_left":[],"has_approval_rules":true,"merge_request_approvers_available":false,"multiple_approval_rules_available":false,"invalid_approvers_rules":[]}

View file

@ -0,0 +1,22 @@
Etag: W/"914149155d75f8d8f7ed2e5351f0fadb"
X-Runtime: 0.234286
Ratelimit-Remaining: 1983
Strict-Transport-Security: max-age=31536000
Referrer-Policy: strict-origin-when-cross-origin
Gitlab-Sv: localhost
Set-Cookie: _cfuvid=aYLZ68TRL8gsnraUk.zZIxRvuv981nIhZNIO9vVpgbU-1701333890175-0-604800000; path=/; domain=.gitlab.com; HttpOnly; Secure; SameSite=None
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
Ratelimit-Observed: 17
Content-Type: application/json
Cache-Control: max-age=0, private, must-revalidate
Ratelimit-Resettime: Thu, 30 Nov 2023 08:45:50 GMT
Ratelimit-Limit: 2000
Vary: Origin, Accept-Encoding
Content-Security-Policy: default-src 'none'
Gitlab-Lb: haproxy-main-33-lb-gprd
Cf-Cache-Status: MISS
Ratelimit-Reset: 1701333950
X-Gitlab-Meta: {"correlation_id":"10d576ab8e82b745b7202a6daec3c5e1","version":"1"}
{"id":43524600,"iid":2,"project_id":15578026,"title":"Test branch","description":"do not merge this PR","state":"opened","created_at":"2019-11-28T15:56:54.104Z","updated_at":"2020-04-19T19:24:21.108Z","merged_by":null,"merge_user":null,"merged_at":null,"closed_by":null,"closed_at":null,"target_branch":"master","source_branch":"feat/test","user_notes_count":0,"upvotes":1,"downvotes":0,"author":{"id":1241334,"username":"lafriks","name":"Lauris BH","state":"active","locked":false,"avatar_url":"https://gitlab.com/uploads/-/system/user/avatar/1241334/avatar.png","web_url":"https://gitlab.com/lafriks"},"assignees":[],"assignee":null,"reviewers":[],"source_project_id":15578026,"target_project_id":15578026,"labels":["bug"],"draft":false,"work_in_progress":false,"milestone":{"id":1082926,"iid":1,"project_id":15578026,"title":"1.0.0","description":"","state":"closed","created_at":"2019-11-28T08:42:30.301Z","updated_at":"2019-11-28T15:57:52.401Z","due_date":null,"start_date":null,"expired":false,"web_url":"https://gitlab.com/gitea/test_repo/-/milestones/1"},"merge_when_pipeline_succeeds":false,"merge_status":"can_be_merged","detailed_merge_status":"mergeable","sha":"9f733b96b98a4175276edf6a2e1231489c3bdd23","merge_commit_sha":null,"squash_commit_sha":null,"discussion_locked":null,"should_remove_source_branch":null,"force_remove_source_branch":true,"prepared_at":"2019-11-28T15:56:54.104Z","reference":"!2","references":{"short":"!2","relative":"!2","full":"gitea/test_repo!2"},"web_url":"https://gitlab.com/gitea/test_repo/-/merge_requests/2","time_stats":{"time_estimate":0,"total_time_spent":0,"human_time_estimate":null,"human_total_time_spent":null},"squash":true,"squash_on_merge":true,"task_completion_status":{"count":0,"completed_count":0},"has_conflicts":false,"blocking_discussions_resolved":true,"approvals_before_merge":null,"subscribed":false,"changes_count":"1","latest_build_started_at":null,"latest_build_finished_at":null,"first_deployed_to_production_at":null,"pipeline":null,"head_pipeline":null,"diff_refs":{"base_sha":"c59c9b451acca9d106cc19d61d87afe3fbbb8b83","head_sha":"9f733b96b98a4175276edf6a2e1231489c3bdd23","start_sha":"c59c9b451acca9d106cc19d61d87afe3fbbb8b83"},"merge_error":null,"first_contribution":false,"user":{"can_merge":false}}

View file

@ -0,0 +1,22 @@
Vary: Origin, Accept-Encoding
Ratelimit-Remaining: 1978
Gitlab-Lb: haproxy-main-04-lb-gprd
Set-Cookie: _cfuvid=cKUtcpJODQwk9SDRn91k1DY8CY5Tg238DXGgT0a2go0-1701333891493-0-604800000; path=/; domain=.gitlab.com; HttpOnly; Secure; SameSite=None
Content-Type: application/json
Cf-Cache-Status: MISS
Etag: W/"ce2f774c1b05c5c8a14ec9274444cba3"
Ratelimit-Limit: 2000
X-Content-Type-Options: nosniff
Cache-Control: max-age=0, private, must-revalidate
X-Gitlab-Meta: {"correlation_id":"98ee1da556bdb3d764679ebab9ab5312","version":"1"}
Referrer-Policy: strict-origin-when-cross-origin
X-Frame-Options: SAMEORIGIN
Gitlab-Sv: localhost
Content-Security-Policy: default-src 'none'
X-Runtime: 0.165471
Strict-Transport-Security: max-age=31536000
Ratelimit-Observed: 22
Ratelimit-Reset: 1701333951
Ratelimit-Resettime: Thu, 30 Nov 2023 08:45:51 GMT
{"id":43524600,"iid":2,"project_id":15578026,"title":"Test branch","description":"do not merge this PR","state":"opened","created_at":"2019-11-28T15:56:54.104Z","updated_at":"2020-04-19T19:24:21.108Z","merge_status":"can_be_merged","approved":true,"approvals_required":0,"approvals_left":0,"require_password_to_approve":false,"approved_by":[{"user":{"id":4575606,"username":"real6543","name":"6543","state":"active","locked":false,"avatar_url":"https://gitlab.com/uploads/-/system/user/avatar/4575606/avatar.png","web_url":"https://gitlab.com/real6543"}}],"suggested_approvers":[],"approvers":[],"approver_groups":[{"group":{"id":3181312,"web_url":"https://gitlab.com/groups/gitea","name":"gitea","path":"gitea","description":"Mirror of Gitea source code repositories","visibility":"public","share_with_group_lock":false,"require_two_factor_authentication":false,"two_factor_grace_period":48,"project_creation_level":"maintainer","auto_devops_enabled":null,"subgroup_creation_level":"owner","emails_disabled":false,"emails_enabled":true,"mentions_disabled":null,"lfs_enabled":true,"default_branch_protection":2,"default_branch_protection_defaults":{"allowed_to_push":[{"access_level":30}],"allow_force_push":true,"allowed_to_merge":[{"access_level":30}]},"avatar_url":"https://gitlab.com/uploads/-/system/group/avatar/3181312/gitea.png","request_access_enabled":true,"full_name":"gitea","full_path":"gitea","created_at":"2018-07-04T16:32:10.176Z","parent_id":null,"shared_runners_setting":"enabled","ldap_cn":null,"ldap_access":null,"wiki_access_level":"enabled"}}],"user_has_approved":false,"user_can_approve":false,"approval_rules_left":[],"has_approval_rules":true,"merge_request_approvers_available":false,"multiple_approval_rules_available":false,"invalid_approvers_rules":[]}

View file

@ -0,0 +1,29 @@
X-Prev-Page:
X-Runtime: 0.066211
Strict-Transport-Security: max-age=31536000
Content-Security-Policy: default-src 'none'
X-Per-Page: 1
X-Total: 2
Ratelimit-Reset: 1701333950
Ratelimit-Limit: 2000
Cf-Cache-Status: MISS
X-Content-Type-Options: nosniff
X-Gitlab-Meta: {"correlation_id":"d0f8c843558e938161d7307b686f1cd9","version":"1"}
Gitlab-Sv: localhost
Set-Cookie: _cfuvid=WFMplweUX3zWl6uoteYRHeDcpElbTNYhWrIBbNEVC3A-1701333890399-0-604800000; path=/; domain=.gitlab.com; HttpOnly; Secure; SameSite=None
Content-Type: application/json
X-Total-Pages: 2
Etag: W/"798718b23a2ec66b16cce20cb7155116"
X-Next-Page: 2
Link: <https://gitlab.com/api/v4/projects/15578026/merge_requests/2/award_emoji?id=15578026&merge_request_iid=2&page=2&per_page=1>; rel="next", <https://gitlab.com/api/v4/projects/15578026/merge_requests/2/award_emoji?id=15578026&merge_request_iid=2&page=1&per_page=1>; rel="first", <https://gitlab.com/api/v4/projects/15578026/merge_requests/2/award_emoji?id=15578026&merge_request_iid=2&page=2&per_page=1>; rel="last"
Vary: Origin, Accept-Encoding
Ratelimit-Observed: 18
Ratelimit-Resettime: Thu, 30 Nov 2023 08:45:50 GMT
X-Frame-Options: SAMEORIGIN
Referrer-Policy: strict-origin-when-cross-origin
Ratelimit-Remaining: 1982
Gitlab-Lb: haproxy-main-31-lb-gprd
Cache-Control: max-age=0, private, must-revalidate
X-Page: 1
[{"id":5541414,"name":"thumbsup","user":{"id":4575606,"username":"real6543","name":"6543","state":"active","locked":false,"avatar_url":"https://gitlab.com/uploads/-/system/user/avatar/4575606/avatar.png","web_url":"https://gitlab.com/real6543"},"created_at":"2020-09-02T23:42:34.310Z","updated_at":"2020-09-02T23:42:34.310Z","awardable_id":43524600,"awardable_type":"MergeRequest","url":null}]

View file

@ -0,0 +1,29 @@
Cf-Cache-Status: MISS
Etag: W/"e6776aaa57e6a81bf8a2d8823272cc70"
X-Frame-Options: SAMEORIGIN
X-Gitlab-Meta: {"correlation_id":"db01aae11b0cf7febcf051706159faae","version":"1"}
X-Total-Pages: 2
Strict-Transport-Security: max-age=31536000
Gitlab-Lb: haproxy-main-51-lb-gprd
X-Total: 2
Referrer-Policy: strict-origin-when-cross-origin
Ratelimit-Limit: 2000
Cache-Control: max-age=0, private, must-revalidate
X-Next-Page:
Set-Cookie: _cfuvid=X0n26HdiufQTXsshb4pvCyuf0jDSstPZ8GnIiyx57YU-1701333890628-0-604800000; path=/; domain=.gitlab.com; HttpOnly; Secure; SameSite=None
Link: <https://gitlab.com/api/v4/projects/15578026/merge_requests/2/award_emoji?id=15578026&merge_request_iid=2&page=1&per_page=1>; rel="prev", <https://gitlab.com/api/v4/projects/15578026/merge_requests/2/award_emoji?id=15578026&merge_request_iid=2&page=1&per_page=1>; rel="first", <https://gitlab.com/api/v4/projects/15578026/merge_requests/2/award_emoji?id=15578026&merge_request_iid=2&page=2&per_page=1>; rel="last"
Ratelimit-Observed: 19
Ratelimit-Reset: 1701333950
Content-Type: application/json
Vary: Origin, Accept-Encoding
X-Per-Page: 1
X-Runtime: 0.067511
Gitlab-Sv: localhost
X-Prev-Page: 1
Ratelimit-Remaining: 1981
Ratelimit-Resettime: Thu, 30 Nov 2023 08:45:50 GMT
Content-Security-Policy: default-src 'none'
X-Content-Type-Options: nosniff
X-Page: 2
[{"id":5541415,"name":"tada","user":{"id":4575606,"username":"real6543","name":"6543","state":"active","locked":false,"avatar_url":"https://gitlab.com/uploads/-/system/user/avatar/4575606/avatar.png","web_url":"https://gitlab.com/real6543"},"created_at":"2020-09-02T23:42:59.060Z","updated_at":"2020-09-02T23:42:59.060Z","awardable_id":43524600,"awardable_type":"MergeRequest","url":null}]

View file

@ -0,0 +1,31 @@
X-Page: 3
Strict-Transport-Security: max-age=31536000
Accept-Ranges: bytes
Set-Cookie: _cfuvid=CBSpRhUuajZbJ9Mc_r7SkVmZawoSi5ofuts2TGyHgRk-1701333890842-0-604800000; path=/; domain=.gitlab.com; HttpOnly; Secure; SameSite=None
Cache-Control: max-age=0, private, must-revalidate
X-Prev-Page:
X-Total-Pages: 2
Ratelimit-Reset: 1701333950
Gitlab-Sv: localhost
X-Content-Type-Options: nosniff
Ratelimit-Resettime: Thu, 30 Nov 2023 08:45:50 GMT
Gitlab-Lb: haproxy-main-05-lb-gprd
Etag: W/"4f53cda18c2baa0c0354bb5f9a3ecbe5"
Vary: Origin, Accept-Encoding
X-Frame-Options: SAMEORIGIN
X-Gitlab-Meta: {"correlation_id":"36817edd7cae21d5d6b875faf173ce80","version":"1"}
X-Per-Page: 1
X-Total: 2
Referrer-Policy: strict-origin-when-cross-origin
Content-Security-Policy: default-src 'none'
Link: <https://gitlab.com/api/v4/projects/15578026/merge_requests/2/award_emoji?id=15578026&merge_request_iid=2&page=1&per_page=1>; rel="first", <https://gitlab.com/api/v4/projects/15578026/merge_requests/2/award_emoji?id=15578026&merge_request_iid=2&page=2&per_page=1>; rel="last"
X-Next-Page:
X-Runtime: 0.061075
Ratelimit-Limit: 2000
Ratelimit-Observed: 20
Cf-Cache-Status: MISS
Content-Type: application/json
Content-Length: 2
Ratelimit-Remaining: 1980
[]

View file

@ -0,0 +1,29 @@
X-Content-Type-Options: nosniff
X-Next-Page:
Ratelimit-Observed: 5
X-Frame-Options: SAMEORIGIN
X-Per-Page: 100
Ratelimit-Limit: 2000
Cf-Cache-Status: MISS
Strict-Transport-Security: max-age=31536000
Referrer-Policy: strict-origin-when-cross-origin
Ratelimit-Resettime: Thu, 30 Nov 2023 08:45:46 GMT
Content-Security-Policy: default-src 'none'
Link: <https://gitlab.com/api/v4/projects/15578026/milestones?id=15578026&include_parent_milestones=false&page=1&per_page=100&state=all>; rel="first", <https://gitlab.com/api/v4/projects/15578026/milestones?id=15578026&include_parent_milestones=false&page=1&per_page=100&state=all>; rel="last"
X-Gitlab-Meta: {"correlation_id":"4f72373995f8681ce127c62d384745a3","version":"1"}
Gitlab-Sv: localhost
X-Runtime: 0.073691
Ratelimit-Remaining: 1995
Ratelimit-Reset: 1701333946
Content-Type: application/json
Etag: W/"c8e2d3a5f05ee29c58b665c86684f9f9"
X-Page: 1
Gitlab-Lb: haproxy-main-47-lb-gprd
Vary: Origin, Accept-Encoding
X-Prev-Page:
X-Total: 2
Cache-Control: max-age=0, private, must-revalidate
X-Total-Pages: 1
Set-Cookie: _cfuvid=ZfjvK5rh2nTUjEDt1Guwzd8zrl6uCDplfE8NBPbdJ7c-1701333886832-0-604800000; path=/; domain=.gitlab.com; HttpOnly; Secure; SameSite=None
[{"id":1082927,"iid":2,"project_id":15578026,"title":"1.1.0","description":"","state":"active","created_at":"2019-11-28T08:42:44.575Z","updated_at":"2019-11-28T08:42:44.575Z","due_date":null,"start_date":null,"expired":false,"web_url":"https://gitlab.com/gitea/test_repo/-/milestones/2"},{"id":1082926,"iid":1,"project_id":15578026,"title":"1.0.0","description":"","state":"closed","created_at":"2019-11-28T08:42:30.301Z","updated_at":"2019-11-28T15:57:52.401Z","due_date":null,"start_date":null,"expired":false,"web_url":"https://gitlab.com/gitea/test_repo/-/milestones/1"}]

View file

@ -0,0 +1,29 @@
Strict-Transport-Security: max-age=31536000
Referrer-Policy: strict-origin-when-cross-origin
Ratelimit-Reset: 1701333947
X-Total: 1
Ratelimit-Resettime: Thu, 30 Nov 2023 08:45:47 GMT
Ratelimit-Limit: 2000
X-Runtime: 0.178411
Gitlab-Lb: haproxy-main-15-lb-gprd
Vary: Origin, Accept-Encoding
X-Page: 1
X-Frame-Options: SAMEORIGIN
Ratelimit-Observed: 7
Cf-Cache-Status: MISS
Etag: W/"dccc7159dc4b46989d13128a7d6ee859"
X-Content-Type-Options: nosniff
Ratelimit-Remaining: 1993
X-Gitlab-Meta: {"correlation_id":"0044a3de3ede2f913cabe6e464dd73c2","version":"1"}
X-Total-Pages: 1
X-Next-Page:
X-Per-Page: 100
Content-Type: application/json
Content-Security-Policy: default-src 'none'
Set-Cookie: _cfuvid=h1ayMNs6W_kFPoFe28IpiaFUz1ZAPvY6npUWxARRx4I-1701333887452-0-604800000; path=/; domain=.gitlab.com; HttpOnly; Secure; SameSite=None
Link: <https://gitlab.com/api/v4/projects/15578026/releases?id=15578026&order_by=released_at&page=1&per_page=100&sort=desc>; rel="first", <https://gitlab.com/api/v4/projects/15578026/releases?id=15578026&order_by=released_at&page=1&per_page=100&sort=desc>; rel="last"
Gitlab-Sv: localhost
Cache-Control: max-age=0, private, must-revalidate
X-Prev-Page:
[{"name":"First Release","tag_name":"v0.9.99","description":"A test release","created_at":"2019-11-28T09:09:48.840Z","released_at":"2019-11-28T09:09:48.836Z","upcoming_release":false,"author":{"id":1241334,"username":"lafriks","name":"Lauris BH","state":"active","locked":false,"avatar_url":"https://gitlab.com/uploads/-/system/user/avatar/1241334/avatar.png","web_url":"https://gitlab.com/lafriks"},"commit":{"id":"0720a3ec57c1f843568298117b874319e7deee75","short_id":"0720a3ec","created_at":"2019-11-28T08:49:16.000+00:00","parent_ids":["93ea21ce45d35690c35e80961d239645139e872c"],"title":"Add new file","message":"Add new file","author_name":"Lauris BH","author_email":"lauris@nix.lv","authored_date":"2019-11-28T08:49:16.000+00:00","committer_name":"Lauris BH","committer_email":"lauris@nix.lv","committed_date":"2019-11-28T08:49:16.000+00:00","trailers":{},"extended_trailers":{},"web_url":"https://gitlab.com/gitea/test_repo/-/commit/0720a3ec57c1f843568298117b874319e7deee75"},"commit_path":"/gitea/test_repo/-/commit/0720a3ec57c1f843568298117b874319e7deee75","tag_path":"/gitea/test_repo/-/tags/v0.9.99","assets":{"count":4,"sources":[{"format":"zip","url":"https://gitlab.com/gitea/test_repo/-/archive/v0.9.99/test_repo-v0.9.99.zip"},{"format":"tar.gz","url":"https://gitlab.com/gitea/test_repo/-/archive/v0.9.99/test_repo-v0.9.99.tar.gz"},{"format":"tar.bz2","url":"https://gitlab.com/gitea/test_repo/-/archive/v0.9.99/test_repo-v0.9.99.tar.bz2"},{"format":"tar","url":"https://gitlab.com/gitea/test_repo/-/archive/v0.9.99/test_repo-v0.9.99.tar"}],"links":[]},"evidences":[{"sha":"89f1223473ee01f192a83d0cb89f4d1eac1de74f01ad","filepath":"https://gitlab.com/gitea/test_repo/-/releases/v0.9.99/evidences/52147.json","collected_at":"2019-11-28T09:09:48.888Z"}],"_links":{"closed_issues_url":"https://gitlab.com/gitea/test_repo/-/issues?release_tag=v0.9.99\u0026scope=all\u0026state=closed","closed_merge_requests_url":"https://gitlab.com/gitea/test_repo/-/merge_requests?release_tag=v0.9.99\u0026scope=all\u0026state=closed","merged_merge_requests_url":"https://gitlab.com/gitea/test_repo/-/merge_requests?release_tag=v0.9.99\u0026scope=all\u0026state=merged","opened_issues_url":"https://gitlab.com/gitea/test_repo/-/issues?release_tag=v0.9.99\u0026scope=all\u0026state=opened","opened_merge_requests_url":"https://gitlab.com/gitea/test_repo/-/merge_requests?release_tag=v0.9.99\u0026scope=all\u0026state=opened","self":"https://gitlab.com/gitea/test_repo/-/releases/v0.9.99"}}]

View file

@ -0,0 +1,22 @@
Content-Security-Policy: default-src 'none'
Vary: Origin, Accept-Encoding
Referrer-Policy: strict-origin-when-cross-origin
Gitlab-Sv: localhost
X-Content-Type-Options: nosniff
Ratelimit-Reset: 1701333946
Ratelimit-Resettime: Thu, 30 Nov 2023 08:45:46 GMT
X-Runtime: 0.144599
Content-Type: application/json
X-Gitlab-Meta: {"correlation_id":"919887b868b11ed34c5917e98b4be40d","version":"1"}
Ratelimit-Observed: 2
Gitlab-Lb: haproxy-main-42-lb-gprd
Strict-Transport-Security: max-age=31536000
Cf-Cache-Status: MISS
Ratelimit-Limit: 2000
Set-Cookie: _cfuvid=BENIrMVlxs_tt.JplEkDKbUrMpOF_kjRRLJOifNTLqY-1701333886061-0-604800000; path=/; domain=.gitlab.com; HttpOnly; Secure; SameSite=None
Etag: W/"3cacfe29f44a69e84a577337eac55d89"
X-Frame-Options: SAMEORIGIN
Cache-Control: max-age=0, private, must-revalidate
Ratelimit-Remaining: 1998
{"id":15578026,"description":"Test repository for testing migration from gitlab to gitea","name":"test_repo","name_with_namespace":"gitea / test_repo","path":"test_repo","path_with_namespace":"gitea/test_repo","created_at":"2019-11-28T08:20:33.019Z","default_branch":"master","tag_list":["migration","test"],"topics":["migration","test"],"ssh_url_to_repo":"git@gitlab.com:gitea/test_repo.git","http_url_to_repo":"https://gitlab.com/gitea/test_repo.git","web_url":"https://gitlab.com/gitea/test_repo","readme_url":"https://gitlab.com/gitea/test_repo/-/blob/master/README.md","forks_count":1,"avatar_url":null,"star_count":0,"last_activity_at":"2020-04-19T19:46:04.527Z","namespace":{"id":3181312,"name":"gitea","path":"gitea","kind":"group","full_path":"gitea","parent_id":null,"avatar_url":"/uploads/-/system/group/avatar/3181312/gitea.png","web_url":"https://gitlab.com/groups/gitea"},"container_registry_image_prefix":"registry.gitlab.com/gitea/test_repo","_links":{"self":"https://gitlab.com/api/v4/projects/15578026","issues":"https://gitlab.com/api/v4/projects/15578026/issues","merge_requests":"https://gitlab.com/api/v4/projects/15578026/merge_requests","repo_branches":"https://gitlab.com/api/v4/projects/15578026/repository/branches","labels":"https://gitlab.com/api/v4/projects/15578026/labels","events":"https://gitlab.com/api/v4/projects/15578026/events","members":"https://gitlab.com/api/v4/projects/15578026/members","cluster_agents":"https://gitlab.com/api/v4/projects/15578026/cluster_agents"},"packages_enabled":true,"empty_repo":false,"archived":false,"visibility":"public","resolve_outdated_diff_discussions":false,"issues_enabled":true,"merge_requests_enabled":true,"wiki_enabled":true,"jobs_enabled":true,"snippets_enabled":true,"container_registry_enabled":true,"service_desk_enabled":true,"can_create_merge_request_in":true,"issues_access_level":"enabled","repository_access_level":"enabled","merge_requests_access_level":"enabled","forking_access_level":"enabled","wiki_access_level":"enabled","builds_access_level":"enabled","snippets_access_level":"enabled","pages_access_level":"enabled","analytics_access_level":"enabled","container_registry_access_level":"enabled","security_and_compliance_access_level":"private","releases_access_level":"enabled","environments_access_level":"enabled","feature_flags_access_level":"enabled","infrastructure_access_level":"enabled","monitor_access_level":"enabled","model_experiments_access_level":"enabled","emails_disabled":false,"emails_enabled":true,"shared_runners_enabled":true,"lfs_enabled":true,"creator_id":1241334,"import_status":"none","open_issues_count":0,"description_html":"\u003cp data-sourcepos=\"1:1-1:58\" dir=\"auto\"\u003eTest repository for testing migration from gitlab to gitea\u003c/p\u003e","updated_at":"2022-08-26T19:41:46.691Z","ci_config_path":null,"public_jobs":true,"shared_with_groups":[],"only_allow_merge_if_pipeline_succeeds":false,"allow_merge_on_skipped_pipeline":null,"request_access_enabled":true,"only_allow_merge_if_all_discussions_are_resolved":false,"remove_source_branch_after_merge":true,"printing_merge_request_link_enabled":true,"merge_method":"ff","squash_option":"default_off","enforce_auth_checks_on_uploads":true,"suggestion_commit_message":null,"merge_commit_template":null,"squash_commit_template":null,"issue_branch_template":null,"autoclose_referenced_issues":true,"external_authorization_classification_label":"","requirements_enabled":false,"requirements_access_level":"enabled","security_and_compliance_enabled":false,"compliance_frameworks":[],"permissions":{"project_access":null,"group_access":null}}

View file

@ -0,0 +1,22 @@
Etag: W/"4e5c0a031c3aacb6ba0a3c19e67d7592"
Ratelimit-Observed: 1
Content-Type: application/json
Ratelimit-Remaining: 1999
Set-Cookie: _cfuvid=qtYbzv8YeGg4q7XaV0aAE2.YqWIp_xrYPGilXrlecsk-1701333885742-0-604800000; path=/; domain=.gitlab.com; HttpOnly; Secure; SameSite=None
X-Gitlab-Meta: {"correlation_id":"c7c75f0406e1b1b9705a6d7e9bdb06a5","version":"1"}
X-Runtime: 0.039264
Cf-Cache-Status: MISS
Content-Security-Policy: default-src 'none'
Ratelimit-Reset: 1701333945
Gitlab-Sv: localhost
Strict-Transport-Security: max-age=31536000
Vary: Origin, Accept-Encoding
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
Ratelimit-Resettime: Thu, 30 Nov 2023 08:45:45 GMT
Gitlab-Lb: haproxy-main-23-lb-gprd
Cache-Control: max-age=0, private, must-revalidate
X-Frame-Options: SAMEORIGIN
Ratelimit-Limit: 2000
{"version":"16.7.0-pre","revision":"acd848a9228","kas":{"enabled":true,"externalUrl":"wss://kas.gitlab.com","version":"v16.7.0-rc2"},"enterprise":true}

View file

@ -0,0 +1,22 @@
X-Runtime: 0.088022
Strict-Transport-Security: max-age=31536000
Ratelimit-Observed: 3
Cache-Control: max-age=0, private, must-revalidate
Etag: W/"03ce4f6ce1c1e8c5a31df8a44cf2fbdd"
Gitlab-Lb: haproxy-main-11-lb-gprd
Content-Security-Policy: default-src 'none'
Ratelimit-Limit: 2000
X-Gitlab-Meta: {"correlation_id":"b57b226f741f9140a1fea54f65cb5cfd","version":"1"}
Referrer-Policy: strict-origin-when-cross-origin
Ratelimit-Remaining: 1997
Ratelimit-Resettime: Thu, 30 Nov 2023 08:24:53 GMT
Set-Cookie: _cfuvid=V0ToiOTUW0XbtWq7BirwVNfL1_YP1POMrLBnDSEWS0M-1701332633965-0-604800000; path=/; domain=.gitlab.com; HttpOnly; Secure; SameSite=None
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
Gitlab-Sv: localhost
Content-Type: application/json
Vary: Origin, Accept-Encoding
Ratelimit-Reset: 1701332693
Cf-Cache-Status: MISS
{"id":6590996,"description":"Arch packaging and build files","name":"archbuild","name_with_namespace":"Troy Engel / archbuild","path":"archbuild","path_with_namespace":"troyengel/archbuild","created_at":"2018-06-03T22:53:17.388Z","default_branch":"master","tag_list":[],"topics":[],"ssh_url_to_repo":"git@gitlab.com:troyengel/archbuild.git","http_url_to_repo":"https://gitlab.com/troyengel/archbuild.git","web_url":"https://gitlab.com/troyengel/archbuild","readme_url":"https://gitlab.com/troyengel/archbuild/-/blob/master/README.md","forks_count":0,"avatar_url":null,"star_count":0,"last_activity_at":"2020-12-13T18:09:32.071Z","namespace":{"id":1452515,"name":"Troy Engel","path":"troyengel","kind":"user","full_path":"troyengel","parent_id":null,"avatar_url":"https://secure.gravatar.com/avatar/b226c267929f1bcfcc446e75a025591c?s=80\u0026d=identicon","web_url":"https://gitlab.com/troyengel"},"container_registry_image_prefix":"registry.gitlab.com/troyengel/archbuild","_links":{"self":"https://gitlab.com/api/v4/projects/6590996","issues":"https://gitlab.com/api/v4/projects/6590996/issues","merge_requests":"https://gitlab.com/api/v4/projects/6590996/merge_requests","repo_branches":"https://gitlab.com/api/v4/projects/6590996/repository/branches","labels":"https://gitlab.com/api/v4/projects/6590996/labels","events":"https://gitlab.com/api/v4/projects/6590996/events","members":"https://gitlab.com/api/v4/projects/6590996/members","cluster_agents":"https://gitlab.com/api/v4/projects/6590996/cluster_agents"},"packages_enabled":null,"empty_repo":false,"archived":true,"visibility":"public","owner":{"id":1215848,"username":"troyengel","name":"Troy Engel","state":"active","locked":false,"avatar_url":"https://secure.gravatar.com/avatar/b226c267929f1bcfcc446e75a025591c?s=80\u0026d=identicon","web_url":"https://gitlab.com/troyengel"},"resolve_outdated_diff_discussions":false,"issues_enabled":true,"merge_requests_enabled":true,"wiki_enabled":true,"jobs_enabled":true,"snippets_enabled":true,"container_registry_enabled":true,"service_desk_enabled":true,"can_create_merge_request_in":false,"issues_access_level":"enabled","repository_access_level":"enabled","merge_requests_access_level":"enabled","forking_access_level":"enabled","wiki_access_level":"enabled","builds_access_level":"enabled","snippets_access_level":"enabled","pages_access_level":"enabled","analytics_access_level":"enabled","container_registry_access_level":"enabled","security_and_compliance_access_level":"private","releases_access_level":"enabled","environments_access_level":"enabled","feature_flags_access_level":"enabled","infrastructure_access_level":"enabled","monitor_access_level":"enabled","model_experiments_access_level":"enabled","emails_disabled":false,"emails_enabled":true,"shared_runners_enabled":true,"lfs_enabled":false,"creator_id":1215848,"import_status":"finished","open_issues_count":0,"description_html":"\u003cp data-sourcepos=\"1:1-1:30\" dir=\"auto\"\u003eArch packaging and build files\u003c/p\u003e","updated_at":"2022-07-13T21:32:12.624Z","ci_config_path":null,"public_jobs":true,"shared_with_groups":[],"only_allow_merge_if_pipeline_succeeds":false,"allow_merge_on_skipped_pipeline":null,"request_access_enabled":false,"only_allow_merge_if_all_discussions_are_resolved":false,"remove_source_branch_after_merge":null,"printing_merge_request_link_enabled":true,"merge_method":"merge","squash_option":"default_off","enforce_auth_checks_on_uploads":true,"suggestion_commit_message":null,"merge_commit_template":null,"squash_commit_template":null,"issue_branch_template":null,"autoclose_referenced_issues":true,"external_authorization_classification_label":"","requirements_enabled":false,"requirements_access_level":"enabled","security_and_compliance_enabled":false,"compliance_frameworks":[],"permissions":{"project_access":null,"group_access":null}}

Some files were not shown because too many files have changed in this diff Show more