Compare commits

...

29 Commits

Author SHA1 Message Date
copilot-swe-agent[bot]
074714d61f Fix lint and MySQL test failures: remove unused function and fix UUID column type
Co-authored-by: unknwon <2946214+unknwon@users.noreply.github.com>
2026-01-24 16:53:49 +00:00
copilot-swe-agent[bot]
68f049ad15 Replace XORM with GORM while maintaining functionality
Co-authored-by: unknwon <2946214+unknwon@users.noreply.github.com>
2026-01-24 16:20:20 +00:00
copilot-swe-agent[bot]
ec5eede78f Fix code review issues: FindInBatches usage and memory optimization
Co-authored-by: unknwon <2946214+unknwon@users.noreply.github.com>
2026-01-24 16:17:07 +00:00
copilot-swe-agent[bot]
06b85ac5ba Convert remaining files from XORM to GORM and fix unused imports
Co-authored-by: unknwon <2946214+unknwon@users.noreply.github.com>
2026-01-24 16:11:35 +00:00
copilot-swe-agent[bot]
c74b48bd2d Convert repo_branch.go and repo_collaboration.go from XORM to GORM
Co-authored-by: unknwon <2946214+unknwon@users.noreply.github.com>
2026-01-24 16:10:14 +00:00
copilot-swe-agent[bot]
e89db8393d Fix transaction scope issues in repo.go UpdateRepository and DeleteRepository
Co-authored-by: unknwon <2946214+unknwon@users.noreply.github.com>
2026-01-24 16:03:10 +00:00
copilot-swe-agent[bot]
653c95c842 Complete XORM to GORM migration for issue.go, pull.go, and repo.go
Co-authored-by: unknwon <2946214+unknwon@users.noreply.github.com>
2026-01-24 16:01:10 +00:00
deepsource-autofix[bot]
dfc768b968 style: format code with Go fmt and Gofumpt
This commit fixes the style issues introduced in 63221df according to the output
from Go fmt and Gofumpt.

Details: https://github.com/gogs/gogs/pull/8105
2026-01-24 16:00:41 +00:00
copilot-swe-agent[bot]
63221dfec3 Complete XORM to GORM migration for internal/database/repo.go
- Replace import "xorm.io/xorm" with "gorm.io/gorm"
- Replace all Engine parameters with *gorm.DB
- Replace *xorm.Session parameters with *gorm.DB
- Convert all database queries from XORM to GORM syntax:
  - e.ID(id).Get(&model) → db.First(&model, id).Error
  - e.Insert(&model) → db.Create(&model).Error
  - e.Update(&model) → db.Updates(&model).Error
  - e.In("col", values) → db.Where("col IN ?", values)
  - e.Find(&results) → db.Find(&results).Error
  - e.Iterate() → db.FindInBatches()
- Convert transaction patterns from NewSession/Begin/Commit to db.Transaction()
- Replace global x variable with db
- Convert XORM AfterSet callback to GORM AfterFind hook
- Handle gorm.ErrRecordNotFound for missing records
- Fix related files: repositories.go, repo_collaboration.go, two_factor.go

Co-authored-by: unknwon <2946214+unknwon@users.noreply.github.com>
2026-01-24 16:00:27 +00:00
deepsource-autofix[bot]
d2ff1fa978 style: format code with Go fmt and Gofumpt
This commit fixes the style issues introduced in 748ecbf according to the output
from Go fmt and Gofumpt.

Details: https://github.com/gogs/gogs/pull/8105
2026-01-24 15:48:04 +00:00
copilot-swe-agent[bot]
748ecbfdf3 Complete XORM to GORM migration for internal/database/pull.go
Co-authored-by: unknwon <2946214+unknwon@users.noreply.github.com>
2026-01-24 15:47:50 +00:00
copilot-swe-agent[bot]
04d063c65d Complete XORM to GORM migration for issue.go and pull.go
Co-authored-by: unknwon <2946214+unknwon@users.noreply.github.com>
2026-01-24 15:43:22 +00:00
copilot-swe-agent[bot]
eebe4065c6 Migrate internal/database/backup.go from XORM to GORM
Co-authored-by: unknwon <2946214+unknwon@users.noreply.github.com>
2026-01-24 01:21:38 +00:00
copilot-swe-agent[bot]
de9f03d533 Migrate webhook.go, org_team.go, ssh_key.go from XORM to GORM
Co-authored-by: unknwon <2946214+unknwon@users.noreply.github.com>
2026-01-24 01:17:45 +00:00
copilot-swe-agent[bot]
8d43893af2 Migrate internal/database/ssh_key.go from XORM to GORM
Co-authored-by: unknwon <2946214+unknwon@users.noreply.github.com>
2026-01-24 01:15:23 +00:00
copilot-swe-agent[bot]
233f0fb18d Migrate internal/database/org_team.go from XORM to GORM
Co-authored-by: unknwon <2946214+unknwon@users.noreply.github.com>
2026-01-24 01:07:23 +00:00
copilot-swe-agent[bot]
aba381e7f8 Migrate webhook.go from XORM to GORM
Co-authored-by: unknwon <2946214+unknwon@users.noreply.github.com>
2026-01-24 01:00:42 +00:00
deepsource-autofix[bot]
1a285ca5ce style: format code with Go fmt and Gofumpt
This commit fixes the style issues introduced in b74ce2f according to the output
from Go fmt and Gofumpt.

Details: https://github.com/gogs/gogs/pull/8105
2026-01-24 00:52:55 +00:00
copilot-swe-agent[bot]
b74ce2f6bc Migrate org.go from XORM to GORM
Co-authored-by: unknwon <2946214+unknwon@users.noreply.github.com>
2026-01-24 00:52:43 +00:00
deepsource-autofix[bot]
5469f017bc style: format code with Go fmt and Gofumpt
This commit fixes the style issues introduced in cddfd5f according to the output
from Go fmt and Gofumpt.

Details: https://github.com/gogs/gogs/pull/8105
2026-01-24 00:51:36 +00:00
copilot-swe-agent[bot]
cddfd5fbb9 Migrate issue_label.go and comment.go from XORM to GORM
Co-authored-by: unknwon <2946214+unknwon@users.noreply.github.com>
2026-01-24 00:51:00 +00:00
deepsource-autofix[bot]
9bd5123bcf style: format code with Go fmt and Gofumpt
This commit fixes the style issues introduced in 589bf5f according to the output
from Go fmt and Gofumpt.

Details: https://github.com/gogs/gogs/pull/8105
2026-01-24 00:47:06 +00:00
copilot-swe-agent[bot]
589bf5f532 Convert release.go from XORM to GORM
Co-authored-by: unknwon <2946214+unknwon@users.noreply.github.com>
2026-01-24 00:46:54 +00:00
copilot-swe-agent[bot]
9d9175df80 Convert mirror.go from XORM to GORM
Co-authored-by: unknwon <2946214+unknwon@users.noreply.github.com>
2026-01-24 00:45:29 +00:00
deepsource-autofix[bot]
e3e107f95d style: format code with Go fmt and Gofumpt
This commit fixes the style issues introduced in 36833b1 according to the output
from Go fmt and Gofumpt.

Details: https://github.com/gogs/gogs/pull/8105
2026-01-24 00:44:38 +00:00
copilot-swe-agent[bot]
36833b135e Convert milestone.go from XORM to GORM
Co-authored-by: unknwon <2946214+unknwon@users.noreply.github.com>
2026-01-24 00:44:21 +00:00
deepsource-autofix[bot]
d5954c42eb style: format code with Go fmt and Gofumpt
This commit fixes the style issues introduced in f9478a3 according to the output
from Go fmt and Gofumpt.

Details: https://github.com/gogs/gogs/pull/8105
2026-01-24 00:43:07 +00:00
copilot-swe-agent[bot]
f9478a3f76 Initial XORM to GORM migration: models.go and attachment.go converted
Co-authored-by: unknwon <2946214+unknwon@users.noreply.github.com>
2026-01-24 00:42:51 +00:00
copilot-swe-agent[bot]
9bfb9a719b Initial plan 2026-01-24 00:34:38 +00:00
23 changed files with 2325 additions and 2646 deletions

12
go.mod
View File

@@ -57,13 +57,11 @@ require (
gorm.io/gorm v1.25.12
modernc.org/sqlite v1.38.2
unknwon.dev/clog/v2 v2.2.0
xorm.io/builder v0.3.6
xorm.io/core v0.7.2
xorm.io/xorm v0.8.0
)
require (
bitbucket.org/creachadair/shell v0.0.7 // indirect
filippo.io/edwards25519 v1.1.0 // indirect
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect
github.com/aymerick/douceur v0.2.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
@@ -74,7 +72,6 @@ require (
github.com/cockroachdb/redact v1.1.5 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/denisenkom/go-mssqldb v0.12.0 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/djherbis/buffer v1.2.0 // indirect
github.com/djherbis/nio/v3 v3.0.1 // indirect
@@ -86,7 +83,7 @@ require (
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-macaron/inject v0.0.0-20200308113650-138e5925c53b // indirect
github.com/go-redis/redis/v8 v8.11.5 // indirect
github.com/go-sql-driver/mysql v1.7.0 // indirect
github.com/go-sql-driver/mysql v1.8.1 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
github.com/golang-sql/sqlexp v0.1.0 // indirect
@@ -104,13 +101,12 @@ require (
github.com/klauspost/compress v1.18.0 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/lib/pq v1.10.2 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.14 // indirect
github.com/mattn/go-sqlite3 v1.14.24 // indirect
github.com/mattn/go-sqlite3 v1.14.32 // indirect
github.com/mcuadros/go-version v0.0.0-20190830083331-035f6764e8d2 // indirect
github.com/microsoft/go-mssqldb v0.17.0 // indirect
github.com/microsoft/go-mssqldb v1.8.2 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect

141
go.sum
View File

@@ -1,33 +1,35 @@
bitbucket.org/creachadair/shell v0.0.7 h1:Z96pB6DkSb7F3Y3BBnJeOZH2gazyMTWlvecSD4vDqfk=
bitbucket.org/creachadair/shell v0.0.7/go.mod h1:oqtXSSvSYr4624lnnabXHaBsYW6RD80caLi2b3hJk0U=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw=
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
gitea.com/lunny/log v0.0.0-20190322053110-01b5df579c4e/go.mod h1:uJEsN4LQpeGYRCjuPXPZBClU7N5pWzGuyF4uqLpE/e0=
gitea.com/lunny/nodb v0.0.0-20200923032308-3238c4655727/go.mod h1:h0OwsgcpJLSYtHcM5+Xciw9OEeuxi6ty4HDiO8C7aIY=
github.com/Azure/azure-sdk-for-go/sdk/azcore v0.19.0/go.mod h1:h6H6c8enJmmocHUbLiiGY6sx7f9i+X3m1CHdd5c6Rdw=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.0.0/go.mod h1:uGG2W01BaETf0Ozp+QxxKJdMBNRWPdstHG0Fmdwn1/U=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v0.11.0/go.mod h1:HcM1YX14R7CJcghJGOYCgdezslRSVzqwLf/q+4Y2r/0=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1 h1:E+OJmp2tPvt1W+amx48v1eqbjDYsgN+RzP4q16yV5eM=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1/go.mod h1:a6xsAQUZg+VsS3TJ05SRp524Hs4pZ/AeFSr5ENf0Yjo=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.0.0/go.mod h1:+6sju8gk8FRmSajX3Oz4G5Gm7P+mbqE9FVaXXFYTkCM=
github.com/Azure/azure-sdk-for-go/sdk/internal v0.7.0/go.mod h1:yqy467j36fJxcRV2TzfVZ1pCb5vxm4BtZPUdYWe/Xo8=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.6.0 h1:U2rTu3Ef+7w9FHKIAXM6ZyqF3UOWJZ12zIm8zECAFfg=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.6.0/go.mod h1:9kIvujWAA58nmPmWB1m23fyWic1kYZMxD9CxaWn4Qpg=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.8.0 h1:jBQA3cKT4L2rWMpgE7Yt3Hwh2aUj8KXjIGLxjHeYNNo=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.8.0/go.mod h1:4OG6tQ9EOP/MT0NMjDlRzWoVFxfu9rN9B2X+tlSVktg=
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.0.1 h1:MyVTgWR8qd/Jw1Le0NZebGBUCLbtak3bJ3z1OlqZBpw=
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.0.1/go.mod h1:GpPjLhVR9dnUoJMyHWSPy71xY9/lcmpzIPZXmF0FCVY=
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.0.0 h1:D3occbWoio4EBLkbkevetNMAVX197GkzbUMtqjGWn80=
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.0.0/go.mod h1:bTSOgj05NGRuHHhQwAdPnYr9TOdNmKlZTgGLL6nyAdI=
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8=
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
github.com/AzureAD/microsoft-authentication-library-for-go v0.4.0/go.mod h1:Vt9sXTKwMyGcOxSmLDMnGPgqsUg7m8pe215qMLrDXw4=
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 h1:XHOnouVk1mxXfQidrMEnLlPk9UMeRtyBTnEFtxkV0kU=
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa h1:LHTHcTQiSGT7VVbI0o4wBRNQIgn917usHWOd6VAffYI=
github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4=
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8WK8raXaxBx6fRVTlJILwEwQGL1I/ByEI=
@@ -40,7 +42,6 @@ github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cockroachdb/errors v1.12.0 h1:d7oCs6vuIMUQRVbi6jWWWEJZahLCfJpnJSVobd1/sUo=
github.com/cockroachdb/errors v1.12.0/go.mod h1:SvzfYNNBshAVbZ8wzNc/UPK3w1vf0dKDUP41ucAIf7g=
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE=
@@ -60,9 +61,6 @@ github.com/cupcake/rdb v0.0.0-20161107195141-43ba34106c76/go.mod h1:vYwsqCOLxGii
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/denisenkom/go-mssqldb v0.0.0-20190707035753-2be1aa521ff4/go.mod h1:zAg7JM8CkOJ43xKXIj7eRO9kmWm/TW578qo+oDO6tuM=
github.com/denisenkom/go-mssqldb v0.12.0 h1:VtrkII767ttSPNRfFekePK3sctr+joXgO58stqQbtUA=
github.com/denisenkom/go-mssqldb v0.12.0/go.mod h1:iiK0YP1ZeepvmBQk/QpLEhhTNJgfzrpArPY/aFvc9yU=
github.com/derision-test/go-mockgen/v2 v2.1.1 h1:MXG9rzyvsrDBfa1a1GatvHjCrbmEug3hVt0rSOXipCw=
github.com/derision-test/go-mockgen/v2 v2.1.1/go.mod h1:cDK2Y9IF5roTJgugWV23IvlOJsllhDN5zxRDN+g4cZo=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
@@ -76,9 +74,6 @@ github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/
github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
github.com/editorconfig/editorconfig-core-go/v2 v2.6.3 h1:XVUp6qW3BIkmM3/1EkrHpa6bL56APOynfXcZEmIgOhs=
github.com/editorconfig/editorconfig-core-go/v2 v2.6.3/go.mod h1:ThHVc+hqbUsmE1wmK/MASpQEhCleWu1JDJDNhUOMy0c=
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
@@ -96,10 +91,8 @@ github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 h1:BP4M0CvQ
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-ldap/ldap/v3 v3.4.11 h1:4k0Yxweg+a3OyBLjdYn5OKglv18JNvfDykSoI8bW0gU=
github.com/go-ldap/ldap/v3 v3.4.11/go.mod h1:bY7t0FLK8OAVpp/vV6sSlpz3EQDGcQwc8pF0ujLgKvM=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
@@ -128,14 +121,10 @@ github.com/go-macaron/toolbox v0.0.0-20190813233741-94defb8383c6/go.mod h1:YFNJ/
github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc=
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:9wScpmSP5A3Bk8V3XHWUcJmYTh+ZnlHVyc+A4oZYS3Y=
github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:56xuuqnHyryaerycW3BfssRdxQstACi0Epw/yC5E2xM=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/gogs/chardet v0.0.0-20150115103509-2404f7772561 h1:aBzukfDxQlCTVS0NBUjI5YA3iVeaZ9Tb5PxNrrIP1xs=
@@ -151,17 +140,16 @@ github.com/gogs/go-libravatar v0.0.0-20191106065024-33a75213d0a0/go.mod h1:Zas3B
github.com/gogs/minwinsvc v0.0.0-20170301035411-95be6356811a h1:8DZwxETOVWIinYxDK+i6L+rMb7eGATGaakD6ZucfHVk=
github.com/gogs/minwinsvc v0.0.0-20170301035411-95be6356811a/go.mod h1:TUIZ+29jodWQ8Gk6Pvtg4E09aMsc3C/VLZiVYfUhWQU=
github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8=
github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA=
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
github.com/golang-sql/sqlexp v0.0.0-20170517235910-f1bb20e5a188/go.mod h1:vXjM/+wXQnTPR4KqTKDgJukSZ6amVRtWMPEjE6sQoK8=
github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A=
github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@@ -175,8 +163,6 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
@@ -189,8 +175,6 @@ github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+u
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs=
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
@@ -198,18 +182,14 @@ github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c h1:7lF+Vz0LqiRidnzC1Oq86fpX1q/iEv2KJdrCtttYjT4=
github.com/gopherjs/gopherjs v0.0.0-20190430165422-3e4dfb77656c/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=
github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hexops/autogold v1.3.1 h1:YgxF9OHWbEIUjhDbpnLhgVsjUDsiHDTyDfy2lrfdlzo=
github.com/hexops/autogold v1.3.1/go.mod h1:sQO+mQUCVfxOKPht+ipDSkJ2SCJ7BNJVHZexsXqWMx4=
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
@@ -255,17 +235,13 @@ github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
@@ -275,10 +251,7 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8=
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lunny/log v0.0.0-20160921050905-7887c61bf0de/go.mod h1:3q8WtuPQsoRbatJuy3nvq/hRSvuBJrHHr+ybPPiNvHQ=
github.com/lunny/nodb v0.0.0-20160621015157-fc1ef06ad4af/go.mod h1:Cqz6pqow14VObJ7peltM+2n3PWOz7yTrfUuGbVFkzN0=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
@@ -298,16 +271,16 @@ github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh
github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM=
github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mattn/go-sqlite3 v1.14.32 h1:JD12Ag3oLy1zQA+BNn74xRgaBbdhbNIDYvQUEuuErjs=
github.com/mattn/go-sqlite3 v1.14.32/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/mcuadros/go-version v0.0.0-20190308113854-92cdf37c5b75/go.mod h1:76rfSfYPWj01Z85hUf/ituArm797mNKcvINh1OlsZKo=
github.com/mcuadros/go-version v0.0.0-20190830083331-035f6764e8d2 h1:YocNLcTBdEdvY3iDK6jfWXvEaM5OCKkjxPKoJRdB3Gg=
github.com/mcuadros/go-version v0.0.0-20190830083331-035f6764e8d2/go.mod h1:76rfSfYPWj01Z85hUf/ituArm797mNKcvINh1OlsZKo=
github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=
github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=
github.com/microsoft/go-mssqldb v0.17.0 h1:Fto83dMZPnYv1Zwx5vHHxpNraeEaUlQ/hhHLgZiaenE=
github.com/microsoft/go-mssqldb v0.17.0/go.mod h1:OkoNGhGEs8EZqchVTtochlXruEhEOaO4S0d2sB5aeGQ=
github.com/microsoft/go-mssqldb v1.8.2 h1:236sewazvC8FvG6Dr3bszrVhMkAl4KYImryLkRMCd0I=
github.com/microsoft/go-mssqldb v1.8.2/go.mod h1:vp38dT33FGfVotRiTmDo3bFyaHq+p3LektQrjTULowo=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@@ -319,7 +292,6 @@ github.com/msteinert/pam v1.2.0 h1:mYfjlvN2KYs2Pb9G6nb/1f/nPfAttT/Jee5Sq9r3bGE=
github.com/msteinert/pam v1.2.0/go.mod h1:d2n0DCUK8rGecChV3JzvmsDjOY4R7AYbsNxAT+ftQl0=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
@@ -349,17 +321,15 @@ github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAl
github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=
github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw=
github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro=
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo=
github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc=
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA=
github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4/go.mod h1:N6UoU20jOqggOuDwUaBQpluzLNDqif3kq9z2wpdYEfQ=
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@@ -367,22 +337,14 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pquerna/otp v1.5.0 h1:NMMR+WrmaqXU4EzdGJEE1aUUI0AMRzsp96fFFWNPwxs=
github.com/pquerna/otp v1.5.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
github.com/prometheus/client_golang v1.23.0 h1:ust4zpdl9r4trLY/gSjlm07PuiBq2ynaXXlptpfy8Uc=
github.com/prometheus/client_golang v1.23.0/go.mod h1:i/o0R9ByOnHX0McrTMTyhYvKE4haaf2mW08I+jGAjEE=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE=
github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
@@ -404,7 +366,6 @@ github.com/siddontang/go v0.0.0-20180604090527-bdc77568d726/go.mod h1:3yhqj7WBBf
github.com/siddontang/go-snappy v0.0.0-20140704025258-d8f7bb82a96d/go.mod h1:vq0tzqLRu6TS7Id0wMo2N5QzJoKedVeovOpHjnykSzY=
github.com/siddontang/ledisdb v0.0.0-20190202134119-8ceb77e66a92/go.mod h1:mF1DpOSOUiJRMR+FDqaqu3EBqrybQtrDDszLUZ6oxPg=
github.com/siddontang/rdb v0.0.0-20150307021120-fc89ed2e418d/go.mod h1:AMEsy7v5z92TR1JKMkLLoaOQk++LVnOKL3ScbJ8GNGA=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/assertions v1.0.1 h1:voD4ITNjPL5jjBfgR/r8fPIIBrliWrWHeiJApdr3r4w=
@@ -418,11 +379,9 @@ github.com/sourcegraph/run v0.12.0/go.mod h1:PwaP936BTnAJC1cqR5rSbG5kOs/EWStTK3l
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf h1:pvbZ0lM0XWPBqUKqFU8cmavspvIl9nulOYwdy6IFRRo=
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf/go.mod h1:RJID2RhlZKId02nZ62WenDCkgHFerpIOmW0iT7GKmXM=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
@@ -448,11 +407,8 @@ github.com/urfave/cli v1.22.17 h1:SYzXoiPfQjHBbkYxbew5prZHS1TOLT3ierW8SYLqtVQ=
github.com/urfave/cli v1.22.17/go.mod h1:b0ht0aqgH/6pBYzzxURyrM4xXNgsoT/n2ZzwQiEhNVo=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs=
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
go.bobheadxi.dev/streamline v1.2.1 h1:IqKSA1TbeuDqCzYNAwtlh8sqf3tsQus8XgJdkCWFT8c=
go.bobheadxi.dev/streamline v1.2.1/go.mod h1:yJsVXOSBFLgAKvsnf6WmIzmB2A65nWqkR/sRNxJPa74=
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.opentelemetry.io/otel v1.11.0 h1:kfToEGMDq6TrVrJ9Vht84Y8y9enykSZzDDZglV0kIEk=
go.opentelemetry.io/otel v1.11.0/go.mod h1:H2KtuEphyMvlhZ+F7tg9GRhAOe60moNx61Ex+WmiKkk=
go.opentelemetry.io/otel/sdk v1.11.0 h1:ZnKIL9V9Ztaq+ME43IUi/eo22mNsb6a7tGfzaOWB5fo=
@@ -461,9 +417,7 @@ go.opentelemetry.io/otel/trace v1.11.0 h1:20U/Vj42SX+mASlXLmSGBg6jpI1jQtv682lZtT
go.opentelemetry.io/otel/trace v1.11.0/go.mod h1:nyYjis9jy0gytE9LXGU+/m1sHTKbRY0fX0hulNNDP1U=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
@@ -474,23 +428,13 @@ golang.org/x/crypto v0.0.0-20220511200225-c6db032c6c88/go.mod h1:IxCIyHEi3zRg3s0
golang.org/x/crypto v0.0.0-20221005025214-4161e89ecf1b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b h1:M2rDM6z3Fhozi9O7NWsxAkg/yqS/lQJ6PmkyIV3YP+o=
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA=
golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
@@ -501,28 +445,18 @@ golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -557,19 +491,13 @@ golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuX
golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=
golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190802220118-1d1727260058/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
@@ -583,17 +511,8 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@@ -606,7 +525,6 @@ google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9x
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
gopkg.in/DATA-DOG/go-sqlmock.v2 v2.0.0-20180914054222-c19298f520d0 h1:/21c4hNFgj8A1D54vgJZwQlywp64/RUBHzlPdpy5h4s=
gopkg.in/DATA-DOG/go-sqlmock.v2 v2.0.0-20180914054222-c19298f520d0/go.mod h1:0uueny64T996pN6bez2N3S8HWyPcpyfTPma8Wc1Awx4=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk=
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=
gopkg.in/bufio.v1 v1.0.0-20140618132640-567b2bfa514e h1:wGA78yza6bu/mWcc4QfBuIEHEtc06xdiU0X8sY36yUU=
@@ -656,9 +574,6 @@ gorm.io/gorm v1.24.0/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
gorm.io/gorm v1.25.2-0.20230530020048-26663ab9bf55/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8=
gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
modernc.org/cc/v4 v4.26.2 h1:991HMkLjJzYBIfha6ECZdjrIYz2/1ayr+FL8GN+CNzM=
modernc.org/cc/v4 v4.26.2/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
modernc.org/ccgo/v4 v4.28.0 h1:rjznn6WWehKq7dG4JtLRKxb52Ecv8OUGah8+Z/SfpNU=
@@ -689,9 +604,3 @@ mvdan.cc/gofumpt v0.4.0 h1:JVf4NN1mIpHogBj7ABpgOyZc65/UUOkKQFkoURsz4MM=
mvdan.cc/gofumpt v0.4.0/go.mod h1:PljLOHDeZqgS8opHRKLzp2It2VBuSdteAgqUfzMTxlQ=
unknwon.dev/clog/v2 v2.2.0 h1:jkPdsxux0MC04BT/9NHbT75z4prK92SH10VBNmIpVCc=
unknwon.dev/clog/v2 v2.2.0/go.mod h1:zvUlyibDHI4mykYdWyWje2G9nF/nBzfDOqRo2my4mWc=
xorm.io/builder v0.3.6 h1:ha28mQ2M+TFx96Hxo+iq6tQgnkC9IZkM6D8w9sKHHF8=
xorm.io/builder v0.3.6/go.mod h1:LEFAPISnRzG+zxaxj2vPicRwz67BdhFreKg8yv8/TgU=
xorm.io/core v0.7.2 h1:mEO22A2Z7a3fPaZMk6gKL/jMD80iiyNwRrX5HOv3XLw=
xorm.io/core v0.7.2/go.mod h1:jJfd0UAEzZ4t87nbQYtVjmqpIODugN6PD2D9E+dJvdM=
xorm.io/xorm v0.8.0 h1:iALxgJrX8O00f8Jk22GbZwPmxJNgssV5Mv4uc2HL9PM=
xorm.io/xorm v0.8.0/go.mod h1:ZkJLEYLoVyg7amJK/5r779bHyzs2AU8f8VMiP6BM7uY=

View File

@@ -10,7 +10,7 @@ import (
"github.com/cockroachdb/errors"
gouuid "github.com/satori/go.uuid"
"xorm.io/xorm"
"gorm.io/gorm"
"gogs.io/gogs/internal/conf"
"gogs.io/gogs/internal/errutil"
@@ -19,25 +19,26 @@ import (
// Attachment represent a attachment of issue/comment/release.
type Attachment struct {
ID int64
UUID string `xorm:"uuid UNIQUE"`
IssueID int64 `xorm:"INDEX"`
UUID string `gorm:"column:uuid;type:varchar(191);uniqueIndex"`
IssueID int64 `gorm:"index"`
CommentID int64
ReleaseID int64 `xorm:"INDEX"`
ReleaseID int64 `gorm:"index"`
Name string
Created time.Time `xorm:"-" json:"-" gorm:"-"`
Created time.Time `gorm:"-" json:"-"`
CreatedUnix int64
}
func (a *Attachment) BeforeInsert() {
a.CreatedUnix = time.Now().Unix()
func (a *Attachment) BeforeCreate(tx *gorm.DB) error {
if a.CreatedUnix == 0 {
a.CreatedUnix = tx.NowFunc().Unix()
}
return nil
}
func (a *Attachment) AfterSet(colName string, _ xorm.Cell) {
switch colName {
case "created_unix":
a.Created = time.Unix(a.CreatedUnix, 0).Local()
}
func (a *Attachment) AfterFind(tx *gorm.DB) error {
a.Created = time.Unix(a.CreatedUnix, 0).Local()
return nil
}
// AttachmentLocalPath returns where attachment is stored in local file system based on given UUID.
@@ -74,7 +75,7 @@ func NewAttachment(name string, buf []byte, file multipart.File) (_ *Attachment,
return nil, errors.Newf("copy: %v", err)
}
if _, err := x.Insert(attach); err != nil {
if err := db.Create(attach).Error; err != nil {
return nil, err
}
@@ -100,60 +101,60 @@ func (ErrAttachmentNotExist) NotFound() bool {
return true
}
func getAttachmentByUUID(e Engine, uuid string) (*Attachment, error) {
attach := &Attachment{UUID: uuid}
has, err := e.Get(attach)
func getAttachmentByUUID(e *gorm.DB, uuid string) (*Attachment, error) {
attach := &Attachment{}
err := e.Where("uuid = ?", uuid).First(attach).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, ErrAttachmentNotExist{args: map[string]any{"uuid": uuid}}
}
return nil, err
} else if !has {
return nil, ErrAttachmentNotExist{args: map[string]any{"uuid": uuid}}
}
return attach, nil
}
func getAttachmentsByUUIDs(e Engine, uuids []string) ([]*Attachment, error) {
func getAttachmentsByUUIDs(e *gorm.DB, uuids []string) ([]*Attachment, error) {
if len(uuids) == 0 {
return []*Attachment{}, nil
}
// Silently drop invalid uuids.
attachments := make([]*Attachment, 0, len(uuids))
return attachments, e.In("uuid", uuids).Find(&attachments)
return attachments, e.Where("uuid IN ?", uuids).Find(&attachments).Error
}
// GetAttachmentByUUID returns attachment by given UUID.
func GetAttachmentByUUID(uuid string) (*Attachment, error) {
return getAttachmentByUUID(x, uuid)
return getAttachmentByUUID(db, uuid)
}
func getAttachmentsByIssueID(e Engine, issueID int64) ([]*Attachment, error) {
func getAttachmentsByIssueID(e *gorm.DB, issueID int64) ([]*Attachment, error) {
attachments := make([]*Attachment, 0, 5)
return attachments, e.Where("issue_id = ? AND comment_id = 0", issueID).Find(&attachments)
return attachments, e.Where("issue_id = ? AND comment_id = 0", issueID).Find(&attachments).Error
}
// GetAttachmentsByIssueID returns all attachments of an issue.
func GetAttachmentsByIssueID(issueID int64) ([]*Attachment, error) {
return getAttachmentsByIssueID(x, issueID)
return getAttachmentsByIssueID(db, issueID)
}
func getAttachmentsByCommentID(e Engine, commentID int64) ([]*Attachment, error) {
func getAttachmentsByCommentID(e *gorm.DB, commentID int64) ([]*Attachment, error) {
attachments := make([]*Attachment, 0, 5)
return attachments, e.Where("comment_id=?", commentID).Find(&attachments)
return attachments, e.Where("comment_id = ?", commentID).Find(&attachments).Error
}
// GetAttachmentsByCommentID returns all attachments of a comment.
func GetAttachmentsByCommentID(commentID int64) ([]*Attachment, error) {
return getAttachmentsByCommentID(x, commentID)
return getAttachmentsByCommentID(db, commentID)
}
func getAttachmentsByReleaseID(e Engine, releaseID int64) ([]*Attachment, error) {
func getAttachmentsByReleaseID(e *gorm.DB, releaseID int64) ([]*Attachment, error) {
attachments := make([]*Attachment, 0, 10)
return attachments, e.Where("release_id = ?", releaseID).Find(&attachments)
return attachments, e.Where("release_id = ?", releaseID).Find(&attachments).Error
}
// GetAttachmentsByReleaseID returns all attachments of a release.
func GetAttachmentsByReleaseID(releaseID int64) ([]*Attachment, error) {
return getAttachmentsByReleaseID(x, releaseID)
return getAttachmentsByReleaseID(db, releaseID)
}
// DeleteAttachment deletes the given attachment and optionally the associated file.
@@ -171,7 +172,7 @@ func DeleteAttachments(attachments []*Attachment, remove bool) (int, error) {
}
}
if _, err := x.Delete(a); err != nil {
if err := db.Delete(a).Error; err != nil {
return i, err
}
}

View File

@@ -17,8 +17,6 @@ import (
"gorm.io/gorm"
"gorm.io/gorm/schema"
log "unknwon.dev/clog/v2"
"xorm.io/core"
"xorm.io/xorm"
"gogs.io/gogs/internal/conf"
"gogs.io/gogs/internal/osutil"
@@ -122,19 +120,19 @@ func dumpLegacyTables(ctx context.Context, dirPath string, verbose bool) error {
log.Trace("Dumping table %q...", tableName)
}
tableFile := filepath.Join(dirPath, tableName+".json")
f, err := os.Create(tableFile)
if err != nil {
return errors.Newf("create JSON file: %v", err)
}
err := func() error {
tableFile := filepath.Join(dirPath, tableName+".json")
f, err := os.Create(tableFile)
if err != nil {
return errors.Wrap(err, "create JSON file")
}
defer func() { _ = f.Close() }()
if err = x.Context(ctx).Asc("id").Iterate(table, func(idx int, bean any) (err error) {
return jsoniter.NewEncoder(f).Encode(bean)
}); err != nil {
_ = f.Close()
return errors.Newf("dump table '%s': %v", tableName, err)
return dumpTable(ctx, db, table, f)
}()
if err != nil {
return errors.Wrapf(err, "dump table %q", tableName)
}
_ = f.Close()
}
return nil
}
@@ -229,8 +227,6 @@ func importTable(ctx context.Context, db *gorm.DB, table any, r io.Reader) error
}
func importLegacyTables(ctx context.Context, dirPath string, verbose bool) error {
snakeMapper := core.SnakeMapper{}
skipInsertProcessors := map[string]bool{
"mirror": true,
"milestone": true,
@@ -255,9 +251,9 @@ func importLegacyTables(ctx context.Context, dirPath string, verbose bool) error
log.Trace("Importing table %q...", tableName)
}
if err := x.DropTables(table); err != nil {
if err := db.WithContext(ctx).Migrator().DropTable(table); err != nil {
return errors.Newf("drop table %q: %v", tableName, err)
} else if err = x.Sync2(table); err != nil {
} else if err = db.WithContext(ctx).Migrator().AutoMigrate(table); err != nil {
return errors.Newf("sync table %q: %v", tableName, err)
}
@@ -265,16 +261,28 @@ func importLegacyTables(ctx context.Context, dirPath string, verbose bool) error
if err != nil {
return errors.Newf("open JSON file: %v", err)
}
rawTableName := x.TableName(table)
_, isInsertProcessor := table.(xorm.BeforeInsertProcessor)
s, err := schema.Parse(table, &sync.Map{}, db.NamingStrategy)
if err != nil {
_ = f.Close()
return errors.Wrap(err, "parse schema")
}
rawTableName := s.Table
_, isInsertProcessor := table.(interface{ BeforeCreate(*gorm.DB) error })
scanner := bufio.NewScanner(f)
for scanner.Scan() {
if err = jsoniter.Unmarshal(scanner.Bytes(), table); err != nil {
// PostgreSQL does not like the null characters (U+0000)
cleaned := bytes.ReplaceAll(scanner.Bytes(), []byte("\\u0000"), []byte(""))
if err = jsoniter.Unmarshal(cleaned, table); err != nil {
_ = f.Close()
return errors.Newf("unmarshal to struct: %v", err)
}
if _, err = x.Insert(table); err != nil {
return errors.Newf("insert strcut: %v", err)
if err = db.WithContext(ctx).Create(table).Error; err != nil {
_ = f.Close()
return errors.Newf("insert struct: %v", err)
}
var meta struct {
@@ -283,30 +291,30 @@ func importLegacyTables(ctx context.Context, dirPath string, verbose bool) error
DeadlineUnix int64
ClosedDateUnix int64
}
if err = jsoniter.Unmarshal(scanner.Bytes(), &meta); err != nil {
if err = jsoniter.Unmarshal(cleaned, &meta); err != nil {
log.Error("Failed to unmarshal to map: %v", err)
}
// Reset created_unix back to the date save in archive because Insert method updates its value
// Reset created_unix back to the date saved in archive because Create method updates its value
if isInsertProcessor && !skipInsertProcessors[rawTableName] {
if _, err = x.Exec("UPDATE `"+rawTableName+"` SET created_unix=? WHERE id=?", meta.CreatedUnix, meta.ID); err != nil {
if err = db.WithContext(ctx).Exec("UPDATE `"+rawTableName+"` SET created_unix=? WHERE id=?", meta.CreatedUnix, meta.ID).Error; err != nil {
log.Error("Failed to reset '%s.created_unix': %v", rawTableName, err)
}
}
switch rawTableName {
case "milestone":
if _, err = x.Exec("UPDATE `"+rawTableName+"` SET deadline_unix=?, closed_date_unix=? WHERE id=?", meta.DeadlineUnix, meta.ClosedDateUnix, meta.ID); err != nil {
if err = db.WithContext(ctx).Exec("UPDATE `"+rawTableName+"` SET deadline_unix=?, closed_date_unix=? WHERE id=?", meta.DeadlineUnix, meta.ClosedDateUnix, meta.ID).Error; err != nil {
log.Error("Failed to reset 'milestone.deadline_unix', 'milestone.closed_date_unix': %v", err)
}
}
}
_ = f.Close()
// PostgreSQL needs manually reset table sequence for auto increment keys
if conf.UsePostgreSQL {
rawTableName := snakeMapper.Obj2Table(tableName)
seqName := rawTableName + "_id_seq"
if _, err = x.Exec(fmt.Sprintf(`SELECT setval('%s', COALESCE((SELECT MAX(id)+1 FROM "%s"), 1), false);`, seqName, rawTableName)); err != nil {
if err = db.WithContext(ctx).Exec(fmt.Sprintf(`SELECT setval('%s', COALESCE((SELECT MAX(id)+1 FROM "%s"), 1), false);`, seqName, rawTableName)).Error; err != nil {
return errors.Newf("reset table %q' sequence: %v", rawTableName, err)
}
}

View File

@@ -7,11 +7,10 @@ import (
"time"
"github.com/cockroachdb/errors"
"github.com/unknwon/com"
log "unknwon.dev/clog/v2"
"xorm.io/xorm"
api "github.com/gogs/go-gogs-client"
"github.com/unknwon/com"
"gorm.io/gorm"
log "unknwon.dev/clog/v2"
"gogs.io/gogs/internal/errutil"
"gogs.io/gogs/internal/markup"
@@ -50,47 +49,50 @@ type Comment struct {
ID int64
Type CommentType
PosterID int64
Poster *User `xorm:"-" json:"-" gorm:"-"`
IssueID int64 `xorm:"INDEX"`
Issue *Issue `xorm:"-" json:"-" gorm:"-"`
Poster *User `gorm:"-" json:"-"`
IssueID int64 `gorm:"index"`
Issue *Issue `gorm:"-" json:"-"`
CommitID int64
Line int64
Content string `xorm:"TEXT"`
RenderedContent string `xorm:"-" json:"-" gorm:"-"`
Content string `gorm:"type:text"`
RenderedContent string `gorm:"-" json:"-"`
Created time.Time `xorm:"-" json:"-" gorm:"-"`
Created time.Time `gorm:"-" json:"-"`
CreatedUnix int64
Updated time.Time `xorm:"-" json:"-" gorm:"-"`
Updated time.Time `gorm:"-" json:"-"`
UpdatedUnix int64
// Reference issue in commit message
CommitSHA string `xorm:"VARCHAR(40)"`
CommitSHA string `gorm:"type:varchar(40)"`
Attachments []*Attachment `xorm:"-" json:"-" gorm:"-"`
Attachments []*Attachment `gorm:"-" json:"-"`
// For view issue page.
ShowTag CommentTag `xorm:"-" json:"-" gorm:"-"`
ShowTag CommentTag `gorm:"-" json:"-"`
}
func (c *Comment) BeforeInsert() {
c.CreatedUnix = time.Now().Unix()
c.UpdatedUnix = c.CreatedUnix
}
func (c *Comment) BeforeUpdate() {
c.UpdatedUnix = time.Now().Unix()
}
func (c *Comment) AfterSet(colName string, _ xorm.Cell) {
switch colName {
case "created_unix":
c.Created = time.Unix(c.CreatedUnix, 0).Local()
case "updated_unix":
c.Updated = time.Unix(c.UpdatedUnix, 0).Local()
func (c *Comment) BeforeCreate(tx *gorm.DB) error {
if c.CreatedUnix == 0 {
c.CreatedUnix = tx.NowFunc().Unix()
}
if c.UpdatedUnix == 0 {
c.UpdatedUnix = c.CreatedUnix
}
return nil
}
func (c *Comment) loadAttributes(e Engine) (err error) {
func (c *Comment) BeforeUpdate(tx *gorm.DB) error {
c.UpdatedUnix = tx.NowFunc().Unix()
return nil
}
func (c *Comment) AfterFind(tx *gorm.DB) error {
c.Created = time.Unix(c.CreatedUnix, 0).Local()
c.Updated = time.Unix(c.UpdatedUnix, 0).Local()
return nil
}
func (c *Comment) loadAttributes(tx *gorm.DB) (err error) {
if c.Poster == nil {
c.Poster, err = Handle.Users().GetByID(context.TODO(), c.PosterID)
if err != nil {
@@ -104,12 +106,12 @@ func (c *Comment) loadAttributes(e Engine) (err error) {
}
if c.Issue == nil {
c.Issue, err = getRawIssueByID(e, c.IssueID)
c.Issue, err = getRawIssueByID(tx, c.IssueID)
if err != nil {
return errors.Newf("getIssueByID [%d]: %v", c.IssueID, err)
}
if c.Issue.Repo == nil {
c.Issue.Repo, err = getRepositoryByID(e, c.Issue.RepoID)
c.Issue.Repo, err = getRepositoryByID(tx, c.Issue.RepoID)
if err != nil {
return errors.Newf("getRepositoryByID [%d]: %v", c.Issue.RepoID, err)
}
@@ -117,7 +119,7 @@ func (c *Comment) loadAttributes(e Engine) (err error) {
}
if c.Attachments == nil {
c.Attachments, err = getAttachmentsByCommentID(e, c.ID)
c.Attachments, err = getAttachmentsByCommentID(tx, c.ID)
if err != nil {
return errors.Newf("getAttachmentsByCommentID [%d]: %v", c.ID, err)
}
@@ -127,7 +129,7 @@ func (c *Comment) loadAttributes(e Engine) (err error) {
}
func (c *Comment) LoadAttributes() error {
return c.loadAttributes(x)
return c.loadAttributes(db)
}
func (c *Comment) HTMLURL() string {
@@ -163,9 +165,9 @@ func (c *Comment) EventTag() string {
// mailParticipants sends new comment emails to repository watchers
// and mentioned people.
func (c *Comment) mailParticipants(e Engine, opType ActionType, issue *Issue) (err error) {
func (c *Comment) mailParticipants(tx *gorm.DB, opType ActionType, issue *Issue) (err error) {
mentions := markup.FindAllMentions(c.Content)
if err = updateIssueMentions(e, c.IssueID, mentions); err != nil {
if err = updateIssueMentions(tx, c.IssueID, mentions); err != nil {
return errors.Newf("UpdateIssueMentions [%d]: %v", c.IssueID, err)
}
@@ -184,7 +186,7 @@ func (c *Comment) mailParticipants(e Engine, opType ActionType, issue *Issue) (e
return nil
}
func createComment(e *xorm.Session, opts *CreateCommentOptions) (_ *Comment, err error) {
func createComment(tx *gorm.DB, opts *CreateCommentOptions) (_ *Comment, err error) {
comment := &Comment{
Type: opts.Type,
PosterID: opts.Doer.ID,
@@ -195,7 +197,7 @@ func createComment(e *xorm.Session, opts *CreateCommentOptions) (_ *Comment, err
Line: opts.LineNum,
Content: opts.Content,
}
if _, err = e.Insert(comment); err != nil {
if err = tx.Create(comment).Error; err != nil {
return nil, err
}
@@ -216,14 +218,14 @@ func createComment(e *xorm.Session, opts *CreateCommentOptions) (_ *Comment, err
case CommentTypeComment:
act.OpType = ActionCommentIssue
if _, err = e.Exec("UPDATE `issue` SET num_comments=num_comments+1 WHERE id=?", opts.Issue.ID); err != nil {
if err = tx.Exec("UPDATE `issue` SET num_comments=num_comments+1 WHERE id=?", opts.Issue.ID).Error; err != nil {
return nil, err
}
// Check attachments
attachments := make([]*Attachment, 0, len(opts.Attachments))
for _, uuid := range opts.Attachments {
attach, err := getAttachmentByUUID(e, uuid)
attach, err := getAttachmentByUUID(tx, uuid)
if err != nil {
if IsErrAttachmentNotExist(err) {
continue
@@ -236,8 +238,10 @@ func createComment(e *xorm.Session, opts *CreateCommentOptions) (_ *Comment, err
for i := range attachments {
attachments[i].IssueID = opts.Issue.ID
attachments[i].CommentID = comment.ID
// No assign value could be 0, so ignore AllCols().
if _, err = e.ID(attachments[i].ID).Update(attachments[i]); err != nil {
if err = tx.Model(attachments[i]).Where("id = ?", attachments[i].ID).Updates(map[string]any{
"issue_id": attachments[i].IssueID,
"comment_id": attachments[i].CommentID,
}).Error; err != nil {
return nil, errors.Newf("update attachment [%d]: %v", attachments[i].ID, err)
}
}
@@ -249,9 +253,9 @@ func createComment(e *xorm.Session, opts *CreateCommentOptions) (_ *Comment, err
}
if opts.Issue.IsPull {
_, err = e.Exec("UPDATE `repository` SET num_closed_pulls=num_closed_pulls-1 WHERE id=?", opts.Repo.ID)
err = tx.Exec("UPDATE `repository` SET num_closed_pulls=num_closed_pulls-1 WHERE id=?", opts.Repo.ID).Error
} else {
_, err = e.Exec("UPDATE `repository` SET num_closed_issues=num_closed_issues-1 WHERE id=?", opts.Repo.ID)
err = tx.Exec("UPDATE `repository` SET num_closed_issues=num_closed_issues-1 WHERE id=?", opts.Repo.ID).Error
}
if err != nil {
return nil, err
@@ -264,38 +268,38 @@ func createComment(e *xorm.Session, opts *CreateCommentOptions) (_ *Comment, err
}
if opts.Issue.IsPull {
_, err = e.Exec("UPDATE `repository` SET num_closed_pulls=num_closed_pulls+1 WHERE id=?", opts.Repo.ID)
err = tx.Exec("UPDATE `repository` SET num_closed_pulls=num_closed_pulls+1 WHERE id=?", opts.Repo.ID).Error
} else {
_, err = e.Exec("UPDATE `repository` SET num_closed_issues=num_closed_issues+1 WHERE id=?", opts.Repo.ID)
err = tx.Exec("UPDATE `repository` SET num_closed_issues=num_closed_issues+1 WHERE id=?", opts.Repo.ID).Error
}
if err != nil {
return nil, err
}
}
if _, err = e.Exec("UPDATE `issue` SET updated_unix = ? WHERE id = ?", time.Now().Unix(), opts.Issue.ID); err != nil {
if err = tx.Exec("UPDATE `issue` SET updated_unix = ? WHERE id = ?", tx.NowFunc().Unix(), opts.Issue.ID).Error; err != nil {
return nil, errors.Newf("update issue 'updated_unix': %v", err)
}
// Notify watchers for whatever action comes in, ignore if no action type.
if act.OpType > 0 {
if err = notifyWatchers(e, act); err != nil {
if err = notifyWatchers(tx, act); err != nil {
log.Error("notifyWatchers: %v", err)
}
if err = comment.mailParticipants(e, act.OpType, opts.Issue); err != nil {
if err = comment.mailParticipants(tx, act.OpType, opts.Issue); err != nil {
log.Error("MailParticipants: %v", err)
}
}
return comment, comment.loadAttributes(e)
return comment, comment.loadAttributes(tx)
}
func createStatusComment(e *xorm.Session, doer *User, repo *Repository, issue *Issue) (*Comment, error) {
func createStatusComment(tx *gorm.DB, doer *User, repo *Repository, issue *Issue) (*Comment, error) {
cmtType := CommentTypeClose
if !issue.IsClosed {
cmtType = CommentTypeReopen
}
return createComment(e, &CreateCommentOptions{
return createComment(tx, &CreateCommentOptions{
Type: cmtType,
Doer: doer,
Repo: repo,
@@ -318,18 +322,12 @@ type CreateCommentOptions struct {
// CreateComment creates comment of issue or commit.
func CreateComment(opts *CreateCommentOptions) (comment *Comment, err error) {
sess := x.NewSession()
defer sess.Close()
if err = sess.Begin(); err != nil {
return nil, err
}
comment, err = createComment(sess, opts)
if err != nil {
return nil, err
}
return comment, sess.Commit()
err = db.Transaction(func(tx *gorm.DB) error {
var err error
comment, err = createComment(tx, opts)
return err
})
return comment, err
}
// CreateIssueComment creates a plain issue comment.
@@ -367,14 +365,12 @@ func CreateRefComment(doer *User, repo *Repository, issue *Issue, content, commi
}
// Check if same reference from same commit has already existed.
has, err := x.Get(&Comment{
Type: CommentTypeCommitRef,
IssueID: issue.ID,
CommitSHA: commitSHA,
})
var count int64
err := db.Model(new(Comment)).Where("type = ? AND issue_id = ? AND commit_sha = ?",
CommentTypeCommitRef, issue.ID, commitSHA).Count(&count).Error
if err != nil {
return errors.Newf("check reference comment: %v", err)
} else if has {
} else if count > 0 {
return nil
}
@@ -411,19 +407,20 @@ func (ErrCommentNotExist) NotFound() bool {
// GetCommentByID returns the comment by given ID.
func GetCommentByID(id int64) (*Comment, error) {
c := new(Comment)
has, err := x.Id(id).Get(c)
err := db.Where("id = ?", id).First(c).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, ErrCommentNotExist{args: map[string]any{"commentID": id}}
}
return nil, err
} else if !has {
return nil, ErrCommentNotExist{args: map[string]any{"commentID": id}}
}
return c, c.LoadAttributes()
}
// FIXME: use CommentList to improve performance.
func loadCommentsAttributes(e Engine, comments []*Comment) (err error) {
func loadCommentsAttributes(tx *gorm.DB, comments []*Comment) (err error) {
for i := range comments {
if err = comments[i].loadAttributes(e); err != nil {
if err = comments[i].loadAttributes(tx); err != nil {
return errors.Newf("loadAttributes [%d]: %v", comments[i].ID, err)
}
}
@@ -431,53 +428,55 @@ func loadCommentsAttributes(e Engine, comments []*Comment) (err error) {
return nil
}
func getCommentsByIssueIDSince(e Engine, issueID, since int64) ([]*Comment, error) {
func getCommentsByIssueIDSince(tx *gorm.DB, issueID, since int64) ([]*Comment, error) {
comments := make([]*Comment, 0, 10)
sess := e.Where("issue_id = ?", issueID).Asc("created_unix")
query := tx.Where("issue_id = ?", issueID).Order("created_unix ASC")
if since > 0 {
sess.And("updated_unix >= ?", since)
query = query.Where("updated_unix >= ?", since)
}
if err := sess.Find(&comments); err != nil {
if err := query.Find(&comments).Error; err != nil {
return nil, err
}
return comments, loadCommentsAttributes(e, comments)
return comments, loadCommentsAttributes(tx, comments)
}
func getCommentsByRepoIDSince(e Engine, repoID, since int64) ([]*Comment, error) {
func getCommentsByRepoIDSince(tx *gorm.DB, repoID, since int64) ([]*Comment, error) {
comments := make([]*Comment, 0, 10)
sess := e.Where("issue.repo_id = ?", repoID).Join("INNER", "issue", "issue.id = comment.issue_id").Asc("comment.created_unix")
query := tx.Joins("INNER JOIN issue ON issue.id = comment.issue_id").
Where("issue.repo_id = ?", repoID).
Order("comment.created_unix ASC")
if since > 0 {
sess.And("comment.updated_unix >= ?", since)
query = query.Where("comment.updated_unix >= ?", since)
}
if err := sess.Find(&comments); err != nil {
if err := query.Find(&comments).Error; err != nil {
return nil, err
}
return comments, loadCommentsAttributes(e, comments)
return comments, loadCommentsAttributes(tx, comments)
}
func getCommentsByIssueID(e Engine, issueID int64) ([]*Comment, error) {
return getCommentsByIssueIDSince(e, issueID, -1)
func getCommentsByIssueID(tx *gorm.DB, issueID int64) ([]*Comment, error) {
return getCommentsByIssueIDSince(tx, issueID, -1)
}
// GetCommentsByIssueID returns all comments of an issue.
func GetCommentsByIssueID(issueID int64) ([]*Comment, error) {
return getCommentsByIssueID(x, issueID)
return getCommentsByIssueID(db, issueID)
}
// GetCommentsByIssueIDSince returns a list of comments of an issue since a given time point.
func GetCommentsByIssueIDSince(issueID, since int64) ([]*Comment, error) {
return getCommentsByIssueIDSince(x, issueID, since)
return getCommentsByIssueIDSince(db, issueID, since)
}
// GetCommentsByRepoIDSince returns a list of comments for all issues in a repo since a given time point.
func GetCommentsByRepoIDSince(repoID, since int64) ([]*Comment, error) {
return getCommentsByRepoIDSince(x, repoID, since)
return getCommentsByRepoIDSince(db, repoID, since)
}
// UpdateComment updates information of comment.
func UpdateComment(doer *User, c *Comment, oldContent string) (err error) {
if _, err = x.Id(c.ID).AllCols().Update(c); err != nil {
if err = db.Model(c).Where("id = ?", c.ID).Updates(c).Error; err != nil {
return err
}
@@ -511,24 +510,21 @@ func DeleteCommentByID(doer *User, id int64) error {
return err
}
sess := x.NewSession()
defer sess.Close()
if err = sess.Begin(); err != nil {
return err
}
if _, err = sess.ID(comment.ID).Delete(new(Comment)); err != nil {
return err
}
if comment.Type == CommentTypeComment {
if _, err = sess.Exec("UPDATE `issue` SET num_comments = num_comments - 1 WHERE id = ?", comment.IssueID); err != nil {
err = db.Transaction(func(tx *gorm.DB) error {
if err := tx.Where("id = ?", comment.ID).Delete(new(Comment)).Error; err != nil {
return err
}
}
if err = sess.Commit(); err != nil {
return errors.Newf("commit: %v", err)
if comment.Type == CommentTypeComment {
if err := tx.Exec("UPDATE `issue` SET num_comments = num_comments - 1 WHERE id = ?", comment.IssueID).Error; err != nil {
return err
}
}
return nil
})
if err != nil {
return errors.Newf("transaction: %v", err)
}
_, err = DeleteAttachmentsByComment(comment.ID, true)

File diff suppressed because it is too large Load Diff

View File

@@ -6,14 +6,12 @@ import (
"strconv"
"strings"
"xorm.io/xorm"
"github.com/cockroachdb/errors"
api "github.com/gogs/go-gogs-client"
"gorm.io/gorm"
"gogs.io/gogs/internal/errutil"
"gogs.io/gogs/internal/lazyregexp"
"gogs.io/gogs/internal/tool"
)
var labelColorPattern = lazyregexp.New("#([a-fA-F0-9]{6})")
@@ -53,13 +51,13 @@ func GetLabelTemplateFile(name string) ([][2]string, error) {
// Label represents a label of repository for issues.
type Label struct {
ID int64
RepoID int64 `xorm:"INDEX"`
RepoID int64 `gorm:"index"`
Name string
Color string `xorm:"VARCHAR(7)"`
Color string `gorm:"type:varchar(7)"`
NumIssues int
NumClosedIssues int
NumOpenIssues int `xorm:"-" json:"-" gorm:"-"`
IsChecked bool `xorm:"-" json:"-" gorm:"-"`
NumOpenIssues int `gorm:"-" json:"-"`
IsChecked bool `gorm:"-" json:"-"`
}
func (l *Label) APIFormat() *api.Label {
@@ -97,8 +95,7 @@ func (l *Label) ForegroundColor() template.CSS {
// NewLabels creates new label(s) for a repository.
func NewLabels(labels ...*Label) error {
_, err := x.Insert(labels)
return err
return db.Create(labels).Error
}
var _ errutil.NotFound = (*ErrLabelNotExist)(nil)
@@ -123,20 +120,22 @@ func (ErrLabelNotExist) NotFound() bool {
// getLabelOfRepoByName returns a label by Name in given repository.
// If pass repoID as 0, then ORM will ignore limitation of repository
// and can return arbitrary label with any valid ID.
func getLabelOfRepoByName(e Engine, repoID int64, labelName string) (*Label, error) {
func getLabelOfRepoByName(tx *gorm.DB, repoID int64, labelName string) (*Label, error) {
if len(labelName) <= 0 {
return nil, ErrLabelNotExist{args: map[string]any{"repoID": repoID}}
}
l := &Label{
Name: labelName,
RepoID: repoID,
l := &Label{}
query := tx.Where("name = ?", labelName)
if repoID > 0 {
query = query.Where("repo_id = ?", repoID)
}
has, err := e.Get(l)
err := query.First(l).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, ErrLabelNotExist{args: map[string]any{"repoID": repoID}}
}
return nil, err
} else if !has {
return nil, ErrLabelNotExist{args: map[string]any{"repoID": repoID}}
}
return l, nil
}
@@ -144,54 +143,56 @@ func getLabelOfRepoByName(e Engine, repoID int64, labelName string) (*Label, err
// getLabelInRepoByID returns a label by ID in given repository.
// If pass repoID as 0, then ORM will ignore limitation of repository
// and can return arbitrary label with any valid ID.
func getLabelOfRepoByID(e Engine, repoID, labelID int64) (*Label, error) {
func getLabelOfRepoByID(tx *gorm.DB, repoID, labelID int64) (*Label, error) {
if labelID <= 0 {
return nil, ErrLabelNotExist{args: map[string]any{"repoID": repoID, "labelID": labelID}}
}
l := &Label{
ID: labelID,
RepoID: repoID,
l := &Label{}
query := tx.Where("id = ?", labelID)
if repoID > 0 {
query = query.Where("repo_id = ?", repoID)
}
has, err := e.Get(l)
err := query.First(l).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, ErrLabelNotExist{args: map[string]any{"repoID": repoID, "labelID": labelID}}
}
return nil, err
} else if !has {
return nil, ErrLabelNotExist{args: map[string]any{"repoID": repoID, "labelID": labelID}}
}
return l, nil
}
// GetLabelByID returns a label by given ID.
func GetLabelByID(id int64) (*Label, error) {
return getLabelOfRepoByID(x, 0, id)
return getLabelOfRepoByID(db, 0, id)
}
// GetLabelOfRepoByID returns a label by ID in given repository.
func GetLabelOfRepoByID(repoID, labelID int64) (*Label, error) {
return getLabelOfRepoByID(x, repoID, labelID)
return getLabelOfRepoByID(db, repoID, labelID)
}
// GetLabelOfRepoByName returns a label by name in given repository.
func GetLabelOfRepoByName(repoID int64, labelName string) (*Label, error) {
return getLabelOfRepoByName(x, repoID, labelName)
return getLabelOfRepoByName(db, repoID, labelName)
}
// GetLabelsInRepoByIDs returns a list of labels by IDs in given repository,
// it silently ignores label IDs that are not belong to the repository.
func GetLabelsInRepoByIDs(repoID int64, labelIDs []int64) ([]*Label, error) {
labels := make([]*Label, 0, len(labelIDs))
return labels, x.Where("repo_id = ?", repoID).In("id", tool.Int64sToStrings(labelIDs)).Asc("name").Find(&labels)
return labels, db.Where("repo_id = ? AND id IN ?", repoID, labelIDs).Order("name ASC").Find(&labels).Error
}
// GetLabelsByRepoID returns all labels that belong to given repository by ID.
func GetLabelsByRepoID(repoID int64) ([]*Label, error) {
labels := make([]*Label, 0, 10)
return labels, x.Where("repo_id = ?", repoID).Asc("name").Find(&labels)
return labels, db.Where("repo_id = ?", repoID).Order("name ASC").Find(&labels).Error
}
func getLabelsByIssueID(e Engine, issueID int64) ([]*Label, error) {
issueLabels, err := getIssueLabels(e, issueID)
func getLabelsByIssueID(tx *gorm.DB, issueID int64) ([]*Label, error) {
issueLabels, err := getIssueLabels(tx, issueID)
if err != nil {
return nil, errors.Newf("getIssueLabels: %v", err)
} else if len(issueLabels) == 0 {
@@ -204,22 +205,21 @@ func getLabelsByIssueID(e Engine, issueID int64) ([]*Label, error) {
}
labels := make([]*Label, 0, len(labelIDs))
return labels, e.Where("id > 0").In("id", tool.Int64sToStrings(labelIDs)).Asc("name").Find(&labels)
return labels, tx.Where("id > 0 AND id IN ?", labelIDs).Order("name ASC").Find(&labels).Error
}
// GetLabelsByIssueID returns all labels that belong to given issue by ID.
func GetLabelsByIssueID(issueID int64) ([]*Label, error) {
return getLabelsByIssueID(x, issueID)
return getLabelsByIssueID(db, issueID)
}
func updateLabel(e Engine, l *Label) error {
_, err := e.ID(l.ID).AllCols().Update(l)
return err
func updateLabel(tx *gorm.DB, l *Label) error {
return tx.Model(l).Where("id = ?", l.ID).Updates(l).Error
}
// UpdateLabel updates label information.
func UpdateLabel(l *Label) error {
return updateLabel(x, l)
return updateLabel(db, l)
}
// DeleteLabel delete a label of given repository.
@@ -232,19 +232,15 @@ func DeleteLabel(repoID, labelID int64) error {
return err
}
sess := x.NewSession()
defer sess.Close()
if err = sess.Begin(); err != nil {
return err
}
if _, err = sess.ID(labelID).Delete(new(Label)); err != nil {
return err
} else if _, err = sess.Where("label_id = ?", labelID).Delete(new(IssueLabel)); err != nil {
return err
}
return sess.Commit()
return db.Transaction(func(tx *gorm.DB) error {
if err := tx.Where("id = ?", labelID).Delete(new(Label)).Error; err != nil {
return err
}
if err := tx.Where("label_id = ?", labelID).Delete(new(IssueLabel)).Error; err != nil {
return err
}
return nil
})
}
// .___ .____ ___. .__
@@ -257,25 +253,26 @@ func DeleteLabel(repoID, labelID int64) error {
// IssueLabel represents an issue-lable relation.
type IssueLabel struct {
ID int64
IssueID int64 `xorm:"UNIQUE(s)"`
LabelID int64 `xorm:"UNIQUE(s)"`
IssueID int64 `gorm:"uniqueIndex:issue_label_unique"`
LabelID int64 `gorm:"uniqueIndex:issue_label_unique"`
}
func hasIssueLabel(e Engine, issueID, labelID int64) bool {
has, _ := e.Where("issue_id = ? AND label_id = ?", issueID, labelID).Get(new(IssueLabel))
return has
func hasIssueLabel(tx *gorm.DB, issueID, labelID int64) bool {
var count int64
tx.Model(new(IssueLabel)).Where("issue_id = ? AND label_id = ?", issueID, labelID).Count(&count)
return count > 0
}
// HasIssueLabel returns true if issue has been labeled.
func HasIssueLabel(issueID, labelID int64) bool {
return hasIssueLabel(x, issueID, labelID)
return hasIssueLabel(db, issueID, labelID)
}
func newIssueLabel(e *xorm.Session, issue *Issue, label *Label) (err error) {
if _, err = e.Insert(&IssueLabel{
func newIssueLabel(tx *gorm.DB, issue *Issue, label *Label) (err error) {
if err = tx.Create(&IssueLabel{
IssueID: issue.ID,
LabelID: label.ID,
}); err != nil {
}).Error; err != nil {
return err
}
@@ -284,7 +281,7 @@ func newIssueLabel(e *xorm.Session, issue *Issue, label *Label) (err error) {
label.NumClosedIssues++
}
if err = updateLabel(e, label); err != nil {
if err = updateLabel(tx, label); err != nil {
return errors.Newf("updateLabel: %v", err)
}
@@ -298,26 +295,18 @@ func NewIssueLabel(issue *Issue, label *Label) (err error) {
return nil
}
sess := x.NewSession()
defer sess.Close()
if err = sess.Begin(); err != nil {
return err
}
if err = newIssueLabel(sess, issue, label); err != nil {
return err
}
return sess.Commit()
return db.Transaction(func(tx *gorm.DB) error {
return newIssueLabel(tx, issue, label)
})
}
func newIssueLabels(e *xorm.Session, issue *Issue, labels []*Label) (err error) {
func newIssueLabels(tx *gorm.DB, issue *Issue, labels []*Label) (err error) {
for i := range labels {
if hasIssueLabel(e, issue.ID, labels[i].ID) {
if hasIssueLabel(tx, issue.ID, labels[i].ID) {
continue
}
if err = newIssueLabel(e, issue, labels[i]); err != nil {
if err = newIssueLabel(tx, issue, labels[i]); err != nil {
return errors.Newf("newIssueLabel: %v", err)
}
}
@@ -327,34 +316,23 @@ func newIssueLabels(e *xorm.Session, issue *Issue, labels []*Label) (err error)
// NewIssueLabels creates a list of issue-label relations.
func NewIssueLabels(issue *Issue, labels []*Label) (err error) {
sess := x.NewSession()
defer sess.Close()
if err = sess.Begin(); err != nil {
return err
}
if err = newIssueLabels(sess, issue, labels); err != nil {
return err
}
return sess.Commit()
return db.Transaction(func(tx *gorm.DB) error {
return newIssueLabels(tx, issue, labels)
})
}
func getIssueLabels(e Engine, issueID int64) ([]*IssueLabel, error) {
func getIssueLabels(tx *gorm.DB, issueID int64) ([]*IssueLabel, error) {
issueLabels := make([]*IssueLabel, 0, 10)
return issueLabels, e.Where("issue_id=?", issueID).Asc("label_id").Find(&issueLabels)
return issueLabels, tx.Where("issue_id = ?", issueID).Order("label_id ASC").Find(&issueLabels).Error
}
// GetIssueLabels returns all issue-label relations of given issue by ID.
func GetIssueLabels(issueID int64) ([]*IssueLabel, error) {
return getIssueLabels(x, issueID)
return getIssueLabels(db, issueID)
}
func deleteIssueLabel(e *xorm.Session, issue *Issue, label *Label) (err error) {
if _, err = e.Delete(&IssueLabel{
IssueID: issue.ID,
LabelID: label.ID,
}); err != nil {
func deleteIssueLabel(tx *gorm.DB, issue *Issue, label *Label) (err error) {
if err = tx.Where("issue_id = ? AND label_id = ?", issue.ID, label.ID).Delete(&IssueLabel{}).Error; err != nil {
return err
}
@@ -362,7 +340,7 @@ func deleteIssueLabel(e *xorm.Session, issue *Issue, label *Label) (err error) {
if issue.IsClosed {
label.NumClosedIssues--
}
if err = updateLabel(e, label); err != nil {
if err = updateLabel(tx, label); err != nil {
return errors.Newf("updateLabel: %v", err)
}
@@ -377,15 +355,7 @@ func deleteIssueLabel(e *xorm.Session, issue *Issue, label *Label) (err error) {
// DeleteIssueLabel deletes issue-label relation.
func DeleteIssueLabel(issue *Issue, label *Label) (err error) {
sess := x.NewSession()
defer sess.Close()
if err = sess.Begin(); err != nil {
return err
}
if err = deleteIssueLabel(sess, issue, label); err != nil {
return err
}
return sess.Commit()
return db.Transaction(func(tx *gorm.DB) error {
return deleteIssueLabel(tx, issue, label)
})
}

View File

@@ -176,7 +176,7 @@ func mailIssueCommentToParticipants(issue *Issue, doer *User, mentions []string)
// and mentioned people.
func (issue *Issue) MailParticipants() (err error) {
mentions := markup.FindAllMentions(issue.Content)
if err = updateIssueMentions(x, issue.ID, mentions); err != nil {
if err = updateIssueMentions(db, issue.ID, mentions); err != nil {
return errors.Newf("UpdateIssueMentions [%d]: %v", issue.ID, err)
}

View File

@@ -4,11 +4,10 @@ import (
"fmt"
"time"
log "unknwon.dev/clog/v2"
"xorm.io/xorm"
"github.com/cockroachdb/errors"
api "github.com/gogs/go-gogs-client"
"gorm.io/gorm"
log "unknwon.dev/clog/v2"
"gogs.io/gogs/internal/conf"
"gogs.io/gogs/internal/errutil"
@@ -17,29 +16,30 @@ import (
// Milestone represents a milestone of repository.
type Milestone struct {
ID int64
RepoID int64 `xorm:"INDEX"`
RepoID int64 `gorm:"index"`
Name string
Content string `xorm:"TEXT"`
RenderedContent string `xorm:"-" json:"-" gorm:"-"`
Content string `gorm:"type:text"`
RenderedContent string `gorm:"-" json:"-"`
IsClosed bool
NumIssues int
NumClosedIssues int
NumOpenIssues int `xorm:"-" json:"-" gorm:"-"`
NumOpenIssues int `gorm:"-" json:"-"`
Completeness int // Percentage(1-100).
IsOverDue bool `xorm:"-" json:"-" gorm:"-"`
IsOverDue bool `gorm:"-" json:"-"`
DeadlineString string `xorm:"-" json:"-" gorm:"-"`
Deadline time.Time `xorm:"-" json:"-" gorm:"-"`
DeadlineString string `gorm:"-" json:"-"`
Deadline time.Time `gorm:"-" json:"-"`
DeadlineUnix int64
ClosedDate time.Time `xorm:"-" json:"-" gorm:"-"`
ClosedDate time.Time `gorm:"-" json:"-"`
ClosedDateUnix int64
}
func (m *Milestone) BeforeInsert() {
func (m *Milestone) BeforeCreate(tx *gorm.DB) error {
m.DeadlineUnix = m.Deadline.Unix()
return nil
}
func (m *Milestone) BeforeUpdate() {
func (m *Milestone) BeforeUpdate(tx *gorm.DB) error {
if m.NumIssues > 0 {
m.Completeness = m.NumClosedIssues * 100 / m.NumIssues
} else {
@@ -48,27 +48,22 @@ func (m *Milestone) BeforeUpdate() {
m.DeadlineUnix = m.Deadline.Unix()
m.ClosedDateUnix = m.ClosedDate.Unix()
return nil
}
func (m *Milestone) AfterSet(colName string, _ xorm.Cell) {
switch colName {
case "num_closed_issues":
m.NumOpenIssues = m.NumIssues - m.NumClosedIssues
case "deadline_unix":
m.Deadline = time.Unix(m.DeadlineUnix, 0).Local()
if m.Deadline.Year() == 9999 {
return
}
func (m *Milestone) AfterFind(tx *gorm.DB) error {
m.NumOpenIssues = m.NumIssues - m.NumClosedIssues
m.Deadline = time.Unix(m.DeadlineUnix, 0).Local()
if m.Deadline.Year() != 9999 {
m.DeadlineString = m.Deadline.Format("2006-01-02")
if time.Now().Local().After(m.Deadline) {
m.IsOverDue = true
}
case "closed_date_unix":
m.ClosedDate = time.Unix(m.ClosedDateUnix, 0).Local()
}
m.ClosedDate = time.Unix(m.ClosedDateUnix, 0).Local()
return nil
}
// State returns string representation of milestone status.
@@ -102,30 +97,24 @@ func (m *Milestone) APIFormat() *api.Milestone {
}
func (m *Milestone) CountIssues(isClosed, includePulls bool) int64 {
sess := x.Where("milestone_id = ?", m.ID).And("is_closed = ?", isClosed)
query := db.Model(new(Issue)).Where("milestone_id = ? AND is_closed = ?", m.ID, isClosed)
if !includePulls {
sess.And("is_pull = ?", false)
query = query.Where("is_pull = ?", false)
}
count, _ := sess.Count(new(Issue))
var count int64
query.Count(&count)
return count
}
// NewMilestone creates new milestone of repository.
func NewMilestone(m *Milestone) (err error) {
sess := x.NewSession()
defer sess.Close()
if err = sess.Begin(); err != nil {
return err
}
return db.Transaction(func(tx *gorm.DB) error {
if err := tx.Create(m).Error; err != nil {
return err
}
if _, err = sess.Insert(m); err != nil {
return err
}
if _, err = sess.Exec("UPDATE `repository` SET num_milestones = num_milestones + 1 WHERE id = ?", m.RepoID); err != nil {
return err
}
return sess.Commit()
return tx.Exec("UPDATE `repository` SET num_milestones = num_milestones + 1 WHERE id = ?", m.RepoID).Error
})
}
var _ errutil.NotFound = (*ErrMilestoneNotExist)(nil)
@@ -147,74 +136,73 @@ func (ErrMilestoneNotExist) NotFound() bool {
return true
}
func getMilestoneByRepoID(e Engine, repoID, id int64) (*Milestone, error) {
m := &Milestone{
ID: id,
RepoID: repoID,
}
has, err := e.Get(m)
func getMilestoneByRepoID(e *gorm.DB, repoID, id int64) (*Milestone, error) {
m := &Milestone{}
err := e.Where("id = ? AND repo_id = ?", id, repoID).First(m).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, ErrMilestoneNotExist{args: map[string]any{"repoID": repoID, "milestoneID": id}}
}
return nil, err
} else if !has {
return nil, ErrMilestoneNotExist{args: map[string]any{"repoID": repoID, "milestoneID": id}}
}
return m, nil
}
// GetWebhookByRepoID returns the milestone in a repository.
func GetMilestoneByRepoID(repoID, id int64) (*Milestone, error) {
return getMilestoneByRepoID(x, repoID, id)
return getMilestoneByRepoID(db, repoID, id)
}
// GetMilestonesByRepoID returns all milestones of a repository.
func GetMilestonesByRepoID(repoID int64) ([]*Milestone, error) {
miles := make([]*Milestone, 0, 10)
return miles, x.Where("repo_id = ?", repoID).Find(&miles)
return miles, db.Where("repo_id = ?", repoID).Find(&miles).Error
}
// GetMilestones returns a list of milestones of given repository and status.
func GetMilestones(repoID int64, page int, isClosed bool) ([]*Milestone, error) {
miles := make([]*Milestone, 0, conf.UI.IssuePagingNum)
sess := x.Where("repo_id = ? AND is_closed = ?", repoID, isClosed)
query := db.Where("repo_id = ? AND is_closed = ?", repoID, isClosed)
if page > 0 {
sess = sess.Limit(conf.UI.IssuePagingNum, (page-1)*conf.UI.IssuePagingNum)
query = query.Limit(conf.UI.IssuePagingNum).Offset((page - 1) * conf.UI.IssuePagingNum)
}
return miles, sess.Find(&miles)
return miles, query.Find(&miles).Error
}
func updateMilestone(e Engine, m *Milestone) error {
_, err := e.ID(m.ID).AllCols().Update(m)
return err
func updateMilestone(e *gorm.DB, m *Milestone) error {
return e.Model(m).Where("id = ?", m.ID).Updates(m).Error
}
// UpdateMilestone updates information of given milestone.
func UpdateMilestone(m *Milestone) error {
return updateMilestone(x, m)
return updateMilestone(db, m)
}
func countRepoMilestones(e Engine, repoID int64) int64 {
count, _ := e.Where("repo_id=?", repoID).Count(new(Milestone))
func countRepoMilestones(e *gorm.DB, repoID int64) int64 {
var count int64
e.Model(new(Milestone)).Where("repo_id = ?", repoID).Count(&count)
return count
}
// CountRepoMilestones returns number of milestones in given repository.
func CountRepoMilestones(repoID int64) int64 {
return countRepoMilestones(x, repoID)
return countRepoMilestones(db, repoID)
}
func countRepoClosedMilestones(e Engine, repoID int64) int64 {
closed, _ := e.Where("repo_id=? AND is_closed=?", repoID, true).Count(new(Milestone))
return closed
func countRepoClosedMilestones(e *gorm.DB, repoID int64) int64 {
var count int64
e.Model(new(Milestone)).Where("repo_id = ? AND is_closed = ?", repoID, true).Count(&count)
return count
}
// CountRepoClosedMilestones returns number of closed milestones in given repository.
func CountRepoClosedMilestones(repoID int64) int64 {
return countRepoClosedMilestones(x, repoID)
return countRepoClosedMilestones(db, repoID)
}
// MilestoneStats returns number of open and closed milestones of given repository.
func MilestoneStats(repoID int64) (open, closed int64) {
open, _ = x.Where("repo_id=? AND is_closed=?", repoID, false).Count(new(Milestone))
db.Model(new(Milestone)).Where("repo_id = ? AND is_closed = ?", repoID, false).Count(&open)
return open, CountRepoClosedMilestones(repoID)
}
@@ -227,26 +215,19 @@ func ChangeMilestoneStatus(m *Milestone, isClosed bool) (err error) {
return err
}
sess := x.NewSession()
defer sess.Close()
if err = sess.Begin(); err != nil {
return err
}
return db.Transaction(func(tx *gorm.DB) error {
m.IsClosed = isClosed
if err := updateMilestone(tx, m); err != nil {
return err
}
m.IsClosed = isClosed
if err = updateMilestone(sess, m); err != nil {
return err
}
repo.NumMilestones = int(countRepoMilestones(sess, repo.ID))
repo.NumClosedMilestones = int(countRepoClosedMilestones(sess, repo.ID))
if _, err = sess.ID(repo.ID).AllCols().Update(repo); err != nil {
return err
}
return sess.Commit()
repo.NumMilestones = int(countRepoMilestones(tx, repo.ID))
repo.NumClosedMilestones = int(countRepoClosedMilestones(tx, repo.ID))
return tx.Model(repo).Where("id = ?", repo.ID).Updates(repo).Error
})
}
func changeMilestoneIssueStats(e *xorm.Session, issue *Issue) error {
func changeMilestoneIssueStats(e *gorm.DB, issue *Issue) error {
if issue.MilestoneID == 0 {
return nil
}
@@ -270,20 +251,12 @@ func changeMilestoneIssueStats(e *xorm.Session, issue *Issue) error {
// ChangeMilestoneIssueStats updates the open/closed issues counter and progress
// for the milestone associated with the given issue.
func ChangeMilestoneIssueStats(issue *Issue) (err error) {
sess := x.NewSession()
defer sess.Close()
if err = sess.Begin(); err != nil {
return err
}
if err = changeMilestoneIssueStats(sess, issue); err != nil {
return err
}
return sess.Commit()
return db.Transaction(func(tx *gorm.DB) error {
return changeMilestoneIssueStats(tx, issue)
})
}
func changeMilestoneAssign(e *xorm.Session, issue *Issue, oldMilestoneID int64) error {
func changeMilestoneAssign(e *gorm.DB, issue *Issue, oldMilestoneID int64) error {
if oldMilestoneID > 0 {
m, err := getMilestoneByRepoID(e, issue.RepoID, oldMilestoneID)
if err != nil {
@@ -297,7 +270,9 @@ func changeMilestoneAssign(e *xorm.Session, issue *Issue, oldMilestoneID int64)
if err = updateMilestone(e, m); err != nil {
return err
} else if _, err = e.Exec("UPDATE `issue_user` SET milestone_id = 0 WHERE issue_id = ?", issue.ID); err != nil {
}
if err = e.Exec("UPDATE `issue_user` SET milestone_id = 0 WHERE issue_id = ?", issue.ID).Error; err != nil {
return err
}
@@ -317,7 +292,9 @@ func changeMilestoneAssign(e *xorm.Session, issue *Issue, oldMilestoneID int64)
if err = updateMilestone(e, m); err != nil {
return err
} else if _, err = e.Exec("UPDATE `issue_user` SET milestone_id = ? WHERE issue_id = ?", m.ID, issue.ID); err != nil {
}
if err = e.Exec("UPDATE `issue_user` SET milestone_id = ? WHERE issue_id = ?", m.ID, issue.ID).Error; err != nil {
return err
}
@@ -329,18 +306,11 @@ func changeMilestoneAssign(e *xorm.Session, issue *Issue, oldMilestoneID int64)
// ChangeMilestoneAssign changes assignment of milestone for issue.
func ChangeMilestoneAssign(doer *User, issue *Issue, oldMilestoneID int64) (err error) {
sess := x.NewSession()
defer sess.Close()
if err = sess.Begin(); err != nil {
return err
}
if err = changeMilestoneAssign(sess, issue, oldMilestoneID); err != nil {
return err
}
if err = sess.Commit(); err != nil {
return errors.Newf("commit: %v", err)
err = db.Transaction(func(tx *gorm.DB) error {
return changeMilestoneAssign(tx, issue, oldMilestoneID)
})
if err != nil {
return errors.Newf("transaction: %v", err)
}
var hookAction api.HookIssueAction
@@ -394,26 +364,21 @@ func DeleteMilestoneOfRepoByID(repoID, id int64) error {
return err
}
sess := x.NewSession()
defer sess.Close()
if err = sess.Begin(); err != nil {
return err
}
return db.Transaction(func(tx *gorm.DB) error {
if err := tx.Where("id = ?", m.ID).Delete(new(Milestone)).Error; err != nil {
return err
}
if _, err = sess.ID(m.ID).Delete(new(Milestone)); err != nil {
return err
}
repo.NumMilestones = int(countRepoMilestones(tx, repo.ID))
repo.NumClosedMilestones = int(countRepoClosedMilestones(tx, repo.ID))
if err := tx.Model(repo).Where("id = ?", repo.ID).Updates(repo).Error; err != nil {
return err
}
repo.NumMilestones = int(countRepoMilestones(sess, repo.ID))
repo.NumClosedMilestones = int(countRepoClosedMilestones(sess, repo.ID))
if _, err = sess.ID(repo.ID).AllCols().Update(repo); err != nil {
return err
}
if err := tx.Exec("UPDATE `issue` SET milestone_id = 0 WHERE milestone_id = ?", m.ID).Error; err != nil {
return err
}
if _, err = sess.Exec("UPDATE `issue` SET milestone_id = 0 WHERE milestone_id = ?", m.ID); err != nil {
return err
} else if _, err = sess.Exec("UPDATE `issue_user` SET milestone_id = 0 WHERE milestone_id = ?", m.ID); err != nil {
return err
}
return sess.Commit()
return tx.Exec("UPDATE `issue_user` SET milestone_id = 0 WHERE milestone_id = ?", m.ID).Error
})
}

View File

@@ -8,12 +8,11 @@ import (
"time"
"github.com/cockroachdb/errors"
"github.com/gogs/git-module"
"github.com/unknwon/com"
"gopkg.in/ini.v1"
"gorm.io/gorm"
log "unknwon.dev/clog/v2"
"xorm.io/xorm"
"github.com/gogs/git-module"
"gogs.io/gogs/internal/conf"
"gogs.io/gogs/internal/process"
@@ -41,41 +40,41 @@ func (err MirrorNotExist) Error() string {
type Mirror struct {
ID int64
RepoID int64
Repo *Repository `xorm:"-" json:"-" gorm:"-"`
Repo *Repository `gorm:"-" json:"-"`
Interval int // Hour.
EnablePrune bool `xorm:"NOT NULL DEFAULT true"`
EnablePrune bool `gorm:"not null;default:true"`
// Last and next sync time of Git data from upstream
LastSync time.Time `xorm:"-" json:"-" gorm:"-"`
LastSyncUnix int64 `xorm:"updated_unix"`
NextSync time.Time `xorm:"-" json:"-" gorm:"-"`
NextSyncUnix int64 `xorm:"next_update_unix"`
LastSync time.Time `gorm:"-" json:"-"`
LastSyncUnix int64 `gorm:"column:updated_unix"`
NextSync time.Time `gorm:"-" json:"-"`
NextSyncUnix int64 `gorm:"column:next_update_unix"`
address string `xorm:"-"`
address string `gorm:"-"`
}
func (m *Mirror) BeforeInsert() {
func (m *Mirror) BeforeCreate(tx *gorm.DB) error {
m.NextSyncUnix = m.NextSync.Unix()
return nil
}
func (m *Mirror) BeforeUpdate() {
func (m *Mirror) BeforeUpdate(tx *gorm.DB) error {
m.LastSyncUnix = m.LastSync.Unix()
m.NextSyncUnix = m.NextSync.Unix()
return nil
}
func (m *Mirror) AfterSet(colName string, _ xorm.Cell) {
var err error
switch colName {
case "repo_id":
func (m *Mirror) AfterFind(tx *gorm.DB) error {
if m.RepoID > 0 {
var err error
m.Repo, err = GetRepositoryByID(m.RepoID)
if err != nil {
log.Error("GetRepositoryByID [%d]: %v", m.ID, err)
}
case "updated_unix":
m.LastSync = time.Unix(m.LastSyncUnix, 0).Local()
case "next_update_unix":
m.NextSync = time.Unix(m.NextSyncUnix, 0).Local()
}
m.LastSync = time.Unix(m.LastSyncUnix, 0).Local()
m.NextSync = time.Unix(m.NextSyncUnix, 0).Local()
return nil
}
// ScheduleNextSync calculates and sets next sync time based on repository mirror setting.
@@ -277,34 +276,33 @@ func (m *Mirror) runSync() ([]*mirrorSyncResult, bool) {
return parseRemoteUpdateOutput(output), true
}
func getMirrorByRepoID(e Engine, repoID int64) (*Mirror, error) {
m := &Mirror{RepoID: repoID}
has, err := e.Get(m)
func getMirrorByRepoID(e *gorm.DB, repoID int64) (*Mirror, error) {
m := &Mirror{}
err := e.Where("repo_id = ?", repoID).First(m).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, MirrorNotExist{RepoID: repoID}
}
return nil, err
} else if !has {
return nil, MirrorNotExist{RepoID: repoID}
}
return m, nil
}
// GetMirrorByRepoID returns mirror information of a repository.
func GetMirrorByRepoID(repoID int64) (*Mirror, error) {
return getMirrorByRepoID(x, repoID)
return getMirrorByRepoID(db, repoID)
}
func updateMirror(e Engine, m *Mirror) error {
_, err := e.ID(m.ID).AllCols().Update(m)
return err
func updateMirror(e *gorm.DB, m *Mirror) error {
return e.Model(m).Where("id = ?", m.ID).Updates(m).Error
}
func UpdateMirror(m *Mirror) error {
return updateMirror(x, m)
return updateMirror(db, m)
}
func DeleteMirrorByRepoID(repoID int64) error {
_, err := x.Delete(&Mirror{RepoID: repoID})
return err
return db.Where("repo_id = ?", repoID).Delete(&Mirror{}).Error
}
// MirrorUpdate checks and updates mirror repositories.
@@ -317,17 +315,18 @@ func MirrorUpdate() {
log.Trace("Doing: MirrorUpdate")
if err := x.Where("next_update_unix<=?", time.Now().Unix()).Iterate(new(Mirror), func(idx int, bean any) error {
m := bean.(*Mirror)
var mirrors []*Mirror
if err := db.Where("next_update_unix <= ?", time.Now().Unix()).Find(&mirrors).Error; err != nil {
log.Error("MirrorUpdate: find mirrors: %v", err)
return
}
for _, m := range mirrors {
if m.Repo == nil {
log.Error("Disconnected mirror repository found: %d", m.ID)
return nil
continue
}
MirrorQueue.Add(m.RepoID)
return nil
}); err != nil {
log.Error("MirrorUpdate: %v", err)
}
}
@@ -454,7 +453,7 @@ func SyncMirrors() {
}
}
if _, err = x.Exec("UPDATE mirror SET updated_unix = ? WHERE repo_id = ?", time.Now().Unix(), m.RepoID); err != nil {
if err = db.Exec("UPDATE mirror SET updated_unix = ? WHERE repo_id = ?", time.Now().Unix(), m.RepoID).Error; err != nil {
log.Error("Update 'mirror.updated_unix' [%d]: %v", m.RepoID, err)
continue
}
@@ -469,7 +468,7 @@ func SyncMirrors() {
continue
}
if _, err = x.Exec("UPDATE repository SET updated_unix = ? WHERE id = ?", latestCommitTime.Unix(), m.RepoID); err != nil {
if err = db.Exec("UPDATE repository SET updated_unix = ? WHERE id = ?", latestCommitTime.Unix(), m.RepoID).Error; err != nil {
log.Error("Update 'repository.updated_unix' [%d]: %v", m.RepoID, err)
continue
}

View File

@@ -2,7 +2,6 @@ package database
import (
"context"
"database/sql"
"fmt"
"os"
"path"
@@ -13,33 +12,16 @@ import (
"github.com/cockroachdb/errors"
"gorm.io/gorm"
"gorm.io/gorm/logger"
"gorm.io/gorm/schema"
log "unknwon.dev/clog/v2"
"xorm.io/core"
"xorm.io/xorm"
"gogs.io/gogs/internal/conf"
"gogs.io/gogs/internal/database/migrations"
"gogs.io/gogs/internal/dbutil"
)
// Engine represents a XORM engine or session.
type Engine interface {
Delete(any) (int64, error)
Exec(...any) (sql.Result, error)
Find(any, ...any) error
Get(any) (bool, error)
ID(any) *xorm.Session
In(string, ...any) *xorm.Session
Insert(...any) (int64, error)
InsertOne(any) (int64, error)
Iterate(any, xorm.IterFunc) error
Sql(string, ...any) *xorm.Session
Table(any) *xorm.Session
Where(any, ...any) *xorm.Session
}
var (
x *xorm.Engine
db *gorm.DB
legacyTables []any
HasEngine bool
)
@@ -55,93 +37,90 @@ func init() {
new(ProtectBranch), new(ProtectBranchWhitelist),
new(Team), new(OrgUser), new(TeamUser), new(TeamRepo),
)
gonicNames := []string{"SSL"}
for _, name := range gonicNames {
core.LintGonicMapper[name] = true
}
}
func getEngine() (*xorm.Engine, error) {
Param := "?"
if strings.Contains(conf.Database.Name, Param) {
Param = "&"
}
driver := conf.Database.Type
connStr := ""
switch conf.Database.Type {
case "mysql":
conf.UseMySQL = true
if conf.Database.Host[0] == '/' { // looks like a unix socket
connStr = fmt.Sprintf("%s:%s@unix(%s)/%s%scharset=utf8mb4&parseTime=true",
conf.Database.User, conf.Database.Password, conf.Database.Host, conf.Database.Name, Param)
} else {
connStr = fmt.Sprintf("%s:%s@tcp(%s)/%s%scharset=utf8mb4&parseTime=true",
conf.Database.User, conf.Database.Password, conf.Database.Host, conf.Database.Name, Param)
}
engineParams := map[string]string{"rowFormat": "DYNAMIC"}
return xorm.NewEngineWithParams(conf.Database.Type, connStr, engineParams)
case "postgres":
conf.UsePostgreSQL = true
host, port := dbutil.ParsePostgreSQLHostPort(conf.Database.Host)
connStr = fmt.Sprintf("user='%s' password='%s' host='%s' port='%s' dbname='%s' sslmode='%s' search_path='%s'",
conf.Database.User, conf.Database.Password, host, port, conf.Database.Name, conf.Database.SSLMode, conf.Database.Schema)
driver = "pgx"
case "mssql":
conf.UseMSSQL = true
host, port := dbutil.ParseMSSQLHostPort(conf.Database.Host)
connStr = fmt.Sprintf("server=%s; port=%s; database=%s; user id=%s; password=%s;", host, port, conf.Database.Name, conf.Database.User, conf.Database.Password)
case "sqlite3":
func getGormDB(gormLogger logger.Writer) (*gorm.DB, error) {
if conf.Database.Type == "sqlite3" {
if err := os.MkdirAll(path.Dir(conf.Database.Path), os.ModePerm); err != nil {
return nil, errors.Newf("create directories: %v", err)
}
conf.UseSQLite3 = true
connStr = "file:" + conf.Database.Path + "?cache=shared&mode=rwc"
default:
return nil, errors.Newf("unknown database type: %s", conf.Database.Type)
}
return xorm.NewEngine(driver, connStr)
level := logger.Info
if conf.IsProdMode() {
level = logger.Warn
}
logger.Default = logger.New(gormLogger, logger.Config{
SlowThreshold: 100 * time.Millisecond,
LogLevel: level,
})
gormDB, err := dbutil.OpenDB(
conf.Database,
&gorm.Config{
SkipDefaultTransaction: true,
NamingStrategy: schema.NamingStrategy{
SingularTable: true,
},
NowFunc: func() time.Time {
return time.Now().UTC().Truncate(time.Microsecond)
},
},
)
if err != nil {
return nil, errors.Wrap(err, "open database")
}
sqlDB, err := gormDB.DB()
if err != nil {
return nil, errors.Wrap(err, "get underlying *sql.DB")
}
sqlDB.SetMaxOpenConns(conf.Database.MaxOpenConns)
sqlDB.SetMaxIdleConns(conf.Database.MaxIdleConns)
sqlDB.SetConnMaxLifetime(time.Minute)
switch conf.Database.Type {
case "postgres":
conf.UsePostgreSQL = true
case "mysql":
conf.UseMySQL = true
gormDB = gormDB.Set("gorm:table_options", "ENGINE=InnoDB").Session(&gorm.Session{})
case "sqlite3":
conf.UseSQLite3 = true
case "mssql":
conf.UseMSSQL = true
}
return gormDB, nil
}
func NewTestEngine() error {
x, err := getEngine()
var err error
db, err = getGormDB(&dbutil.Logger{Writer: os.Stdout})
if err != nil {
return errors.Newf("connect to database: %v", err)
}
if conf.UsePostgreSQL {
x.SetSchema(conf.Database.Schema)
for _, table := range legacyTables {
if db.Migrator().HasTable(table) {
continue
}
if err = db.Migrator().AutoMigrate(table); err != nil {
return errors.Wrap(err, "auto migrate")
}
}
x.SetMapper(core.GonicMapper{})
return x.StoreEngine("InnoDB").Sync2(legacyTables...)
return nil
}
func SetEngine() (*gorm.DB, error) {
var err error
x, err = getEngine()
if err != nil {
return nil, errors.Newf("connect to database: %v", err)
}
if conf.UsePostgreSQL {
x.SetSchema(conf.Database.Schema)
}
x.SetMapper(core.GonicMapper{})
var logPath string
if conf.HookMode {
logPath = filepath.Join(conf.Log.RootPath, "hooks", "xorm.log")
logPath = filepath.Join(conf.Log.RootPath, "hooks", "gorm.log")
} else {
logPath = filepath.Join(conf.Log.RootPath, "xorm.log")
logPath = filepath.Join(conf.Log.RootPath, "gorm.log")
}
sec := conf.File.Section("log.xorm")
sec := conf.File.Section("log.gorm")
fileWriter, err := log.NewFileWriter(logPath,
log.FileRotationConfig{
Rotate: sec.Key("ROTATE").MustBool(true),
@@ -151,20 +130,9 @@ func SetEngine() (*gorm.DB, error) {
},
)
if err != nil {
return nil, errors.Newf("create 'xorm.log': %v", err)
return nil, errors.Newf("create 'gorm.log': %v", err)
}
x.SetMaxOpenConns(conf.Database.MaxOpenConns)
x.SetMaxIdleConns(conf.Database.MaxIdleConns)
x.SetConnMaxLifetime(time.Second)
if conf.IsProdMode() {
x.SetLogger(xorm.NewSimpleLogger3(fileWriter, xorm.DEFAULT_LOG_PREFIX, xorm.DEFAULT_LOG_FLAG, core.LOG_ERR))
} else {
x.SetLogger(xorm.NewSimpleLogger(fileWriter))
}
x.ShowSQL(true)
var gormLogger logger.Writer
if conf.HookMode {
gormLogger = &dbutil.Logger{Writer: fileWriter}
@@ -174,23 +142,37 @@ func SetEngine() (*gorm.DB, error) {
return nil, errors.Wrap(err, "new log writer")
}
}
db, err = getGormDB(gormLogger)
if err != nil {
return nil, err
}
return NewConnection(gormLogger)
}
func NewEngine() error {
db, err := SetEngine()
gormDB, err := SetEngine()
if err != nil {
return err
}
if err = migrations.Migrate(db); err != nil {
if err = migrations.Migrate(gormDB); err != nil {
return errors.Newf("migrate: %v", err)
}
if err = x.StoreEngine("InnoDB").Sync2(legacyTables...); err != nil {
return errors.Wrap(err, "sync tables")
for _, table := range legacyTables {
if gormDB.Migrator().HasTable(table) {
continue
}
name := strings.TrimPrefix(fmt.Sprintf("%T", table), "*database.")
if err = gormDB.Migrator().AutoMigrate(table); err != nil {
return errors.Wrapf(err, "auto migrate %q", name)
}
log.Trace("Auto migrated %q", name)
}
HasEngine = true
return nil
}
@@ -208,33 +190,54 @@ type Statistic struct {
func GetStatistic(ctx context.Context) (stats Statistic) {
stats.Counter.User = Handle.Users().Count(ctx)
stats.Counter.Org = CountOrganizations()
stats.Counter.PublicKey, _ = x.Count(new(PublicKey))
var count int64
db.Model(new(PublicKey)).Count(&count)
stats.Counter.PublicKey = count
stats.Counter.Repo = CountRepositories(true)
stats.Counter.Watch, _ = x.Count(new(Watch))
stats.Counter.Star, _ = x.Count(new(Star))
stats.Counter.Action, _ = x.Count(new(Action))
stats.Counter.Access, _ = x.Count(new(Access))
stats.Counter.Issue, _ = x.Count(new(Issue))
stats.Counter.Comment, _ = x.Count(new(Comment))
db.Model(new(Watch)).Count(&count)
stats.Counter.Watch = count
db.Model(new(Star)).Count(&count)
stats.Counter.Star = count
db.Model(new(Action)).Count(&count)
stats.Counter.Action = count
db.Model(new(Access)).Count(&count)
stats.Counter.Access = count
db.Model(new(Issue)).Count(&count)
stats.Counter.Issue = count
db.Model(new(Comment)).Count(&count)
stats.Counter.Comment = count
stats.Counter.Oauth = 0
stats.Counter.Follow, _ = x.Count(new(Follow))
stats.Counter.Mirror, _ = x.Count(new(Mirror))
stats.Counter.Release, _ = x.Count(new(Release))
db.Model(new(Follow)).Count(&count)
stats.Counter.Follow = count
db.Model(new(Mirror)).Count(&count)
stats.Counter.Mirror = count
db.Model(new(Release)).Count(&count)
stats.Counter.Release = count
stats.Counter.LoginSource = Handle.LoginSources().Count(ctx)
stats.Counter.Webhook, _ = x.Count(new(Webhook))
stats.Counter.Milestone, _ = x.Count(new(Milestone))
stats.Counter.Label, _ = x.Count(new(Label))
stats.Counter.HookTask, _ = x.Count(new(HookTask))
stats.Counter.Team, _ = x.Count(new(Team))
stats.Counter.Attachment, _ = x.Count(new(Attachment))
db.Model(new(Webhook)).Count(&count)
stats.Counter.Webhook = count
db.Model(new(Milestone)).Count(&count)
stats.Counter.Milestone = count
db.Model(new(Label)).Count(&count)
stats.Counter.Label = count
db.Model(new(HookTask)).Count(&count)
stats.Counter.HookTask = count
db.Model(new(Team)).Count(&count)
stats.Counter.Team = count
db.Model(new(Attachment)).Count(&count)
stats.Counter.Attachment = count
return stats
}
func Ping() error {
if x == nil {
if db == nil {
return errors.New("database not available")
}
return x.Ping()
sqlDB, err := db.DB()
if err != nil {
return err
}
return sqlDB.Ping()
}
// The version table. Should have only one row with id==1

View File

@@ -6,8 +6,7 @@ import (
"strings"
"github.com/cockroachdb/errors"
"xorm.io/builder"
"xorm.io/xorm"
"gorm.io/gorm"
"gogs.io/gogs/internal/errutil"
"gogs.io/gogs/internal/repoutil"
@@ -26,32 +25,32 @@ func (org *User) IsOrgMember(uid int64) bool {
return org.IsOrganization() && IsOrganizationMember(org.ID, uid)
}
func (org *User) getTeam(e Engine, name string) (*Team, error) {
return getTeamOfOrgByName(e, org.ID, name)
func (org *User) getTeam(tx *gorm.DB, name string) (*Team, error) {
return getTeamOfOrgByName(tx, org.ID, name)
}
// GetTeamOfOrgByName returns named team of organization.
func (org *User) GetTeam(name string) (*Team, error) {
return org.getTeam(x, name)
return org.getTeam(db, name)
}
func (org *User) getOwnerTeam(e Engine) (*Team, error) {
return org.getTeam(e, ownerTeamName)
func (org *User) getOwnerTeam(tx *gorm.DB) (*Team, error) {
return org.getTeam(tx, ownerTeamName)
}
// GetOwnerTeam returns owner team of organization.
func (org *User) GetOwnerTeam() (*Team, error) {
return org.getOwnerTeam(x)
return org.getOwnerTeam(db)
}
func (org *User) getTeams(e Engine) (err error) {
org.Teams, err = getTeamsByOrgID(e, org.ID)
func (org *User) getTeams(tx *gorm.DB) (err error) {
org.Teams, err = getTeamsByOrgID(tx, org.ID)
return err
}
// GetTeams returns all teams that belong to organization.
func (org *User) GetTeams() error {
return org.getTeams(x)
return org.getTeams(db)
}
// TeamsHaveAccessToRepo returns all teams that have given access level to the repository.
@@ -86,13 +85,13 @@ func (org *User) RemoveMember(uid int64) error {
return RemoveOrgUser(org.ID, uid)
}
func (org *User) removeOrgRepo(e Engine, repoID int64) error {
return removeOrgRepo(e, org.ID, repoID)
func (org *User) removeOrgRepo(tx *gorm.DB, repoID int64) error {
return removeOrgRepo(tx, org.ID, repoID)
}
// RemoveOrgRepo removes all team-repository relations of organization.
func (org *User) RemoveOrgRepo(repoID int64) error {
return org.removeOrgRepo(x, repoID)
return org.removeOrgRepo(db, repoID)
}
// CreateOrganization creates record of a new organization.
@@ -121,52 +120,48 @@ func CreateOrganization(org, owner *User) (err error) {
org.NumTeams = 1
org.NumMembers = 1
sess := x.NewSession()
defer sess.Close()
if err = sess.Begin(); err != nil {
return err
}
return db.Transaction(func(tx *gorm.DB) error {
if err := tx.Create(org).Error; err != nil {
return errors.Newf("insert organization: %v", err)
}
_ = userutil.GenerateRandomAvatar(org.ID, org.Name, org.Email)
if _, err = sess.Insert(org); err != nil {
return errors.Newf("insert organization: %v", err)
}
_ = userutil.GenerateRandomAvatar(org.ID, org.Name, org.Email)
// Add initial creator to organization and owner team.
if err := tx.Create(&OrgUser{
UID: owner.ID,
OrgID: org.ID,
IsOwner: true,
NumTeams: 1,
}).Error; err != nil {
return errors.Newf("insert org-user relation: %v", err)
}
// Add initial creator to organization and owner team.
if _, err = sess.Insert(&OrgUser{
UID: owner.ID,
OrgID: org.ID,
IsOwner: true,
NumTeams: 1,
}); err != nil {
return errors.Newf("insert org-user relation: %v", err)
}
// Create default owner team.
t := &Team{
OrgID: org.ID,
LowerName: strings.ToLower(ownerTeamName),
Name: ownerTeamName,
Authorize: AccessModeOwner,
NumMembers: 1,
}
if err := tx.Create(t).Error; err != nil {
return errors.Newf("insert owner team: %v", err)
}
// Create default owner team.
t := &Team{
OrgID: org.ID,
LowerName: strings.ToLower(ownerTeamName),
Name: ownerTeamName,
Authorize: AccessModeOwner,
NumMembers: 1,
}
if _, err = sess.Insert(t); err != nil {
return errors.Newf("insert owner team: %v", err)
}
if err := tx.Create(&TeamUser{
UID: owner.ID,
OrgID: org.ID,
TeamID: t.ID,
}).Error; err != nil {
return errors.Newf("insert team-user relation: %v", err)
}
if _, err = sess.Insert(&TeamUser{
UID: owner.ID,
OrgID: org.ID,
TeamID: t.ID,
}); err != nil {
return errors.Newf("insert team-user relation: %v", err)
}
if err := os.MkdirAll(repoutil.UserPath(org.Name), os.ModePerm); err != nil {
return errors.Newf("create directory: %v", err)
}
if err = os.MkdirAll(repoutil.UserPath(org.Name), os.ModePerm); err != nil {
return errors.Newf("create directory: %v", err)
}
return sess.Commit()
return nil
})
}
// GetOrgByName returns organization by given name.
@@ -174,35 +169,36 @@ func GetOrgByName(name string) (*User, error) {
if name == "" {
return nil, ErrOrgNotExist
}
u := &User{
LowerName: strings.ToLower(name),
Type: UserTypeOrganization,
}
has, err := x.Get(u)
u := &User{}
err := db.Where("lower_name = ? AND type = ?", strings.ToLower(name), UserTypeOrganization).First(u).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, ErrOrgNotExist
}
return nil, err
} else if !has {
return nil, ErrOrgNotExist
}
return u, nil
}
// CountOrganizations returns number of organizations.
func CountOrganizations() int64 {
count, _ := x.Where("type=1").Count(new(User))
var count int64
db.Model(new(User)).Where("type = ?", UserTypeOrganization).Count(&count)
return count
}
// Organizations returns number of organizations in given page.
func Organizations(page, pageSize int) ([]*User, error) {
orgs := make([]*User, 0, pageSize)
return orgs, x.Limit(pageSize, (page-1)*pageSize).Where("type=1").Asc("id").Find(&orgs)
return orgs, db.Where("type = ?", UserTypeOrganization).
Offset((page - 1) * pageSize).Limit(pageSize).
Order("id ASC").Find(&orgs).Error
}
// deleteBeans deletes all given beans, beans should contain delete conditions.
func deleteBeans(e Engine, beans ...any) (err error) {
func deleteBeans(tx *gorm.DB, beans ...any) (err error) {
for i := range beans {
if _, err = e.Delete(beans[i]); err != nil {
if err = tx.Delete(beans[i]).Error; err != nil {
return err
}
}
@@ -216,20 +212,13 @@ func DeleteOrganization(org *User) error {
return err
}
sess := x.NewSession()
defer sess.Close()
if err = sess.Begin(); err != nil {
return err
}
if err = deleteBeans(sess,
&Team{OrgID: org.ID},
&OrgUser{OrgID: org.ID},
&TeamUser{OrgID: org.ID},
); err != nil {
return errors.Newf("deleteBeans: %v", err)
}
return sess.Commit()
return db.Transaction(func(tx *gorm.DB) error {
return deleteBeans(tx,
&Team{OrgID: org.ID},
&OrgUser{OrgID: org.ID},
&TeamUser{OrgID: org.ID},
)
})
}
// ________ ____ ___
@@ -251,84 +240,94 @@ type OrgUser struct {
// IsOrganizationOwner returns true if given user is in the owner team.
func IsOrganizationOwner(orgID, userID int64) bool {
has, _ := x.Where("is_owner = ?", true).And("uid = ?", userID).And("org_id = ?", orgID).Get(new(OrgUser))
return has
var count int64
db.Model(new(OrgUser)).Where("is_owner = ? AND uid = ? AND org_id = ?", true, userID, orgID).Count(&count)
return count > 0
}
// IsOrganizationMember returns true if given user is member of organization.
func IsOrganizationMember(orgID, uid int64) bool {
has, _ := x.Where("uid=?", uid).And("org_id=?", orgID).Get(new(OrgUser))
return has
var count int64
db.Model(new(OrgUser)).Where("uid = ? AND org_id = ?", uid, orgID).Count(&count)
return count > 0
}
// IsPublicMembership returns true if given user public his/her membership.
func IsPublicMembership(orgID, uid int64) bool {
has, _ := x.Where("uid=?", uid).And("org_id=?", orgID).And("is_public=?", true).Get(new(OrgUser))
return has
var count int64
db.Model(new(OrgUser)).Where("uid = ? AND org_id = ? AND is_public = ?", uid, orgID, true).Count(&count)
return count > 0
}
func getOrgsByUserID(sess *xorm.Session, userID int64, showAll bool) ([]*User, error) {
func getOrgsByUserID(tx *gorm.DB, userID int64, showAll bool) ([]*User, error) {
orgs := make([]*User, 0, 10)
query := tx.Table("`user`").
Joins("INNER JOIN `org_user` ON `org_user`.org_id = `user`.id").
Where("`org_user`.uid = ?", userID)
if !showAll {
sess.And("`org_user`.is_public=?", true)
query = query.Where("`org_user`.is_public = ?", true)
}
return orgs, sess.And("`org_user`.uid=?", userID).
Join("INNER", "`org_user`", "`org_user`.org_id=`user`.id").Find(&orgs)
return orgs, query.Find(&orgs).Error
}
// GetOrgsByUserID returns a list of organizations that the given user ID
// has joined.
func GetOrgsByUserID(userID int64, showAll bool) ([]*User, error) {
return getOrgsByUserID(x.NewSession(), userID, showAll)
return getOrgsByUserID(db, userID, showAll)
}
func getOwnedOrgsByUserID(sess *xorm.Session, userID int64) ([]*User, error) {
func getOwnedOrgsByUserID(tx *gorm.DB, userID int64) ([]*User, error) {
orgs := make([]*User, 0, 10)
return orgs, sess.Where("`org_user`.uid=?", userID).And("`org_user`.is_owner=?", true).
Join("INNER", "`org_user`", "`org_user`.org_id=`user`.id").Find(&orgs)
return orgs, tx.Table("`user`").
Joins("INNER JOIN `org_user` ON `org_user`.org_id = `user`.id").
Where("`org_user`.uid = ? AND `org_user`.is_owner = ?", userID, true).
Find(&orgs).Error
}
// GetOwnedOrgsByUserID returns a list of organizations are owned by given user ID.
func GetOwnedOrgsByUserID(userID int64) ([]*User, error) {
sess := x.NewSession()
return getOwnedOrgsByUserID(sess, userID)
return getOwnedOrgsByUserID(db, userID)
}
// GetOwnedOrganizationsByUserIDDesc returns a list of organizations are owned by
// given user ID, ordered descending by the given condition.
func GetOwnedOrgsByUserIDDesc(userID int64, desc string) ([]*User, error) {
sess := x.NewSession()
return getOwnedOrgsByUserID(sess.Desc(desc), userID)
orgs := make([]*User, 0, 10)
return orgs, db.Table("`user`").
Joins("INNER JOIN `org_user` ON `org_user`.org_id = `user`.id").
Where("`org_user`.uid = ? AND `org_user`.is_owner = ?", userID, true).
Order(desc + " DESC").
Find(&orgs).Error
}
func getOrgUsersByOrgID(e Engine, orgID int64, limit int) ([]*OrgUser, error) {
func getOrgUsersByOrgID(tx *gorm.DB, orgID int64, limit int) ([]*OrgUser, error) {
orgUsers := make([]*OrgUser, 0, 10)
sess := e.Where("org_id=?", orgID)
query := tx.Where("org_id = ?", orgID)
if limit > 0 {
sess = sess.Limit(limit)
query = query.Limit(limit)
}
return orgUsers, sess.Find(&orgUsers)
return orgUsers, query.Find(&orgUsers).Error
}
// GetOrgUsersByOrgID returns all organization-user relations by organization ID.
func GetOrgUsersByOrgID(orgID int64, limit int) ([]*OrgUser, error) {
return getOrgUsersByOrgID(x, orgID, limit)
return getOrgUsersByOrgID(db, orgID, limit)
}
// ChangeOrgUserStatus changes public or private membership status.
func ChangeOrgUserStatus(orgID, uid int64, public bool) error {
ou := new(OrgUser)
has, err := x.Where("uid=?", uid).And("org_id=?", orgID).Get(ou)
err := db.Where("uid = ? AND org_id = ?", uid, orgID).First(ou).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil
}
return err
} else if !has {
return nil
}
ou.IsPublic = public
_, err = x.Id(ou.ID).AllCols().Update(ou)
return err
return db.Model(ou).Where("id = ?", ou.ID).Updates(ou).Error
}
// AddOrgUser adds new user to given organization.
@@ -337,35 +336,33 @@ func AddOrgUser(orgID, uid int64) error {
return nil
}
sess := x.NewSession()
defer sess.Close()
if err := sess.Begin(); err != nil {
return err
}
return db.Transaction(func(tx *gorm.DB) error {
ou := &OrgUser{
UID: uid,
OrgID: orgID,
}
ou := &OrgUser{
UID: uid,
OrgID: orgID,
}
if err := tx.Create(ou).Error; err != nil {
return err
}
if err := tx.Exec("UPDATE `user` SET num_members = num_members + 1 WHERE id = ?", orgID).Error; err != nil {
return err
}
if _, err := sess.Insert(ou); err != nil {
return err
} else if _, err = sess.Exec("UPDATE `user` SET num_members = num_members + 1 WHERE id = ?", orgID); err != nil {
return err
}
return sess.Commit()
return nil
})
}
// RemoveOrgUser removes user from given organization.
func RemoveOrgUser(orgID, userID int64) error {
ou := new(OrgUser)
has, err := x.Where("uid=?", userID).And("org_id=?", orgID).Get(ou)
err := db.Where("uid = ? AND org_id = ?", userID, orgID).First(ou).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil
}
return errors.Newf("get org-user: %v", err)
} else if !has {
return nil
}
user, err := Handle.Users().GetByID(context.TODO(), userID)
@@ -394,71 +391,69 @@ func RemoveOrgUser(orgID, userID int64) error {
}
}
sess := x.NewSession()
defer sess.Close()
if err := sess.Begin(); err != nil {
return err
}
if _, err := sess.ID(ou.ID).Delete(ou); err != nil {
return err
} else if _, err = sess.Exec("UPDATE `user` SET num_members=num_members-1 WHERE id=?", orgID); err != nil {
return err
}
// Delete all repository accesses and unwatch them.
repoIDs := make([]int64, 0, len(repos))
for i := range repos {
repoIDs = append(repoIDs, repos[i].ID)
if err = watchRepo(sess, user.ID, repos[i].ID, false); err != nil {
return db.Transaction(func(tx *gorm.DB) error {
if err := tx.Where("id = ?", ou.ID).Delete(ou).Error; err != nil {
return err
}
}
if len(repoIDs) > 0 {
if _, err = sess.Where("user_id = ?", user.ID).In("repo_id", repoIDs).Delete(new(Access)); err != nil {
if err := tx.Exec("UPDATE `user` SET num_members = num_members - 1 WHERE id = ?", orgID).Error; err != nil {
return err
}
}
// Delete member in his/her teams.
teams, err := getUserTeams(sess, org.ID, user.ID)
if err != nil {
return err
}
for _, t := range teams {
if err = removeTeamMember(sess, org.ID, t.ID, user.ID); err != nil {
// Delete all repository accesses and unwatch them.
repoIDs := make([]int64, 0, len(repos))
for i := range repos {
repoIDs = append(repoIDs, repos[i].ID)
if err = watchRepo(tx, user.ID, repos[i].ID, false); err != nil {
return err
}
}
if len(repoIDs) > 0 {
if err := tx.Where("user_id = ? AND repo_id IN ?", user.ID, repoIDs).Delete(new(Access)).Error; err != nil {
return err
}
}
// Delete member in his/her teams.
teams, err := getUserTeams(tx, org.ID, user.ID)
if err != nil {
return err
}
}
for _, t := range teams {
if err = removeTeamMember(tx, org.ID, t.ID, user.ID); err != nil {
return err
}
}
return sess.Commit()
return nil
})
}
func removeOrgRepo(e Engine, orgID, repoID int64) error {
_, err := e.Delete(&TeamRepo{
OrgID: orgID,
RepoID: repoID,
})
return err
func removeOrgRepo(tx *gorm.DB, orgID, repoID int64) error {
return tx.Where("org_id = ? AND repo_id = ?", orgID, repoID).Delete(&TeamRepo{}).Error
}
// RemoveOrgRepo removes all team-repository relations of given organization.
func RemoveOrgRepo(orgID, repoID int64) error {
return removeOrgRepo(x, orgID, repoID)
return removeOrgRepo(db, orgID, repoID)
}
func (org *User) getUserTeams(e Engine, userID int64, cols ...string) ([]*Team, error) {
func (org *User) getUserTeams(tx *gorm.DB, userID int64, cols ...string) ([]*Team, error) {
teams := make([]*Team, 0, org.NumTeams)
return teams, e.Where("team_user.org_id = ?", org.ID).
And("team_user.uid = ?", userID).
Join("INNER", "team_user", "team_user.team_id = team.id").
Cols(cols...).Find(&teams)
query := tx.Table("team").
Joins("INNER JOIN team_user ON team_user.team_id = team.id").
Where("team_user.org_id = ? AND team_user.uid = ?", org.ID, userID)
if len(cols) > 0 {
query = query.Select(cols)
}
return teams, query.Find(&teams).Error
}
// GetUserTeamIDs returns of all team IDs of the organization that user is member of.
func (org *User) GetUserTeamIDs(userID int64) ([]int64, error) {
teams, err := org.getUserTeams(x, userID, "team.id")
teams, err := org.getUserTeams(db, userID, "team.id")
if err != nil {
return nil, errors.Newf("getUserTeams [%d]: %v", userID, err)
}
@@ -473,7 +468,7 @@ func (org *User) GetUserTeamIDs(userID int64) ([]int64, error) {
// GetTeams returns all teams that belong to organization,
// and that the user has joined.
func (org *User) GetUserTeams(userID int64) ([]*Team, error) {
return org.getUserTeams(x, userID)
return org.getUserTeams(db, userID)
}
// GetUserRepositories returns a range of repositories in organization which the user has access to,
@@ -489,7 +484,7 @@ func (org *User) GetUserRepositories(userID int64, page, pageSize int) ([]*Repos
}
var teamRepoIDs []int64
if err = x.Table("team_repo").In("team_id", teamIDs).Distinct("repo_id").Find(&teamRepoIDs); err != nil {
if err = db.Table("team_repo").Where("team_id IN ?", teamIDs).Distinct("repo_id").Find(&teamRepoIDs).Error; err != nil {
return nil, 0, errors.Newf("get team repository IDs: %v", err)
}
if len(teamRepoIDs) == 0 {
@@ -501,22 +496,18 @@ func (org *User) GetUserRepositories(userID int64, page, pageSize int) ([]*Repos
page = 1
}
repos := make([]*Repository, 0, pageSize)
if err = x.Where("owner_id = ?", org.ID).
And(builder.Or(
builder.And(builder.Expr("is_private = ?", false), builder.Expr("is_unlisted = ?", false)),
builder.In("id", teamRepoIDs))).
Desc("updated_unix").
Limit(pageSize, (page-1)*pageSize).
Find(&repos); err != nil {
if err = db.Where("owner_id = ?", org.ID).
Where(db.Where("is_private = ? AND is_unlisted = ?", false, false).Or("id IN ?", teamRepoIDs)).
Order("updated_unix DESC").
Limit(pageSize).Offset((page - 1) * pageSize).
Find(&repos).Error; err != nil {
return nil, 0, errors.Newf("get user repositories: %v", err)
}
repoCount, err := x.Where("owner_id = ?", org.ID).
And(builder.Or(
builder.Expr("is_private = ?", false),
builder.In("id", teamRepoIDs))).
Count(new(Repository))
if err != nil {
var repoCount int64
if err = db.Model(&Repository{}).Where("owner_id = ?", org.ID).
Where(db.Where("is_private = ?", false).Or("id IN ?", teamRepoIDs)).
Count(&repoCount).Error; err != nil {
return nil, 0, errors.Newf("count user repositories: %v", err)
}
@@ -534,7 +525,7 @@ func (org *User) GetUserMirrorRepositories(userID int64) ([]*Repository, error)
}
var teamRepoIDs []int64
err = x.Table("team_repo").In("team_id", teamIDs).Distinct("repo_id").Find(&teamRepoIDs)
err = db.Table("team_repo").Where("team_id IN ?", teamIDs).Distinct("repo_id").Find(&teamRepoIDs).Error
if err != nil {
return nil, errors.Newf("get team repository ids: %v", err)
}
@@ -544,12 +535,12 @@ func (org *User) GetUserMirrorRepositories(userID int64) ([]*Repository, error)
}
repos := make([]*Repository, 0, 10)
if err = x.Where("owner_id = ?", org.ID).
And("is_private = ?", false).
Or(builder.In("id", teamRepoIDs)).
And("is_mirror = ?", true). // Don't move up because it's an independent condition
Desc("updated_unix").
Find(&repos); err != nil {
if err = db.Where("owner_id = ?", org.ID).
Where("is_private = ?", false).
Or("id IN ?", teamRepoIDs).
Where("is_mirror = ?", true). // Don't move up because it's an independent condition
Order("updated_unix DESC").
Find(&repos).Error; err != nil {
return nil, errors.Newf("get user repositories: %v", err)
}
return repos, nil

View File

@@ -6,7 +6,7 @@ import (
"strings"
"github.com/cockroachdb/errors"
"xorm.io/xorm"
"gorm.io/gorm"
"gogs.io/gogs/internal/errutil"
)
@@ -16,7 +16,7 @@ const ownerTeamName = "Owners"
// Team represents a organization team.
type Team struct {
ID int64
OrgID int64 `xorm:"INDEX"`
OrgID int64 `gorm:"index"`
LowerName string
Name string
Description string
@@ -27,14 +27,12 @@ type Team struct {
NumMembers int
}
func (t *Team) AfterSet(colName string, _ xorm.Cell) {
switch colName {
case "num_repos":
// LEGACY [1.0]: this is backward compatibility bug fix for https://gogs.io/gogs/issues/3671
if t.NumRepos < 0 {
t.NumRepos = 0
}
func (t *Team) AfterFind(tx *gorm.DB) error {
// LEGACY [1.0]: this is backward compatibility bug fix for https://gogs.io/gogs/issues/3671
if t.NumRepos < 0 {
t.NumRepos = 0
}
return nil
}
// IsOwnerTeam returns true if team is owner team.
@@ -52,15 +50,15 @@ func (t *Team) IsMember(userID int64) bool {
return IsTeamMember(t.OrgID, t.ID, userID)
}
func (t *Team) getRepositories(e Engine) (err error) {
func (t *Team) getRepositories(tx *gorm.DB) (err error) {
teamRepos := make([]*TeamRepo, 0, t.NumRepos)
if err = x.Where("team_id=?", t.ID).Find(&teamRepos); err != nil {
if err = tx.Where("team_id = ?", t.ID).Find(&teamRepos).Error; err != nil {
return errors.Newf("get team-repos: %v", err)
}
t.Repos = make([]*Repository, 0, len(teamRepos))
for i := range teamRepos {
repo, err := getRepositoryByID(e, teamRepos[i].RepoID)
repo, err := getRepositoryByID(tx, teamRepos[i].RepoID)
if err != nil {
return errors.Newf("getRepositoryById(%d): %v", teamRepos[i].RepoID, err)
}
@@ -71,17 +69,17 @@ func (t *Team) getRepositories(e Engine) (err error) {
// GetRepositories returns all repositories in team of organization.
func (t *Team) GetRepositories() error {
return t.getRepositories(x)
return t.getRepositories(db)
}
func (t *Team) getMembers(e Engine) (err error) {
t.Members, err = getTeamMembers(e, t.ID)
func (t *Team) getMembers(tx *gorm.DB) (err error) {
t.Members, err = getTeamMembers(tx, t.ID)
return err
}
// GetMembers returns all members in team of organization.
func (t *Team) GetMembers() (err error) {
return t.getMembers(x)
return t.getMembers(db)
}
// AddMember adds new membership of the team to the organization,
@@ -95,34 +93,34 @@ func (t *Team) RemoveMember(uid int64) error {
return RemoveTeamMember(t.OrgID, t.ID, uid)
}
func (t *Team) hasRepository(e Engine, repoID int64) bool {
return hasTeamRepo(e, t.OrgID, t.ID, repoID)
func (t *Team) hasRepository(tx *gorm.DB, repoID int64) bool {
return hasTeamRepo(tx, t.OrgID, t.ID, repoID)
}
// HasRepository returns true if given repository belong to team.
func (t *Team) HasRepository(repoID int64) bool {
return t.hasRepository(x, repoID)
return t.hasRepository(db, repoID)
}
func (t *Team) addRepository(e Engine, repo *Repository) (err error) {
if err = addTeamRepo(e, t.OrgID, t.ID, repo.ID); err != nil {
func (t *Team) addRepository(tx *gorm.DB, repo *Repository) (err error) {
if err = addTeamRepo(tx, t.OrgID, t.ID, repo.ID); err != nil {
return err
}
t.NumRepos++
if _, err = e.ID(t.ID).AllCols().Update(t); err != nil {
if err = tx.Model(&Team{}).Where("id = ?", t.ID).Updates(t).Error; err != nil {
return errors.Newf("update team: %v", err)
}
if err = repo.recalculateTeamAccesses(e, 0); err != nil {
if err = repo.recalculateTeamAccesses(tx, 0); err != nil {
return errors.Newf("recalculateAccesses: %v", err)
}
if err = t.getMembers(e); err != nil {
if err = t.getMembers(tx); err != nil {
return errors.Newf("getMembers: %v", err)
}
for _, u := range t.Members {
if err = watchRepo(e, u.ID, repo.ID, true); err != nil {
if err = watchRepo(tx, u.ID, repo.ID, true); err != nil {
return errors.Newf("watchRepo: %v", err)
}
}
@@ -137,42 +135,34 @@ func (t *Team) AddRepository(repo *Repository) (err error) {
return nil
}
sess := x.NewSession()
defer sess.Close()
if err = sess.Begin(); err != nil {
return err
}
if err = t.addRepository(sess, repo); err != nil {
return err
}
return sess.Commit()
return db.Transaction(func(tx *gorm.DB) error {
return t.addRepository(tx, repo)
})
}
func (t *Team) removeRepository(e Engine, repo *Repository, recalculate bool) (err error) {
if err = removeTeamRepo(e, t.ID, repo.ID); err != nil {
func (t *Team) removeRepository(tx *gorm.DB, repo *Repository, recalculate bool) (err error) {
if err = removeTeamRepo(tx, t.ID, repo.ID); err != nil {
return err
}
t.NumRepos--
if _, err = e.ID(t.ID).AllCols().Update(t); err != nil {
if err = tx.Model(&Team{}).Where("id = ?", t.ID).Updates(t).Error; err != nil {
return err
}
// Don't need to recalculate when delete a repository from organization.
if recalculate {
if err = repo.recalculateTeamAccesses(e, t.ID); err != nil {
if err = repo.recalculateTeamAccesses(tx, t.ID); err != nil {
return err
}
}
if err = t.getMembers(e); err != nil {
if err = t.getMembers(tx); err != nil {
return errors.Newf("get team members: %v", err)
}
// TODO: Delete me when this method is migrated to use GORM.
userAccessMode := func(e Engine, userID int64, repo *Repository) (AccessMode, error) {
userAccessMode := func(tx *gorm.DB, userID int64, repo *Repository) (AccessMode, error) {
mode := AccessModeNone
// Everyone has read access to public repository
if !repo.IsPrivate {
@@ -191,26 +181,29 @@ func (t *Team) removeRepository(e Engine, repo *Repository, recalculate bool) (e
UserID: userID,
RepoID: repo.ID,
}
if has, err := e.Get(access); !has || err != nil {
err := tx.Where("user_id = ? AND repo_id = ?", userID, repo.ID).First(access).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
return mode, nil
} else if err != nil {
return mode, err
}
return access.Mode, nil
}
hasAccess := func(e Engine, userID int64, repo *Repository, testMode AccessMode) (bool, error) {
mode, err := userAccessMode(e, userID, repo)
hasAccess := func(tx *gorm.DB, userID int64, repo *Repository, testMode AccessMode) (bool, error) {
mode, err := userAccessMode(tx, userID, repo)
return mode >= testMode, err
}
for _, member := range t.Members {
has, err := hasAccess(e, member.ID, repo, AccessModeRead)
has, err := hasAccess(tx, member.ID, repo, AccessModeRead)
if err != nil {
return err
} else if has {
continue
}
if err = watchRepo(e, member.ID, repo.ID, false); err != nil {
if err = watchRepo(tx, member.ID, repo.ID, false); err != nil {
return err
}
}
@@ -229,17 +222,9 @@ func (t *Team) RemoveRepository(repoID int64) error {
return err
}
sess := x.NewSession()
defer sess.Close()
if err = sess.Begin(); err != nil {
return err
}
if err = t.removeRepository(sess, repo, true); err != nil {
return err
}
return sess.Commit()
return db.Transaction(func(tx *gorm.DB) error {
return t.removeRepository(tx, repo, true)
})
}
var reservedTeamNames = map[string]struct{}{
@@ -264,37 +249,30 @@ func NewTeam(t *Team) error {
return err
}
has, err := x.Id(t.OrgID).Get(new(User))
if err != nil {
return err
} else if !has {
err := db.Where("id = ?", t.OrgID).First(new(User)).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
return ErrOrgNotExist
} else if err != nil {
return err
}
t.LowerName = strings.ToLower(t.Name)
existingTeam := Team{}
has, err = x.Where("org_id=?", t.OrgID).And("lower_name=?", t.LowerName).Get(&existingTeam)
if err != nil {
return err
} else if has {
err = db.Where("org_id = ? AND lower_name = ?", t.OrgID, t.LowerName).First(&existingTeam).Error
if err == nil {
return ErrTeamAlreadyExist{existingTeam.ID, t.OrgID, t.LowerName}
}
sess := x.NewSession()
defer sess.Close()
if err = sess.Begin(); err != nil {
} else if !errors.Is(err, gorm.ErrRecordNotFound) {
return err
}
if _, err = sess.Insert(t); err != nil {
return err
}
return db.Transaction(func(tx *gorm.DB) error {
if err := tx.Create(t).Error; err != nil {
return err
}
// Update organization number of teams.
if _, err = sess.Exec("UPDATE `user` SET num_teams=num_teams+1 WHERE id = ?", t.OrgID); err != nil {
return err
}
return sess.Commit()
// Update organization number of teams.
return tx.Exec("UPDATE `user` SET num_teams=num_teams+1 WHERE id = ?", t.OrgID).Error
})
}
var _ errutil.NotFound = (*ErrTeamNotExist)(nil)
@@ -316,49 +294,46 @@ func (ErrTeamNotExist) NotFound() bool {
return true
}
func getTeamOfOrgByName(e Engine, orgID int64, name string) (*Team, error) {
t := &Team{
OrgID: orgID,
LowerName: strings.ToLower(name),
}
has, err := e.Get(t)
if err != nil {
return nil, err
} else if !has {
func getTeamOfOrgByName(tx *gorm.DB, orgID int64, name string) (*Team, error) {
t := new(Team)
err := tx.Where("org_id = ? AND lower_name = ?", orgID, strings.ToLower(name)).First(t).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, ErrTeamNotExist{args: map[string]any{"orgID": orgID, "name": name}}
} else if err != nil {
return nil, err
}
return t, nil
}
// GetTeamOfOrgByName returns team by given team name and organization.
func GetTeamOfOrgByName(orgID int64, name string) (*Team, error) {
return getTeamOfOrgByName(x, orgID, name)
return getTeamOfOrgByName(db, orgID, name)
}
func getTeamByID(e Engine, teamID int64) (*Team, error) {
func getTeamByID(tx *gorm.DB, teamID int64) (*Team, error) {
t := new(Team)
has, err := e.ID(teamID).Get(t)
if err != nil {
return nil, err
} else if !has {
err := tx.Where("id = ?", teamID).First(t).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, ErrTeamNotExist{args: map[string]any{"teamID": teamID}}
} else if err != nil {
return nil, err
}
return t, nil
}
// GetTeamByID returns team by given ID.
func GetTeamByID(teamID int64) (*Team, error) {
return getTeamByID(x, teamID)
return getTeamByID(db, teamID)
}
func getTeamsByOrgID(e Engine, orgID int64) ([]*Team, error) {
func getTeamsByOrgID(tx *gorm.DB, orgID int64) ([]*Team, error) {
teams := make([]*Team, 0, 3)
return teams, e.Where("org_id = ?", orgID).Find(&teams)
return teams, tx.Where("org_id = ?", orgID).Find(&teams).Error
}
// GetTeamsByOrgID returns all teams belong to given organization.
func GetTeamsByOrgID(orgID int64) ([]*Team, error) {
return getTeamsByOrgID(x, orgID)
return getTeamsByOrgID(db, orgID)
}
// UpdateTeam updates information of team.
@@ -371,39 +346,35 @@ func UpdateTeam(t *Team, authChanged bool) (err error) {
t.Description = t.Description[:255]
}
sess := x.NewSession()
defer sess.Close()
if err = sess.Begin(); err != nil {
return err
}
t.LowerName = strings.ToLower(t.Name)
existingTeam := new(Team)
has, err := x.Where("org_id=?", t.OrgID).And("lower_name=?", t.LowerName).And("id!=?", t.ID).Get(existingTeam)
if err != nil {
return err
} else if has {
err = db.Where("org_id = ? AND lower_name = ? AND id != ?", t.OrgID, t.LowerName, t.ID).First(existingTeam).Error
if err == nil {
return ErrTeamAlreadyExist{existingTeam.ID, t.OrgID, t.LowerName}
} else if !errors.Is(err, gorm.ErrRecordNotFound) {
return err
}
if _, err = sess.ID(t.ID).AllCols().Update(t); err != nil {
return errors.Newf("update: %v", err)
}
// Update access for team members if needed.
if authChanged {
if err = t.getRepositories(sess); err != nil {
return errors.Newf("getRepositories:%v", err)
return db.Transaction(func(tx *gorm.DB) error {
if err := tx.Model(&Team{}).Where("id = ?", t.ID).Updates(t).Error; err != nil {
return errors.Newf("update: %v", err)
}
for _, repo := range t.Repos {
if err = repo.recalculateTeamAccesses(sess, 0); err != nil {
return errors.Newf("recalculateTeamAccesses: %v", err)
// Update access for team members if needed.
if authChanged {
if err := t.getRepositories(tx); err != nil {
return errors.Newf("getRepositories:%v", err)
}
for _, repo := range t.Repos {
if err := repo.recalculateTeamAccesses(tx, 0); err != nil {
return errors.Newf("recalculateTeamAccesses: %v", err)
}
}
}
}
return sess.Commit()
return nil
})
}
// DeleteTeam deletes given team.
@@ -419,34 +390,26 @@ func DeleteTeam(t *Team) error {
return err
}
sess := x.NewSession()
defer sess.Close()
if err = sess.Begin(); err != nil {
return err
}
return db.Transaction(func(tx *gorm.DB) error {
// Delete all accesses.
for _, repo := range t.Repos {
if err := repo.recalculateTeamAccesses(tx, t.ID); err != nil {
return err
}
}
// Delete all accesses.
for _, repo := range t.Repos {
if err = repo.recalculateTeamAccesses(sess, t.ID); err != nil {
// Delete team-user.
if err := tx.Where("org_id = ? AND team_id = ?", org.ID, t.ID).Delete(new(TeamUser)).Error; err != nil {
return err
}
}
// Delete team-user.
if _, err = sess.Where("org_id=?", org.ID).Where("team_id=?", t.ID).Delete(new(TeamUser)); err != nil {
return err
}
// Delete team.
if _, err = sess.ID(t.ID).Delete(new(Team)); err != nil {
return err
}
// Update organization number of teams.
if _, err = sess.Exec("UPDATE `user` SET num_teams=num_teams-1 WHERE id=?", t.OrgID); err != nil {
return err
}
return sess.Commit()
// Delete team.
if err := tx.Where("id = ?", t.ID).Delete(new(Team)).Error; err != nil {
return err
}
// Update organization number of teams.
return tx.Exec("UPDATE `user` SET num_teams=num_teams-1 WHERE id = ?", t.OrgID).Error
})
}
// ___________ ____ ___
@@ -459,31 +422,30 @@ func DeleteTeam(t *Team) error {
// TeamUser represents an team-user relation.
type TeamUser struct {
ID int64
OrgID int64 `xorm:"INDEX"`
TeamID int64 `xorm:"UNIQUE(s)"`
UID int64 `xorm:"UNIQUE(s)"`
OrgID int64 `gorm:"index"`
TeamID int64 `gorm:"uniqueIndex:team_user_team_id_uid"`
UID int64 `gorm:"uniqueIndex:team_user_team_id_uid"`
}
func isTeamMember(e Engine, orgID, teamID, uid int64) bool {
has, _ := e.Where("org_id=?", orgID).And("team_id=?", teamID).And("uid=?", uid).Get(new(TeamUser))
return has
func isTeamMember(tx *gorm.DB, orgID, teamID, uid int64) bool {
err := tx.Where("org_id = ? AND team_id = ? AND uid = ?", orgID, teamID, uid).First(new(TeamUser)).Error
return err == nil
}
// IsTeamMember returns true if given user is a member of team.
func IsTeamMember(orgID, teamID, uid int64) bool {
return isTeamMember(x, orgID, teamID, uid)
return isTeamMember(db, orgID, teamID, uid)
}
func getTeamMembers(e Engine, teamID int64) (_ []*User, err error) {
func getTeamMembers(tx *gorm.DB, teamID int64) (_ []*User, err error) {
teamUsers := make([]*TeamUser, 0, 10)
if err = e.Sql("SELECT `id`, `org_id`, `team_id`, `uid` FROM `team_user` WHERE team_id = ?", teamID).
Find(&teamUsers); err != nil {
if err = tx.Select("id, org_id, team_id, uid").Where("team_id = ?", teamID).Find(&teamUsers).Error; err != nil {
return nil, errors.Newf("get team-users: %v", err)
}
members := make([]*User, 0, len(teamUsers))
for i := range teamUsers {
member := new(User)
if _, err = e.ID(teamUsers[i].UID).Get(member); err != nil {
if err = tx.Where("id = ?", teamUsers[i].UID).First(member).Error; err != nil {
return nil, errors.Newf("get user '%d': %v", teamUsers[i].UID, err)
}
members = append(members, member)
@@ -493,12 +455,12 @@ func getTeamMembers(e Engine, teamID int64) (_ []*User, err error) {
// GetTeamMembers returns all members in given team of organization.
func GetTeamMembers(teamID int64) ([]*User, error) {
return getTeamMembers(x, teamID)
return getTeamMembers(db, teamID)
}
func getUserTeams(e Engine, orgID, userID int64) ([]*Team, error) {
func getUserTeams(tx *gorm.DB, orgID, userID int64) ([]*Team, error) {
teamUsers := make([]*TeamUser, 0, 5)
if err := e.Where("uid = ?", userID).And("org_id = ?", orgID).Find(&teamUsers); err != nil {
if err := tx.Where("uid = ? AND org_id = ?", userID, orgID).Find(&teamUsers).Error; err != nil {
return nil, err
}
@@ -509,12 +471,12 @@ func getUserTeams(e Engine, orgID, userID int64) ([]*Team, error) {
teamIDs[len(teamUsers)] = -1
teams := make([]*Team, 0, len(teamIDs))
return teams, e.Where("org_id = ?", orgID).In("id", teamIDs).Find(&teams)
return teams, tx.Where("org_id = ? AND id IN ?", orgID, teamIDs).Find(&teams).Error
}
// GetUserTeams returns all teams that user belongs to in given organization.
func GetUserTeams(orgID, userID int64) ([]*Team, error) {
return getUserTeams(x, orgID, userID)
return getUserTeams(db, orgID, userID)
}
// AddTeamMember adds new membership of given team to given organization,
@@ -539,53 +501,46 @@ func AddTeamMember(orgID, teamID, userID int64) error {
return err
}
sess := x.NewSession()
defer sess.Close()
if err = sess.Begin(); err != nil {
return err
}
tu := &TeamUser{
UID: userID,
OrgID: orgID,
TeamID: teamID,
}
if _, err = sess.Insert(tu); err != nil {
return err
} else if _, err = sess.ID(t.ID).Update(t); err != nil {
return err
}
// Give access to team repositories.
for _, repo := range t.Repos {
if err = repo.recalculateTeamAccesses(sess, 0); err != nil {
return db.Transaction(func(tx *gorm.DB) error {
tu := &TeamUser{
UID: userID,
OrgID: orgID,
TeamID: teamID,
}
if err := tx.Create(tu).Error; err != nil {
return err
}
if err := tx.Model(&Team{}).Where("id = ?", t.ID).Updates(t).Error; err != nil {
return err
}
}
// We make sure it exists before.
ou := new(OrgUser)
if _, err = sess.Where("uid = ?", userID).And("org_id = ?", orgID).Get(ou); err != nil {
return err
}
ou.NumTeams++
if t.IsOwnerTeam() {
ou.IsOwner = true
}
if _, err = sess.ID(ou.ID).AllCols().Update(ou); err != nil {
return err
}
// Give access to team repositories.
for _, repo := range t.Repos {
if err := repo.recalculateTeamAccesses(tx, 0); err != nil {
return err
}
}
return sess.Commit()
// We make sure it exists before.
ou := new(OrgUser)
if err := tx.Where("uid = ? AND org_id = ?", userID, orgID).First(ou).Error; err != nil {
return err
}
ou.NumTeams++
if t.IsOwnerTeam() {
ou.IsOwner = true
}
return tx.Model(&OrgUser{}).Where("id = ?", ou.ID).Updates(ou).Error
})
}
func removeTeamMember(e Engine, orgID, teamID, uid int64) error {
if !isTeamMember(e, orgID, teamID, uid) {
func removeTeamMember(tx *gorm.DB, orgID, teamID, uid int64) error {
if !isTeamMember(tx, orgID, teamID, uid) {
return nil
}
// Get team and its repositories.
t, err := getTeamByID(e, teamID)
t, err := getTeamByID(tx, teamID)
if err != nil {
return err
}
@@ -597,12 +552,12 @@ func removeTeamMember(e Engine, orgID, teamID, uid int64) error {
t.NumMembers--
if err = t.getRepositories(e); err != nil {
if err = t.getRepositories(tx); err != nil {
return err
}
// Get organization.
org, err := getUserByID(e, orgID)
org, err := getUserByID(tx, orgID)
if err != nil {
return err
}
@@ -612,46 +567,37 @@ func removeTeamMember(e Engine, orgID, teamID, uid int64) error {
OrgID: orgID,
TeamID: teamID,
}
if _, err := e.Delete(tu); err != nil {
if err := tx.Where("uid = ? AND org_id = ? AND team_id = ?", uid, orgID, teamID).Delete(tu).Error; err != nil {
return err
} else if _, err = e.ID(t.ID).AllCols().Update(t); err != nil {
}
if err = tx.Model(&Team{}).Where("id = ?", t.ID).Updates(t).Error; err != nil {
return err
}
// Delete access to team repositories.
for _, repo := range t.Repos {
if err = repo.recalculateTeamAccesses(e, 0); err != nil {
if err = repo.recalculateTeamAccesses(tx, 0); err != nil {
return err
}
}
// This must exist.
ou := new(OrgUser)
_, err = e.Where("uid = ?", uid).And("org_id = ?", org.ID).Get(ou)
if err != nil {
if err = tx.Where("uid = ? AND org_id = ?", uid, org.ID).First(ou).Error; err != nil {
return err
}
ou.NumTeams--
if t.IsOwnerTeam() {
ou.IsOwner = false
}
if _, err = e.ID(ou.ID).AllCols().Update(ou); err != nil {
return err
}
return nil
return tx.Model(&OrgUser{}).Where("id = ?", ou.ID).Updates(ou).Error
}
// RemoveTeamMember removes member from given team of given organization.
func RemoveTeamMember(orgID, teamID, uid int64) error {
sess := x.NewSession()
defer sess.Close()
if err := sess.Begin(); err != nil {
return err
}
if err := removeTeamMember(sess, orgID, teamID, uid); err != nil {
return err
}
return sess.Commit()
return db.Transaction(func(tx *gorm.DB) error {
return removeTeamMember(tx, orgID, teamID, uid)
})
}
// ___________ __________
@@ -664,54 +610,49 @@ func RemoveTeamMember(orgID, teamID, uid int64) error {
// TeamRepo represents an team-repository relation.
type TeamRepo struct {
ID int64
OrgID int64 `xorm:"INDEX"`
TeamID int64 `xorm:"UNIQUE(s)"`
RepoID int64 `xorm:"UNIQUE(s)"`
OrgID int64 `gorm:"index"`
TeamID int64 `gorm:"uniqueIndex:team_repo_team_id_repo_id"`
RepoID int64 `gorm:"uniqueIndex:team_repo_team_id_repo_id"`
}
func hasTeamRepo(e Engine, orgID, teamID, repoID int64) bool {
has, _ := e.Where("org_id = ?", orgID).And("team_id = ?", teamID).And("repo_id = ?", repoID).Get(new(TeamRepo))
return has
func hasTeamRepo(tx *gorm.DB, orgID, teamID, repoID int64) bool {
err := tx.Where("org_id = ? AND team_id = ? AND repo_id = ?", orgID, teamID, repoID).First(new(TeamRepo)).Error
return err == nil
}
// HasTeamRepo returns true if given team has access to the repository of the organization.
func HasTeamRepo(orgID, teamID, repoID int64) bool {
return hasTeamRepo(x, orgID, teamID, repoID)
return hasTeamRepo(db, orgID, teamID, repoID)
}
func addTeamRepo(e Engine, orgID, teamID, repoID int64) error {
_, err := e.InsertOne(&TeamRepo{
func addTeamRepo(tx *gorm.DB, orgID, teamID, repoID int64) error {
return tx.Create(&TeamRepo{
OrgID: orgID,
TeamID: teamID,
RepoID: repoID,
})
return err
}).Error
}
// AddTeamRepo adds new repository relation to team.
func AddTeamRepo(orgID, teamID, repoID int64) error {
return addTeamRepo(x, orgID, teamID, repoID)
return addTeamRepo(db, orgID, teamID, repoID)
}
func removeTeamRepo(e Engine, teamID, repoID int64) error {
_, err := e.Delete(&TeamRepo{
TeamID: teamID,
RepoID: repoID,
})
return err
func removeTeamRepo(tx *gorm.DB, teamID, repoID int64) error {
return tx.Where("team_id = ? AND repo_id = ?", teamID, repoID).Delete(new(TeamRepo)).Error
}
// RemoveTeamRepo deletes repository relation to team.
func RemoveTeamRepo(teamID, repoID int64) error {
return removeTeamRepo(x, teamID, repoID)
return removeTeamRepo(db, teamID, repoID)
}
// GetTeamsHaveAccessToRepo returns all teams in an organization that have given access level to the repository.
func GetTeamsHaveAccessToRepo(orgID, repoID int64, mode AccessMode) ([]*Team, error) {
teams := make([]*Team, 0, 5)
return teams, x.Where("team.authorize >= ?", mode).
Join("INNER", "team_repo", "team_repo.team_id = team.id").
And("team_repo.org_id = ?", orgID).
And("team_repo.repo_id = ?", repoID).
Find(&teams)
return teams, db.Table("team").
Where("team.authorize >= ?", mode).
Joins("INNER JOIN team_repo ON team_repo.team_id = team.id").
Where("team_repo.org_id = ? AND team_repo.repo_id = ?", orgID, repoID).
Find(&teams).Error
}

View File

@@ -9,8 +9,8 @@ import (
"github.com/cockroachdb/errors"
"github.com/unknwon/com"
"gorm.io/gorm"
log "unknwon.dev/clog/v2"
"xorm.io/xorm"
"github.com/gogs/git-module"
api "github.com/gogs/go-gogs-client"
@@ -71,35 +71,31 @@ func (pr *PullRequest) BeforeUpdate() {
}
// Note: don't try to get Issue because will end up recursive querying.
func (pr *PullRequest) AfterSet(colName string, _ xorm.Cell) {
switch colName {
case "merged_unix":
if !pr.HasMerged {
return
}
func (pr *PullRequest) AfterFind(tx *gorm.DB) error {
if pr.HasMerged {
pr.Merged = time.Unix(pr.MergedUnix, 0).Local()
}
return nil
}
// Note: don't try to get Issue because will end up recursive querying.
func (pr *PullRequest) loadAttributes(e Engine) (err error) {
func (pr *PullRequest) loadAttributes(db *gorm.DB) (err error) {
if pr.HeadRepo == nil {
pr.HeadRepo, err = getRepositoryByID(e, pr.HeadRepoID)
pr.HeadRepo, err = getRepositoryByID(db, pr.HeadRepoID)
if err != nil && !IsErrRepoNotExist(err) {
return errors.Newf("get head repository by ID: %v", err)
}
}
if pr.BaseRepo == nil {
pr.BaseRepo, err = getRepositoryByID(e, pr.BaseRepoID)
pr.BaseRepo, err = getRepositoryByID(db, pr.BaseRepoID)
if err != nil {
return errors.Newf("get base repository by ID: %v", err)
}
}
if pr.HasMerged && pr.Merger == nil {
pr.Merger, err = getUserByID(e, pr.MergerID)
pr.Merger, err = getUserByID(db, pr.MergerID)
if IsErrUserNotExist(err) {
pr.MergerID = -1
pr.Merger = NewGhostUser()
@@ -112,7 +108,7 @@ func (pr *PullRequest) loadAttributes(e Engine) (err error) {
}
func (pr *PullRequest) LoadAttributes() error {
return pr.loadAttributes(x)
return pr.loadAttributes(db)
}
func (pr *PullRequest) LoadIssue() (err error) {
@@ -199,198 +195,190 @@ func (pr *PullRequest) Merge(doer *User, baseGitRepo *git.Repository, mergeStyle
go AddTestPullRequestTask(doer, pr.BaseRepo.ID, pr.BaseBranch, false)
}()
sess := x.NewSession()
defer sess.Close()
if err = sess.Begin(); err != nil {
return err
}
if err = pr.Issue.changeStatus(sess, doer, pr.Issue.Repo, true); err != nil {
return errors.Newf("Issue.changeStatus: %v", err)
}
headRepoPath := RepoPath(pr.HeadUserName, pr.HeadRepo.Name)
headGitRepo, err := git.Open(headRepoPath)
if err != nil {
return errors.Newf("open repository: %v", err)
}
// Create temporary directory to store temporary copy of the base repository,
// and clean it up when operation finished regardless of succeed or not.
tmpBasePath := filepath.Join(conf.Server.AppDataPath, "tmp", "repos", com.ToStr(time.Now().Nanosecond())+".git")
if err = os.MkdirAll(filepath.Dir(tmpBasePath), os.ModePerm); err != nil {
return err
}
defer func() {
_ = os.RemoveAll(filepath.Dir(tmpBasePath))
}()
// Clone the base repository to the defined temporary directory,
// and checks out to base branch directly.
var stderr string
if _, stderr, err = process.ExecTimeout(5*time.Minute,
fmt.Sprintf("PullRequest.Merge (git clone): %s", tmpBasePath),
"git", "clone", "-b", pr.BaseBranch, baseGitRepo.Path(), tmpBasePath); err != nil {
return errors.Newf("git clone: %s", stderr)
}
// Add remote which points to the head repository.
if _, stderr, err = process.ExecDir(-1, tmpBasePath,
fmt.Sprintf("PullRequest.Merge (git remote add): %s", tmpBasePath),
"git", "remote", "add", "head_repo", headRepoPath); err != nil {
return errors.Newf("git remote add [%s -> %s]: %s", headRepoPath, tmpBasePath, stderr)
}
// Fetch information from head repository to the temporary copy.
if _, stderr, err = process.ExecDir(-1, tmpBasePath,
fmt.Sprintf("PullRequest.Merge (git fetch): %s", tmpBasePath),
"git", "fetch", "head_repo"); err != nil {
return errors.Newf("git fetch [%s -> %s]: %s", headRepoPath, tmpBasePath, stderr)
}
remoteHeadBranch := "head_repo/" + pr.HeadBranch
// Check if merge style is allowed, reset to default style if not
if mergeStyle == MergeStyleRebase && !pr.BaseRepo.PullsAllowRebase {
mergeStyle = MergeStyleRegular
}
switch mergeStyle {
case MergeStyleRegular: // Create merge commit
// Merge changes from head branch.
if _, stderr, err = process.ExecDir(-1, tmpBasePath,
fmt.Sprintf("PullRequest.Merge (git merge --no-ff --no-commit): %s", tmpBasePath),
"git", "merge", "--no-ff", "--no-commit", remoteHeadBranch); err != nil {
return errors.Newf("git merge --no-ff --no-commit [%s]: %v - %s", tmpBasePath, err, stderr)
return db.Transaction(func(tx *gorm.DB) error {
if err := pr.Issue.changeStatus(tx, doer, pr.Issue.Repo, true); err != nil {
return errors.Newf("Issue.changeStatus: %v", err)
}
// Create a merge commit for the base branch.
if _, stderr, err = process.ExecDir(-1, tmpBasePath,
fmt.Sprintf("PullRequest.Merge (git merge): %s", tmpBasePath),
"git", "commit", fmt.Sprintf("--author='%s <%s>'", doer.DisplayName(), doer.Email),
"-m", fmt.Sprintf("Merge branch '%s' of %s/%s into %s", pr.HeadBranch, pr.HeadUserName, pr.HeadRepo.Name, pr.BaseBranch),
"-m", commitDescription); err != nil {
return errors.Newf("git commit [%s]: %v - %s", tmpBasePath, err, stderr)
headRepoPath := RepoPath(pr.HeadUserName, pr.HeadRepo.Name)
headGitRepo, err := git.Open(headRepoPath)
if err != nil {
return errors.Newf("open repository: %v", err)
}
case MergeStyleRebase: // Rebase before merging
// Create temporary directory to store temporary copy of the base repository,
// and clean it up when operation finished regardless of succeed or not.
tmpBasePath := filepath.Join(conf.Server.AppDataPath, "tmp", "repos", com.ToStr(time.Now().Nanosecond())+".git")
if err = os.MkdirAll(filepath.Dir(tmpBasePath), os.ModePerm); err != nil {
return err
}
defer func() {
_ = os.RemoveAll(filepath.Dir(tmpBasePath))
}()
// Rebase head branch based on base branch, this creates a non-branch commit state.
if _, stderr, err = process.ExecDir(-1, tmpBasePath,
fmt.Sprintf("PullRequest.Merge (git rebase): %s", tmpBasePath),
"git", "rebase", "--quiet", pr.BaseBranch, remoteHeadBranch); err != nil {
return errors.Newf("git rebase [%s on %s]: %s", remoteHeadBranch, pr.BaseBranch, stderr)
// Clone the base repository to the defined temporary directory,
// and checks out to base branch directly.
var stderr string
if _, stderr, err = process.ExecTimeout(5*time.Minute,
fmt.Sprintf("PullRequest.Merge (git clone): %s", tmpBasePath),
"git", "clone", "-b", pr.BaseBranch, baseGitRepo.Path(), tmpBasePath); err != nil {
return errors.Newf("git clone: %s", stderr)
}
// Name non-branch commit state to a new temporary branch in order to save changes.
tmpBranch := com.ToStr(time.Now().UnixNano(), 10)
// Add remote which points to the head repository.
if _, stderr, err = process.ExecDir(-1, tmpBasePath,
fmt.Sprintf("PullRequest.Merge (git checkout): %s", tmpBasePath),
"git", "checkout", "-b", tmpBranch); err != nil {
return errors.Newf("git checkout '%s': %s", tmpBranch, stderr)
fmt.Sprintf("PullRequest.Merge (git remote add): %s", tmpBasePath),
"git", "remote", "add", "head_repo", headRepoPath); err != nil {
return errors.Newf("git remote add [%s -> %s]: %s", headRepoPath, tmpBasePath, stderr)
}
// Check out the base branch to be operated on.
// Fetch information from head repository to the temporary copy.
if _, stderr, err = process.ExecDir(-1, tmpBasePath,
fmt.Sprintf("PullRequest.Merge (git checkout): %s", tmpBasePath),
"git", "checkout", pr.BaseBranch); err != nil {
return errors.Newf("git checkout '%s': %s", pr.BaseBranch, stderr)
fmt.Sprintf("PullRequest.Merge (git fetch): %s", tmpBasePath),
"git", "fetch", "head_repo"); err != nil {
return errors.Newf("git fetch [%s -> %s]: %s", headRepoPath, tmpBasePath, stderr)
}
// Merge changes from temporary branch to the base branch.
if _, stderr, err = process.ExecDir(-1, tmpBasePath,
fmt.Sprintf("PullRequest.Merge (git merge): %s", tmpBasePath),
"git", "merge", tmpBranch); err != nil {
return errors.Newf("git merge [%s]: %v - %s", tmpBasePath, err, stderr)
remoteHeadBranch := "head_repo/" + pr.HeadBranch
// Check if merge style is allowed, reset to default style if not
if mergeStyle == MergeStyleRebase && !pr.BaseRepo.PullsAllowRebase {
mergeStyle = MergeStyleRegular
}
default:
return errors.Newf("unknown merge style: %s", mergeStyle)
}
switch mergeStyle {
case MergeStyleRegular: // Create merge commit
// Push changes on base branch to upstream.
if _, stderr, err = process.ExecDir(-1, tmpBasePath,
fmt.Sprintf("PullRequest.Merge (git push): %s", tmpBasePath),
"git", "push", baseGitRepo.Path(), pr.BaseBranch); err != nil {
return errors.Newf("git push: %s", stderr)
}
// Merge changes from head branch.
if _, stderr, err = process.ExecDir(-1, tmpBasePath,
fmt.Sprintf("PullRequest.Merge (git merge --no-ff --no-commit): %s", tmpBasePath),
"git", "merge", "--no-ff", "--no-commit", remoteHeadBranch); err != nil {
return errors.Newf("git merge --no-ff --no-commit [%s]: %v - %s", tmpBasePath, err, stderr)
}
pr.MergedCommitID, err = headGitRepo.BranchCommitID(pr.HeadBranch)
if err != nil {
return errors.Newf("get head branch %q commit ID: %v", pr.HeadBranch, err)
}
// Create a merge commit for the base branch.
if _, stderr, err = process.ExecDir(-1, tmpBasePath,
fmt.Sprintf("PullRequest.Merge (git merge): %s", tmpBasePath),
"git", "commit", fmt.Sprintf("--author='%s <%s>'", doer.DisplayName(), doer.Email),
"-m", fmt.Sprintf("Merge branch '%s' of %s/%s into %s", pr.HeadBranch, pr.HeadUserName, pr.HeadRepo.Name, pr.BaseBranch),
"-m", commitDescription); err != nil {
return errors.Newf("git commit [%s]: %v - %s", tmpBasePath, err, stderr)
}
pr.HasMerged = true
pr.Merged = time.Now()
pr.MergerID = doer.ID
if _, err = sess.ID(pr.ID).AllCols().Update(pr); err != nil {
return errors.Newf("update pull request: %v", err)
}
case MergeStyleRebase: // Rebase before merging
if err = sess.Commit(); err != nil {
return errors.Newf("commit: %v", err)
}
// Rebase head branch based on base branch, this creates a non-branch commit state.
if _, stderr, err = process.ExecDir(-1, tmpBasePath,
fmt.Sprintf("PullRequest.Merge (git rebase): %s", tmpBasePath),
"git", "rebase", "--quiet", pr.BaseBranch, remoteHeadBranch); err != nil {
return errors.Newf("git rebase [%s on %s]: %s", remoteHeadBranch, pr.BaseBranch, stderr)
}
if err = Handle.Actions().MergePullRequest(ctx, doer, pr.Issue.Repo.Owner, pr.Issue.Repo, pr.Issue); err != nil {
log.Error("Failed to create action for merge pull request, pull_request_id: %d, error: %v", pr.ID, err)
}
// Name non-branch commit state to a new temporary branch in order to save changes.
tmpBranch := com.ToStr(time.Now().UnixNano(), 10)
if _, stderr, err = process.ExecDir(-1, tmpBasePath,
fmt.Sprintf("PullRequest.Merge (git checkout): %s", tmpBasePath),
"git", "checkout", "-b", tmpBranch); err != nil {
return errors.Newf("git checkout '%s': %s", tmpBranch, stderr)
}
// Reload pull request information.
if err = pr.LoadAttributes(); err != nil {
log.Error("LoadAttributes: %v", err)
// Check out the base branch to be operated on.
if _, stderr, err = process.ExecDir(-1, tmpBasePath,
fmt.Sprintf("PullRequest.Merge (git checkout): %s", tmpBasePath),
"git", "checkout", pr.BaseBranch); err != nil {
return errors.Newf("git checkout '%s': %s", pr.BaseBranch, stderr)
}
// Merge changes from temporary branch to the base branch.
if _, stderr, err = process.ExecDir(-1, tmpBasePath,
fmt.Sprintf("PullRequest.Merge (git merge): %s", tmpBasePath),
"git", "merge", tmpBranch); err != nil {
return errors.Newf("git merge [%s]: %v - %s", tmpBasePath, err, stderr)
}
default:
return errors.Newf("unknown merge style: %s", mergeStyle)
}
// Push changes on base branch to upstream.
if _, stderr, err = process.ExecDir(-1, tmpBasePath,
fmt.Sprintf("PullRequest.Merge (git push): %s", tmpBasePath),
"git", "push", baseGitRepo.Path(), pr.BaseBranch); err != nil {
return errors.Newf("git push: %s", stderr)
}
pr.MergedCommitID, err = headGitRepo.BranchCommitID(pr.HeadBranch)
if err != nil {
return errors.Newf("get head branch %q commit ID: %v", pr.HeadBranch, err)
}
pr.HasMerged = true
pr.Merged = time.Now()
pr.MergerID = doer.ID
if err := tx.Model(&PullRequest{}).Where("id = ?", pr.ID).Updates(pr).Error; err != nil {
return errors.Newf("update pull request: %v", err)
}
if err = Handle.Actions().MergePullRequest(ctx, doer, pr.Issue.Repo.Owner, pr.Issue.Repo, pr.Issue); err != nil {
log.Error("Failed to create action for merge pull request, pull_request_id: %d, error: %v", pr.ID, err)
}
// Reload pull request information.
if err = pr.LoadAttributes(); err != nil {
log.Error("LoadAttributes: %v", err)
return nil
}
if err = PrepareWebhooks(pr.Issue.Repo, HookEventTypePullRequest, &api.PullRequestPayload{
Action: api.HOOK_ISSUE_CLOSED,
Index: pr.Index,
PullRequest: pr.APIFormat(),
Repository: pr.Issue.Repo.APIFormatLegacy(nil),
Sender: doer.APIFormat(),
}); err != nil {
log.Error("PrepareWebhooks: %v", err)
return nil
}
commits, err := headGitRepo.RevList([]string{pr.MergeBase + "..." + pr.MergedCommitID})
if err != nil {
log.Error("Failed to list commits [merge_base: %s, merged_commit_id: %s]: %v", pr.MergeBase, pr.MergedCommitID, err)
return nil
}
// NOTE: It is possible that head branch is not fully sync with base branch
// for merge commits, so we need to get latest head commit and append merge
// commit manually to avoid strange diff commits produced.
mergeCommit, err := baseGitRepo.BranchCommit(pr.BaseBranch)
if err != nil {
log.Error("Failed to get base branch %q commit: %v", pr.BaseBranch, err)
return nil
}
if mergeStyle == MergeStyleRegular {
commits = append([]*git.Commit{mergeCommit}, commits...)
}
pcs, err := CommitsToPushCommits(commits).APIFormat(ctx, Handle.Users(), pr.BaseRepo.RepoPath(), pr.BaseRepo.HTMLURL())
if err != nil {
log.Error("Failed to convert to API payload commits: %v", err)
return nil
}
p := &api.PushPayload{
Ref: git.RefsHeads + pr.BaseBranch,
Before: pr.MergeBase,
After: mergeCommit.ID.String(),
CompareURL: conf.Server.ExternalURL + pr.BaseRepo.ComposeCompareURL(pr.MergeBase, pr.MergedCommitID),
Commits: pcs,
Repo: pr.BaseRepo.APIFormatLegacy(nil),
Pusher: pr.HeadRepo.MustOwner().APIFormat(),
Sender: doer.APIFormat(),
}
if err = PrepareWebhooks(pr.BaseRepo, HookEventTypePush, p); err != nil {
log.Error("Failed to prepare webhooks: %v", err)
return nil
}
return nil
}
if err = PrepareWebhooks(pr.Issue.Repo, HookEventTypePullRequest, &api.PullRequestPayload{
Action: api.HOOK_ISSUE_CLOSED,
Index: pr.Index,
PullRequest: pr.APIFormat(),
Repository: pr.Issue.Repo.APIFormatLegacy(nil),
Sender: doer.APIFormat(),
}); err != nil {
log.Error("PrepareWebhooks: %v", err)
return nil
}
commits, err := headGitRepo.RevList([]string{pr.MergeBase + "..." + pr.MergedCommitID})
if err != nil {
log.Error("Failed to list commits [merge_base: %s, merged_commit_id: %s]: %v", pr.MergeBase, pr.MergedCommitID, err)
return nil
}
// NOTE: It is possible that head branch is not fully sync with base branch
// for merge commits, so we need to get latest head commit and append merge
// commit manually to avoid strange diff commits produced.
mergeCommit, err := baseGitRepo.BranchCommit(pr.BaseBranch)
if err != nil {
log.Error("Failed to get base branch %q commit: %v", pr.BaseBranch, err)
return nil
}
if mergeStyle == MergeStyleRegular {
commits = append([]*git.Commit{mergeCommit}, commits...)
}
pcs, err := CommitsToPushCommits(commits).APIFormat(ctx, Handle.Users(), pr.BaseRepo.RepoPath(), pr.BaseRepo.HTMLURL())
if err != nil {
log.Error("Failed to convert to API payload commits: %v", err)
return nil
}
p := &api.PushPayload{
Ref: git.RefsHeads + pr.BaseBranch,
Before: pr.MergeBase,
After: mergeCommit.ID.String(),
CompareURL: conf.Server.ExternalURL + pr.BaseRepo.ComposeCompareURL(pr.MergeBase, pr.MergedCommitID),
Commits: pcs,
Repo: pr.BaseRepo.APIFormatLegacy(nil),
Pusher: pr.HeadRepo.MustOwner().APIFormat(),
Sender: doer.APIFormat(),
}
if err = PrepareWebhooks(pr.BaseRepo, HookEventTypePush, p); err != nil {
log.Error("Failed to prepare webhooks: %v", err)
return nil
}
return nil
})
}
// testPatch checks if patch can be merged to base repository without conflict.
@@ -443,45 +431,42 @@ func (pr *PullRequest) testPatch() (err error) {
// NewPullRequest creates new pull request with labels for repository.
func NewPullRequest(repo *Repository, pull *Issue, labelIDs []int64, uuids []string, pr *PullRequest, patch []byte) (err error) {
sess := x.NewSession()
defer sess.Close()
if err = sess.Begin(); err != nil {
err = db.Transaction(func(tx *gorm.DB) error {
if err := newIssue(tx, NewIssueOptions{
Repo: repo,
Issue: pull,
LableIDs: labelIDs,
Attachments: uuids,
IsPull: true,
}); err != nil {
return errors.Newf("newIssue: %v", err)
}
pr.Index = pull.Index
if err := repo.SavePatch(pr.Index, patch); err != nil {
return errors.Newf("SavePatch: %v", err)
}
pr.BaseRepo = repo
if err := pr.testPatch(); err != nil {
return errors.Newf("testPatch: %v", err)
}
// No conflict appears after test means mergeable.
if pr.Status == PullRequestStatusChecking {
pr.Status = PullRequestStatusMergeable
}
pr.IssueID = pull.ID
if err := tx.Create(pr).Error; err != nil {
return errors.Newf("insert pull repo: %v", err)
}
return nil
})
if err != nil {
return err
}
if err = newIssue(sess, NewIssueOptions{
Repo: repo,
Issue: pull,
LableIDs: labelIDs,
Attachments: uuids,
IsPull: true,
}); err != nil {
return errors.Newf("newIssue: %v", err)
}
pr.Index = pull.Index
if err = repo.SavePatch(pr.Index, patch); err != nil {
return errors.Newf("SavePatch: %v", err)
}
pr.BaseRepo = repo
if err = pr.testPatch(); err != nil {
return errors.Newf("testPatch: %v", err)
}
// No conflict appears after test means mergeable.
if pr.Status == PullRequestStatusChecking {
pr.Status = PullRequestStatusMergeable
}
pr.IssueID = pull.ID
if _, err = sess.Insert(pr); err != nil {
return errors.Newf("insert pull repo: %v", err)
}
if err = sess.Commit(); err != nil {
return errors.Newf("commit: %v", err)
}
if err = NotifyWatchers(&Action{
ActUserID: pull.Poster.ID,
ActUserName: pull.Poster.Name,
@@ -517,18 +502,20 @@ func NewPullRequest(repo *Repository, pull *Issue, labelIDs []int64, uuids []str
// by given head/base and repo/branch.
func GetUnmergedPullRequest(headRepoID, baseRepoID int64, headBranch, baseBranch string) (*PullRequest, error) {
pr := new(PullRequest)
has, err := x.Where("head_repo_id=? AND head_branch=? AND base_repo_id=? AND base_branch=? AND has_merged=? AND issue.is_closed=?",
headRepoID, headBranch, baseRepoID, baseBranch, false, false).
Join("INNER", "issue", "issue.id=pull_request.issue_id").Get(pr)
err := db.Joins("INNER JOIN issue ON issue.id = pull_request.issue_id").
Where("pull_request.head_repo_id = ? AND pull_request.head_branch = ? AND pull_request.base_repo_id = ? AND pull_request.base_branch = ? AND pull_request.has_merged = ? AND issue.is_closed = ?",
headRepoID, headBranch, baseRepoID, baseBranch, false, false).
First(pr).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, ErrPullRequestNotExist{args: map[string]any{
"headRepoID": headRepoID,
"baseRepoID": baseRepoID,
"headBranch": headBranch,
"baseBranch": baseBranch,
}}
}
return nil, err
} else if !has {
return nil, ErrPullRequestNotExist{args: map[string]any{
"headRepoID": headRepoID,
"baseRepoID": baseRepoID,
"headBranch": headBranch,
"baseBranch": baseBranch,
}}
}
return pr, nil
@@ -538,18 +525,22 @@ func GetUnmergedPullRequest(headRepoID, baseRepoID int64, headBranch, baseBranch
// by given head information (repo and branch).
func GetUnmergedPullRequestsByHeadInfo(repoID int64, branch string) ([]*PullRequest, error) {
prs := make([]*PullRequest, 0, 2)
return prs, x.Where("head_repo_id = ? AND head_branch = ? AND has_merged = ? AND issue.is_closed = ?",
repoID, branch, false, false).
Join("INNER", "issue", "issue.id = pull_request.issue_id").Find(&prs)
err := db.Joins("INNER JOIN issue ON issue.id = pull_request.issue_id").
Where("pull_request.head_repo_id = ? AND pull_request.head_branch = ? AND pull_request.has_merged = ? AND issue.is_closed = ?",
repoID, branch, false, false).
Find(&prs).Error
return prs, err
}
// GetUnmergedPullRequestsByBaseInfo returns all pull requests that are open and has not been merged
// by given base information (repo and branch).
func GetUnmergedPullRequestsByBaseInfo(repoID int64, branch string) ([]*PullRequest, error) {
prs := make([]*PullRequest, 0, 2)
return prs, x.Where("base_repo_id=? AND base_branch=? AND has_merged=? AND issue.is_closed=?",
repoID, branch, false, false).
Join("INNER", "issue", "issue.id=pull_request.issue_id").Find(&prs)
err := db.Joins("INNER JOIN issue ON issue.id = pull_request.issue_id").
Where("pull_request.base_repo_id = ? AND pull_request.base_branch = ? AND pull_request.has_merged = ? AND issue.is_closed = ?",
repoID, branch, false, false).
Find(&prs).Error
return prs, err
}
var _ errutil.NotFound = (*ErrPullRequestNotExist)(nil)
@@ -571,50 +562,65 @@ func (ErrPullRequestNotExist) NotFound() bool {
return true
}
func getPullRequestByID(e Engine, id int64) (*PullRequest, error) {
func getPullRequestByID(db *gorm.DB, id int64) (*PullRequest, error) {
pr := new(PullRequest)
has, err := e.ID(id).Get(pr)
err := db.First(pr, id).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, ErrPullRequestNotExist{args: map[string]any{"pullRequestID": id}}
}
return nil, err
} else if !has {
return nil, ErrPullRequestNotExist{args: map[string]any{"pullRequestID": id}}
}
return pr, pr.loadAttributes(e)
return pr, pr.loadAttributes(db)
}
// GetPullRequestByID returns a pull request by given ID.
func GetPullRequestByID(id int64) (*PullRequest, error) {
return getPullRequestByID(x, id)
return getPullRequestByID(db, id)
}
func getPullRequestByIssueID(e Engine, issueID int64) (*PullRequest, error) {
pr := &PullRequest{
IssueID: issueID,
}
has, err := e.Get(pr)
func getPullRequestByIssueID(db *gorm.DB, issueID int64) (*PullRequest, error) {
pr := &PullRequest{}
err := db.Where("issue_id = ?", issueID).First(pr).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, ErrPullRequestNotExist{args: map[string]any{"issueID": issueID}}
}
return nil, err
} else if !has {
return nil, ErrPullRequestNotExist{args: map[string]any{"issueID": issueID}}
}
return pr, pr.loadAttributes(e)
return pr, pr.loadAttributes(db)
}
// GetPullRequestByIssueID returns pull request by given issue ID.
func GetPullRequestByIssueID(issueID int64) (*PullRequest, error) {
return getPullRequestByIssueID(x, issueID)
return getPullRequestByIssueID(db, issueID)
}
// Update updates all fields of pull request.
func (pr *PullRequest) Update() error {
_, err := x.Id(pr.ID).AllCols().Update(pr)
return err
return db.Model(&PullRequest{}).Where("id = ?", pr.ID).Updates(pr).Error
}
// Update updates specific fields of pull request.
func (pr *PullRequest) UpdateCols(cols ...string) error {
_, err := x.Id(pr.ID).Cols(cols...).Update(pr)
return err
updates := make(map[string]any)
for _, col := range cols {
switch col {
case "status":
updates["status"] = pr.Status
case "merge_base":
updates["merge_base"] = pr.MergeBase
case "has_merged":
updates["has_merged"] = pr.HasMerged
case "merged_commit_id":
updates["merged_commit_id"] = pr.MergedCommitID
case "merger_id":
updates["merger_id"] = pr.MergerID
case "merged_unix":
updates["merged_unix"] = pr.MergedUnix
}
}
return db.Model(&PullRequest{}).Where("id = ?", pr.ID).Updates(updates).Error
}
// UpdatePatch generates and saves a new patch.
@@ -711,7 +717,7 @@ func (pr *PullRequest) AddToTaskQueue() {
type PullRequestList []*PullRequest
func (prs PullRequestList) loadAttributes(e Engine) (err error) {
func (prs PullRequestList) loadAttributes(db *gorm.DB) (err error) {
if len(prs) == 0 {
return nil
}
@@ -726,7 +732,7 @@ func (prs PullRequestList) loadAttributes(e Engine) (err error) {
issueIDs = append(issueIDs, issueID)
}
issues := make([]*Issue, 0, len(issueIDs))
if err = e.Where("id > 0").In("id", issueIDs).Find(&issues); err != nil {
if err = db.Where("id IN ?", issueIDs).Find(&issues).Error; err != nil {
return errors.Newf("find issues: %v", err)
}
for i := range issues {
@@ -738,7 +744,7 @@ func (prs PullRequestList) loadAttributes(e Engine) (err error) {
// Load attributes
for i := range prs {
if err = prs[i].loadAttributes(e); err != nil {
if err = prs[i].loadAttributes(db); err != nil {
return errors.Newf("loadAttributes [%d]: %v", prs[i].ID, err)
}
}
@@ -747,7 +753,7 @@ func (prs PullRequestList) loadAttributes(e Engine) (err error) {
}
func (prs PullRequestList) LoadAttributes() error {
return prs.loadAttributes(x)
return prs.loadAttributes(db)
}
func addHeadRepoTasks(prs []*PullRequest) {
@@ -838,25 +844,23 @@ func (pr *PullRequest) checkAndUpdateStatus() {
// TestPullRequests checks and tests untested patches of pull requests.
// TODO: test more pull requests at same time.
func TestPullRequests() {
prs := make([]*PullRequest, 0, 10)
_ = x.Iterate(PullRequest{
Status: PullRequestStatusChecking,
},
func(idx int, bean any) error {
pr := bean.(*PullRequest)
var prs []*PullRequest
_ = db.Where("status = ?", PullRequestStatusChecking).FindInBatches(&prs, 100, func(tx *gorm.DB, batch int) error {
for i := range prs {
pr := prs[i]
if err := pr.LoadAttributes(); err != nil {
log.Error("LoadAttributes: %v", err)
return nil
continue
}
if err := pr.testPatch(); err != nil {
log.Error("testPatch: %v", err)
return nil
continue
}
prs = append(prs, pr)
return nil
})
}
return nil
})
// Update pull request status.
for _, pr := range prs {

View File

@@ -6,12 +6,11 @@ import (
"strings"
"time"
log "unknwon.dev/clog/v2"
"xorm.io/xorm"
"github.com/cockroachdb/errors"
"github.com/gogs/git-module"
api "github.com/gogs/go-gogs-client"
"gorm.io/gorm"
log "unknwon.dev/clog/v2"
"gogs.io/gogs/internal/errutil"
"gogs.io/gogs/internal/process"
@@ -21,40 +20,39 @@ import (
type Release struct {
ID int64
RepoID int64
Repo *Repository `xorm:"-" json:"-" gorm:"-"`
Repo *Repository `gorm:"-" json:"-"`
PublisherID int64
Publisher *User `xorm:"-" json:"-" gorm:"-"`
Publisher *User `gorm:"-" json:"-"`
TagName string
LowerTagName string
Target string
Title string
Sha1 string `xorm:"VARCHAR(40)"`
Sha1 string `gorm:"type:varchar(40)"`
NumCommits int64
NumCommitsBehind int64 `xorm:"-" json:"-" gorm:"-"`
Note string `xorm:"TEXT"`
IsDraft bool `xorm:"NOT NULL DEFAULT false"`
NumCommitsBehind int64 `gorm:"-" json:"-"`
Note string `gorm:"type:text"`
IsDraft bool `gorm:"not null;default:false"`
IsPrerelease bool
Created time.Time `xorm:"-" json:"-" gorm:"-"`
Created time.Time `gorm:"-" json:"-"`
CreatedUnix int64
Attachments []*Attachment `xorm:"-" json:"-" gorm:"-"`
Attachments []*Attachment `gorm:"-" json:"-"`
}
func (r *Release) BeforeInsert() {
func (r *Release) BeforeCreate(tx *gorm.DB) error {
if r.CreatedUnix == 0 {
r.CreatedUnix = time.Now().Unix()
r.CreatedUnix = tx.NowFunc().Unix()
}
return nil
}
func (r *Release) AfterSet(colName string, _ xorm.Cell) {
switch colName {
case "created_unix":
r.Created = time.Unix(r.CreatedUnix, 0).Local()
}
func (r *Release) AfterFind(tx *gorm.DB) error {
r.Created = time.Unix(r.CreatedUnix, 0).Local()
return nil
}
func (r *Release) loadAttributes(e Engine) (err error) {
func (r *Release) loadAttributes(e *gorm.DB) (err error) {
if r.Repo == nil {
r.Repo, err = getRepositoryByID(e, r.RepoID)
if err != nil {
@@ -85,7 +83,7 @@ func (r *Release) loadAttributes(e Engine) (err error) {
}
func (r *Release) LoadAttributes() error {
return r.loadAttributes(x)
return r.loadAttributes(db)
}
// This method assumes some fields assigned with values:
@@ -110,7 +108,9 @@ func IsReleaseExist(repoID int64, tagName string) (bool, error) {
return false, nil
}
return x.Get(&Release{RepoID: repoID, LowerTagName: strings.ToLower(tagName)})
var count int64
err := db.Model(&Release{}).Where("repo_id = ? AND lower_tag_name = ?", repoID, strings.ToLower(tagName)).Count(&count).Error
return count > 0, err
}
func createTag(gitRepo *git.Repository, r *Release) error {
@@ -171,24 +171,21 @@ func NewRelease(gitRepo *git.Repository, r *Release, uuids []string) error {
}
r.LowerTagName = strings.ToLower(r.TagName)
sess := x.NewSession()
defer sess.Close()
if err = sess.Begin(); err != nil {
return err
}
if _, err = sess.Insert(r); err != nil {
return errors.Newf("insert: %v", err)
}
if len(uuids) > 0 {
if _, err = sess.In("uuid", uuids).Cols("release_id").Update(&Attachment{ReleaseID: r.ID}); err != nil {
return errors.Newf("link attachments: %v", err)
err = db.Transaction(func(tx *gorm.DB) error {
if err := tx.Create(r).Error; err != nil {
return errors.Newf("insert: %v", err)
}
}
if err = sess.Commit(); err != nil {
return errors.Newf("commit: %v", err)
if len(uuids) > 0 {
if err := tx.Model(&Attachment{}).Where("uuid IN ?", uuids).Update("release_id", r.ID).Error; err != nil {
return errors.Newf("link attachments: %v", err)
}
}
return nil
})
if err != nil {
return err
}
// Only send webhook when actually published, skip drafts
@@ -231,8 +228,8 @@ func GetRelease(repoID int64, tagName string) (*Release, error) {
return nil, ErrReleaseNotExist{args: map[string]any{"tag": tagName}}
}
r := &Release{RepoID: repoID, LowerTagName: strings.ToLower(tagName)}
if _, err = x.Get(r); err != nil {
r := &Release{}
if err = db.Where("repo_id = ? AND lower_tag_name = ?", repoID, strings.ToLower(tagName)).First(r).Error; err != nil {
return nil, errors.Newf("get: %v", err)
}
@@ -242,11 +239,12 @@ func GetRelease(repoID int64, tagName string) (*Release, error) {
// GetReleaseByID returns release with given ID.
func GetReleaseByID(id int64) (*Release, error) {
r := new(Release)
has, err := x.Id(id).Get(r)
err := db.Where("id = ?", id).First(r).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, ErrReleaseNotExist{args: map[string]any{"releaseID": id}}
}
return nil, err
} else if !has {
return nil, ErrReleaseNotExist{args: map[string]any{"releaseID": id}}
}
return r, r.LoadAttributes()
@@ -256,24 +254,24 @@ func GetReleaseByID(id int64) (*Release, error) {
// If matches is not empty, only published releases in matches will be returned.
// In any case, drafts won't be returned by this function.
func GetPublishedReleasesByRepoID(repoID int64, matches ...string) ([]*Release, error) {
sess := x.Where("repo_id = ?", repoID).And("is_draft = ?", false).Desc("created_unix")
query := db.Where("repo_id = ? AND is_draft = ?", repoID, false).Order("created_unix DESC")
if len(matches) > 0 {
sess.In("tag_name", matches)
query = query.Where("tag_name IN ?", matches)
}
releases := make([]*Release, 0, 5)
return releases, sess.Find(&releases, new(Release))
return releases, query.Find(&releases).Error
}
// GetReleasesByRepoID returns a list of all releases (including drafts) of given repository.
func GetReleasesByRepoID(repoID int64) ([]*Release, error) {
releases := make([]*Release, 0)
return releases, x.Where("repo_id = ?", repoID).Find(&releases)
return releases, db.Where("repo_id = ?", repoID).Find(&releases).Error
}
// GetDraftReleasesByRepoID returns all draft releases of repository.
func GetDraftReleasesByRepoID(repoID int64) ([]*Release, error) {
releases := make([]*Release, 0)
return releases, x.Where("repo_id = ?", repoID).And("is_draft = ?", true).Find(&releases)
return releases, db.Where("repo_id = ? AND is_draft = ?", repoID, true).Find(&releases).Error
}
type ReleaseSorter struct {
@@ -310,28 +308,26 @@ func UpdateRelease(doer *User, gitRepo *git.Repository, r *Release, isPublish bo
r.PublisherID = doer.ID
sess := x.NewSession()
defer sess.Close()
if err = sess.Begin(); err != nil {
return err
}
if _, err = sess.ID(r.ID).AllCols().Update(r); err != nil {
return errors.Newf("Update: %v", err)
}
// Unlink all current attachments and link back later if still valid
if _, err = sess.Exec("UPDATE attachment SET release_id = 0 WHERE release_id = ?", r.ID); err != nil {
return errors.Newf("unlink current attachments: %v", err)
}
if len(uuids) > 0 {
if _, err = sess.In("uuid", uuids).Cols("release_id").Update(&Attachment{ReleaseID: r.ID}); err != nil {
return errors.Newf("link attachments: %v", err)
err = db.Transaction(func(tx *gorm.DB) error {
if err := tx.Model(r).Where("id = ?", r.ID).Updates(r).Error; err != nil {
return errors.Newf("Update: %v", err)
}
}
if err = sess.Commit(); err != nil {
return errors.Newf("commit: %v", err)
// Unlink all current attachments and link back later if still valid
if err := tx.Exec("UPDATE attachment SET release_id = 0 WHERE release_id = ?", r.ID).Error; err != nil {
return errors.Newf("unlink current attachments: %v", err)
}
if len(uuids) > 0 {
if err := tx.Model(&Attachment{}).Where("uuid IN ?", uuids).Update("release_id", r.ID).Error; err != nil {
return errors.Newf("link attachments: %v", err)
}
}
return nil
})
if err != nil {
return err
}
if !isPublish {
@@ -366,7 +362,7 @@ func DeleteReleaseOfRepoByID(repoID, id int64) error {
return errors.Newf("git tag -d: %v - %s", err, stderr)
}
if _, err = x.Id(rel.ID).Delete(new(Release)); err != nil {
if err = db.Where("id = ?", rel.ID).Delete(new(Release)).Error; err != nil {
return errors.Newf("delete: %v", err)
}

File diff suppressed because it is too large Load Diff

View File

@@ -8,6 +8,7 @@ import (
"github.com/cockroachdb/errors"
"github.com/gogs/git-module"
"github.com/unknwon/com"
"gorm.io/gorm"
"gogs.io/gogs/internal/errutil"
"gogs.io/gogs/internal/tool"
@@ -93,8 +94,9 @@ type ProtectBranchWhitelist struct {
// IsUserInProtectBranchWhitelist returns true if given user is in the whitelist of a branch in a repository.
func IsUserInProtectBranchWhitelist(repoID, userID int64, branch string) bool {
has, err := x.Where("repo_id = ?", repoID).And("user_id = ?", userID).And("name = ?", branch).Get(new(ProtectBranchWhitelist))
return has && err == nil
var whitelist ProtectBranchWhitelist
err := db.Where("repo_id = ?", repoID).Where("user_id = ?", userID).Where("name = ?", branch).First(&whitelist).Error
return err == nil
}
// ProtectBranch contains options of a protected branch.
@@ -115,11 +117,11 @@ func GetProtectBranchOfRepoByName(repoID int64, name string) (*ProtectBranch, er
RepoID: repoID,
Name: name,
}
has, err := x.Get(protectBranch)
if err != nil {
return nil, err
} else if !has {
err := db.Where("repo_id = ? AND name = ?", repoID, name).First(protectBranch).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, ErrBranchNotExist{args: map[string]any{"name": name}}
} else if err != nil {
return nil, err
}
return protectBranch, nil
}
@@ -136,23 +138,19 @@ func IsBranchOfRepoRequirePullRequest(repoID int64, name string) bool {
// UpdateProtectBranch saves branch protection options.
// If ID is 0, it creates a new record. Otherwise, updates existing record.
func UpdateProtectBranch(protectBranch *ProtectBranch) (err error) {
sess := x.NewSession()
defer sess.Close()
if err = sess.Begin(); err != nil {
return err
}
if protectBranch.ID == 0 {
if _, err = sess.Insert(protectBranch); err != nil {
return errors.Newf("insert: %v", err)
return db.Transaction(func(tx *gorm.DB) error {
if protectBranch.ID == 0 {
if err := tx.Create(protectBranch).Error; err != nil {
return errors.Newf("insert: %v", err)
}
}
}
if _, err = sess.ID(protectBranch.ID).AllCols().Update(protectBranch); err != nil {
return errors.Newf("update: %v", err)
}
if err := tx.Model(&ProtectBranch{}).Where("id = ?", protectBranch.ID).Updates(protectBranch).Error; err != nil {
return errors.Newf("update: %v", err)
}
return sess.Commit()
return nil
})
}
// UpdateOrgProtectBranch saves branch protection options of organizational repository.
@@ -209,7 +207,7 @@ func UpdateOrgProtectBranch(repo *Repository, protectBranch *ProtectBranch, whit
// Make sure protectBranch.ID is not 0 for whitelists
if protectBranch.ID == 0 {
if _, err = x.Insert(protectBranch); err != nil {
if err = db.Create(protectBranch).Error; err != nil {
return errors.Newf("insert: %v", err)
}
}
@@ -247,30 +245,29 @@ func UpdateOrgProtectBranch(repo *Repository, protectBranch *ProtectBranch, whit
}
}
sess := x.NewSession()
defer sess.Close()
if err = sess.Begin(); err != nil {
return err
}
if _, err = sess.ID(protectBranch.ID).AllCols().Update(protectBranch); err != nil {
return errors.Newf("Update: %v", err)
}
// Refresh whitelists
if hasUsersChanged || hasTeamsChanged {
if _, err = sess.Delete(&ProtectBranchWhitelist{ProtectBranchID: protectBranch.ID}); err != nil {
return errors.Newf("delete old protect branch whitelists: %v", err)
} else if _, err = sess.Insert(whitelists); err != nil {
return errors.Newf("insert new protect branch whitelists: %v", err)
return db.Transaction(func(tx *gorm.DB) error {
if err := tx.Model(&ProtectBranch{}).Where("id = ?", protectBranch.ID).Updates(protectBranch).Error; err != nil {
return errors.Newf("Update: %v", err)
}
}
return sess.Commit()
// Refresh whitelists
if hasUsersChanged || hasTeamsChanged {
if err := tx.Delete(&ProtectBranchWhitelist{}, "protect_branch_id = ?", protectBranch.ID).Error; err != nil {
return errors.Newf("delete old protect branch whitelists: %v", err)
}
if len(whitelists) > 0 {
if err := tx.Create(&whitelists).Error; err != nil {
return errors.Newf("insert new protect branch whitelists: %v", err)
}
}
}
return nil
})
}
// GetProtectBranchesByRepoID returns a list of *ProtectBranch in given repository.
func GetProtectBranchesByRepoID(repoID int64) ([]*ProtectBranch, error) {
protectBranches := make([]*ProtectBranch, 0, 2)
return protectBranches, x.Where("repo_id = ? and protected = ?", repoID, true).Asc("name").Find(&protectBranches)
return protectBranches, db.Where("repo_id = ? AND protected = ?", repoID, true).Order("name ASC").Find(&protectBranches).Error
}

View File

@@ -5,6 +5,7 @@ import (
"github.com/cockroachdb/errors"
api "github.com/gogs/go-gogs-client"
"gorm.io/gorm"
)
// Collaboration represent the relation between an individual and a repository.
@@ -34,12 +35,12 @@ func IsCollaborator(repoID, userID int64) bool {
RepoID: repoID,
UserID: userID,
}
has, err := x.Get(collaboration)
err := db.Where("repo_id = ? AND user_id = ?", repoID, userID).First(collaboration).Error
if err != nil {
log.Error("get collaboration [repo_id: %d, user_id: %d]: %v", repoID, userID, err)
return false
}
return has
return true
}
func (r *Repository) IsCollaborator(userID int64) bool {
@@ -53,32 +54,29 @@ func (r *Repository) AddCollaborator(u *User) error {
UserID: u.ID,
}
has, err := x.Get(collaboration)
if err != nil {
return err
} else if has {
var existing Collaboration
err := db.Where("repo_id = ? AND user_id = ?", r.ID, u.ID).First(&existing).Error
if err == nil {
return nil
} else if !errors.Is(err, gorm.ErrRecordNotFound) {
return err
}
collaboration.Mode = AccessModeWrite
sess := x.NewSession()
defer sess.Close()
if err = sess.Begin(); err != nil {
return err
}
if _, err = sess.Insert(collaboration); err != nil {
return err
} else if err = r.recalculateAccesses(sess); err != nil {
return errors.Newf("recalculateAccesses [repo_id: %v]: %v", r.ID, err)
}
return sess.Commit()
return db.Transaction(func(tx *gorm.DB) error {
if err := tx.Create(collaboration).Error; err != nil {
return err
}
if err := r.recalculateAccesses(tx); err != nil {
return errors.Newf("recalculateAccesses [repo_id: %v]: %v", r.ID, err)
}
return nil
})
}
func (r *Repository) getCollaborations(e Engine) ([]*Collaboration, error) {
func (r *Repository) getCollaborations(e *gorm.DB) ([]*Collaboration, error) {
collaborations := make([]*Collaboration, 0)
return collaborations, e.Find(&collaborations, &Collaboration{RepoID: r.ID})
return collaborations, e.Where("repo_id = ?", r.ID).Find(&collaborations).Error
}
// Collaborator represents a user with collaboration details.
@@ -98,7 +96,7 @@ func (c *Collaborator) APIFormat() *api.Collaborator {
}
}
func (r *Repository) getCollaborators(e Engine) ([]*Collaborator, error) {
func (r *Repository) getCollaborators(e *gorm.DB) ([]*Collaborator, error) {
collaborations, err := r.getCollaborations(e)
if err != nil {
return nil, errors.Newf("getCollaborations: %v", err)
@@ -120,7 +118,7 @@ func (r *Repository) getCollaborators(e Engine) ([]*Collaborator, error) {
// GetCollaborators returns the collaborators for a repository
func (r *Repository) GetCollaborators() ([]*Collaborator, error) {
return r.getCollaborators(x)
return r.getCollaborators(db)
}
// ChangeCollaborationAccessMode sets new access mode for the collaboration.
@@ -134,11 +132,11 @@ func (r *Repository) ChangeCollaborationAccessMode(userID int64, mode AccessMode
RepoID: r.ID,
UserID: userID,
}
has, err := x.Get(collaboration)
if err != nil {
return errors.Newf("get collaboration: %v", err)
} else if !has {
err := db.Where("repo_id = ? AND user_id = ?", r.ID, userID).First(collaboration).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil
} else if err != nil {
return errors.Newf("get collaboration: %v", err)
}
if collaboration.Mode == mode {
@@ -159,35 +157,31 @@ func (r *Repository) ChangeCollaborationAccessMode(userID int64, mode AccessMode
}
}
sess := x.NewSession()
defer sess.Close()
if err = sess.Begin(); err != nil {
return err
}
return db.Transaction(func(tx *gorm.DB) error {
if err := tx.Model(&Collaboration{}).Where("id = ?", collaboration.ID).Updates(collaboration).Error; err != nil {
return errors.Newf("update collaboration: %v", err)
}
if _, err = sess.ID(collaboration.ID).AllCols().Update(collaboration); err != nil {
return errors.Newf("update collaboration: %v", err)
}
access := &Access{
UserID: userID,
RepoID: r.ID,
}
err := tx.Where("user_id = ? AND repo_id = ?", userID, r.ID).First(access).Error
if err == nil {
if err := tx.Exec("UPDATE access SET mode = ? WHERE user_id = ? AND repo_id = ?", mode, userID, r.ID).Error; err != nil {
return errors.Newf("update access table: %v", err)
}
} else if errors.Is(err, gorm.ErrRecordNotFound) {
access.Mode = mode
if err := tx.Create(access).Error; err != nil {
return errors.Newf("insert access table: %v", err)
}
} else {
return errors.Newf("get access record: %v", err)
}
access := &Access{
UserID: userID,
RepoID: r.ID,
}
has, err = sess.Get(access)
if err != nil {
return errors.Newf("get access record: %v", err)
}
if has {
_, err = sess.Exec("UPDATE access SET mode = ? WHERE user_id = ? AND repo_id = ?", mode, userID, r.ID)
} else {
access.Mode = mode
_, err = sess.Insert(access)
}
if err != nil {
return errors.Newf("update/insert access table: %v", err)
}
return sess.Commit()
return nil
})
}
// DeleteCollaboration removes collaboration relation between the user and repository.
@@ -201,19 +195,20 @@ func DeleteCollaboration(repo *Repository, userID int64) (err error) {
UserID: userID,
}
sess := x.NewSession()
defer sess.Close()
if err = sess.Begin(); err != nil {
return err
}
return db.Transaction(func(tx *gorm.DB) error {
result := tx.Delete(collaboration, "repo_id = ? AND user_id = ?", repo.ID, userID)
if result.Error != nil {
return result.Error
} else if result.RowsAffected == 0 {
return nil
}
if has, err := sess.Delete(collaboration); err != nil || has == 0 {
return err
} else if err = repo.recalculateAccesses(sess); err != nil {
return err
}
if err := repo.recalculateAccesses(tx); err != nil {
return err
}
return sess.Commit()
return nil
})
}
func (r *Repository) DeleteCollaboration(userID int64) error {

View File

@@ -14,6 +14,7 @@ import (
"github.com/cockroachdb/errors"
gouuid "github.com/satori/go.uuid"
"github.com/unknwon/com"
"gorm.io/gorm"
"github.com/gogs/git-module"
@@ -441,7 +442,7 @@ func NewUpload(name string, buf []byte, file multipart.File) (_ *Upload, err err
return nil, errors.Newf("copy: %v", err)
}
if _, err := x.Insert(upload); err != nil {
if err := db.Create(upload).Error; err != nil {
return nil, err
}
@@ -450,11 +451,12 @@ func NewUpload(name string, buf []byte, file multipart.File) (_ *Upload, err err
func GetUploadByUUID(uuid string) (*Upload, error) {
upload := &Upload{UUID: uuid}
has, err := x.Get(upload)
err := db.Where("uuid = ?", uuid).First(upload).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, ErrUploadNotExist{0, uuid}
}
return nil, err
} else if !has {
return nil, ErrUploadNotExist{0, uuid}
}
return upload, nil
}
@@ -466,7 +468,7 @@ func GetUploadsByUUIDs(uuids []string) ([]*Upload, error) {
// Silently drop invalid uuids.
uploads := make([]*Upload, 0, len(uuids))
return uploads, x.In("uuid", uuids).Find(&uploads)
return uploads, db.Where("uuid IN ?", uuids).Find(&uploads).Error
}
func DeleteUploads(uploads ...*Upload) (err error) {
@@ -474,32 +476,28 @@ func DeleteUploads(uploads ...*Upload) (err error) {
return nil
}
sess := x.NewSession()
defer sess.Close()
if err = sess.Begin(); err != nil {
return err
}
ids := make([]int64, len(uploads))
for i := 0; i < len(uploads); i++ {
ids[i] = uploads[i].ID
}
if _, err = sess.In("id", ids).Delete(new(Upload)); err != nil {
return errors.Newf("delete uploads: %v", err)
}
for _, upload := range uploads {
localPath := upload.LocalPath()
if !osutil.IsFile(localPath) {
continue
return db.Transaction(func(tx *gorm.DB) error {
ids := make([]int64, len(uploads))
for i := 0; i < len(uploads); i++ {
ids[i] = uploads[i].ID
}
if err := tx.Where("id IN ?", ids).Delete(new(Upload)).Error; err != nil {
return errors.Newf("delete uploads: %v", err)
}
if err := os.Remove(localPath); err != nil {
return errors.Newf("remove upload: %v", err)
}
}
for _, upload := range uploads {
localPath := upload.LocalPath()
if !osutil.IsFile(localPath) {
continue
}
return sess.Commit()
if err := os.Remove(localPath); err != nil {
return errors.Newf("remove upload: %v", err)
}
}
return nil
})
}
func DeleteUpload(u *Upload) error {

View File

@@ -4,7 +4,6 @@ import (
"context"
"fmt"
"strings"
"time"
"github.com/cockroachdb/errors"
api "github.com/gogs/go-gogs-client"
@@ -28,13 +27,6 @@ func (r *Repository) BeforeUpdate(tx *gorm.DB) error {
return nil
}
// AfterFind implements the GORM query hook.
func (r *Repository) AfterFind(_ *gorm.DB) error {
r.Created = time.Unix(r.CreatedUnix, 0).Local()
r.Updated = time.Unix(r.UpdatedUnix, 0).Local()
return nil
}
type RepositoryAPIFormatOptions struct {
Permission *api.Permission
Parent *api.Repository

View File

@@ -16,8 +16,8 @@ import (
"github.com/cockroachdb/errors"
"github.com/unknwon/com"
"golang.org/x/crypto/ssh"
"gorm.io/gorm"
log "unknwon.dev/clog/v2"
"xorm.io/xorm"
"gogs.io/gogs/internal/conf"
"gogs.io/gogs/internal/errutil"
@@ -40,38 +40,41 @@ const (
// PublicKey represents a user or deploy SSH public key.
type PublicKey struct {
ID int64 `gorm:"primaryKey"`
OwnerID int64 `xorm:"INDEX NOT NULL" gorm:"index;not null"`
Name string `xorm:"NOT NULL" gorm:"not null"`
Fingerprint string `xorm:"NOT NULL" gorm:"not null"`
Content string `xorm:"TEXT NOT NULL" gorm:"type:TEXT;not null"`
Mode AccessMode `xorm:"NOT NULL DEFAULT 2" gorm:"not null;default:2"`
Type KeyType `xorm:"NOT NULL DEFAULT 1" gorm:"not null;default:1"`
OwnerID int64 `gorm:"index;not null"`
Name string `gorm:"not null"`
Fingerprint string `gorm:"not null"`
Content string `gorm:"type:text;not null"`
Mode AccessMode `gorm:"not null;default:2"`
Type KeyType `gorm:"not null;default:1"`
Created time.Time `xorm:"-" json:"-" gorm:"-"`
Created time.Time `gorm:"-" json:"-"`
CreatedUnix int64
Updated time.Time `xorm:"-" json:"-" gorm:"-"` // Note: Updated must below Created for AfterSet.
Updated time.Time `gorm:"-" json:"-"` // Note: Updated must below Created for AfterFind.
UpdatedUnix int64
HasRecentActivity bool `xorm:"-" json:"-" gorm:"-"`
HasUsed bool `xorm:"-" json:"-" gorm:"-"`
HasRecentActivity bool `gorm:"-" json:"-"`
HasUsed bool `gorm:"-" json:"-"`
}
func (k *PublicKey) BeforeInsert() {
k.CreatedUnix = time.Now().Unix()
func (k *PublicKey) BeforeCreate(tx *gorm.DB) error {
if k.CreatedUnix == 0 {
k.CreatedUnix = tx.NowFunc().Unix()
}
return nil
}
func (k *PublicKey) BeforeUpdate() {
k.UpdatedUnix = time.Now().Unix()
func (k *PublicKey) BeforeUpdate(tx *gorm.DB) error {
k.UpdatedUnix = tx.NowFunc().Unix()
return nil
}
func (k *PublicKey) AfterSet(colName string, _ xorm.Cell) {
switch colName {
case "created_unix":
k.Created = time.Unix(k.CreatedUnix, 0).Local()
case "updated_unix":
func (k *PublicKey) AfterFind(tx *gorm.DB) error {
k.Created = time.Unix(k.CreatedUnix, 0).Local()
if k.UpdatedUnix > 0 {
k.Updated = time.Unix(k.UpdatedUnix, 0).Local()
k.HasUsed = k.Updated.After(k.Created)
k.HasRecentActivity = k.Updated.Add(7 * 24 * time.Hour).After(time.Now())
k.HasRecentActivity = k.Updated.Add(7 * 24 * time.Hour).After(tx.NowFunc())
}
return nil
}
// OmitEmail returns content of public key without email address.
@@ -356,19 +359,16 @@ func appendAuthorizedKeysToFile(keys ...*PublicKey) error {
// checkKeyContent onlys checks if key content has been used as public key,
// it is OK to use same key as deploy key for multiple repositories/users.
func checkKeyContent(content string) error {
has, err := x.Get(&PublicKey{
Content: content,
Type: KeyTypeUser,
})
if err != nil {
return err
} else if has {
err := db.Where("content = ? AND type = ?", content, KeyTypeUser).First(&PublicKey{}).Error
if err == nil {
return ErrKeyAlreadyExist{0, content}
} else if !errors.Is(err, gorm.ErrRecordNotFound) {
return err
}
return nil
}
func addKey(e Engine, key *PublicKey) (err error) {
func addKey(tx *gorm.DB, key *PublicKey) (err error) {
// Calculate fingerprint.
tmpPath := strings.ReplaceAll(path.Join(os.TempDir(), fmt.Sprintf("%d", time.Now().Nanosecond()), "id_rsa.pub"), "\\", "/")
_ = os.MkdirAll(path.Dir(tmpPath), os.ModePerm)
@@ -385,7 +385,7 @@ func addKey(e Engine, key *PublicKey) (err error) {
key.Fingerprint = strings.Split(stdout, " ")[1]
// Save SSH key.
if _, err = e.Insert(key); err != nil {
if err = tx.Create(key).Error; err != nil {
return err
}
@@ -404,16 +404,10 @@ func AddPublicKey(ownerID int64, name, content string) (*PublicKey, error) {
}
// Key name of same user cannot be duplicated.
has, err := x.Where("owner_id = ? AND name = ?", ownerID, name).Get(new(PublicKey))
if err != nil {
return nil, err
} else if has {
err := db.Where("owner_id = ? AND name = ?", ownerID, name).First(new(PublicKey)).Error
if err == nil {
return nil, ErrKeyNameAlreadyUsed{ownerID, name}
}
sess := x.NewSession()
defer sess.Close()
if err = sess.Begin(); err != nil {
} else if !errors.Is(err, gorm.ErrRecordNotFound) {
return nil, err
}
@@ -424,21 +418,25 @@ func AddPublicKey(ownerID int64, name, content string) (*PublicKey, error) {
Mode: AccessModeWrite,
Type: KeyTypeUser,
}
if err = addKey(sess, key); err != nil {
err = db.Transaction(func(tx *gorm.DB) error {
return addKey(tx, key)
})
if err != nil {
return nil, errors.Newf("addKey: %v", err)
}
return key, sess.Commit()
return key, nil
}
// GetPublicKeyByID returns public key by given ID.
func GetPublicKeyByID(keyID int64) (*PublicKey, error) {
key := new(PublicKey)
has, err := x.Id(keyID).Get(key)
err := db.Where("id = ?", keyID).First(key).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, ErrKeyNotExist{keyID}
}
return nil, err
} else if !has {
return nil, ErrKeyNotExist{keyID}
}
return key, nil
}
@@ -448,11 +446,12 @@ func GetPublicKeyByID(keyID int64) (*PublicKey, error) {
// exists.
func SearchPublicKeyByContent(content string) (*PublicKey, error) {
key := new(PublicKey)
has, err := x.Where("content like ?", content+"%").Get(key)
err := db.Where("content LIKE ?", content+"%").First(key).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, ErrKeyNotExist{}
}
return nil, err
} else if !has {
return nil, ErrKeyNotExist{}
}
return key, nil
}
@@ -460,23 +459,21 @@ func SearchPublicKeyByContent(content string) (*PublicKey, error) {
// ListPublicKeys returns a list of public keys belongs to given user.
func ListPublicKeys(uid int64) ([]*PublicKey, error) {
keys := make([]*PublicKey, 0, 5)
return keys, x.Where("owner_id = ?", uid).Find(&keys)
return keys, db.Where("owner_id = ?", uid).Find(&keys).Error
}
// UpdatePublicKey updates given public key.
func UpdatePublicKey(key *PublicKey) error {
_, err := x.Id(key.ID).AllCols().Update(key)
return err
return db.Model(key).Where("id = ?", key.ID).Updates(key).Error
}
// deletePublicKeys does the actual key deletion but does not update authorized_keys file.
func deletePublicKeys(e *xorm.Session, keyIDs ...int64) error {
func deletePublicKeys(tx *gorm.DB, keyIDs ...int64) error {
if len(keyIDs) == 0 {
return nil
}
_, err := e.In("id", keyIDs).Delete(new(PublicKey))
return err
return tx.Where("id IN ?", keyIDs).Delete(new(PublicKey)).Error
}
// DeletePublicKey deletes SSH key information both in database and authorized_keys file.
@@ -494,17 +491,10 @@ func DeletePublicKey(doer *User, id int64) (err error) {
return ErrKeyAccessDenied{doer.ID, key.ID, "public"}
}
sess := x.NewSession()
defer sess.Close()
if err = sess.Begin(); err != nil {
return err
}
if err = deletePublicKeys(sess, id); err != nil {
return err
}
if err = sess.Commit(); err != nil {
err = db.Transaction(func(tx *gorm.DB) error {
return deletePublicKeys(tx, id)
})
if err != nil {
return err
}
@@ -531,14 +521,24 @@ func RewriteAuthorizedKeys() error {
}
defer os.Remove(tmpPath)
err = x.Iterate(new(PublicKey), func(idx int, bean any) (err error) {
_, err = f.WriteString((bean.(*PublicKey)).AuthorizedString())
return err
})
_ = f.Close()
// Use FindInBatches to process keys in chunks to avoid memory issues with large datasets
err = db.FindInBatches(&[]PublicKey{}, 100, func(tx *gorm.DB, batch int) error {
var keys []PublicKey
if err := tx.Find(&keys).Error; err != nil {
return err
}
for _, key := range keys {
if _, err := f.WriteString(key.AuthorizedString()); err != nil {
return err
}
}
return nil
}).Error
if err != nil {
_ = f.Close()
return err
}
_ = f.Close()
if com.IsExist(fpath) {
if err = os.Remove(fpath); err != nil {
@@ -562,37 +562,40 @@ func RewriteAuthorizedKeys() error {
// DeployKey represents deploy key information and its relation with repository.
type DeployKey struct {
ID int64
KeyID int64 `xorm:"UNIQUE(s) INDEX"`
RepoID int64 `xorm:"UNIQUE(s) INDEX"`
KeyID int64 `gorm:"uniqueIndex:s;index"`
RepoID int64 `gorm:"uniqueIndex:s;index"`
Name string
Fingerprint string
Content string `xorm:"-" json:"-" gorm:"-"`
Content string `gorm:"-" json:"-"`
Created time.Time `xorm:"-" json:"-" gorm:"-"`
Created time.Time `gorm:"-" json:"-"`
CreatedUnix int64
Updated time.Time `xorm:"-" json:"-" gorm:"-"` // Note: Updated must below Created for AfterSet.
Updated time.Time `gorm:"-" json:"-"` // Note: Updated must below Created for AfterFind.
UpdatedUnix int64
HasRecentActivity bool `xorm:"-" json:"-" gorm:"-"`
HasUsed bool `xorm:"-" json:"-" gorm:"-"`
HasRecentActivity bool `gorm:"-" json:"-"`
HasUsed bool `gorm:"-" json:"-"`
}
func (k *DeployKey) BeforeInsert() {
k.CreatedUnix = time.Now().Unix()
func (k *DeployKey) BeforeCreate(tx *gorm.DB) error {
if k.CreatedUnix == 0 {
k.CreatedUnix = tx.NowFunc().Unix()
}
return nil
}
func (k *DeployKey) BeforeUpdate() {
k.UpdatedUnix = time.Now().Unix()
func (k *DeployKey) BeforeUpdate(tx *gorm.DB) error {
k.UpdatedUnix = tx.NowFunc().Unix()
return nil
}
func (k *DeployKey) AfterSet(colName string, _ xorm.Cell) {
switch colName {
case "created_unix":
k.Created = time.Unix(k.CreatedUnix, 0).Local()
case "updated_unix":
func (k *DeployKey) AfterFind(tx *gorm.DB) error {
k.Created = time.Unix(k.CreatedUnix, 0).Local()
if k.UpdatedUnix > 0 {
k.Updated = time.Unix(k.UpdatedUnix, 0).Local()
k.HasUsed = k.Updated.After(k.Created)
k.HasRecentActivity = k.Updated.Add(7 * 24 * time.Hour).After(time.Now())
k.HasRecentActivity = k.Updated.Add(7 * 24 * time.Hour).After(tx.NowFunc())
}
return nil
}
// GetContent gets associated public key content.
@@ -605,28 +608,28 @@ func (k *DeployKey) GetContent() error {
return nil
}
func checkDeployKey(e Engine, keyID, repoID int64, name string) error {
func checkDeployKey(tx *gorm.DB, keyID, repoID int64, name string) error {
// Note: We want error detail, not just true or false here.
has, err := e.Where("key_id = ? AND repo_id = ?", keyID, repoID).Get(new(DeployKey))
if err != nil {
return err
} else if has {
err := tx.Where("key_id = ? AND repo_id = ?", keyID, repoID).First(new(DeployKey)).Error
if err == nil {
return ErrDeployKeyAlreadyExist{keyID, repoID}
} else if !errors.Is(err, gorm.ErrRecordNotFound) {
return err
}
has, err = e.Where("repo_id = ? AND name = ?", repoID, name).Get(new(DeployKey))
if err != nil {
return err
} else if has {
err = tx.Where("repo_id = ? AND name = ?", repoID, name).First(new(DeployKey)).Error
if err == nil {
return ErrDeployKeyNameAlreadyUsed{repoID, name}
} else if !errors.Is(err, gorm.ErrRecordNotFound) {
return err
}
return nil
}
// addDeployKey adds new key-repo relation.
func addDeployKey(e *xorm.Session, keyID, repoID int64, name, fingerprint string) (*DeployKey, error) {
if err := checkDeployKey(e, keyID, repoID, name); err != nil {
func addDeployKey(tx *gorm.DB, keyID, repoID int64, name, fingerprint string) (*DeployKey, error) {
if err := checkDeployKey(tx, keyID, repoID, name); err != nil {
return nil, err
}
@@ -636,14 +639,14 @@ func addDeployKey(e *xorm.Session, keyID, repoID int64, name, fingerprint string
Name: name,
Fingerprint: fingerprint,
}
_, err := e.Insert(key)
err := tx.Create(key).Error
return key, err
}
// HasDeployKey returns true if public key is a deploy key of given repository.
func HasDeployKey(keyID, repoID int64) bool {
has, _ := x.Where("key_id = ? AND repo_id = ?", keyID, repoID).Get(new(DeployKey))
return has
err := db.Where("key_id = ? AND repo_id = ?", keyID, repoID).First(new(DeployKey)).Error
return err == nil
}
// AddDeployKey add new deploy key to database and authorized_keys file.
@@ -657,30 +660,34 @@ func AddDeployKey(repoID int64, name, content string) (*DeployKey, error) {
Mode: AccessModeRead,
Type: KeyTypeDeploy,
}
has, err := x.Get(pkey)
if err != nil {
err := db.Where("content = ? AND mode = ? AND type = ?", content, AccessModeRead, KeyTypeDeploy).First(pkey).Error
has := err == nil
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
return nil, err
}
sess := x.NewSession()
defer sess.Close()
if err = sess.Begin(); err != nil {
return nil, err
}
// First time use this deploy key.
if !has {
if err = addKey(sess, pkey); err != nil {
return nil, errors.Newf("addKey: %v", err)
var key *DeployKey
err = db.Transaction(func(tx *gorm.DB) error {
// First time use this deploy key.
if !has {
if err := addKey(tx, pkey); err != nil {
return errors.Newf("addKey: %v", err)
}
}
}
key, err := addDeployKey(sess, pkey.ID, repoID, name, pkey.Fingerprint)
var err error
key, err = addDeployKey(tx, pkey.ID, repoID, name, pkey.Fingerprint)
if err != nil {
return errors.Newf("addDeployKey: %v", err)
}
return nil
})
if err != nil {
return nil, errors.Newf("addDeployKey: %v", err)
return nil, err
}
return key, sess.Commit()
return key, nil
}
var _ errutil.NotFound = (*ErrDeployKeyNotExist)(nil)
@@ -705,11 +712,12 @@ func (ErrDeployKeyNotExist) NotFound() bool {
// GetDeployKeyByID returns deploy key by given ID.
func GetDeployKeyByID(id int64) (*DeployKey, error) {
key := new(DeployKey)
has, err := x.Id(id).Get(key)
err := db.Where("id = ?", id).First(key).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, ErrDeployKeyNotExist{args: map[string]any{"deployKeyID": id}}
}
return nil, err
} else if !has {
return nil, ErrDeployKeyNotExist{args: map[string]any{"deployKeyID": id}}
}
return key, nil
}
@@ -720,19 +728,19 @@ func GetDeployKeyByRepo(keyID, repoID int64) (*DeployKey, error) {
KeyID: keyID,
RepoID: repoID,
}
has, err := x.Get(key)
err := db.Where("key_id = ? AND repo_id = ?", keyID, repoID).First(key).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, ErrDeployKeyNotExist{args: map[string]any{"keyID": keyID, "repoID": repoID}}
}
return nil, err
} else if !has {
return nil, ErrDeployKeyNotExist{args: map[string]any{"keyID": keyID, "repoID": repoID}}
}
return key, nil
}
// UpdateDeployKey updates deploy key information.
func UpdateDeployKey(key *DeployKey) error {
_, err := x.Id(key.ID).AllCols().Update(key)
return err
return db.Model(key).Where("id = ?", key.ID).Updates(key).Error
}
// DeleteDeployKey deletes deploy key from its repository authorized_keys file if needed.
@@ -761,31 +769,27 @@ func DeleteDeployKey(doer *User, id int64) error {
}
}
sess := x.NewSession()
defer sess.Close()
if err = sess.Begin(); err != nil {
return err
}
return db.Transaction(func(tx *gorm.DB) error {
if err := tx.Where("id = ?", key.ID).Delete(new(DeployKey)).Error; err != nil {
return errors.Newf("delete deploy key [%d]: %v", key.ID, err)
}
if _, err = sess.ID(key.ID).Delete(new(DeployKey)); err != nil {
return errors.Newf("delete deploy key [%d]: %v", key.ID, err)
}
// Check if this is the last reference to same key content.
has, err := sess.Where("key_id = ?", key.KeyID).Get(new(DeployKey))
if err != nil {
return err
} else if !has {
if err = deletePublicKeys(sess, key.KeyID); err != nil {
// Check if this is the last reference to same key content.
err := tx.Where("key_id = ?", key.KeyID).First(new(DeployKey)).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
if err = deletePublicKeys(tx, key.KeyID); err != nil {
return err
}
} else if err != nil {
return err
}
}
return sess.Commit()
return nil
})
}
// ListDeployKeys returns all deploy keys by given repository ID.
func ListDeployKeys(repoID int64) ([]*DeployKey, error) {
keys := make([]*DeployKey, 0, 5)
return keys, x.Where("repo_id = ?", repoID).Find(&keys)
return keys, db.Where("repo_id = ?", repoID).Find(&keys).Error
}

View File

@@ -8,6 +8,7 @@ import (
"github.com/cockroachdb/errors"
"github.com/pquerna/otp/totp"
"github.com/unknwon/com"
"gorm.io/gorm"
"gogs.io/gogs/internal/conf"
"gogs.io/gogs/internal/cryptoutil"
@@ -37,20 +38,16 @@ func (t *TwoFactor) ValidateTOTP(passcode string) (bool, error) {
}
// DeleteTwoFactor removes two-factor authentication token and recovery codes of given user.
func DeleteTwoFactor(userID int64) (err error) {
sess := x.NewSession()
defer sess.Close()
if err = sess.Begin(); err != nil {
return err
}
if _, err = sess.Where("user_id = ?", userID).Delete(new(TwoFactor)); err != nil {
return errors.Newf("delete two-factor: %v", err)
} else if err = deleteRecoveryCodesByUserID(sess, userID); err != nil {
return errors.Newf("deleteRecoveryCodesByUserID: %v", err)
}
return sess.Commit()
func DeleteTwoFactor(userID int64) error {
return db.Transaction(func(tx *gorm.DB) error {
if err := tx.Where("user_id = ?", userID).Delete(new(TwoFactor)).Error; err != nil {
return errors.Newf("delete two-factor: %v", err)
}
if err := deleteRecoveryCodesByUserID(tx, userID); err != nil {
return errors.Newf("deleteRecoveryCodesByUserID: %v", err)
}
return nil
})
}
// TwoFactorRecoveryCode represents a two-factor authentication recovery code.
@@ -64,12 +61,11 @@ type TwoFactorRecoveryCode struct {
// GetRecoveryCodesByUserID returns all recovery codes of given user.
func GetRecoveryCodesByUserID(userID int64) ([]*TwoFactorRecoveryCode, error) {
recoveryCodes := make([]*TwoFactorRecoveryCode, 0, 10)
return recoveryCodes, x.Where("user_id = ?", userID).Find(&recoveryCodes)
return recoveryCodes, db.Where("user_id = ?", userID).Find(&recoveryCodes).Error
}
func deleteRecoveryCodesByUserID(e Engine, userID int64) error {
_, err := e.Where("user_id = ?", userID).Delete(new(TwoFactorRecoveryCode))
return err
func deleteRecoveryCodesByUserID(e *gorm.DB, userID int64) error {
return e.Where("user_id = ?", userID).Delete(&TwoFactorRecoveryCode{}).Error
}
// RegenerateRecoveryCodes regenerates new set of recovery codes for given user.
@@ -79,19 +75,15 @@ func RegenerateRecoveryCodes(userID int64) error {
return errors.Newf("generateRecoveryCodes: %v", err)
}
sess := x.NewSession()
defer sess.Close()
if err = sess.Begin(); err != nil {
return err
}
if err = deleteRecoveryCodesByUserID(sess, userID); err != nil {
return errors.Newf("deleteRecoveryCodesByUserID: %v", err)
} else if _, err = sess.Insert(recoveryCodes); err != nil {
return errors.Newf("insert new recovery codes: %v", err)
}
return sess.Commit()
return db.Transaction(func(tx *gorm.DB) error {
if err := deleteRecoveryCodesByUserID(tx, userID); err != nil {
return errors.Newf("deleteRecoveryCodesByUserID: %v", err)
}
if err := tx.Create(recoveryCodes).Error; err != nil {
return errors.Newf("insert new recovery codes: %v", err)
}
return nil
})
}
type ErrTwoFactorRecoveryCodeNotFound struct {

View File

@@ -14,8 +14,8 @@ import (
"github.com/cockroachdb/errors"
jsoniter "github.com/json-iterator/go"
gouuid "github.com/satori/go.uuid"
"gorm.io/gorm"
log "unknwon.dev/clog/v2"
"xorm.io/xorm"
api "github.com/gogs/go-gogs-client"
@@ -95,45 +95,42 @@ type Webhook struct {
ID int64
RepoID int64
OrgID int64
URL string `xorm:"url TEXT"`
URL string `gorm:"type:text;column:url"`
ContentType HookContentType
Secret string `xorm:"TEXT"`
Events string `xorm:"TEXT"`
*HookEvent `xorm:"-"` // LEGACY [1.0]: Cannot ignore JSON (i.e. json:"-") here, it breaks old backup archive
IsSSL bool `xorm:"is_ssl"`
Secret string `gorm:"type:text"`
Events string `gorm:"type:text"`
*HookEvent `gorm:"-"` // LEGACY [1.0]: Cannot ignore JSON (i.e. json:"-") here, it breaks old backup archive
IsSSL bool `gorm:"column:is_ssl"`
IsActive bool
HookTaskType HookTaskType
Meta string `xorm:"TEXT"` // store hook-specific attributes
Meta string `gorm:"type:text"` // store hook-specific attributes
LastStatus HookStatus // Last delivery status
Created time.Time `xorm:"-" json:"-" gorm:"-"`
Created time.Time `gorm:"-" json:"-"`
CreatedUnix int64
Updated time.Time `xorm:"-" json:"-" gorm:"-"`
Updated time.Time `gorm:"-" json:"-"`
UpdatedUnix int64
}
func (w *Webhook) BeforeInsert() {
w.CreatedUnix = time.Now().Unix()
func (w *Webhook) BeforeCreate(tx *gorm.DB) error {
w.CreatedUnix = tx.NowFunc().Unix()
w.UpdatedUnix = w.CreatedUnix
return nil
}
func (w *Webhook) BeforeUpdate() {
w.UpdatedUnix = time.Now().Unix()
func (w *Webhook) BeforeUpdate(tx *gorm.DB) error {
w.UpdatedUnix = tx.NowFunc().Unix()
return nil
}
func (w *Webhook) AfterSet(colName string, _ xorm.Cell) {
var err error
switch colName {
case "events":
w.HookEvent = &HookEvent{}
if err = jsoniter.Unmarshal([]byte(w.Events), w.HookEvent); err != nil {
log.Error("Unmarshal [%d]: %v", w.ID, err)
}
case "created_unix":
w.Created = time.Unix(w.CreatedUnix, 0).Local()
case "updated_unix":
w.Updated = time.Unix(w.UpdatedUnix, 0).Local()
func (w *Webhook) AfterFind(tx *gorm.DB) error {
w.HookEvent = &HookEvent{}
if err := jsoniter.Unmarshal([]byte(w.Events), w.HookEvent); err != nil {
log.Error("Unmarshal [%d]: %v", w.ID, err)
}
w.Created = time.Unix(w.CreatedUnix, 0).Local()
w.Updated = time.Unix(w.UpdatedUnix, 0).Local()
return nil
}
func (w *Webhook) SlackMeta() *SlackMeta {
@@ -231,8 +228,7 @@ func (w *Webhook) EventsArray() []string {
// CreateWebhook creates a new web hook.
func CreateWebhook(w *Webhook) error {
_, err := x.Insert(w)
return err
return db.Create(w).Error
}
var _ errutil.NotFound = (*ErrWebhookNotExist)(nil)
@@ -257,11 +253,12 @@ func (ErrWebhookNotExist) NotFound() bool {
// getWebhook uses argument bean as query condition,
// ID must be specified and do not assign unnecessary fields.
func getWebhook(bean *Webhook) (*Webhook, error) {
has, err := x.Get(bean)
err := db.Where(bean).First(bean).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, ErrWebhookNotExist{args: map[string]any{"webhookID": bean.ID}}
}
return nil, err
} else if !has {
return nil, ErrWebhookNotExist{args: map[string]any{"webhookID": bean.ID}}
}
return bean, nil
}
@@ -292,39 +289,34 @@ func GetWebhookByOrgID(orgID, id int64) (*Webhook, error) {
}
// getActiveWebhooksByRepoID returns all active webhooks of repository.
func getActiveWebhooksByRepoID(e Engine, repoID int64) ([]*Webhook, error) {
func getActiveWebhooksByRepoID(tx *gorm.DB, repoID int64) ([]*Webhook, error) {
webhooks := make([]*Webhook, 0, 5)
return webhooks, e.Where("repo_id = ?", repoID).And("is_active = ?", true).Find(&webhooks)
return webhooks, tx.Where("repo_id = ? AND is_active = ?", repoID, true).Find(&webhooks).Error
}
// GetWebhooksByRepoID returns all webhooks of a repository.
func GetWebhooksByRepoID(repoID int64) ([]*Webhook, error) {
webhooks := make([]*Webhook, 0, 5)
return webhooks, x.Find(&webhooks, &Webhook{RepoID: repoID})
return webhooks, db.Where("repo_id = ?", repoID).Find(&webhooks).Error
}
// UpdateWebhook updates information of webhook.
func UpdateWebhook(w *Webhook) error {
_, err := x.Id(w.ID).AllCols().Update(w)
return err
return db.Model(w).Where("id = ?", w.ID).Updates(w).Error
}
// deleteWebhook uses argument bean as query condition,
// ID must be specified and do not assign unnecessary fields.
func deleteWebhook(bean *Webhook) (err error) {
sess := x.NewSession()
defer sess.Close()
if err = sess.Begin(); err != nil {
return err
}
if _, err = sess.Delete(bean); err != nil {
return err
} else if _, err = sess.Delete(&HookTask{HookID: bean.ID}); err != nil {
return err
}
return sess.Commit()
func deleteWebhook(bean *Webhook) error {
return db.Transaction(func(tx *gorm.DB) error {
if err := tx.Delete(bean).Error; err != nil {
return err
}
if err := tx.Where("hook_id = ?", bean.ID).Delete(&HookTask{}).Error; err != nil {
return err
}
return nil
})
}
// DeleteWebhookOfRepoByID deletes webhook of repository by given ID.
@@ -345,14 +337,14 @@ func DeleteWebhookOfOrgByID(orgID, id int64) error {
// GetWebhooksByOrgID returns all webhooks for an organization.
func GetWebhooksByOrgID(orgID int64) (ws []*Webhook, err error) {
err = x.Find(&ws, &Webhook{OrgID: orgID})
err = db.Where("org_id = ?", orgID).Find(&ws).Error
return ws, err
}
// getActiveWebhooksByOrgID returns all active webhooks for an organization.
func getActiveWebhooksByOrgID(e Engine, orgID int64) ([]*Webhook, error) {
func getActiveWebhooksByOrgID(tx *gorm.DB, orgID int64) ([]*Webhook, error) {
ws := make([]*Webhook, 0, 3)
return ws, e.Where("org_id=?", orgID).And("is_active=?", true).Find(&ws)
return ws, tx.Where("org_id = ? AND is_active = ?", orgID, true).Find(&ws).Error
}
// ___ ___ __ ___________ __
@@ -431,64 +423,56 @@ type HookResponse struct {
// HookTask represents a hook task.
type HookTask struct {
ID int64
RepoID int64 `xorm:"INDEX"`
RepoID int64 `gorm:"index"`
HookID int64
UUID string
Type HookTaskType
URL string `xorm:"TEXT"`
Signature string `xorm:"TEXT"`
api.Payloader `xorm:"-" json:"-" gorm:"-"`
PayloadContent string `xorm:"TEXT"`
URL string `gorm:"type:text"`
Signature string `gorm:"type:text"`
api.Payloader `gorm:"-" json:"-"`
PayloadContent string `gorm:"type:text"`
ContentType HookContentType
EventType HookEventType
IsSSL bool
IsDelivered bool
Delivered int64
DeliveredString string `xorm:"-" json:"-" gorm:"-"`
DeliveredString string `gorm:"-" json:"-"`
// History info.
IsSucceed bool
RequestContent string `xorm:"TEXT"`
RequestInfo *HookRequest `xorm:"-" json:"-" gorm:"-"`
ResponseContent string `xorm:"TEXT"`
ResponseInfo *HookResponse `xorm:"-" json:"-" gorm:"-"`
RequestContent string `gorm:"type:text"`
RequestInfo *HookRequest `gorm:"-" json:"-"`
ResponseContent string `gorm:"type:text"`
ResponseInfo *HookResponse `gorm:"-" json:"-"`
}
func (t *HookTask) BeforeUpdate() {
func (t *HookTask) BeforeUpdate(tx *gorm.DB) error {
if t.RequestInfo != nil {
t.RequestContent = t.ToJSON(t.RequestInfo)
}
if t.ResponseInfo != nil {
t.ResponseContent = t.ToJSON(t.ResponseInfo)
}
return nil
}
func (t *HookTask) AfterSet(colName string, _ xorm.Cell) {
var err error
switch colName {
case "delivered":
t.DeliveredString = time.Unix(0, t.Delivered).Format("2006-01-02 15:04:05 MST")
case "request_content":
if t.RequestContent == "" {
return
}
func (t *HookTask) AfterFind(tx *gorm.DB) error {
t.DeliveredString = time.Unix(0, t.Delivered).Format("2006-01-02 15:04:05 MST")
if t.RequestContent != "" {
t.RequestInfo = &HookRequest{}
if err = jsoniter.Unmarshal([]byte(t.RequestContent), t.RequestInfo); err != nil {
if err := jsoniter.Unmarshal([]byte(t.RequestContent), t.RequestInfo); err != nil {
log.Error("Unmarshal[%d]: %v", t.ID, err)
}
}
case "response_content":
if t.ResponseContent == "" {
return
}
if t.ResponseContent != "" {
t.ResponseInfo = &HookResponse{}
if err = jsoniter.Unmarshal([]byte(t.ResponseContent), t.ResponseInfo); err != nil {
if err := jsoniter.Unmarshal([]byte(t.ResponseContent), t.ResponseInfo); err != nil {
log.Error("Unmarshal [%d]: %v", t.ID, err)
}
}
return nil
}
func (t *HookTask) ToJSON(v any) string {
@@ -502,20 +486,19 @@ func (t *HookTask) ToJSON(v any) string {
// HookTasks returns a list of hook tasks by given conditions.
func HookTasks(hookID int64, page int) ([]*HookTask, error) {
tasks := make([]*HookTask, 0, conf.Webhook.PagingNum)
return tasks, x.Limit(conf.Webhook.PagingNum, (page-1)*conf.Webhook.PagingNum).Where("hook_id=?", hookID).Desc("id").Find(&tasks)
return tasks, db.Where("hook_id = ?", hookID).Order("id DESC").Limit(conf.Webhook.PagingNum).Offset((page - 1) * conf.Webhook.PagingNum).Find(&tasks).Error
}
// createHookTask creates a new hook task,
// it handles conversion from Payload to PayloadContent.
func createHookTask(e Engine, t *HookTask) error {
func createHookTask(tx *gorm.DB, t *HookTask) error {
data, err := t.JSONPayload()
if err != nil {
return err
}
t.UUID = gouuid.NewV4().String()
t.PayloadContent = string(data)
_, err = e.Insert(t)
return err
return tx.Create(t).Error
}
var _ errutil.NotFound = (*ErrHookTaskNotExist)(nil)
@@ -543,23 +526,23 @@ func GetHookTaskOfWebhookByUUID(webhookID int64, uuid string) (*HookTask, error)
HookID: webhookID,
UUID: uuid,
}
has, err := x.Get(hookTask)
err := db.Where("hook_id = ? AND uuid = ?", webhookID, uuid).First(hookTask).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, ErrHookTaskNotExist{args: map[string]any{"webhookID": webhookID, "uuid": uuid}}
}
return nil, err
} else if !has {
return nil, ErrHookTaskNotExist{args: map[string]any{"webhookID": webhookID, "uuid": uuid}}
}
return hookTask, nil
}
// UpdateHookTask updates information of hook task.
func UpdateHookTask(t *HookTask) error {
_, err := x.Id(t.ID).AllCols().Update(t)
return err
return db.Model(t).Where("id = ?", t.ID).Updates(t).Error
}
// prepareHookTasks adds list of webhooks to task queue.
func prepareHookTasks(e Engine, repo *Repository, event HookEventType, p api.Payloader, webhooks []*Webhook) (err error) {
func prepareHookTasks(tx *gorm.DB, repo *Repository, event HookEventType, p api.Payloader, webhooks []*Webhook) (err error) {
if len(webhooks) == 0 {
return nil
}
@@ -633,7 +616,7 @@ func prepareHookTasks(e Engine, repo *Repository, event HookEventType, p api.Pay
signature = hex.EncodeToString(sig.Sum(nil))
}
if err = createHookTask(e, &HookTask{
if err = createHookTask(tx, &HookTask{
RepoID: repo.ID,
HookID: w.ID,
Type: w.HookTaskType,
@@ -655,32 +638,32 @@ func prepareHookTasks(e Engine, repo *Repository, event HookEventType, p api.Pay
return nil
}
func prepareWebhooks(e Engine, repo *Repository, event HookEventType, p api.Payloader) error {
webhooks, err := getActiveWebhooksByRepoID(e, repo.ID)
func prepareWebhooks(tx *gorm.DB, repo *Repository, event HookEventType, p api.Payloader) error {
webhooks, err := getActiveWebhooksByRepoID(tx, repo.ID)
if err != nil {
return errors.Newf("getActiveWebhooksByRepoID [%d]: %v", repo.ID, err)
}
// check if repo belongs to org and append additional webhooks
if repo.mustOwner(e).IsOrganization() {
if repo.mustOwner(tx).IsOrganization() {
// get hooks for org
orgws, err := getActiveWebhooksByOrgID(e, repo.OwnerID)
orgws, err := getActiveWebhooksByOrgID(tx, repo.OwnerID)
if err != nil {
return errors.Newf("getActiveWebhooksByOrgID [%d]: %v", repo.OwnerID, err)
}
webhooks = append(webhooks, orgws...)
}
return prepareHookTasks(e, repo, event, p, webhooks)
return prepareHookTasks(tx, repo, event, p, webhooks)
}
// PrepareWebhooks adds all active webhooks to task queue.
func PrepareWebhooks(repo *Repository, event HookEventType, p api.Payloader) error {
// NOTE: To prevent too many cascading changes in a single refactoring PR, we
// choose to ignore this function in tests.
if x == nil && testutil.InTest {
if db == nil && testutil.InTest {
return nil
}
return prepareWebhooks(x, repo, event, p)
return prepareWebhooks(db, repo, event, p)
}
// TestWebhook adds the test webhook matches the ID to task queue.
@@ -689,7 +672,7 @@ func TestWebhook(repo *Repository, event HookEventType, p api.Payloader, webhook
if err != nil {
return errors.Newf("GetWebhookOfRepoByID [repo_id: %d, id: %d]: %v", repo.ID, webhookID, err)
}
return prepareHookTasks(x, repo, event, p, []*Webhook{webhook})
return prepareHookTasks(db, repo, event, p, []*Webhook{webhook})
}
func (t *HookTask) deliver() {
@@ -784,18 +767,15 @@ func (t *HookTask) deliver() {
// TODO: shoot more hooks at same time.
func DeliverHooks() {
tasks := make([]*HookTask, 0, 10)
_ = x.Where("is_delivered = ?", false).Iterate(new(HookTask),
func(idx int, bean any) error {
t := bean.(*HookTask)
err := db.Where("is_delivered = ?", false).Find(&tasks).Error
if err != nil {
log.Error("Find undelivered hook tasks: %v", err)
} else {
for _, t := range tasks {
t.deliver()
tasks = append(tasks, t)
return nil
})
// Update hook task status.
for _, t := range tasks {
if err := UpdateHookTask(t); err != nil {
log.Error("UpdateHookTask [%d]: %v", t.ID, err)
if err := UpdateHookTask(t); err != nil {
log.Error("UpdateHookTask [%d]: %v", t.ID, err)
}
}
}
@@ -805,7 +785,7 @@ func DeliverHooks() {
HookQueue.Remove(repoID)
tasks = make([]*HookTask, 0, 5)
if err := x.Where("repo_id = ?", repoID).And("is_delivered = ?", false).Find(&tasks); err != nil {
if err := db.Where("repo_id = ? AND is_delivered = ?", repoID, false).Find(&tasks).Error; err != nil {
log.Error("Get repository [%s] hook tasks: %v", repoID, err)
continue
}