Merge branch 'main' into archived-lable-highglight-via-linear-gradient

This commit is contained in:
6543 2024-11-12 22:51:16 +01:00 committed by GitHub
commit 0c1ddc76ba
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
462 changed files with 13867 additions and 12596 deletions

View File

@ -27,7 +27,6 @@ plugins:
- "@stylistic/eslint-plugin-js"
- "@typescript-eslint/eslint-plugin"
- eslint-plugin-array-func
- eslint-plugin-deprecation
- eslint-plugin-github
- eslint-plugin-i
- eslint-plugin-no-jquery
@ -248,6 +247,7 @@ rules:
"@typescript-eslint/no-base-to-string": [0]
"@typescript-eslint/no-confusing-non-null-assertion": [2]
"@typescript-eslint/no-confusing-void-expression": [0]
"@typescript-eslint/no-deprecated": [2]
"@typescript-eslint/no-dupe-class-members": [0]
"@typescript-eslint/no-duplicate-enum-values": [2]
"@typescript-eslint/no-duplicate-type-constituents": [2, {ignoreUnions: true}]
@ -359,7 +359,6 @@ rules:
default-case-last: [2]
default-case: [0]
default-param-last: [0]
deprecation/deprecation: [2]
dot-notation: [0]
eqeqeq: [2]
for-direction: [2]
@ -816,6 +815,7 @@ rules:
unicorn/catch-error-name: [0]
unicorn/consistent-destructuring: [2]
unicorn/consistent-empty-array-spread: [2]
unicorn/consistent-existence-index-check: [0]
unicorn/consistent-function-scoping: [2]
unicorn/custom-error-definition: [0]
unicorn/empty-brace-spaces: [2]
@ -892,10 +892,12 @@ rules:
unicorn/prefer-dom-node-text-content: [2]
unicorn/prefer-event-target: [2]
unicorn/prefer-export-from: [0]
unicorn/prefer-global-this: [0]
unicorn/prefer-includes: [2]
unicorn/prefer-json-parse-buffer: [0]
unicorn/prefer-keyboard-event-key: [2]
unicorn/prefer-logical-operator-over-ternary: [2]
unicorn/prefer-math-min-max: [2]
unicorn/prefer-math-trunc: [2]
unicorn/prefer-modern-dom-apis: [0]
unicorn/prefer-modern-math-apis: [2]

File diff suppressed because one or more lines are too long

View File

@ -53,8 +53,6 @@ func (e Emoji) MarshalJSON() ([]byte, error) {
}
func main() {
var err error
flag.Parse()
// generate data
@ -83,8 +81,6 @@ var replacer = strings.NewReplacer(
var emojiRE = regexp.MustCompile(`\{Emoji:"([^"]*)"`)
func generate() ([]byte, error) {
var err error
// load gemoji data
res, err := http.Get(gemojiURL)
if err != nil {

View File

@ -111,13 +111,11 @@ func fail(ctx context.Context, userMessage, logMsgFmt string, args ...any) error
if !setting.IsProd {
_, _ = fmt.Fprintln(os.Stderr, "Gitea:", logMsg)
}
if userMessage != "" {
if unicode.IsPunct(rune(userMessage[len(userMessage)-1])) {
logMsg = userMessage + " " + logMsg
} else {
logMsg = userMessage + ". " + logMsg
}
}
_ = private.SSHLog(ctx, true, logMsg)
}
return cli.Exit("", 1)
@ -288,10 +286,10 @@ func runServ(c *cli.Context) error {
if allowedCommands.Contains(verb) {
if allowedCommandsLfs.Contains(verb) {
if !setting.LFS.StartServer {
return fail(ctx, "Unknown git command", "LFS authentication request over SSH denied, LFS support is disabled")
return fail(ctx, "LFS Server is not enabled", "")
}
if verb == verbLfsTransfer && !setting.LFS.AllowPureSSH {
return fail(ctx, "Unknown git command", "LFS SSH transfer connection denied, pure SSH protocol is disabled")
return fail(ctx, "LFS SSH transfer is not enabled", "")
}
if len(words) > 2 {
lfsVerb = words[2]

View File

@ -324,6 +324,10 @@ RUN_USER = ; git
;; Maximum number of locks returned per page
;LFS_LOCKS_PAGING_NUM = 50
;;
;; When clients make lfs batch requests, reject them if there are more pointers than this number
;; zero means 'unlimited'
;LFS_MAX_BATCH_SIZE = 0
;;
;; Allow graceful restarts using SIGHUP to fork
;ALLOW_GRACEFUL_RESTARTS = true
;;
@ -1003,6 +1007,14 @@ LEVEL = Info
;; The set of allowed values and rules are the same as DEFAULT_REPO_UNITS.
;DEFAULT_FORK_REPO_UNITS = repo.code,repo.pulls
;;
;; Comma separated list of default mirror repo units.
;; The set of allowed values and rules are the same as DEFAULT_REPO_UNITS.
;DEFAULT_MIRROR_REPO_UNITS = repo.code,repo.releases,repo.issues,repo.wiki,repo.projects,repo.packages
;;
;; Comma separated list of default template repo units.
;; The set of allowed values and rules are the same as DEFAULT_REPO_UNITS.
;DEFAULT_TEMPLATE_REPO_UNITS = repo.code,repo.releases,repo.issues,repo.pulls,repo.wiki,repo.projects,repo.packages
;;
;; Prefix archive files by placing them in a directory named after the repository
;PREFIX_ARCHIVE_FILES = true
;;
@ -2638,6 +2650,16 @@ LEVEL = Info
;; override the azure blob base path if storage type is azureblob
;AZURE_BLOB_BASE_PATH = lfs/
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; settings for Gitea's LFS client (eg: mirroring an upstream lfs endpoint)
;;
;[lfs_client]
;; Limit the number of pointers in each batch request to this number
;BATCH_SIZE = 20
;; Limit the number of concurrent upload/download operations within a batch
;BATCH_OPERATION_CONCURRENCY = 8
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; settings for packages, will override storage setting

195
go.mod
View File

@ -10,9 +10,9 @@ godebug x509negativeserial=1
require (
code.gitea.io/actions-proto-go v0.4.0
code.gitea.io/gitea-vet v0.2.3
code.gitea.io/sdk/gitea v0.17.1
code.gitea.io/sdk/gitea v0.19.0
codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570
connectrpc.com/connect v1.15.0
connectrpc.com/connect v1.17.0
gitea.com/go-chi/binding v0.0.0-20240430071103-39a851e106ed
gitea.com/go-chi/cache v0.2.1
gitea.com/go-chi/captcha v0.0.0-20240315150714-fb487f629098
@ -20,21 +20,21 @@ require (
gitea.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96
gitea.com/lunny/levelqueue v0.4.2-0.20230414023320-3c0159fe0fe4
github.com/42wim/httpsig v1.2.2
github.com/42wim/sshsig v0.0.0-20211121163825-841cf5bbc121
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.12.0
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.3.2
github.com/42wim/sshsig v0.0.0-20240818000253-e3a6333df815
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.16.0
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.4.1
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358
github.com/ProtonMail/go-crypto v1.0.0
github.com/PuerkitoBio/goquery v1.9.2
github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.7.2
github.com/PuerkitoBio/goquery v1.10.0
github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.7.3
github.com/alecthomas/chroma/v2 v2.14.0
github.com/aws/aws-sdk-go v1.43.21
github.com/aws/aws-sdk-go-v2/credentials v1.17.30
github.com/aws/aws-sdk-go-v2/service/codecommit v1.25.1
github.com/aws/aws-sdk-go v1.55.5
github.com/aws/aws-sdk-go-v2/credentials v1.17.42
github.com/aws/aws-sdk-go-v2/service/codecommit v1.27.3
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb
github.com/blevesearch/bleve/v2 v2.4.2
github.com/buildkite/terminal-to-html/v3 v3.12.1
github.com/caddyserver/certmagic v0.21.3
github.com/buildkite/terminal-to-html/v3 v3.16.3
github.com/caddyserver/certmagic v0.21.4
github.com/charmbracelet/git-lfs-transfer v0.2.0
github.com/chi-middleware/proxy v1.1.1
github.com/dimiro1/reply v0.0.0-20200315094148-d0136a4c9e21
@ -46,53 +46,53 @@ require (
github.com/emersion/go-imap v1.2.1
github.com/emirpasic/gods v1.18.1
github.com/ethantkoenig/rupture v1.0.1
github.com/felixge/fgprof v0.9.4
github.com/felixge/fgprof v0.9.5
github.com/fsnotify/fsnotify v1.7.0
github.com/gliderlabs/ssh v0.3.7
github.com/go-ap/activitypub v0.0.0-20240408091739-ba76b44c2594
github.com/go-ap/activitypub v0.0.0-20240910141749-b4b8c8aa484c
github.com/go-ap/jsonld v0.0.0-20221030091449-f2a191312c73
github.com/go-chi/chi/v5 v5.0.13
github.com/go-chi/chi/v5 v5.1.0
github.com/go-chi/cors v1.2.1
github.com/go-co-op/gocron v1.37.0
github.com/go-enry/go-enry/v2 v2.9.1
github.com/go-git/go-billy/v5 v5.5.0
github.com/go-git/go-billy/v5 v5.6.0
github.com/go-git/go-git/v5 v5.12.0
github.com/go-ldap/ldap/v3 v3.4.6
github.com/go-ldap/ldap/v3 v3.4.8
github.com/go-redsync/redsync/v4 v4.13.0
github.com/go-sql-driver/mysql v1.8.1
github.com/go-swagger/go-swagger v0.31.0
github.com/go-testfixtures/testfixtures/v3 v3.11.0
github.com/go-webauthn/webauthn v0.10.2
github.com/go-webauthn/webauthn v0.11.2
github.com/gobwas/glob v0.2.3
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f
github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85
github.com/golang-jwt/jwt/v5 v5.2.1
github.com/google/go-github/v61 v61.0.0
github.com/google/licenseclassifier/v2 v2.0.0
github.com/google/pprof v0.0.0-20240618054019-d3b898a103f8
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db
github.com/google/uuid v1.6.0
github.com/gorilla/feeds v1.2.0
github.com/gorilla/sessions v1.3.0
github.com/gorilla/sessions v1.4.0
github.com/h2non/gock v1.2.0
github.com/hashicorp/go-version v1.7.0
github.com/hashicorp/golang-lru/v2 v2.0.7
github.com/huandu/xstrings v1.5.0
github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056
github.com/jhillyerd/enmime v1.2.0
github.com/jhillyerd/enmime v1.3.0
github.com/json-iterator/go v1.1.12
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
github.com/keybase/go-crypto v0.0.0-20200123153347-de78d2cb44f4
github.com/klauspost/compress v1.17.9
github.com/klauspost/compress v1.17.11
github.com/klauspost/cpuid/v2 v2.2.8
github.com/lib/pq v1.10.9
github.com/markbates/goth v1.80.0
github.com/mattn/go-isatty v0.0.20
github.com/mattn/go-sqlite3 v1.14.22
github.com/meilisearch/meilisearch-go v0.26.3
github.com/mattn/go-sqlite3 v1.14.24
github.com/meilisearch/meilisearch-go v0.29.1-0.20241106140435-0bf60fad690a
github.com/mholt/archiver/v3 v3.5.1
github.com/microcosm-cc/bluemonday v1.0.26
github.com/microcosm-cc/bluemonday v1.0.27
github.com/microsoft/go-mssqldb v1.7.2
github.com/minio/minio-go/v7 v7.0.77
github.com/minio/minio-go/v7 v7.0.80
github.com/msteinert/pam v1.2.0
github.com/nektos/act v0.2.63
github.com/niklasfasching/go-org v1.7.0
@ -101,9 +101,9 @@ require (
github.com/opencontainers/image-spec v1.1.0
github.com/pkg/errors v0.9.1
github.com/pquerna/otp v1.4.0
github.com/prometheus/client_golang v1.19.1
github.com/prometheus/client_golang v1.20.5
github.com/quasoft/websspi v1.1.2
github.com/redis/go-redis/v9 v9.6.0
github.com/redis/go-redis/v9 v9.7.0
github.com/robfig/cron/v3 v3.0.1
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1
github.com/sassoftware/go-rpmutils v0.4.0
@ -113,22 +113,23 @@ require (
github.com/syndtr/goleveldb v1.0.0
github.com/tstranex/u2f v1.0.0
github.com/ulikunitz/xz v0.5.12
github.com/urfave/cli/v2 v2.27.2
github.com/xanzy/go-gitlab v0.105.0
github.com/urfave/cli/v2 v2.27.5
github.com/xanzy/go-gitlab v0.112.0
github.com/xeipuuv/gojsonschema v1.2.0
github.com/yohcop/openid-go v1.0.1
github.com/yuin/goldmark v1.7.2
github.com/yuin/goldmark v1.7.8
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc
github.com/yuin/goldmark-meta v1.1.0
golang.org/x/crypto v0.26.0
golang.org/x/image v0.18.0
golang.org/x/net v0.28.0
golang.org/x/oauth2 v0.21.0
golang.org/x/sys v0.24.0
golang.org/x/text v0.17.0
golang.org/x/tools v0.24.0
google.golang.org/grpc v1.62.1
google.golang.org/protobuf v1.34.2
golang.org/x/crypto v0.28.0
golang.org/x/image v0.21.0
golang.org/x/net v0.30.0
golang.org/x/oauth2 v0.23.0
golang.org/x/sync v0.8.0
golang.org/x/sys v0.26.0
golang.org/x/text v0.19.0
golang.org/x/tools v0.26.0
google.golang.org/grpc v1.67.1
google.golang.org/protobuf v1.35.1
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
gopkg.in/ini.v1 v1.67.0
gopkg.in/yaml.v3 v3.0.1
@ -139,37 +140,37 @@ require (
)
require (
cloud.google.com/go/compute/metadata v0.3.0 // indirect
dario.cat/mergo v1.0.0 // indirect
cloud.google.com/go/compute/metadata v0.5.2 // indirect
dario.cat/mergo v1.0.1 // indirect
filippo.io/edwards25519 v1.1.0 // indirect
git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.9.0 // indirect
github.com/ClickHouse/ch-go v0.61.5 // indirect
github.com/ClickHouse/clickhouse-go/v2 v2.25.0 // indirect
github.com/DataDog/zstd v1.5.5 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect
github.com/ClickHouse/ch-go v0.63.1 // indirect
github.com/ClickHouse/clickhouse-go/v2 v2.24.0 // indirect
github.com/DataDog/zstd v1.5.6 // indirect
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/semver/v3 v3.2.1 // indirect
github.com/Masterminds/sprig/v3 v3.2.3 // indirect
github.com/Masterminds/semver/v3 v3.3.0 // indirect
github.com/Masterminds/sprig/v3 v3.3.0 // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/RoaringBitmap/roaring v1.9.4 // indirect
github.com/andybalholm/brotli v1.1.0 // indirect
github.com/andybalholm/brotli v1.1.1 // indirect
github.com/andybalholm/cascadia v1.3.2 // indirect
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
github.com/aws/aws-sdk-go-v2 v1.30.4 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.16 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.16 // indirect
github.com/aws/smithy-go v1.20.4 // indirect
github.com/aws/aws-sdk-go-v2 v1.32.3 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.22 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.22 // indirect
github.com/aws/smithy-go v1.22.0 // indirect
github.com/aymerick/douceur v0.2.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bits-and-blooms/bitset v1.13.0 // indirect
github.com/blevesearch/bleve_index_api v1.1.10 // indirect
github.com/bits-and-blooms/bitset v1.14.3 // indirect
github.com/blevesearch/bleve_index_api v1.1.12 // indirect
github.com/blevesearch/geo v0.1.20 // indirect
github.com/blevesearch/go-faiss v1.0.20 // indirect
github.com/blevesearch/go-faiss v1.0.23 // indirect
github.com/blevesearch/go-porterstemmer v1.0.3 // indirect
github.com/blevesearch/gtreap v0.1.1 // indirect
github.com/blevesearch/mmap-go v1.0.4 // indirect
github.com/blevesearch/scorch_segment_api/v2 v2.2.15 // indirect
github.com/blevesearch/scorch_segment_api/v2 v2.2.16 // indirect
github.com/blevesearch/segment v0.9.1 // indirect
github.com/blevesearch/snowballstem v0.9.0 // indirect
github.com/blevesearch/upsidedown_store_api v1.0.2 // indirect
@ -178,29 +179,29 @@ require (
github.com/blevesearch/zapx/v12 v12.3.10 // indirect
github.com/blevesearch/zapx/v13 v13.3.10 // indirect
github.com/blevesearch/zapx/v14 v14.3.10 // indirect
github.com/blevesearch/zapx/v15 v15.3.13 // indirect
github.com/blevesearch/zapx/v16 v16.1.5 // indirect
github.com/boombuler/barcode v1.0.1 // indirect
github.com/blevesearch/zapx/v15 v15.3.16 // indirect
github.com/blevesearch/zapx/v16 v16.1.7 // indirect
github.com/boombuler/barcode v1.0.2 // indirect
github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 // indirect
github.com/caddyserver/zerossl v0.1.3 // indirect
github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/cloudflare/circl v1.3.9 // indirect
github.com/cloudflare/circl v1.5.0 // indirect
github.com/couchbase/go-couchbase v0.1.1 // indirect
github.com/couchbase/gomemcached v0.3.1 // indirect
github.com/couchbase/gomemcached v0.3.2 // indirect
github.com/couchbase/goutils v0.1.2 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
github.com/cyphar/filepath-securejoin v0.2.5 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect
github.com/cyphar/filepath-securejoin v0.3.4 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/davidmz/go-pageant v1.0.2 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/dlclark/regexp2 v1.11.0 // indirect
github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43 // indirect
github.com/fatih/color v1.17.0 // indirect
github.com/dlclark/regexp2 v1.11.4 // indirect
github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6 // indirect
github.com/fatih/color v1.18.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fxamacker/cbor/v2 v2.6.0 // indirect
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
github.com/git-lfs/pktline v0.0.0-20230103162542-ca444d533ef1 // indirect
github.com/go-ap/errors v0.0.0-20240304112515-6077fa9c17b0 // indirect
github.com/go-ap/errors v0.0.0-20240910140019-1e9d33cc1568 // indirect
github.com/go-asn1-ber/asn1-ber v1.5.7 // indirect
github.com/go-enry/go-oniguruma v1.2.1 // indirect
github.com/go-faster/city v1.0.1 // indirect
@ -219,18 +220,18 @@ require (
github.com/go-openapi/strfmt v0.23.0 // indirect
github.com/go-openapi/swag v0.23.0 // indirect
github.com/go-openapi/validate v0.24.0 // indirect
github.com/go-webauthn/x v0.1.9 // indirect
github.com/go-webauthn/x v0.1.15 // indirect
github.com/goccy/go-json v0.10.3 // indirect
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
github.com/golang-jwt/jwt/v4 v4.5.1 // indirect
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
github.com/golang-sql/sqlexp v0.1.0 // indirect
github.com/golang/geo v0.0.0-20230421003525-6adc56603217 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/btree v1.1.2 // indirect
github.com/google/btree v1.1.3 // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/google/go-tpm v0.9.0 // indirect
github.com/google/go-tpm v0.9.1 // indirect
github.com/gorilla/css v1.0.1 // indirect
github.com/gorilla/handlers v1.5.2 // indirect
github.com/gorilla/mux v1.8.1 // indirect
@ -241,9 +242,8 @@ require (
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/imdario/mergo v0.3.16 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/jessevdk/go-flags v1.5.0 // indirect
github.com/jessevdk/go-flags v1.6.1 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/kevinburke/ssh_config v1.2.0 // indirect
github.com/klauspost/pgzip v1.2.6 // indirect
@ -254,9 +254,9 @@ require (
github.com/mailru/easyjson v0.7.7 // indirect
github.com/markbates/going v1.0.3 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/mholt/acmez/v2 v2.0.1 // indirect
github.com/miekg/dns v1.1.61 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/mholt/acmez/v2 v2.0.3 // indirect
github.com/miekg/dns v1.1.62 // indirect
github.com/minio/md5-simd v1.1.2 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
@ -270,38 +270,35 @@ require (
github.com/oklog/ulid v1.3.1 // indirect
github.com/olekukonko/tablewriter v0.0.5 // indirect
github.com/onsi/ginkgo v1.16.5 // indirect
github.com/onsi/gomega v1.33.1 // indirect
github.com/paulmach/orb v0.11.1 // indirect
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
github.com/pierrec/lz4/v4 v4.1.21 // indirect
github.com/pjbgf/sha1cd v0.3.0 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.55.0 // indirect
github.com/prometheus/common v0.60.1 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/rhysd/actionlint v1.7.1 // indirect
github.com/rhysd/actionlint v1.7.3 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/rogpeppe/go-internal v1.12.0 // indirect
github.com/rogpeppe/go-internal v1.13.1 // indirect
github.com/rs/xid v1.6.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect
github.com/sagikazarmark/locafero v0.6.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/segmentio/asm v1.2.0 // indirect
github.com/shopspring/decimal v1.4.0 // indirect
github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/skeema/knownhosts v1.2.2 // indirect
github.com/skeema/knownhosts v1.3.0 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/afero v1.11.0 // indirect
github.com/spf13/cast v1.6.0 // indirect
github.com/spf13/cast v1.7.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/spf13/viper v1.18.2 // indirect
github.com/spf13/viper v1.19.0 // indirect
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/toqueteos/webbrowser v1.2.0 // indirect
github.com/unknwon/com v1.0.1 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasthttp v1.55.0 // indirect
github.com/valyala/fastjson v1.6.4 // indirect
github.com/x448/float16 v0.8.4 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect
@ -309,19 +306,19 @@ require (
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
github.com/zeebo/blake3 v0.2.3 // indirect
go.etcd.io/bbolt v1.3.10 // indirect
go.mongodb.org/mongo-driver v1.14.0 // indirect
go.opentelemetry.io/otel v1.27.0 // indirect
go.opentelemetry.io/otel/trace v1.27.0 // indirect
github.com/zeebo/assert v1.3.0 // indirect
github.com/zeebo/blake3 v0.2.4 // indirect
go.etcd.io/bbolt v1.3.11 // indirect
go.mongodb.org/mongo-driver v1.17.1 // indirect
go.opentelemetry.io/otel v1.31.0 // indirect
go.opentelemetry.io/otel/trace v1.31.0 // indirect
go.uber.org/atomic v1.11.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
golang.org/x/exp v0.0.0-20240314144324-c7f7c6466f7f // indirect
golang.org/x/mod v0.20.0 // indirect
golang.org/x/sync v0.8.0 // indirect
golang.org/x/time v0.5.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240314234333-6e1732d8331c // indirect
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c // indirect
golang.org/x/mod v0.21.0 // indirect
golang.org/x/time v0.7.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38 // indirect
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect

467
go.sum
View File

@ -1,17 +1,17 @@
cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc=
cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
cloud.google.com/go/compute/metadata v0.5.2 h1:UxK4uu/Tn+I3p2dYWTfiX4wva7aYlKixAHn3fyqngqo=
cloud.google.com/go/compute/metadata v0.5.2/go.mod h1:C66sj2AluDcIqakBq/M8lw8/ybHgOZqin2obFxa/E5k=
code.gitea.io/actions-proto-go v0.4.0 h1:OsPBPhodXuQnsspG1sQ4eRE1PeoZyofd7+i73zCwnsU=
code.gitea.io/actions-proto-go v0.4.0/go.mod h1:mn7Wkqz6JbnTOHQpot3yDeHx+O5C9EGhMEE+htvHBas=
code.gitea.io/gitea-vet v0.2.3 h1:gdFmm6WOTM65rE8FUBTRzeQZYzXePKSSB1+r574hWwI=
code.gitea.io/gitea-vet v0.2.3/go.mod h1:zcNbT/aJEmivCAhfmkHOlT645KNOf9W2KnkLgFjGGfE=
code.gitea.io/sdk/gitea v0.17.1 h1:3jCPOG2ojbl8AcfaUCRYLT5MUcBMFwS0OSK2mA5Zok8=
code.gitea.io/sdk/gitea v0.17.1/go.mod h1:aCnBqhHpoEWA180gMbaCtdX9Pl6BWBAuuP2miadoTNM=
code.gitea.io/sdk/gitea v0.19.0 h1:8I6s1s4RHgzxiPHhOQdgim1RWIRcr0LVMbHBjBFXq4Y=
code.gitea.io/sdk/gitea v0.19.0/go.mod h1:IG9xZJoltDNeDSW0qiF2Vqx5orMWa7OhVWrjvrd5NpI=
codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570 h1:TXbikPqa7YRtfU9vS6QJBg77pUvbEb6StRdZO8t1bEY=
codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570/go.mod h1:IIAjsijsd8q1isWX8MACefDEgTQslQ4stk2AeeTt3kM=
connectrpc.com/connect v1.15.0 h1:lFdeCbZrVVDydAqwr4xGV2y+ULn+0Z73s5JBj2LikWo=
connectrpc.com/connect v1.15.0/go.mod h1:bQmjpDY8xItMnttnurVgOkHUBMRT9cpsNi2O4AjKhmA=
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
connectrpc.com/connect v1.17.0 h1:W0ZqMhtVzn9Zhn2yATuUokDLO5N+gIuBWMOnsQrfmZk=
connectrpc.com/connect v1.17.0/go.mod h1:0292hj1rnx8oFrStN7cB4jjVBeqs+Yx5yDIC2prWDO8=
dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078 h1:cliQ4HHsCo6xi2oWZYKWW4bly/Ory9FuTpFPRxj/mAg=
@ -36,56 +36,55 @@ gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:lSA0F4e9A2NcQSqGq
gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:EXuID2Zs0pAQhH8yz+DNjUbjppKQzKFAn28TMYPB6IU=
github.com/42wim/httpsig v1.2.2 h1:ofAYoHUNs/MJOLqQ8hIxeyz2QxOz8qdSVvp3PX/oPgA=
github.com/42wim/httpsig v1.2.2/go.mod h1:P/UYo7ytNBFwc+dg35IubuAUIs8zj5zzFIgUCEl55WY=
github.com/42wim/sshsig v0.0.0-20211121163825-841cf5bbc121 h1:r3qt8PCHnfjOv9PN3H+XXKmDA1dfFMIN1AislhlA/ps=
github.com/42wim/sshsig v0.0.0-20211121163825-841cf5bbc121/go.mod h1:Ock8XgA7pvULhIaHGAk/cDnRfNrF9Jey81nPcc403iU=
github.com/42wim/sshsig v0.0.0-20240818000253-e3a6333df815 h1:5EoemV++kUK2Sw98yWP/RWyduvP7IaBgWWHe+4BWcSw=
github.com/42wim/sshsig v0.0.0-20240818000253-e3a6333df815/go.mod h1:zjsWZdDLrcDojDIfpQg7A6J4YZLT0cbwuAD26AppDBo=
github.com/6543/go-version v1.3.1 h1:HvOp+Telns7HWJ2Xo/05YXQSB2bE0WmVgbHqwMPZT4U=
github.com/6543/go-version v1.3.1/go.mod h1:oqFAHCwtLVUTLdhQmVZWYvaHXTdsbB4SY85at64SQEo=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.12.0 h1:1nGuui+4POelzDwI7RG56yfQJHCnKvwfMoU7VsEp+Zg=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.12.0/go.mod h1:99EvauvlcJ1U06amZiksfYz/3aFGyIhWGHVyiZXtBAI=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.2 h1:FDif4R1+UUR+00q6wquyX90K7A8dN+R5E8GEadoP7sU=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.2/go.mod h1:aiYBYui4BJ/BJCAIKs92XiPyQfTaBWqvHujDwKb6CBU=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.9.0 h1:H+U3Gk9zY56G3u872L82bk4thcsy2Gghb9ExT4Zvm1o=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.9.0/go.mod h1:mgrmMSgaLp9hmax62XQTd0N4aAqSE5E0DulSpVYK7vc=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.5.0 h1:AifHbc4mg0x9zW52WOpKbsHaDKuRhlI7TVl47thgQ70=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.5.0/go.mod h1:T5RfihdXtBDxt1Ch2wobif3TvzTdumDy29kahv6AV9A=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.16.0 h1:JZg6HRh6W6U4OLl6lk7BZ7BLisIzM9dG1R50zUk9C/M=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.16.0/go.mod h1:YL1xnZ6QejvQHWJrX/AvhFl4WW4rqHVoKspWNVwFk0M=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0 h1:tfLQ34V6F7tVSwoTf/4lH5sE0o6eCJuNDTmH09nDpbc=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0/go.mod h1:9kIvujWAA58nmPmWB1m23fyWic1kYZMxD9CxaWn4Qpg=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 h1:ywEEhmNahHBihViHepv3xPBn1663uRv2t2q/ESv9seY=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0/go.mod h1:iZDifYGJTIgIIkYRNWPENUnqx6bJ2xnSDFI2tjwZNuY=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.6.0 h1:PiSrjRPpkQNjrM8H0WwKMnZUdu1RGMtd/LdGKUrOo+c=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.6.0/go.mod h1:oDrbWx4ewMylP7xHivfgixbfGBT6APAwsSoHRKotnIc=
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/azure-sdk-for-go/sdk/storage/azblob v1.3.2 h1:YUUxeiOWgdAQE3pXt2H7QXzZs0q8UBjgRbl56qo8GYM=
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.3.2/go.mod h1:dmXQgZuiSubAecswZE+Sm8jkvEa7kQgTPVRvwL/nd0E=
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.4.1 h1:cf+OIKbkmMHBaC3u78AXomweqM0oxQSgBXRZf3WH4yM=
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.4.1/go.mod h1:ap1dmS6vQKJxSMNiGJcq4QuUQkOynyD93gLw6MDF7ek=
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 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/ClickHouse/ch-go v0.61.5 h1:zwR8QbYI0tsMiEcze/uIMK+Tz1D3XZXLdNrlaOpeEI4=
github.com/ClickHouse/ch-go v0.61.5/go.mod h1:s1LJW/F/LcFs5HJnuogFMta50kKDO0lf9zzfrbl0RQg=
github.com/ClickHouse/clickhouse-go/v2 v2.25.0 h1:rKscwqgQHzWBTZySZDcHKxgs0Ad+xFULfZvo26W5UlY=
github.com/ClickHouse/clickhouse-go/v2 v2.25.0/go.mod h1:iDTViXk2Fgvf1jn2dbJd1ys+fBkdD1UMRnXlwmhijhQ=
github.com/DataDog/zstd v1.5.5 h1:oWf5W7GtOLgp6bciQYDmhHHjdhYkALu6S/5Ni9ZgSvQ=
github.com/DataDog/zstd v1.5.5/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw=
github.com/ClickHouse/ch-go v0.63.1 h1:s2JyZvWLTCSAGdtjMBBmAgQQHMco6pawLJMOXi0FODM=
github.com/ClickHouse/ch-go v0.63.1/go.mod h1:I1kJJCL3WJcBMGe1m+HVK0+nREaG+JOYYBWjrDrF3R0=
github.com/ClickHouse/clickhouse-go/v2 v2.24.0 h1:L/n/pVVpk95KtkHOiKuSnO7cu2ckeW4gICbbOh5qs74=
github.com/ClickHouse/clickhouse-go/v2 v2.24.0/go.mod h1:iDTViXk2Fgvf1jn2dbJd1ys+fBkdD1UMRnXlwmhijhQ=
github.com/DataDog/zstd v1.5.6 h1:LbEglqepa/ipmmQJUDnSsfvA8e8IStVcGaFWDuxvGOY=
github.com/DataDog/zstd v1.5.6/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw=
github.com/Julusian/godocdown v0.0.0-20170816220326-6d19f8ff2df8/go.mod h1:INZr5t32rG59/5xeltqoCJoNY7e5x/3xoY9WSWVWg74=
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=
github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA=
github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM=
github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0=
github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs=
github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0=
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/ProtonMail/go-crypto v1.0.0 h1:LRuvITjQWX+WIfr930YHG2HNfjR1uOfyf5vE0kC2U78=
github.com/ProtonMail/go-crypto v1.0.0/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0=
github.com/PuerkitoBio/goquery v1.9.2 h1:4/wZksC3KgkQw7SQgkKotmKljk0M6V8TUvA8Wb4yPeE=
github.com/PuerkitoBio/goquery v1.9.2/go.mod h1:GHPCaP0ODyyxqcNoFGYlAprUFH81NuRPd0GX3Zu2Mvk=
github.com/PuerkitoBio/goquery v1.10.0 h1:6fiXdLuUvYs2OJSvNRqlNPoBm6YABE226xrbavY5Wv4=
github.com/PuerkitoBio/goquery v1.10.0/go.mod h1:TjZZl68Q3eGHNBA8CWaxAN7rOU1EbDz3CWuolcO5Yu4=
github.com/RoaringBitmap/roaring v0.4.23/go.mod h1:D0gp8kJQgE1A4LQ5wFLggQEyvDi06Mq5mKs52e1TwOo=
github.com/RoaringBitmap/roaring v0.7.1/go.mod h1:jdT9ykXwHFNdJbEtxePexlFYH9LXucApeS0/+/g+p1I=
github.com/RoaringBitmap/roaring v1.9.4 h1:yhEIoH4YezLYT04s1nHehNO64EKFTop/wBhxv2QzDdQ=
github.com/RoaringBitmap/roaring v1.9.4/go.mod h1:6AXUsoIEzDTFFQCe1RbGA6uFONMhvejWj5rqITANK90=
github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.7.2 h1:cSXom2MoKJ9KPPw29RoZtHvUETY4F4n/kXl8m9btnQ0=
github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.7.2/go.mod h1:JitQWJ8JuV4Y87l8VsHiiwhb3cgdyn68mX40s7NT6PA=
github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.7.3 h1:BP0HiyNT3AQEYi+if3wkRcIdQFHtsw6xX3Kx0glckgA=
github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.7.3/go.mod h1:hMNtySovKkn2gdDuLqnqveP+mfhUSaBdoBcr2I7Zt0E=
github.com/alecthomas/assert/v2 v2.7.0 h1:QtqSACNS3tF7oasA8CU6A6sXZSBDqnm7RfpLl9bZqbE=
github.com/alecthomas/assert/v2 v2.7.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
github.com/alecthomas/chroma/v2 v2.2.0/go.mod h1:vf4zrexSH54oEjJ7EdB65tGNHmH3pGZmVkgTP5RHvAs=
@ -94,14 +93,13 @@ github.com/alecthomas/chroma/v2 v2.14.0/go.mod h1:QolEbTfmUHIMVpBqxeDnNBj2uoeI4E
github.com/alecthomas/repr v0.0.0-20220113201626-b1b626ac65ae/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8=
github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc=
github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74 h1:Kk6a4nehpJ3UuJRqlA3JxYxBZEqCeOmATOvrbT4p9RA=
github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4=
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/anchore/archiver/v3 v3.5.2 h1:Bjemm2NzuRhmHy3m0lRe5tNoClB9A4zYyDV58PaB6aA=
github.com/anchore/archiver/v3 v3.5.2/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssnDhppzS1L4=
github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA=
github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA=
github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsViSLyss=
github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
@ -111,20 +109,20 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPd
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
github.com/aws/aws-sdk-go v1.43.21 h1:E4S2eX3d2gKJyI/ISrcIrSwXwqjIvCK85gtBMt4sAPE=
github.com/aws/aws-sdk-go v1.43.21/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo=
github.com/aws/aws-sdk-go-v2 v1.30.4 h1:frhcagrVNrzmT95RJImMHgabt99vkXGslubDaDagTk8=
github.com/aws/aws-sdk-go-v2 v1.30.4/go.mod h1:CT+ZPWXbYrci8chcARI3OmI/qgd+f6WtuLOoaIA8PR0=
github.com/aws/aws-sdk-go-v2/credentials v1.17.30 h1:aau/oYFtibVovr2rDt8FHlU17BTicFEMAi29V1U+L5Q=
github.com/aws/aws-sdk-go-v2/credentials v1.17.30/go.mod h1:BPJ/yXV92ZVq6G8uYvbU0gSl8q94UB63nMT5ctNO38g=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.16 h1:TNyt/+X43KJ9IJJMjKfa3bNTiZbUP7DeCxfbTROESwY=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.16/go.mod h1:2DwJF39FlNAUiX5pAc0UNeiz16lK2t7IaFcm0LFHEgc=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.16 h1:jYfy8UPmd+6kJW5YhY0L1/KftReOGxI/4NtVSTh9O/I=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.16/go.mod h1:7ZfEPZxkW42Afq4uQB8H2E2e6ebh6mXTueEpYzjCzcs=
github.com/aws/aws-sdk-go-v2/service/codecommit v1.25.1 h1:mOOALIM4JzhYkq3voCBbmZqmyEVEhHsfasMTbVxLkNs=
github.com/aws/aws-sdk-go-v2/service/codecommit v1.25.1/go.mod h1:6zf5j3mIUXKM0s2iz5ttR2Qwq+o47D0jotpAyaKgZRA=
github.com/aws/smithy-go v1.20.4 h1:2HK1zBdPgRbjFOHlfeQZfpC4r72MOb9bZkiFwggKO+4=
github.com/aws/smithy-go v1.20.4/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg=
github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU=
github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
github.com/aws/aws-sdk-go-v2 v1.32.3 h1:T0dRlFBKcdaUPGNtkBSwHZxrtis8CQU17UpNBZYd0wk=
github.com/aws/aws-sdk-go-v2 v1.32.3/go.mod h1:2SK5n0a2karNTv5tbP1SjsX0uhttou00v/HpXKM1ZUo=
github.com/aws/aws-sdk-go-v2/credentials v1.17.42 h1:sBP0RPjBU4neGpIYyx8mkU2QqLPl5u9cmdTWVzIpHkM=
github.com/aws/aws-sdk-go-v2/credentials v1.17.42/go.mod h1:FwZBfU530dJ26rv9saAbxa9Ej3eF/AK0OAY86k13n4M=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.22 h1:Jw50LwEkVjuVzE1NzkhNKkBf9cRN7MtE1F/b2cOKTUM=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.22/go.mod h1:Y/SmAyPcOTmpeVaWSzSKiILfXTVJwrGmYZhcRbhWuEY=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.22 h1:981MHwBaRZM7+9QSR6XamDzF/o7ouUGxFzr+nVSIhrs=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.22/go.mod h1:1RA1+aBEfn+CAB/Mh0MB6LsdCYCnjZm7tKXtnk499ZQ=
github.com/aws/aws-sdk-go-v2/service/codecommit v1.27.3 h1:NbAYtQnTXzv4u5uesdDhXGWDTsEtTyFXlzfXELtI6cc=
github.com/aws/aws-sdk-go-v2/service/codecommit v1.27.3/go.mod h1:ZhgiuB7I3d3UU6PnCWwb0IYFgGzJz0VT+DK3Xv1xtF8=
github.com/aws/smithy-go v1.22.0 h1:uunKnWlcoL3zO7q+gG2Pk53joueEOsnNB28QdMsmiMM=
github.com/aws/smithy-go v1.22.0/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg=
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 v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
@ -132,20 +130,20 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r
github.com/bits-and-blooms/bitset v1.1.10/go.mod h1:w0XsmFg8qg6cmpTtJ0z3pKgjTDBMMnI/+I2syrE6XBE=
github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=
github.com/bits-and-blooms/bitset v1.12.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
github.com/bits-and-blooms/bitset v1.13.0 h1:bAQ9OPNFYbGHV6Nez0tmNI0RiEu7/hxlYJRUA0wFAVE=
github.com/bits-and-blooms/bitset v1.13.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
github.com/bits-and-blooms/bitset v1.14.3 h1:Gd2c8lSNf9pKXom5JtD7AaKO8o7fGQ2LtFj1436qilA=
github.com/bits-and-blooms/bitset v1.14.3/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb h1:m935MPodAbYS46DG4pJSv7WO+VECIWUQ7OJYSoTrMh4=
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb/go.mod h1:PkYb9DJNAwrSvRx5DYA+gUcOIgTGVMNkfSCbZM8cWpI=
github.com/blevesearch/bleve/v2 v2.0.5/go.mod h1:ZjWibgnbRX33c+vBRgla9QhPb4QOjD6fdVJ+R1Bk8LM=
github.com/blevesearch/bleve/v2 v2.4.2 h1:NooYP1mb3c0StkiY9/xviiq2LGSaE8BQBCc/pirMx0U=
github.com/blevesearch/bleve/v2 v2.4.2/go.mod h1:ATNKj7Yl2oJv/lGuF4kx39bST2dveX6w0th2FFYLkc8=
github.com/blevesearch/bleve_index_api v1.0.0/go.mod h1:fiwKS0xLEm+gBRgv5mumf0dhgFr2mDgZah1pqv1c1M4=
github.com/blevesearch/bleve_index_api v1.1.10 h1:PDLFhVjrjQWr6jCuU7TwlmByQVCSEURADHdCqVS9+g0=
github.com/blevesearch/bleve_index_api v1.1.10/go.mod h1:PbcwjIcRmjhGbkS/lJCpfgVSMROV6TRubGGAODaK1W8=
github.com/blevesearch/bleve_index_api v1.1.12 h1:P4bw9/G/5rulOF7SJ9l4FsDoo7UFJ+5kexNy1RXfegY=
github.com/blevesearch/bleve_index_api v1.1.12/go.mod h1:PbcwjIcRmjhGbkS/lJCpfgVSMROV6TRubGGAODaK1W8=
github.com/blevesearch/geo v0.1.20 h1:paaSpu2Ewh/tn5DKn/FB5SzvH0EWupxHEIwbCk/QPqM=
github.com/blevesearch/geo v0.1.20/go.mod h1:DVG2QjwHNMFmjo+ZgzrIq2sfCh6rIHzy9d9d0B59I6w=
github.com/blevesearch/go-faiss v1.0.20 h1:AIkdTQFWuZ5LQmKQSebgMR4RynGNw8ZseJXaan5kvtI=
github.com/blevesearch/go-faiss v1.0.20/go.mod h1:jrxHrbl42X/RnDPI+wBoZU8joxxuRwedrxqswQ3xfU8=
github.com/blevesearch/go-faiss v1.0.23 h1:Wmc5AFwDLKGl2L6mjLX1Da3vCL0EKa2uHHSorcIS1Uc=
github.com/blevesearch/go-faiss v1.0.23/go.mod h1:OMGQwOaRRYxrmeNdMrXJPvVx8gBnvE5RYrr0BahNnkk=
github.com/blevesearch/go-porterstemmer v1.0.3 h1:GtmsqID0aZdCSNiY8SkuPJ12pD4jI+DdXTAn4YRcHCo=
github.com/blevesearch/go-porterstemmer v1.0.3/go.mod h1:angGc5Ht+k2xhJdZi511LtmxuEf0OVpvUUNrwmM1P7M=
github.com/blevesearch/gtreap v0.1.1 h1:2JWigFrzDMR+42WGIN/V2p0cUvn4UP3C4Q5nmaZGW8Y=
@ -154,8 +152,8 @@ github.com/blevesearch/mmap-go v1.0.2/go.mod h1:ol2qBqYaOUsGdm7aRMRrYGgPvnwLe6Y+
github.com/blevesearch/mmap-go v1.0.4 h1:OVhDhT5B/M1HNPpYPBKIEJaD0F3Si+CrEKULGCDPWmc=
github.com/blevesearch/mmap-go v1.0.4/go.mod h1:EWmEAOmdAS9z/pi/+Toxu99DnsbhG1TIxUoRmJw/pSs=
github.com/blevesearch/scorch_segment_api/v2 v2.0.1/go.mod h1:lq7yK2jQy1yQjtjTfU931aVqz7pYxEudHaDwOt1tXfU=
github.com/blevesearch/scorch_segment_api/v2 v2.2.15 h1:prV17iU/o+A8FiZi9MXmqbagd8I0bCqM7OKUYPbnb5Y=
github.com/blevesearch/scorch_segment_api/v2 v2.2.15/go.mod h1:db0cmP03bPNadXrCDuVkKLV6ywFSiRgPFT1YVrestBc=
github.com/blevesearch/scorch_segment_api/v2 v2.2.16 h1:uGvKVvG7zvSxCwcm4/ehBa9cCEuZVE+/zvrSl57QUVY=
github.com/blevesearch/scorch_segment_api/v2 v2.2.16/go.mod h1:VF5oHVbIFTu+znY1v30GjSpT5+9YFs9dV2hjvuh34F0=
github.com/blevesearch/segment v0.9.0/go.mod h1:9PfHYUdQCgHktBgvtUOF4x+pc4/l8rdH0u5spnW85UQ=
github.com/blevesearch/segment v0.9.1 h1:+dThDy+Lvgj5JMxhmOVlgFfkUtZV2kw49xax4+jTfSU=
github.com/blevesearch/segment v0.9.1/go.mod h1:zN21iLm7+GnBHWTao9I+Au/7MBiL8pPFtJBJTsk6kQw=
@ -181,24 +179,24 @@ github.com/blevesearch/zapx/v14 v14.2.0/go.mod h1:GNgZusc1p4ot040cBQMRGEZobvwjCq
github.com/blevesearch/zapx/v14 v14.3.10 h1:SG6xlsL+W6YjhX5N3aEiL/2tcWh3DO75Bnz77pSwwKU=
github.com/blevesearch/zapx/v14 v14.3.10/go.mod h1:qqyuR0u230jN1yMmE4FIAuCxmahRQEOehF78m6oTgns=
github.com/blevesearch/zapx/v15 v15.2.0/go.mod h1:MmQceLpWfME4n1WrBFIwplhWmaQbQqLQARpaKUEOs/A=
github.com/blevesearch/zapx/v15 v15.3.13 h1:6EkfaZiPlAxqXz0neniq35my6S48QI94W/wyhnpDHHQ=
github.com/blevesearch/zapx/v15 v15.3.13/go.mod h1:Turk/TNRKj9es7ZpKK95PS7f6D44Y7fAFy8F4LXQtGg=
github.com/blevesearch/zapx/v16 v16.1.5 h1:b0sMcarqNFxuXvjoXsF8WtwVahnxyhEvBSRJi/AUHjU=
github.com/blevesearch/zapx/v16 v16.1.5/go.mod h1:J4mSF39w1QELc11EWRSBFkPeZuO7r/NPKkHzDCoiaI8=
github.com/blevesearch/zapx/v15 v15.3.16 h1:Ct3rv7FUJPfPk99TI/OofdC+Kpb4IdyfdMH48sb+FmE=
github.com/blevesearch/zapx/v15 v15.3.16/go.mod h1:Turk/TNRKj9es7ZpKK95PS7f6D44Y7fAFy8F4LXQtGg=
github.com/blevesearch/zapx/v16 v16.1.7 h1:I07qV6l1rPda19zyof9Q2J9E8cjZ57pQhNY0+ePI5vM=
github.com/blevesearch/zapx/v16 v16.1.7/go.mod h1:JqQlOqlRVaYDkpLIl3JnKql8u4zKTNlVEa3nLsi0Gn8=
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/boombuler/barcode v1.0.1 h1:NDBbPmhS+EqABEs5Kg3n/5ZNjy73Pz7SIV+KCeqyXcs=
github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/boombuler/barcode v1.0.2 h1:79yrbttoZrLGkL/oOI8hBrUKucwOL0oOjUgEguGMcJ4=
github.com/boombuler/barcode v1.0.2/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 h1:N7oVaKyGp8bttX0bfZGmcGkjz7DLQXhAn3DNd3T0ous=
github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874/go.mod h1:r5xuitiExdLAJ09PR7vBVENGvp4ZuTBeWTGtxuX3K+c=
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
github.com/buildkite/terminal-to-html/v3 v3.12.1 h1:Wwec2uOu35zNPEQTzDyXQPyr/VUW6lzEwiYede1CaoE=
github.com/buildkite/terminal-to-html/v3 v3.12.1/go.mod h1:PfNtCsLnMZs7X9X2gcngOutmgSp7/oGBUIpVzRnD09A=
github.com/buildkite/terminal-to-html/v3 v3.16.3 h1:IGuJjboHjuMLWOGsKZKNxbbn41emOLiHzXPmQZk31fk=
github.com/buildkite/terminal-to-html/v3 v3.16.3/go.mod h1:r/J7cC9c3EzBzP3/wDz0RJLPwv5PUAMp+KF2w+ntMc0=
github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
github.com/caddyserver/certmagic v0.21.3 h1:pqRRry3yuB4CWBVq9+cUqu+Y6E2z8TswbhNx1AZeYm0=
github.com/caddyserver/certmagic v0.21.3/go.mod h1:Zq6pklO9nVRl3DIFUw9gVUfXKdpc/0qwTUAQMBlfgtI=
github.com/caddyserver/certmagic v0.21.4 h1:e7VobB8rffHv8ZZpSiZtEwnLDHUwLVYLWzWSa1FfKI0=
github.com/caddyserver/certmagic v0.21.4/go.mod h1:swUXjQ1T9ZtMv95qj7/InJvWLXURU85r+CfG0T+ZbDE=
github.com/caddyserver/zerossl v0.1.3 h1:onS+pxp3M8HnHpN5MMbOMyNjmTheJyWRaZYwn+YTAyA=
github.com/caddyserver/zerossl v0.1.3/go.mod h1:CxA0acn7oEGO6//4rtrRjYgEoa4MFw/XofZnrYwGqG4=
github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a h1:MISbI8sU/PSK/ztvmWKFcI7UGb5/HQT7B+i3a2myKgI=
@ -214,25 +212,25 @@ github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwys
github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk=
github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8=
github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
github.com/cloudflare/circl v1.3.9 h1:QFrlgFYf2Qpi8bSpVPK1HBvWpx16v/1TZivyo7pGuBE=
github.com/cloudflare/circl v1.3.9/go.mod h1:PDRU+oXvdD7KCtgKxW95M5Z8BpSCJXQORiZFnBQS5QU=
github.com/cloudflare/circl v1.5.0 h1:hxIWksrX6XN5a1L2TI/h53AGPhNHoUBo+TD1ms9+pys=
github.com/cloudflare/circl v1.5.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/couchbase/ghistogram v0.1.0/go.mod h1:s1Jhy76zqfEecpNWJfWUiKZookAFaiGOEoyzgHt9i7k=
github.com/couchbase/go-couchbase v0.1.1 h1:ClFXELcKj/ojyoTYbsY34QUrrYCBi/1G749sXSCkdhk=
github.com/couchbase/go-couchbase v0.1.1/go.mod h1:+/bddYDxXsf9qt0xpDUtRR47A2GjaXmGGAqQ/k3GJ8A=
github.com/couchbase/gomemcached v0.3.1 h1:jfspNuQIXgWy+5GUPQrsQ6yC5uJCfMmd/JKvK6C26r8=
github.com/couchbase/gomemcached v0.3.1/go.mod h1:mxliKQxOv84gQ0bJWbI+w9Wxdpt9HjDvgW9MjCym5Vo=
github.com/couchbase/gomemcached v0.3.2 h1:08rxiOoNcv0x5LTxgcYhnx1aPvV7iEtfeyUgqsJyPk0=
github.com/couchbase/gomemcached v0.3.2/go.mod h1:mxliKQxOv84gQ0bJWbI+w9Wxdpt9HjDvgW9MjCym5Vo=
github.com/couchbase/goutils v0.1.2 h1:gWr8B6XNWPIhfalHNog3qQKfGiYyh4K4VhO3P2o9BCs=
github.com/couchbase/goutils v0.1.2/go.mod h1:h89Ek/tiOxxqjz30nPPlwZdQbdB8BwgnuBxeoUe/ViE=
github.com/couchbase/moss v0.1.0/go.mod h1:9MaHIaRuy9pvLPUJxB8sh8OrLfyDczECVL37grCIubs=
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4=
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc=
github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/cyphar/filepath-securejoin v0.2.5 h1:6iR5tXJ/e6tJZzzdMc1km3Sa7RRIVBKAK32O2s7AYfo=
github.com/cyphar/filepath-securejoin v0.2.5/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
github.com/cyphar/filepath-securejoin v0.3.4 h1:VBWugsJh2ZxJmLFSM06/0qzQyiQX2Qs0ViKrUAcqdZ8=
github.com/cyphar/filepath-securejoin v0.3.4/go.mod h1:8s/MCNJREmFK0H02MF6Ihv1nakJe4L/w3WZLHNkvlYM=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
@ -253,8 +251,8 @@ github.com/djherbis/nio/v3 v3.0.1/go.mod h1:Ng4h80pbZFMla1yKzm61cF0tqqilXZYrogmW
github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI=
github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/dlclark/regexp2 v1.11.4 h1:rPYF9/LECdNymJufQKmri9gV604RvvABwgOA8un7yAo=
github.com/dlclark/regexp2 v1.11.4/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 h1:iFaUwBSo5Svw6L7HYpRu/0lE3e0BaElwnNO1qkNQxBY=
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s=
github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY=
@ -270,17 +268,17 @@ github.com/emersion/go-imap v1.2.1 h1:+s9ZjMEjOB8NzZMVTM3cCenz2JrQIGGo5j1df19WjT
github.com/emersion/go-imap v1.2.1/go.mod h1:Qlx1FSx2FTxjnjWpIlVNEuX+ylerZQNFE5NsmKFSejY=
github.com/emersion/go-message v0.15.0/go.mod h1:wQUEfE+38+7EW8p8aZ96ptg6bAb1iwdgej19uXASlE4=
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ=
github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43 h1:hH4PQfOndHDlpzYfLAAfl63E8Le6F2+EL/cdhlkyRJY=
github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ=
github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6 h1:oP4q0fw+fOSWn3DfFi4EXdT+B+gTtzx8GC9xsc26Znk=
github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ=
github.com/emersion/go-textwrapper v0.0.0-20200911093747-65d896831594/go.mod h1:aqO8z8wPrjkscevZJFVE1wXJrLpC5LtJG7fqLOsPb2U=
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
github.com/ethantkoenig/rupture v1.0.1 h1:6aAXghmvtnngMgQzy7SMGdicMvkV86V4n9fT0meE5E4=
github.com/ethantkoenig/rupture v1.0.1/go.mod h1:Sjqo/nbffZp1pVVXNGhpugIjsWmuS9KiIB4GtpEBur4=
github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4=
github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI=
github.com/felixge/fgprof v0.9.4 h1:ocDNwMFlnA0NU0zSB3I52xkO4sFXk80VK9lXjLClu88=
github.com/felixge/fgprof v0.9.4/go.mod h1:yKl+ERSa++RYOs32d8K6WEXCB4uXdLls4ZaZPpayhMM=
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
github.com/felixge/fgprof v0.9.5 h1:8+vR6yu2vvSKn08urWyEuxx75NWPEvybbkBirEpsbVY=
github.com/felixge/fgprof v0.9.5/go.mod h1:yKl+ERSa++RYOs32d8K6WEXCB4uXdLls4ZaZPpayhMM=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
@ -291,26 +289,26 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/fxamacker/cbor/v2 v2.6.0 h1:sU6J2usfADwWlYDAFhZBQ6TnLFBHxgesMrQfQgk1tWA=
github.com/fxamacker/cbor/v2 v2.6.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
github.com/git-lfs/pktline v0.0.0-20230103162542-ca444d533ef1 h1:mtDjlmloH7ytdblogrMz1/8Hqua1y8B4ID+bh3rvod0=
github.com/git-lfs/pktline v0.0.0-20230103162542-ca444d533ef1/go.mod h1:fenKRzpXDjNpsIBhuhUzvjCKlDjKam0boRAenTE0Q6A=
github.com/gliderlabs/ssh v0.3.7 h1:iV3Bqi942d9huXnzEF2Mt+CY9gLu8DNM4Obd+8bODRE=
github.com/gliderlabs/ssh v0.3.7/go.mod h1:zpHEXBstFnQYtGnB8k8kQLol82umzn/2/snG7alWVD8=
github.com/glycerine/go-unsnap-stream v0.0.0-20181221182339-f9677308dec2/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE=
github.com/glycerine/goconvey v0.0.0-20190410193231-58a59202ab31/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24=
github.com/go-ap/activitypub v0.0.0-20240408091739-ba76b44c2594 h1:er3GvGCm7bJwHostjZlsRy7uiUuCquUVF9Fe0TrwiPI=
github.com/go-ap/activitypub v0.0.0-20240408091739-ba76b44c2594/go.mod h1:yRUfFCoZY6C1CWalauqEQ5xYgSckzEBEO/2MBC6BOME=
github.com/go-ap/errors v0.0.0-20240304112515-6077fa9c17b0 h1:H9MGShwybHLSln6K8RxHPMHiLcD86Lru+5TVW2TcXHY=
github.com/go-ap/errors v0.0.0-20240304112515-6077fa9c17b0/go.mod h1:5x8a6P/dhmMGFxWLcyYlyOuJ2lRNaHGhRv+yu8BaTSI=
github.com/go-ap/activitypub v0.0.0-20240910141749-b4b8c8aa484c h1:82lzmsy5Nr6JA6HcLRVxGfbdSoWfW45C6jnY3zFS7Ks=
github.com/go-ap/activitypub v0.0.0-20240910141749-b4b8c8aa484c/go.mod h1:rpIPGre4qtTgSpVT0zz3hycAMuLtUt7BNngVNpyXhL8=
github.com/go-ap/errors v0.0.0-20240910140019-1e9d33cc1568 h1:eQEXAzWEijFbwtm/Hr2EtFbM0LvATRd1ltpDb+mfjQk=
github.com/go-ap/errors v0.0.0-20240910140019-1e9d33cc1568/go.mod h1:Vkh+Z3f24K8nMsJKXo1FHn5ebPsXvB/WDH5JRtYqdNo=
github.com/go-ap/jsonld v0.0.0-20221030091449-f2a191312c73 h1:GMKIYXyXPGIp+hYiWOhfqK4A023HdgisDT4YGgf99mw=
github.com/go-ap/jsonld v0.0.0-20221030091449-f2a191312c73/go.mod h1:jyveZeGw5LaADntW+UEsMjl3IlIwk+DxlYNsbofQkGA=
github.com/go-asn1-ber/asn1-ber v1.5.5/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
github.com/go-asn1-ber/asn1-ber v1.5.7 h1:DTX+lbVTWaTw1hQ+PbZPlnDZPEIs0SS/GCZAl535dDk=
github.com/go-asn1-ber/asn1-ber v1.5.7/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
github.com/go-chi/chi/v5 v5.0.1/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-chi/chi/v5 v5.0.13 h1:JlH2F2M8qnwl0N1+JFFzlX9TlKJYas3aPXdiuTmJL+w=
github.com/go-chi/chi/v5 v5.0.13/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw=
github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4=
github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=
github.com/go-co-op/gocron v1.37.0 h1:ZYDJGtQ4OMhTLKOKMIch+/CY70Brbb1dGdooLEhh7b0=
@ -323,21 +321,20 @@ github.com/go-faster/city v1.0.1 h1:4WAxSZ3V2Ws4QRDrscLEDcibJY8uf41H6AhXDrNDcGw=
github.com/go-faster/city v1.0.1/go.mod h1:jKcUJId49qdW3L1qKHH/3wPeUstCVpVSXTM6vO3VcTw=
github.com/go-faster/errors v0.7.1 h1:MkJTnDoEdi9pDabt1dpWf7AA8/BaSYZqibYyhZ20AYg=
github.com/go-faster/errors v0.7.1/go.mod h1:5ySTjWFiphBs07IKuiL69nxdfd5+fzh1u7FPGZP2quo=
github.com/go-fed/httpsig v1.1.0/go.mod h1:RCMrTZvN1bJYtofsG4rd5NaO5obxQ5xBkdiS7xsT7bM=
github.com/go-fed/httpsig v1.1.1-0.20201223112313-55836744818e h1:oRq/fiirun5HqlEWMLIcDmLpIELlG4iGbd0s8iqgPi8=
github.com/go-fed/httpsig v1.1.1-0.20201223112313-55836744818e/go.mod h1:RCMrTZvN1bJYtofsG4rd5NaO5obxQ5xBkdiS7xsT7bM=
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+mTU=
github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow=
github.com/go-git/go-billy/v5 v5.6.0 h1:w2hPNtoehvJIxR00Vb4xX94qHQi/ApZfX+nBE2Cjio8=
github.com/go-git/go-billy/v5 v5.6.0/go.mod h1:sFDq7xD3fn3E0GOwUSZqHo9lrkmx8xJhA0ZrfvjBRGM=
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
github.com/go-git/go-git/v5 v5.12.0 h1:7Md+ndsjrzZxbddRDZjF14qK+NN56sy6wkqaVrjZtys=
github.com/go-git/go-git/v5 v5.12.0/go.mod h1:FTM9VKtnI2m65hNI/TenDDDnUf2Q9FHnXYjuz9i5OEY=
github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A=
github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
github.com/go-ldap/ldap/v3 v3.4.6 h1:ert95MdbiG7aWo/oPYp9btL3KJlMPKnP58r09rI8T+A=
github.com/go-ldap/ldap/v3 v3.4.6/go.mod h1:IGMQANNtxpsOzj7uUAMjpGBaOVTC4DYyIy8VsTdxmtc=
github.com/go-ldap/ldap/v3 v3.4.8 h1:loKJyspcRezt2Q3ZRMq2p/0v8iOurlmeXDPw6fikSvQ=
github.com/go-ldap/ldap/v3 v3.4.8/go.mod h1:qS3Sjlu76eHfHGpUdWkAXQTw4beih+cHsco2jXlIXrk=
github.com/go-openapi/analysis v0.23.0 h1:aGday7OWupfMs+LbmLZG4k0MYXIANxcuBTYUC03zFCU=
github.com/go-openapi/analysis v0.23.0/go.mod h1:9mz9ZWaSlV8TvjQHLl2mUW2PbZtemkE8yA5v22ohupo=
github.com/go-openapi/errors v0.22.0 h1:c4xY/OLxUBSTiepAg3j/MHuAv5mJhnf53LLMWFB+u/w=
@ -377,10 +374,10 @@ github.com/go-test/deep v1.1.0 h1:WOcxcdHcvdgThNXjw0t76K42FXTU7HpNQWHpA2HHNlg=
github.com/go-test/deep v1.1.0/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
github.com/go-testfixtures/testfixtures/v3 v3.11.0 h1:XxQr8AnPORcZkyNd7go5UNLPD3dULN8ixYISlzrlfEQ=
github.com/go-testfixtures/testfixtures/v3 v3.11.0/go.mod h1:THmudHF1Ixq++J2/UodcJpxUphfyEd77m83TvDtryqE=
github.com/go-webauthn/webauthn v0.10.2 h1:OG7B+DyuTytrEPFmTX503K77fqs3HDK/0Iv+z8UYbq4=
github.com/go-webauthn/webauthn v0.10.2/go.mod h1:Gd1IDsGAybuvK1NkwUTLbGmeksxuRJjVN2PE/xsPxHs=
github.com/go-webauthn/x v0.1.9 h1:v1oeLmoaa+gPOaZqUdDentu6Rl7HkSSsmOT6gxEQHhE=
github.com/go-webauthn/x v0.1.9/go.mod h1:pJNMlIMP1SU7cN8HNlKJpLEnFHCygLCvaLZ8a1xeoQA=
github.com/go-webauthn/webauthn v0.11.2 h1:Fgx0/wlmkClTKlnOsdOQ+K5HcHDsDcYIvtYmfhEOSUc=
github.com/go-webauthn/webauthn v0.11.2/go.mod h1:aOtudaF94pM71g3jRwTYYwQTG1KyTILTcZqN1srkmD0=
github.com/go-webauthn/x v0.1.15 h1:eG1OhggBJTkDE8gUeOlGRbRe8E/PSVG26YG4AyFbwkU=
github.com/go-webauthn/x v0.1.15/go.mod h1:pf7VI23raFLHPO9VVIs9/u1etqwAOP0S2KoHGL6WbZ8=
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
@ -393,8 +390,8 @@ github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f h1:3BSP1Tbs2djlpprl7w
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f/go.mod h1:Pcatq5tYkCW2Q6yrR2VRHlbHpZ/R4/7qyL1TCF7vl14=
github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85 h1:UjoPNDAQ5JPCjlxoJd6K8ALZqSDDhk2ymieAZOVaDg0=
github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85/go.mod h1:fR6z1Ie6rtF7kl/vBYMfgD5/G5B1blui7z426/sj2DU=
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo=
github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA=
@ -423,8 +420,8 @@ github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/gomodule/redigo v1.8.9 h1:Sl3u+2BI/kk+VEatbj0scLdrFhjPmbxOc1myhDP41ws=
github.com/gomodule/redigo v1.8.9/go.mod h1:7ArFNvsTjH8GMMzB4uy1snslv2BwmginuMs06a1uzZE=
github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
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=
@ -436,18 +433,16 @@ github.com/google/go-github/v61 v61.0.0 h1:VwQCBwhyE9JclCI+22/7mLB1PuU9eowCXKY5p
github.com/google/go-github/v61 v61.0.0/go.mod h1:0WR+KmsWX75G2EbpyGsGmradjo3IiciuI4BmdVCobQY=
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
github.com/google/go-tpm v0.9.0 h1:sQF6YqWMi+SCXpsmS3fd21oPy/vSddwZry4JnmltHVk=
github.com/google/go-tpm v0.9.0/go.mod h1:FkNVkc6C+IsvDI9Jw1OveJmxGZUUaKxtrpOS47QWKfU=
github.com/google/go-tpm v0.9.1 h1:0pGc4X//bAlmZzMKf8iz6IsDo1nYTbYJ6FZN/rg4zdM=
github.com/google/go-tpm v0.9.1/go.mod h1:h9jEsEECg7gtLis0upRBQU+GhYVH6jMjrFxI8u6bVUY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/licenseclassifier/v2 v2.0.0 h1:1Y57HHILNf4m0ABuMVb6xk4vAJYEUO0gDxNpog0pyeA=
github.com/google/licenseclassifier/v2 v2.0.0/go.mod h1:cOjbdH0kyC9R22sdQbYsFkto4NGCAc+ZSwbeThazEtM=
github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
github.com/google/pprof v0.0.0-20240618054019-d3b898a103f8 h1:ASJ/LAqdCHOyMYI+dwNxn7Rd8FscNkMyTr1KZU1JI/M=
github.com/google/pprof v0.0.0-20240618054019-d3b898a103f8/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo=
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
github.com/google/uuid v1.4.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=
@ -470,8 +465,9 @@ github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+
github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA=
github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=
github.com/gorilla/sessions v1.2.0/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
github.com/gorilla/sessions v1.3.0 h1:XYlkq7KcpOB2ZhHBPv5WpjMIxrQosiZanfoy1HLZFzg=
github.com/gorilla/sessions v1.3.0/go.mod h1:ePLdVu+jbEgHH+KWw8I1z2wqd0BAdAQh/8LRvBeoNcQ=
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
github.com/gorilla/sessions v1.4.0 h1:kpIYOp/oi6MG/p5PgxApU8srsSw9tuFbt46Lt7auzqQ=
github.com/gorilla/sessions v1.4.0/go.mod h1:FLWm50oby91+hl7p/wRxDth9bWSuk0qVL2emc7lT5ik=
github.com/h2non/gock v1.2.0 h1:K6ol8rfrRkUOefooBC8elXoaNGYkpp7y2qcxGG6BzUE=
github.com/h2non/gock v1.2.0/go.mod h1:tNhoxHYW2W42cYkYb1WqzdbYIieALC99kpYr7rH/BQk=
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=
@ -487,6 +483,9 @@ github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+l
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU=
github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk=
github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
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/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
@ -494,13 +493,9 @@ github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI=
github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/ianlancetaylor/demangle v0.0.0-20230524184225-eabc099b10ab/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw=
github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=
github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8=
github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
@ -522,13 +517,24 @@ github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056 h1:iCHtR9CQykt
github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jessevdk/go-flags v1.5.0 h1:1jKYvbxEjfUl0fmqTCOfonvskHHXMjBySTLW4y9LFvc=
github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
github.com/jhillyerd/enmime v1.2.0 h1:dIu1IPEymQgoT2dzuB//ttA/xcV40NMPpQtmd4wslHk=
github.com/jhillyerd/enmime v1.2.0/go.mod h1:FRFuUPCLh8PByQv+8xRcLO9QHqaqTqreYhopv5eyk4I=
github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8=
github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs=
github.com/jcmturner/dnsutils/v2 v2.0.0 h1:lltnkeZGL0wILNvrNiVCR6Ro5PGU/SeBvVO/8c/iPbo=
github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM=
github.com/jcmturner/gofork v1.7.6 h1:QH0l3hzAU1tfT3rZCnW5zXl+orbkNMMRGJfdJjHVETg=
github.com/jcmturner/gofork v1.7.6/go.mod h1:1622LH6i/EZqLloHfE7IeZ0uEJwMSUyQ/nDd82IeqRo=
github.com/jcmturner/goidentity/v6 v6.0.1 h1:VKnZd2oEIMorCTsFBnJWbExfNN7yZr3EhJAxwOkZg6o=
github.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg=
github.com/jcmturner/gokrb5/v8 v8.4.4 h1:x1Sv4HaTpepFkXbt2IkL29DXRf8sOfZXo8eRKh687T8=
github.com/jcmturner/gokrb5/v8 v8.4.4/go.mod h1:1btQEpgT6k+unzCwX1KdWMEwPPkkgBtP+F6aCACiMrs=
github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY=
github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc=
github.com/jessevdk/go-flags v1.6.1 h1:Cvu5U8UGrLay1rZfv/zP7iLpSHGUZ/Ou68T0iX1bBK4=
github.com/jessevdk/go-flags v1.6.1/go.mod h1:Mk8T1hIAWpOiJiHa9rJASDK2UGWji0EuPGBnNLMooyc=
github.com/jhillyerd/enmime v1.3.0 h1:LV5kzfLidiOr8qRGIpYYmUZCnhrPbcFAnAFUnWn99rw=
github.com/jhillyerd/enmime v1.3.0/go.mod h1:6c6jg5HdRRV2FtvVL69LjiX1M8oE0xDX9VEhV3oy4gs=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
@ -549,13 +555,10 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/compress v1.11.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/klauspost/compress v1.15.0/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/klauspost/compress v1.15.6/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM=
github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
@ -595,32 +598,30 @@ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/meilisearch/meilisearch-go v0.26.3 h1:EYt+C1n7IvjKzgXM3xqce5iJzNBq33F7ayZOKx96TKY=
github.com/meilisearch/meilisearch-go v0.26.3/go.mod h1:SxuSqDcPBIykjWz1PX+KzsYzArNLSCadQodWs8extS0=
github.com/mholt/acmez/v2 v2.0.1 h1:3/3N0u1pLjMK4sNEAFSI+bcvzbPhRpY383sy1kLHJ6k=
github.com/mholt/acmez/v2 v2.0.1/go.mod h1:fX4c9r5jYwMyMsC+7tkYRxHibkOTgta5DIFGoe67e1U=
github.com/microcosm-cc/bluemonday v1.0.26 h1:xbqSvqzQMeEHCqMi64VAs4d8uy6Mequs3rQ0k/Khz58=
github.com/microcosm-cc/bluemonday v1.0.26/go.mod h1:JyzOCs9gkyQyjs+6h10UEVSe02CGwkhd72Xdqh78TWs=
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
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/meilisearch/meilisearch-go v0.29.1-0.20241106140435-0bf60fad690a h1:F0y+3QtCG00mr4KueQWuHv1tlIQeNXhH+XAKYLhb3X4=
github.com/meilisearch/meilisearch-go v0.29.1-0.20241106140435-0bf60fad690a/go.mod h1:NYOgjEGt/+oExD+NixreBMqxtIB0kCndXOOgpGhoqEs=
github.com/mholt/acmez/v2 v2.0.3 h1:CgDBlEwg3QBp6s45tPQmFIBrkRIkBT4rW4orMM6p4sw=
github.com/mholt/acmez/v2 v2.0.3/go.mod h1:pQ1ysaDeGrIMvJ9dfJMk5kJNkn7L2sb3UhyrX6Q91cw=
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 v1.7.2 h1:CHkFJiObW7ItKTJfHo1QX7QBBD1iV+mn1eOyRP3b/PA=
github.com/microsoft/go-mssqldb v1.7.2/go.mod h1:kOvZKUdrhhFQmxLZqbwUV0rHkNkZpthMITIb2Ko1IoA=
github.com/miekg/dns v1.1.61 h1:nLxbwF3XxhwVSm8g9Dghm9MHPaUZuqhPiGL+675ZmEs=
github.com/miekg/dns v1.1.61/go.mod h1:mnAarhS3nWaW+NVP2wTkYVIZyHNJ098SJZUki3eykwQ=
github.com/miekg/dns v1.1.62 h1:cN8OuEF1/x5Rq6Np+h1epln8OiyPWV+lROx9LxcGgIQ=
github.com/miekg/dns v1.1.62/go.mod h1:mvDlcItzm+br7MToIKqkglaGhlFMHJ9DTNNWONWXbNQ=
github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34=
github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM=
github.com/minio/minio-go/v7 v7.0.77 h1:GaGghJRg9nwDVlNbwYjSDJT1rqltQkBFDsypWX1v3Bw=
github.com/minio/minio-go/v7 v7.0.77/go.mod h1:AVM3IUN6WwKzmwBxVdjzhH8xq+f57JSbbvzqvUzR6eg=
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
github.com/minio/minio-go/v7 v7.0.80 h1:2mdUHXEykRdY/BigLt3Iuu1otL0JTogT0Nmltg0wujk=
github.com/minio/minio-go/v7 v7.0.80/go.mod h1:84gmIilaX4zcvAWWzJ5Z1WI5axN+hAbM5w25xf8xvC0=
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@ -662,8 +663,8 @@ github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk=
github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0=
github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
@ -673,8 +674,8 @@ github.com/paulmach/orb v0.11.1 h1:3koVegMC4X/WeiXYz9iswopaTwMem53NzTJuTF20JzU=
github.com/paulmach/orb v0.11.1/go.mod h1:5mULz1xQfs3bmQm63QEJA6lNGujuRafwA5S/EnuLaLU=
github.com/paulmach/protoscan v0.2.1/go.mod h1:SpcSwydNLrxUGSDvXvO0P7g7AuhJ7lcKfDlhJCDw2gY=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
github.com/pierrec/lz4/v4 v4.1.2/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ=
@ -691,25 +692,25 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pquerna/otp v1.4.0 h1:wZvl1TIVxKRThZIBiwOOHOGP/1+nZyWBil9Y2XNEDzg=
github.com/pquerna/otp v1.4.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE=
github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho=
github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=
github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc=
github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8=
github.com/prometheus/common v0.60.1 h1:FUas6GcOw66yB/73KC+BOZoFJmbo/1pojoILArPAaSc=
github.com/prometheus/common v0.60.1/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/quasoft/websspi v1.1.2 h1:/mA4w0LxWlE3novvsoEL6BBA1WnjJATbjkh1kFrTidw=
github.com/quasoft/websspi v1.1.2/go.mod h1:HmVdl939dQ0WIXZhyik+ARdI03M6bQzaSEKcgpFmewk=
github.com/rcrowley/go-metrics v0.0.0-20190826022208-cac0b30c2563/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/redis/go-redis/v9 v9.6.0 h1:NLck+Rab3AOTHw21CGRpvQpgTrAU4sgdCswqGtlhGRA=
github.com/redis/go-redis/v9 v9.6.0/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M=
github.com/redis/go-redis/v9 v9.7.0 h1:HhLSs+B6O021gwzl+locl0zEDnyNkxMtf/Z3NNBMa9E=
github.com/redis/go-redis/v9 v9.7.0/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw=
github.com/redis/rueidis v1.0.19 h1:s65oWtotzlIFN8eMPhyYwxlwLR1lUdhza2KtWprKYSo=
github.com/redis/rueidis v1.0.19/go.mod h1:8B+r5wdnjwK3lTFml5VtxjzGOQAC+5UmujoD12pDrEo=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/rhysd/actionlint v1.7.1 h1:WJaDzyT1StBWVKGSsZPYnbV0HF9Y9/vD6KFdZQL42qE=
github.com/rhysd/actionlint v1.7.1/go.mod h1:lNjNNlZY0BdBl8l837Z9ZiBpu8v+5lzfoJQFdSk4xss=
github.com/rhysd/actionlint v1.7.3 h1:WD919WuLYrSCwY8VGBqJBEuzyVEIL5viXmXqRRcKOVs=
github.com/rhysd/actionlint v1.7.3/go.mod h1:rl+8ZoX1rqnbcMWKaTyOHmw08mmb/zlmG/Zu1fY47F4=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
@ -719,15 +720,15 @@ github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzG
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU=
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=
github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
github.com/sagikazarmark/locafero v0.6.0 h1:ON7AQg37yzcRPU69mt7gwhFEBwxI6P9T4Qu3N51bwOk=
github.com/sagikazarmark/locafero v0.6.0/go.mod h1:77OmuIc6VTraTXKXIs/uvUxKGUXjE1GbemJYHqdNjX0=
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 h1:lZUw3E0/J3roVtGQ+SCrUrg3ON6NgVqpn3+iol9aGu4=
@ -740,7 +741,6 @@ github.com/serenize/snaker v0.0.0-20171204205717-a683aaf2d516/go.mod h1:Yow6lPLS
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c h1:aqg5Vm5dwtvL+YgDpBcK1ITf3o96N/K7/wsRXQnUTEs=
@ -748,8 +748,8 @@ github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c/go.mod h1:owqhoLW1
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/skeema/knownhosts v1.2.2 h1:Iug2P4fLmDw9f41PB6thxUkNUkJzB5i+1/exaj40L3A=
github.com/skeema/knownhosts v1.2.2/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo=
github.com/skeema/knownhosts v1.3.0 h1:AM+y0rI04VksttfwjkSTNQorvGqmwATnvnAHpSgc0LY=
github.com/skeema/knownhosts v1.3.0/go.mod h1:sPINvnADmT/qYH1kfv+ePMmOBTH6Tbl7b5LvTDjFK7M=
github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/assertions v1.1.1 h1:T/YLemO5Yp7KPzS+lVtu+WsHn8yoSwTfItdAd1r3cck=
github.com/smartystreets/assertions v1.1.1/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo=
@ -762,17 +762,16 @@ github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w=
github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ=
github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk=
github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=
github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg=
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/stephens2424/writerset v1.0.2/go.mod h1:aS2JhsMn6eA7e82oNmW4rfsgAOp9COBTTl8mzkwADnc=
@ -780,7 +779,6 @@ github.com/steveyen/gtreap v0.1.0/go.mod h1:kl/5J7XbrOmlIbYIXdRHDDE5QxHqpk0cmkT7
github.com/stretchr/objx v0.1.0/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=
@ -791,7 +789,6 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stvp/tempredis v0.0.0-20181119212430-b82af8480203 h1:QVqDTf3h2WHt08YuiTGPZLls0Wq99X9bWd0Q5ZSBesM=
@ -813,21 +810,15 @@ github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc=
github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/unknwon/com v1.0.1 h1:3d1LTxD+Lnf3soQiD4Cp/0BRB+Rsa/+RTvz8GMMzIXs=
github.com/unknwon/com v1.0.1/go.mod h1:tOOxU81rwgoCLoOVVPHb6T/wt8HZygqH5id+GNnlCXM=
github.com/urfave/cli/v2 v2.27.2 h1:6e0H+AkS+zDckwPCUrZkKX38mRaau4nL2uipkJpbkcI=
github.com/urfave/cli/v2 v2.27.2/go.mod h1:g0+79LmHHATl7DAcHO99smiR/T7uGLw84w8Y42x+4eM=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.37.1-0.20220607072126-8a320890c08d/go.mod h1:t/G+3rLek+CyY9bnIE+YlMRddxVAAGjhxndDB4i4C0I=
github.com/valyala/fasthttp v1.55.0 h1:Zkefzgt6a7+bVKHnu/YaYSOPfNYNisSVBo/unVCf8k8=
github.com/valyala/fasthttp v1.55.0/go.mod h1:NkY9JtkrpPKmgwV3HTaS2HWaJss9RSIsRVfcxxoHiOM=
github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w=
github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ=
github.com/valyala/fastjson v1.6.4 h1:uAUNq9Z6ymTgGhcm0UynUAB6tlbakBrz6CQFax3BXVQ=
github.com/valyala/fastjson v1.6.4/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY=
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
github.com/willf/bitset v1.1.10/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/xanzy/go-gitlab v0.105.0 h1:3nyLq0ESez0crcaM19o5S//SvezOQguuIHZ3wgX64hM=
github.com/xanzy/go-gitlab v0.105.0/go.mod h1:ETg8tcj4OhrB84UEgeE8dSuV/0h4BBL1uOV/qK0vlyI=
github.com/xanzy/go-gitlab v0.112.0 h1:6Z0cqEooCvBMfBIHw+CgO4AKGRV8na/9781xOb0+DKw=
github.com/xanzy/go-gitlab v0.112.0/go.mod h1:wKNKh3GkYDMOsGmnfuX+ITCmDuSDWFO0G+C4AygL9RY=
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
@ -845,6 +836,8 @@ github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMx
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4=
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM=
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
github.com/yohcop/openid-go v1.0.1 h1:DPRd3iPO5F6O5zX2e62XpVAbPT6wV51cuucH0z9g3js=
github.com/yohcop/openid-go v1.0.1/go.mod h1:b/AvD03P0KHj4yuihb+VtLD6bYYgsy0zqBzPCRjkCNs=
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
@ -853,28 +846,28 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/goldmark v1.4.15/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/goldmark v1.7.2 h1:NjGd7lO7zrUn/A7eKwn5PEOt4ONYGqpxSEeZuduvgxc=
github.com/yuin/goldmark v1.7.2/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
github.com/yuin/goldmark v1.7.8 h1:iERMLn0/QJeHFhxSt3p6PeN9mGnvIKSpG9YYorDMnic=
github.com/yuin/goldmark v1.7.8/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc h1:+IAOyRda+RLrxa1WC7umKOZRsGq4QrFFMYApOeHzQwQ=
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc/go.mod h1:ovIvrum6DQJA4QsJSovrkC4saKHQVs7TvcaeO8AIl5I=
github.com/yuin/goldmark-meta v1.1.0 h1:pWw+JLHGZe8Rk0EGsMVssiNb/AaPMHfSRszZeUeiOUc=
github.com/yuin/goldmark-meta v1.1.0/go.mod h1:U4spWENafuA7Zyg+Lj5RqK/MF+ovMYtBvXi1lBb2VP0=
github.com/zeebo/assert v1.1.0 h1:hU1L1vLTHsnO8x8c9KAR5GmM5QscxHg5RNU5z5qbUWY=
github.com/zeebo/assert v1.1.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
github.com/zeebo/blake3 v0.2.3 h1:TFoLXsjeXqRNFxSbk35Dk4YtszE/MQQGK10BH4ptoTg=
github.com/zeebo/blake3 v0.2.3/go.mod h1:mjJjZpnsyIVtVgTOSpJ9vmRE4wgDeyt2HU3qXvvKCaQ=
github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ=
github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
github.com/zeebo/blake3 v0.2.4 h1:KYQPkhpRtcqh0ssGYcKLG1JYvddkEA8QwCM/yBqhaZI=
github.com/zeebo/blake3 v0.2.4/go.mod h1:7eeQ6d2iXWRGF6npfaxl2CU+xy2Fjo2gxeyZGCRUjcE=
github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo=
github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4=
go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
go.etcd.io/bbolt v1.3.10 h1:+BqfJTcCzTItrop8mq/lbzL8wSGtj94UO/3U31shqG0=
go.etcd.io/bbolt v1.3.10/go.mod h1:bK3UQLPJZly7IlNmV7uVHJDxfe5aK9Ll93e/74Y9oEQ=
go.etcd.io/bbolt v1.3.11 h1:yGEzV1wPz2yVCLsD8ZAiGHhHVlczyC9d1rP43/VCRJ0=
go.etcd.io/bbolt v1.3.11/go.mod h1:dksAq7YMXoljX0xu6VF5DMZGbhYYoLUalEiSySYAS4I=
go.mongodb.org/mongo-driver v1.11.4/go.mod h1:PTSz5yu21bkT/wXpkS7WR5f0ddqw5quethTUn9WM+2g=
go.mongodb.org/mongo-driver v1.14.0 h1:P98w8egYRjYe3XDjxhYJagTokP/H6HzlsnojRgZRd80=
go.mongodb.org/mongo-driver v1.14.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c=
go.opentelemetry.io/otel v1.27.0 h1:9BZoF3yMK/O1AafMiQTVu0YDj5Ea4hPhxCs7sGva+cg=
go.opentelemetry.io/otel v1.27.0/go.mod h1:DMpAK8fzYRzs+bi3rS5REupisuqTheUlSZJ1WnZaPAQ=
go.opentelemetry.io/otel/trace v1.27.0 h1:IqYb813p7cmbHk0a5y6pD5JPakbVfftRXABGt5/Rscw=
go.opentelemetry.io/otel/trace v1.27.0/go.mod h1:6RiD1hkAprV4/q+yd2ln1HG9GoPx39SuvvstaLBl+l4=
go.mongodb.org/mongo-driver v1.17.1 h1:Wic5cJIwJgSpBhe3lx3+/RybR5PiYRMpVFgO7cOHyIM=
go.mongodb.org/mongo-driver v1.17.1/go.mod h1:wwWm/+BuOddhcq3n68LKRmgk2wXzmF6s0SFOa0GINL4=
go.opentelemetry.io/otel v1.31.0 h1:NsJcKPIW0D0H3NgzPDHmo0WW6SptzPdqg/L1zsIm2hY=
go.opentelemetry.io/otel v1.31.0/go.mod h1:O0C14Yl9FgkjqcCZAsE053C13OaddMYr/hz6clDkEJE=
go.opentelemetry.io/otel/trace v1.31.0 h1:ffjsj1aRouKewfr85U2aGagJ46+MvodynlQ1HYdmJys=
go.opentelemetry.io/otel/trace v1.31.0/go.mod h1:TXZkRk7SM2ZQLtR6eoAWQFIHPvzQ06FJAsO1tJg480A=
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
@ -890,38 +883,36 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
golang.org/x/exp v0.0.0-20240314144324-c7f7c6466f7f h1:3CW0unweImhOzd5FmYuRsD4Y4oQFKZIjAnKbjV4WIrw=
golang.org/x/exp v0.0.0-20240314144324-c7f7c6466f7f/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc=
golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ=
golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY=
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8=
golang.org/x/image v0.21.0 h1:c5qV36ajHpdj4Qi0GnE0jUc/yuo33OLFaa0d+crTD5s=
golang.org/x/image v0.21.0/go.mod h1:vUbsLavqK/W303ZroQQVKQ+Af3Yl6Uz1Ppu5J/cLz78=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
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.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
@ -929,10 +920,12 @@ golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=
golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs=
golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs=
golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/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=
@ -960,11 +953,8 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@ -976,10 +966,10 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
@ -987,10 +977,10 @@ golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU=
golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24=
golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
@ -999,12 +989,11 @@ golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
@ -1015,16 +1004,16 @@ golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4f
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ=
golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
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/genproto/googleapis/rpc v0.0.0-20240314234333-6e1732d8331c h1:lfpJ/2rWPa/kJgxyyXM8PrNnfCzcmxJ265mADgwmvLI=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240314234333-6e1732d8331c/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY=
google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk=
google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38 h1:zciRKQ4kBpFgpfC5QQCVtnnNAcLIqweL7plyZRQHVpI=
google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI=
google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E=
google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA=
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=
@ -1033,8 +1022,8 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
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/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

View File

@ -7,19 +7,17 @@ import (
"testing"
"time"
"code.gitea.io/gitea/modules/test"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestActionScheduleSpec_Parse(t *testing.T) {
// Mock the local timezone is not UTC
local := time.Local
tz, err := time.LoadLocation("Asia/Shanghai")
require.NoError(t, err)
defer func() {
time.Local = local
}()
time.Local = tz
defer test.MockVariableValue(&time.Local, tz)()
now, err := time.Parse(time.RFC3339, "2024-07-31T15:47:55+08:00")
require.NoError(t, err)

View File

@ -341,7 +341,7 @@ func UpdateTask(ctx context.Context, task *ActionTask, cols ...string) error {
// UpdateTaskByState updates the task by the state.
// It will always update the task if the state is not final, even there is no change.
// So it will update ActionTask.Updated to avoid the task being judged as a zombie task.
func UpdateTaskByState(ctx context.Context, state *runnerv1.TaskState) (*ActionTask, error) {
func UpdateTaskByState(ctx context.Context, runnerID int64, state *runnerv1.TaskState) (*ActionTask, error) {
stepStates := map[int64]*runnerv1.StepState{}
for _, v := range state.Steps {
stepStates[v.Id] = v
@ -360,6 +360,8 @@ func UpdateTaskByState(ctx context.Context, state *runnerv1.TaskState) (*ActionT
return nil, err
} else if !has {
return nil, util.ErrNotExist
} else if runnerID != task.RunnerID {
return nil, fmt.Errorf("invalid runner for task")
}
if task.Status.IsDone() {

View File

@ -171,7 +171,10 @@ func (a *Action) TableIndices() []*schemas.Index {
cudIndex := schemas.NewIndex("c_u_d", schemas.IndexType)
cudIndex.AddColumn("created_unix", "user_id", "is_deleted")
indices := []*schemas.Index{actUserIndex, repoIndex, cudIndex}
cuIndex := schemas.NewIndex("c_u", schemas.IndexType)
cuIndex.AddColumn("user_id", "is_deleted")
indices := []*schemas.Index{actUserIndex, repoIndex, cudIndex, cuIndex}
return indices
}

View File

@ -6,6 +6,12 @@ package db
import (
"context"
"database/sql"
"errors"
"runtime"
"slices"
"sync"
"code.gitea.io/gitea/modules/setting"
"xorm.io/builder"
"xorm.io/xorm"
@ -15,45 +21,23 @@ import (
// will be overwritten by Init with HammerContext
var DefaultContext context.Context
// contextKey is a value for use with context.WithValue.
type contextKey struct {
name string
}
type engineContextKeyType struct{}
// enginedContextKey is a context key. It is used with context.Value() to get the current Engined for the context
var (
enginedContextKey = &contextKey{"engined"}
_ Engined = &Context{}
)
var engineContextKey = engineContextKeyType{}
// Context represents a db context
type Context struct {
context.Context
e Engine
transaction bool
engine Engine
}
func newContext(ctx context.Context, e Engine, transaction bool) *Context {
return &Context{
Context: ctx,
e: e,
transaction: transaction,
}
}
// InTransaction if context is in a transaction
func (ctx *Context) InTransaction() bool {
return ctx.transaction
}
// Engine returns db engine
func (ctx *Context) Engine() Engine {
return ctx.e
func newContext(ctx context.Context, e Engine) *Context {
return &Context{Context: ctx, engine: e}
}
// Value shadows Value for context.Context but allows us to get ourselves and an Engined object
func (ctx *Context) Value(key any) any {
if key == enginedContextKey {
if key == engineContextKey {
return ctx
}
return ctx.Context.Value(key)
@ -61,30 +45,66 @@ func (ctx *Context) Value(key any) any {
// WithContext returns this engine tied to this context
func (ctx *Context) WithContext(other context.Context) *Context {
return newContext(ctx, ctx.e.Context(other), ctx.transaction)
return newContext(ctx, ctx.engine.Context(other))
}
// Engined structs provide an Engine
type Engined interface {
Engine() Engine
var (
contextSafetyOnce sync.Once
contextSafetyDeniedFuncPCs []uintptr
)
func contextSafetyCheck(e Engine) {
if setting.IsProd && !setting.IsInTesting {
return
}
if e == nil {
return
}
// Only do this check for non-end-users. If the problem could be fixed in the future, this code could be removed.
contextSafetyOnce.Do(func() {
// try to figure out the bad functions to deny
type m struct{}
_ = e.SQL("SELECT 1").Iterate(&m{}, func(int, any) error {
callers := make([]uintptr, 32)
callerNum := runtime.Callers(1, callers)
for i := 0; i < callerNum; i++ {
if funcName := runtime.FuncForPC(callers[i]).Name(); funcName == "xorm.io/xorm.(*Session).Iterate" {
contextSafetyDeniedFuncPCs = append(contextSafetyDeniedFuncPCs, callers[i])
}
}
return nil
})
if len(contextSafetyDeniedFuncPCs) != 1 {
panic(errors.New("unable to determine the functions to deny"))
}
})
// it should be very fast: xxxx ns/op
callers := make([]uintptr, 32)
callerNum := runtime.Callers(3, callers) // skip 3: runtime.Callers, contextSafetyCheck, GetEngine
for i := 0; i < callerNum; i++ {
if slices.Contains(contextSafetyDeniedFuncPCs, callers[i]) {
panic(errors.New("using database context in an iterator would cause corrupted results"))
}
}
}
// GetEngine will get a db Engine from this context or return an Engine restricted to this context
// GetEngine gets an existing db Engine/Statement or creates a new Session
func GetEngine(ctx context.Context) Engine {
if e := getEngine(ctx); e != nil {
if e := getExistingEngine(ctx); e != nil {
return e
}
return x.Context(ctx)
}
// getEngine will get a db Engine from this context or return nil
func getEngine(ctx context.Context) Engine {
if engined, ok := ctx.(Engined); ok {
return engined.Engine()
// getExistingEngine gets an existing db Engine/Statement from this context or returns nil
func getExistingEngine(ctx context.Context) (e Engine) {
defer func() { contextSafetyCheck(e) }()
if engined, ok := ctx.(*Context); ok {
return engined.engine
}
enginedInterface := ctx.Value(enginedContextKey)
if enginedInterface != nil {
return enginedInterface.(Engined).Engine()
if engined, ok := ctx.Value(engineContextKey).(*Context); ok {
return engined.engine
}
return nil
}
@ -132,23 +152,23 @@ func (c *halfCommitter) Close() error {
// d. It doesn't mean rollback is forbidden, but always do it only when there is an error, and you do want to rollback.
func TxContext(parentCtx context.Context) (*Context, Committer, error) {
if sess, ok := inTransaction(parentCtx); ok {
return newContext(parentCtx, sess, true), &halfCommitter{committer: sess}, nil
return newContext(parentCtx, sess), &halfCommitter{committer: sess}, nil
}
sess := x.NewSession()
if err := sess.Begin(); err != nil {
sess.Close()
_ = sess.Close()
return nil, nil, err
}
return newContext(DefaultContext, sess, true), sess, nil
return newContext(DefaultContext, sess), sess, nil
}
// WithTx represents executing database operations on a transaction, if the transaction exist,
// this function will reuse it otherwise will create a new one and close it when finished.
func WithTx(parentCtx context.Context, f func(ctx context.Context) error) error {
if sess, ok := inTransaction(parentCtx); ok {
err := f(newContext(parentCtx, sess, true))
err := f(newContext(parentCtx, sess))
if err != nil {
// rollback immediately, in case the caller ignores returned error and tries to commit the transaction.
_ = sess.Close()
@ -165,7 +185,7 @@ func txWithNoCheck(parentCtx context.Context, f func(ctx context.Context) error)
return err
}
if err := f(newContext(parentCtx, sess, true)); err != nil {
if err := f(newContext(parentCtx, sess)); err != nil {
return err
}
@ -312,7 +332,7 @@ func InTransaction(ctx context.Context) bool {
}
func inTransaction(ctx context.Context) (*xorm.Session, bool) {
e := getEngine(ctx)
e := getExistingEngine(ctx)
if e == nil {
return nil, false
}

View File

@ -84,3 +84,47 @@ func TestTxContext(t *testing.T) {
}))
}
}
func TestContextSafety(t *testing.T) {
type TestModel1 struct {
ID int64
}
type TestModel2 struct {
ID int64
}
assert.NoError(t, unittest.GetXORMEngine().Sync(&TestModel1{}, &TestModel2{}))
assert.NoError(t, db.TruncateBeans(db.DefaultContext, &TestModel1{}, &TestModel2{}))
testCount := 10
for i := 1; i <= testCount; i++ {
assert.NoError(t, db.Insert(db.DefaultContext, &TestModel1{ID: int64(i)}))
assert.NoError(t, db.Insert(db.DefaultContext, &TestModel2{ID: int64(-i)}))
}
actualCount := 0
// here: db.GetEngine(db.DefaultContext) is a new *Session created from *Engine
_ = db.WithTx(db.DefaultContext, func(ctx context.Context) error {
_ = db.GetEngine(ctx).Iterate(&TestModel1{}, func(i int, bean any) error {
// here: db.GetEngine(ctx) is always the unclosed "Iterate" *Session with autoResetStatement=false,
// and the internal states (including "cond" and others) are always there and not be reset in this callback.
m1 := bean.(*TestModel1)
assert.EqualValues(t, i+1, m1.ID)
// here: XORM bug, it fails because the SQL becomes "WHERE id=-1", "WHERE id=-1 AND id=-2", "WHERE id=-1 AND id=-2 AND id=-3" ...
// and it conflicts with the "Iterate"'s internal states.
// has, err := db.GetEngine(ctx).Get(&TestModel2{ID: -m1.ID})
actualCount++
return nil
})
return nil
})
assert.EqualValues(t, testCount, actualCount)
// deny the bad usages
assert.PanicsWithError(t, "using database context in an iterator would cause corrupted results", func() {
_ = unittest.GetXORMEngine().Iterate(&TestModel1{}, func(i int, bean any) error {
_ = db.GetEngine(db.DefaultContext)
return nil
})
})
}

View File

@ -161,10 +161,7 @@ func InitEngine(ctx context.Context) error {
// SetDefaultEngine sets the default engine for db
func SetDefaultEngine(ctx context.Context, eng *xorm.Engine) {
x = eng
DefaultContext = &Context{
Context: ctx,
e: x,
}
DefaultContext = &Context{Context: ctx, engine: x}
}
// UnsetDefaultEngine closes and unsets the default engine

View File

@ -11,7 +11,7 @@ import (
)
func getXORMEngine() *xorm.Engine {
return db.DefaultContext.(*db.Context).Engine().(*xorm.Engine)
return db.GetEngine(db.DefaultContext).(*xorm.Engine)
}
// CheckDatabaseConnection checks the database connection

View File

@ -11,7 +11,7 @@ import (
"xorm.io/builder"
)
// Iterate iterate all the Bean object
// Iterate iterates all the Bean object
func Iterate[Bean any](ctx context.Context, cond builder.Cond, f func(ctx context.Context, bean *Bean) error) error {
var start int
batchSize := setting.Database.IterateBufferSize

View File

@ -0,0 +1,71 @@
-
id: 1
run_id: 791
runner_id: 1
repo_id: 4
owner_id: 1
commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0
storage_path: "26/1/1712166500347189545.chunk"
file_size: 1024
file_compressed_size: 1024
content_encoding: ""
artifact_path: "abc.txt"
artifact_name: "artifact-download"
status: 1
created_unix: 1712338649
updated_unix: 1712338649
expired_unix: 1720114649
-
id: 19
run_id: 791
runner_id: 1
repo_id: 4
owner_id: 1
commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0
storage_path: "26/19/1712348022422036662.chunk"
file_size: 1024
file_compressed_size: 1024
content_encoding: ""
artifact_path: "abc.txt"
artifact_name: "multi-file-download"
status: 2
created_unix: 1712348022
updated_unix: 1712348022
expired_unix: 1720124022
-
id: 20
run_id: 791
runner_id: 1
repo_id: 4
owner_id: 1
commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0
storage_path: "26/20/1712348022423431524.chunk"
file_size: 1024
file_compressed_size: 1024
content_encoding: ""
artifact_path: "xyz/def.txt"
artifact_name: "multi-file-download"
status: 2
created_unix: 1712348022
updated_unix: 1712348022
expired_unix: 1720124022
-
id: 22
run_id: 792
runner_id: 1
repo_id: 4
owner_id: 1
commit_sha: c2d72f548424103f01ee1dc02889c1e2bff816b0
storage_path: "27/5/1730330775594233150.chunk"
file_size: 1024
file_compressed_size: 1024
content_encoding: "application/zip"
artifact_path: "artifact-v4-download.zip"
artifact_name: "artifact-v4-download"
status: 2
created_unix: 1730330775
updated_unix: 1730330775
expired_unix: 1738106775

View File

@ -11,7 +11,7 @@
id: 2
oid: 2eccdb43825d2a49d99d542daa20075cff1d97d9d2349a8977efe9c03661737c
size: 107
size: 2048
repository_id: 54
created_unix: 1671607299

View File

@ -129,3 +129,9 @@
uid: 2
org_id: 35
is_public: true
-
id: 23
uid: 20
org_id: 17
is_public: false

View File

@ -623,7 +623,7 @@
num_stars: 0
num_repos: 2
num_teams: 3
num_members: 4
num_members: 5
visibility: 0
repo_admin_change_team_access: false
theme: ""

View File

@ -136,8 +136,6 @@ var ErrLFSObjectNotExist = db.ErrNotExist{Resource: "LFS Meta object"}
// NewLFSMetaObject stores a given populated LFSMetaObject structure in the database
// if it is not already present.
func NewLFSMetaObject(ctx context.Context, repoID int64, p lfs.Pointer) (*LFSMetaObject, error) {
var err error
ctx, committer, err := db.TxContext(ctx)
if err != nil {
return nil, err

View File

@ -84,15 +84,21 @@ func IsRuleNameSpecial(ruleName string) bool {
}
func (protectBranch *ProtectedBranch) loadGlob() {
if protectBranch.globRule == nil {
if protectBranch.isPlainName || protectBranch.globRule != nil {
return
}
// detect if it is not glob
if !IsRuleNameSpecial(protectBranch.RuleName) {
protectBranch.isPlainName = true
return
}
// now we load the glob
var err error
protectBranch.globRule, err = glob.Compile(protectBranch.RuleName, '/')
if err != nil {
log.Warn("Invalid glob rule for ProtectedBranch[%d]: %s %v", protectBranch.ID, protectBranch.RuleName, err)
protectBranch.globRule = glob.MustCompile(glob.QuoteMeta(protectBranch.RuleName), '/')
}
protectBranch.isPlainName = !IsRuleNameSpecial(protectBranch.RuleName)
}
}
// Match tests if branchName matches the rule

View File

@ -74,3 +74,32 @@ func TestBranchRuleMatchPriority(t *testing.T) {
}
}
}
func TestBranchRuleSort(t *testing.T) {
in := []*ProtectedBranch{{
RuleName: "b",
CreatedUnix: 1,
}, {
RuleName: "b/*",
CreatedUnix: 3,
}, {
RuleName: "a/*",
CreatedUnix: 2,
}, {
RuleName: "c",
CreatedUnix: 0,
}, {
RuleName: "a",
CreatedUnix: 4,
}}
expect := []string{"c", "b", "a", "a/*", "b/*"}
pbr := ProtectedBranchRules(in)
pbr.sort()
var got []string
for i := range pbr {
got = append(got, pbr[i].RuleName)
}
assert.Equal(t, expect, got)
}

View File

@ -229,8 +229,7 @@ func TestGetLabelsByOrgID(t *testing.T) {
testSuccess(3, "reversealphabetically", []int64{4, 3})
testSuccess(3, "default", []int64{3, 4})
var err error
_, err = issues_model.GetLabelsByOrgID(db.DefaultContext, 0, "leastissues", db.ListOptions{})
_, err := issues_model.GetLabelsByOrgID(db.DefaultContext, 0, "leastissues", db.ListOptions{})
assert.True(t, issues_model.IsErrOrgLabelNotExist(err))
_, err = issues_model.GetLabelsByOrgID(db.DefaultContext, -1, "leastissues", db.ListOptions{})

View File

@ -84,10 +84,9 @@ func (m *Milestone) BeforeUpdate() {
// this object.
func (m *Milestone) AfterLoad() {
m.NumOpenIssues = m.NumIssues - m.NumClosedIssues
if m.DeadlineUnix.Year() == 9999 {
if m.DeadlineUnix == 0 {
return
}
m.DeadlineString = m.DeadlineUnix.FormatDate()
if m.IsClosed {
m.IsOverdue = m.ClosedDateUnix >= m.DeadlineUnix

View File

@ -36,25 +36,15 @@ import (
const minDBVersion = 70 // Gitea 1.5.3
// Migration describes on migration from lower version to high version
type Migration interface {
Description() string
Migrate(*xorm.Engine) error
}
type migration struct {
idNumber int64 // DB version is "the last migration's idNumber" + 1
description string
migrate func(*xorm.Engine) error
}
// NewMigration creates a new migration
func NewMigration(desc string, fn func(*xorm.Engine) error) Migration {
return &migration{desc, fn}
}
// Description returns the migration's description
func (m *migration) Description() string {
return m.description
// newMigration creates a new migration
func newMigration(idNumber int64, desc string, fn func(*xorm.Engine) error) *migration {
return &migration{idNumber, desc, fn}
}
// Migrate executes the migration
@ -65,546 +55,319 @@ func (m *migration) Migrate(x *xorm.Engine) error {
// Version describes the version table. Should have only one row with id==1
type Version struct {
ID int64 `xorm:"pk autoincr"`
Version int64
Version int64 // DB version is "the last migration's idNumber" + 1
}
// Use noopMigration when there is a migration that has been no-oped
var noopMigration = func(_ *xorm.Engine) error { return nil }
var preparedMigrations []*migration
// This is a sequence of migrations. Add new migrations to the bottom of the list.
// If you want to "retire" a migration, remove it from the top of the list and
// update minDBVersion accordingly
var migrations = []Migration{
// Gitea 1.5.0 ends at v69
func prepareMigrationTasks() []*migration {
if preparedMigrations != nil {
return preparedMigrations
}
preparedMigrations = []*migration{
// Gitea 1.5.0 ends at database version 69
// v70 -> v71
NewMigration("add issue_dependencies", v1_6.AddIssueDependencies),
// v71 -> v72
NewMigration("protect each scratch token", v1_6.AddScratchHash),
// v72 -> v73
NewMigration("add review", v1_6.AddReview),
newMigration(70, "add issue_dependencies", v1_6.AddIssueDependencies),
newMigration(71, "protect each scratch token", v1_6.AddScratchHash),
newMigration(72, "add review", v1_6.AddReview),
// Gitea 1.6.0 ends at v73
// Gitea 1.6.0 ends at database version 73
// v73 -> v74
NewMigration("add must_change_password column for users table", v1_7.AddMustChangePassword),
// v74 -> v75
NewMigration("add approval whitelists to protected branches", v1_7.AddApprovalWhitelistsToProtectedBranches),
// v75 -> v76
NewMigration("clear nonused data which not deleted when user was deleted", v1_7.ClearNonusedData),
newMigration(73, "add must_change_password column for users table", v1_7.AddMustChangePassword),
newMigration(74, "add approval whitelists to protected branches", v1_7.AddApprovalWhitelistsToProtectedBranches),
newMigration(75, "clear nonused data which not deleted when user was deleted", v1_7.ClearNonusedData),
// Gitea 1.7.0 ends at v76
// Gitea 1.7.0 ends at database version 76
// v76 -> v77
NewMigration("add pull request rebase with merge commit", v1_8.AddPullRequestRebaseWithMerge),
// v77 -> v78
NewMigration("add theme to users", v1_8.AddUserDefaultTheme),
// v78 -> v79
NewMigration("rename repo is_bare to repo is_empty", v1_8.RenameRepoIsBareToIsEmpty),
// v79 -> v80
NewMigration("add can close issues via commit in any branch", v1_8.AddCanCloseIssuesViaCommitInAnyBranch),
// v80 -> v81
NewMigration("add is locked to issues", v1_8.AddIsLockedToIssues),
// v81 -> v82
NewMigration("update U2F counter type", v1_8.ChangeU2FCounterType),
newMigration(76, "add pull request rebase with merge commit", v1_8.AddPullRequestRebaseWithMerge),
newMigration(77, "add theme to users", v1_8.AddUserDefaultTheme),
newMigration(78, "rename repo is_bare to repo is_empty", v1_8.RenameRepoIsBareToIsEmpty),
newMigration(79, "add can close issues via commit in any branch", v1_8.AddCanCloseIssuesViaCommitInAnyBranch),
newMigration(80, "add is locked to issues", v1_8.AddIsLockedToIssues),
newMigration(81, "update U2F counter type", v1_8.ChangeU2FCounterType),
// Gitea 1.8.0 ends at v82
// Gitea 1.8.0 ends at database version 82
// v82 -> v83
NewMigration("hot fix for wrong release sha1 on release table", v1_9.FixReleaseSha1OnReleaseTable),
// v83 -> v84
NewMigration("add uploader id for table attachment", v1_9.AddUploaderIDForAttachment),
// v84 -> v85
NewMigration("add table to store original imported gpg keys", v1_9.AddGPGKeyImport),
// v85 -> v86
NewMigration("hash application token", v1_9.HashAppToken),
// v86 -> v87
NewMigration("add http method to webhook", v1_9.AddHTTPMethodToWebhook),
// v87 -> v88
NewMigration("add avatar field to repository", v1_9.AddAvatarFieldToRepository),
newMigration(82, "hot fix for wrong release sha1 on release table", v1_9.FixReleaseSha1OnReleaseTable),
newMigration(83, "add uploader id for table attachment", v1_9.AddUploaderIDForAttachment),
newMigration(84, "add table to store original imported gpg keys", v1_9.AddGPGKeyImport),
newMigration(85, "hash application token", v1_9.HashAppToken),
newMigration(86, "add http method to webhook", v1_9.AddHTTPMethodToWebhook),
newMigration(87, "add avatar field to repository", v1_9.AddAvatarFieldToRepository),
// Gitea 1.9.0 ends at v88
// Gitea 1.9.0 ends at database version 88
// v88 -> v89
NewMigration("add commit status context field to commit_status", v1_10.AddCommitStatusContext),
// v89 -> v90
NewMigration("add original author/url migration info to issues, comments, and repo ", v1_10.AddOriginalMigrationInfo),
// v90 -> v91
NewMigration("change length of some repository columns", v1_10.ChangeSomeColumnsLengthOfRepo),
// v91 -> v92
NewMigration("add index on owner_id of repository and type, review_id of comment", v1_10.AddIndexOnRepositoryAndComment),
// v92 -> v93
NewMigration("remove orphaned repository index statuses", v1_10.RemoveLingeringIndexStatus),
// v93 -> v94
NewMigration("add email notification enabled preference to user", v1_10.AddEmailNotificationEnabledToUser),
// v94 -> v95
NewMigration("add enable_status_check, status_check_contexts to protected_branch", v1_10.AddStatusCheckColumnsForProtectedBranches),
// v95 -> v96
NewMigration("add table columns for cross referencing issues", v1_10.AddCrossReferenceColumns),
// v96 -> v97
NewMigration("delete orphaned attachments", v1_10.DeleteOrphanedAttachments),
// v97 -> v98
NewMigration("add repo_admin_change_team_access to user", v1_10.AddRepoAdminChangeTeamAccessColumnForUser),
// v98 -> v99
NewMigration("add original author name and id on migrated release", v1_10.AddOriginalAuthorOnMigratedReleases),
// v99 -> v100
NewMigration("add task table and status column for repository table", v1_10.AddTaskTable),
// v100 -> v101
NewMigration("update migration repositories' service type", v1_10.UpdateMigrationServiceTypes),
// v101 -> v102
NewMigration("change length of some external login users columns", v1_10.ChangeSomeColumnsLengthOfExternalLoginUser),
newMigration(88, "add commit status context field to commit_status", v1_10.AddCommitStatusContext),
newMigration(89, "add original author/url migration info to issues, comments, and repo ", v1_10.AddOriginalMigrationInfo),
newMigration(90, "change length of some repository columns", v1_10.ChangeSomeColumnsLengthOfRepo),
newMigration(91, "add index on owner_id of repository and type, review_id of comment", v1_10.AddIndexOnRepositoryAndComment),
newMigration(92, "remove orphaned repository index statuses", v1_10.RemoveLingeringIndexStatus),
newMigration(93, "add email notification enabled preference to user", v1_10.AddEmailNotificationEnabledToUser),
newMigration(94, "add enable_status_check, status_check_contexts to protected_branch", v1_10.AddStatusCheckColumnsForProtectedBranches),
newMigration(95, "add table columns for cross referencing issues", v1_10.AddCrossReferenceColumns),
newMigration(96, "delete orphaned attachments", v1_10.DeleteOrphanedAttachments),
newMigration(97, "add repo_admin_change_team_access to user", v1_10.AddRepoAdminChangeTeamAccessColumnForUser),
newMigration(98, "add original author name and id on migrated release", v1_10.AddOriginalAuthorOnMigratedReleases),
newMigration(99, "add task table and status column for repository table", v1_10.AddTaskTable),
newMigration(100, "update migration repositories' service type", v1_10.UpdateMigrationServiceTypes),
newMigration(101, "change length of some external login users columns", v1_10.ChangeSomeColumnsLengthOfExternalLoginUser),
// Gitea 1.10.0 ends at v102
// Gitea 1.10.0 ends at database version 102
// v102 -> v103
NewMigration("update migration repositories' service type", v1_11.DropColumnHeadUserNameOnPullRequest),
// v103 -> v104
NewMigration("Add WhitelistDeployKeys to protected branch", v1_11.AddWhitelistDeployKeysToBranches),
// v104 -> v105
NewMigration("remove unnecessary columns from label", v1_11.RemoveLabelUneededCols),
// v105 -> v106
NewMigration("add includes_all_repositories to teams", v1_11.AddTeamIncludesAllRepositories),
// v106 -> v107
NewMigration("add column `mode` to table watch", v1_11.AddModeColumnToWatch),
// v107 -> v108
NewMigration("Add template options to repository", v1_11.AddTemplateToRepo),
// v108 -> v109
NewMigration("Add comment_id on table notification", v1_11.AddCommentIDOnNotification),
// v109 -> v110
NewMigration("add can_create_org_repo to team", v1_11.AddCanCreateOrgRepoColumnForTeam),
// v110 -> v111
NewMigration("change review content type to text", v1_11.ChangeReviewContentToText),
// v111 -> v112
NewMigration("update branch protection for can push and whitelist enable", v1_11.AddBranchProtectionCanPushAndEnableWhitelist),
// v112 -> v113
NewMigration("remove release attachments which repository deleted", v1_11.RemoveAttachmentMissedRepo),
// v113 -> v114
NewMigration("new feature: change target branch of pull requests", v1_11.FeatureChangeTargetBranch),
// v114 -> v115
NewMigration("Remove authentication credentials from stored URL", v1_11.SanitizeOriginalURL),
// v115 -> v116
NewMigration("add user_id prefix to existing user avatar name", v1_11.RenameExistingUserAvatarName),
// v116 -> v117
NewMigration("Extend TrackedTimes", v1_11.ExtendTrackedTimes),
newMigration(102, "update migration repositories' service type", v1_11.DropColumnHeadUserNameOnPullRequest),
newMigration(103, "Add WhitelistDeployKeys to protected branch", v1_11.AddWhitelistDeployKeysToBranches),
newMigration(104, "remove unnecessary columns from label", v1_11.RemoveLabelUneededCols),
newMigration(105, "add includes_all_repositories to teams", v1_11.AddTeamIncludesAllRepositories),
newMigration(106, "add column `mode` to table watch", v1_11.AddModeColumnToWatch),
newMigration(107, "Add template options to repository", v1_11.AddTemplateToRepo),
newMigration(108, "Add comment_id on table notification", v1_11.AddCommentIDOnNotification),
newMigration(109, "add can_create_org_repo to team", v1_11.AddCanCreateOrgRepoColumnForTeam),
newMigration(110, "change review content type to text", v1_11.ChangeReviewContentToText),
newMigration(111, "update branch protection for can push and whitelist enable", v1_11.AddBranchProtectionCanPushAndEnableWhitelist),
newMigration(112, "remove release attachments which repository deleted", v1_11.RemoveAttachmentMissedRepo),
newMigration(113, "new feature: change target branch of pull requests", v1_11.FeatureChangeTargetBranch),
newMigration(114, "Remove authentication credentials from stored URL", v1_11.SanitizeOriginalURL),
newMigration(115, "add user_id prefix to existing user avatar name", v1_11.RenameExistingUserAvatarName),
newMigration(116, "Extend TrackedTimes", v1_11.ExtendTrackedTimes),
// Gitea 1.11.0 ends at v117
// Gitea 1.11.0 ends at database version 117
// v117 -> v118
NewMigration("Add block on rejected reviews branch protection", v1_12.AddBlockOnRejectedReviews),
// v118 -> v119
NewMigration("Add commit id and stale to reviews", v1_12.AddReviewCommitAndStale),
// v119 -> v120
NewMigration("Fix migrated repositories' git service type", v1_12.FixMigratedRepositoryServiceType),
// v120 -> v121
NewMigration("Add owner_name on table repository", v1_12.AddOwnerNameOnRepository),
// v121 -> v122
NewMigration("add is_restricted column for users table", v1_12.AddIsRestricted),
// v122 -> v123
NewMigration("Add Require Signed Commits to ProtectedBranch", v1_12.AddRequireSignedCommits),
// v123 -> v124
NewMigration("Add original information for reactions", v1_12.AddReactionOriginals),
// v124 -> v125
NewMigration("Add columns to user and repository", v1_12.AddUserRepoMissingColumns),
// v125 -> v126
NewMigration("Add some columns on review for migration", v1_12.AddReviewMigrateInfo),
// v126 -> v127
NewMigration("Fix topic repository count", v1_12.FixTopicRepositoryCount),
// v127 -> v128
NewMigration("add repository code language statistics", v1_12.AddLanguageStats),
// v128 -> v129
NewMigration("fix merge base for pull requests", v1_12.FixMergeBase),
// v129 -> v130
NewMigration("remove dependencies from deleted repositories", v1_12.PurgeUnusedDependencies),
// v130 -> v131
NewMigration("Expand webhooks for more granularity", v1_12.ExpandWebhooks),
// v131 -> v132
NewMigration("Add IsSystemWebhook column to webhooks table", v1_12.AddSystemWebhookColumn),
// v132 -> v133
NewMigration("Add Branch Protection Protected Files Column", v1_12.AddBranchProtectionProtectedFilesColumn),
// v133 -> v134
NewMigration("Add EmailHash Table", v1_12.AddEmailHashTable),
// v134 -> v135
NewMigration("Refix merge base for merged pull requests", v1_12.RefixMergeBase),
// v135 -> v136
NewMigration("Add OrgID column to Labels table", v1_12.AddOrgIDLabelColumn),
// v136 -> v137
NewMigration("Add CommitsAhead and CommitsBehind Column to PullRequest Table", v1_12.AddCommitDivergenceToPulls),
// v137 -> v138
NewMigration("Add Branch Protection Block Outdated Branch", v1_12.AddBlockOnOutdatedBranch),
// v138 -> v139
NewMigration("Add ResolveDoerID to Comment table", v1_12.AddResolveDoerIDCommentColumn),
// v139 -> v140
NewMigration("prepend refs/heads/ to issue refs", v1_12.PrependRefsHeadsToIssueRefs),
newMigration(117, "Add block on rejected reviews branch protection", v1_12.AddBlockOnRejectedReviews),
newMigration(118, "Add commit id and stale to reviews", v1_12.AddReviewCommitAndStale),
newMigration(119, "Fix migrated repositories' git service type", v1_12.FixMigratedRepositoryServiceType),
newMigration(120, "Add owner_name on table repository", v1_12.AddOwnerNameOnRepository),
newMigration(121, "add is_restricted column for users table", v1_12.AddIsRestricted),
newMigration(122, "Add Require Signed Commits to ProtectedBranch", v1_12.AddRequireSignedCommits),
newMigration(123, "Add original information for reactions", v1_12.AddReactionOriginals),
newMigration(124, "Add columns to user and repository", v1_12.AddUserRepoMissingColumns),
newMigration(125, "Add some columns on review for migration", v1_12.AddReviewMigrateInfo),
newMigration(126, "Fix topic repository count", v1_12.FixTopicRepositoryCount),
newMigration(127, "add repository code language statistics", v1_12.AddLanguageStats),
newMigration(128, "fix merge base for pull requests", v1_12.FixMergeBase),
newMigration(129, "remove dependencies from deleted repositories", v1_12.PurgeUnusedDependencies),
newMigration(130, "Expand webhooks for more granularity", v1_12.ExpandWebhooks),
newMigration(131, "Add IsSystemWebhook column to webhooks table", v1_12.AddSystemWebhookColumn),
newMigration(132, "Add Branch Protection Protected Files Column", v1_12.AddBranchProtectionProtectedFilesColumn),
newMigration(133, "Add EmailHash Table", v1_12.AddEmailHashTable),
newMigration(134, "Refix merge base for merged pull requests", v1_12.RefixMergeBase),
newMigration(135, "Add OrgID column to Labels table", v1_12.AddOrgIDLabelColumn),
newMigration(136, "Add CommitsAhead and CommitsBehind Column to PullRequest Table", v1_12.AddCommitDivergenceToPulls),
newMigration(137, "Add Branch Protection Block Outdated Branch", v1_12.AddBlockOnOutdatedBranch),
newMigration(138, "Add ResolveDoerID to Comment table", v1_12.AddResolveDoerIDCommentColumn),
newMigration(139, "prepend refs/heads/ to issue refs", v1_12.PrependRefsHeadsToIssueRefs),
// Gitea 1.12.0 ends at v140
// Gitea 1.12.0 ends at database version 140
// v140 -> v141
NewMigration("Save detected language file size to database instead of percent", v1_13.FixLanguageStatsToSaveSize),
// v141 -> v142
NewMigration("Add KeepActivityPrivate to User table", v1_13.AddKeepActivityPrivateUserColumn),
// v142 -> v143
NewMigration("Ensure Repository.IsArchived is not null", v1_13.SetIsArchivedToFalse),
// v143 -> v144
NewMigration("recalculate Stars number for all user", v1_13.RecalculateStars),
// v144 -> v145
NewMigration("update Matrix Webhook http method to 'PUT'", v1_13.UpdateMatrixWebhookHTTPMethod),
// v145 -> v146
NewMigration("Increase Language field to 50 in LanguageStats", v1_13.IncreaseLanguageField),
// v146 -> v147
NewMigration("Add projects info to repository table", v1_13.AddProjectsInfo),
// v147 -> v148
NewMigration("create review for 0 review id code comments", v1_13.CreateReviewsForCodeComments),
// v148 -> v149
NewMigration("remove issue dependency comments who refer to non existing issues", v1_13.PurgeInvalidDependenciesComments),
// v149 -> v150
NewMigration("Add Created and Updated to Milestone table", v1_13.AddCreatedAndUpdatedToMilestones),
// v150 -> v151
NewMigration("add primary key to repo_topic", v1_13.AddPrimaryKeyToRepoTopic),
// v151 -> v152
NewMigration("set default password algorithm to Argon2", v1_13.SetDefaultPasswordToArgon2),
// v152 -> v153
NewMigration("add TrustModel field to Repository", v1_13.AddTrustModelToRepository),
// v153 > v154
NewMigration("add Team review request support", v1_13.AddTeamReviewRequestSupport),
// v154 > v155
NewMigration("add timestamps to Star, Label, Follow, Watch and Collaboration", v1_13.AddTimeStamps),
newMigration(140, "Save detected language file size to database instead of percent", v1_13.FixLanguageStatsToSaveSize),
newMigration(141, "Add KeepActivityPrivate to User table", v1_13.AddKeepActivityPrivateUserColumn),
newMigration(142, "Ensure Repository.IsArchived is not null", v1_13.SetIsArchivedToFalse),
newMigration(143, "recalculate Stars number for all user", v1_13.RecalculateStars),
newMigration(144, "update Matrix Webhook http method to 'PUT'", v1_13.UpdateMatrixWebhookHTTPMethod),
newMigration(145, "Increase Language field to 50 in LanguageStats", v1_13.IncreaseLanguageField),
newMigration(146, "Add projects info to repository table", v1_13.AddProjectsInfo),
newMigration(147, "create review for 0 review id code comments", v1_13.CreateReviewsForCodeComments),
newMigration(148, "remove issue dependency comments who refer to non existing issues", v1_13.PurgeInvalidDependenciesComments),
newMigration(149, "Add Created and Updated to Milestone table", v1_13.AddCreatedAndUpdatedToMilestones),
newMigration(150, "add primary key to repo_topic", v1_13.AddPrimaryKeyToRepoTopic),
newMigration(151, "set default password algorithm to Argon2", v1_13.SetDefaultPasswordToArgon2),
newMigration(152, "add TrustModel field to Repository", v1_13.AddTrustModelToRepository),
newMigration(153, "add Team review request support", v1_13.AddTeamReviewRequestSupport),
newMigration(154, "add timestamps to Star, Label, Follow, Watch and Collaboration", v1_13.AddTimeStamps),
// Gitea 1.13.0 ends at v155
// Gitea 1.13.0 ends at database version 155
// v155 -> v156
NewMigration("add changed_protected_files column for pull_request table", v1_14.AddChangedProtectedFilesPullRequestColumn),
// v156 -> v157
NewMigration("fix publisher ID for tag releases", v1_14.FixPublisherIDforTagReleases),
// v157 -> v158
NewMigration("ensure repo topics are up-to-date", v1_14.FixRepoTopics),
// v158 -> v159
NewMigration("code comment replies should have the commitID of the review they are replying to", v1_14.UpdateCodeCommentReplies),
// v159 -> v160
NewMigration("update reactions constraint", v1_14.UpdateReactionConstraint),
// v160 -> v161
NewMigration("Add block on official review requests branch protection", v1_14.AddBlockOnOfficialReviewRequests),
// v161 -> v162
NewMigration("Convert task type from int to string", v1_14.ConvertTaskTypeToString),
// v162 -> v163
NewMigration("Convert webhook task type from int to string", v1_14.ConvertWebhookTaskTypeToString),
// v163 -> v164
NewMigration("Convert topic name from 25 to 50", v1_14.ConvertTopicNameFrom25To50),
// v164 -> v165
NewMigration("Add scope and nonce columns to oauth2_grant table", v1_14.AddScopeAndNonceColumnsToOAuth2Grant),
// v165 -> v166
NewMigration("Convert hook task type from char(16) to varchar(16) and trim the column", v1_14.ConvertHookTaskTypeToVarcharAndTrim),
// v166 -> v167
NewMigration("Where Password is Valid with Empty String delete it", v1_14.RecalculateUserEmptyPWD),
// v167 -> v168
NewMigration("Add user redirect", v1_14.AddUserRedirect),
// v168 -> v169
NewMigration("Recreate user table to fix default values", v1_14.RecreateUserTableToFixDefaultValues),
// v169 -> v170
NewMigration("Update DeleteBranch comments to set the old_ref to the commit_sha", v1_14.CommentTypeDeleteBranchUseOldRef),
// v170 -> v171
NewMigration("Add Dismissed to Review table", v1_14.AddDismissedReviewColumn),
// v171 -> v172
NewMigration("Add Sorting to ProjectBoard table", v1_14.AddSortingColToProjectBoard),
// v172 -> v173
NewMigration("Add sessions table for go-chi/session", v1_14.AddSessionTable),
// v173 -> v174
NewMigration("Add time_id column to Comment", v1_14.AddTimeIDCommentColumn),
// v174 -> v175
NewMigration("Create repo transfer table", v1_14.AddRepoTransfer),
// v175 -> v176
NewMigration("Fix Postgres ID Sequences broken by recreate-table", v1_14.FixPostgresIDSequences),
// v176 -> v177
NewMigration("Remove invalid labels from comments", v1_14.RemoveInvalidLabels),
// v177 -> v178
NewMigration("Delete orphaned IssueLabels", v1_14.DeleteOrphanedIssueLabels),
newMigration(155, "add changed_protected_files column for pull_request table", v1_14.AddChangedProtectedFilesPullRequestColumn),
newMigration(156, "fix publisher ID for tag releases", v1_14.FixPublisherIDforTagReleases),
newMigration(157, "ensure repo topics are up-to-date", v1_14.FixRepoTopics),
newMigration(158, "code comment replies should have the commitID of the review they are replying to", v1_14.UpdateCodeCommentReplies),
newMigration(159, "update reactions constraint", v1_14.UpdateReactionConstraint),
newMigration(160, "Add block on official review requests branch protection", v1_14.AddBlockOnOfficialReviewRequests),
newMigration(161, "Convert task type from int to string", v1_14.ConvertTaskTypeToString),
newMigration(162, "Convert webhook task type from int to string", v1_14.ConvertWebhookTaskTypeToString),
newMigration(163, "Convert topic name from 25 to 50", v1_14.ConvertTopicNameFrom25To50),
newMigration(164, "Add scope and nonce columns to oauth2_grant table", v1_14.AddScopeAndNonceColumnsToOAuth2Grant),
newMigration(165, "Convert hook task type from char(16) to varchar(16) and trim the column", v1_14.ConvertHookTaskTypeToVarcharAndTrim),
newMigration(166, "Where Password is Valid with Empty String delete it", v1_14.RecalculateUserEmptyPWD),
newMigration(167, "Add user redirect", v1_14.AddUserRedirect),
newMigration(168, "Recreate user table to fix default values", v1_14.RecreateUserTableToFixDefaultValues),
newMigration(169, "Update DeleteBranch comments to set the old_ref to the commit_sha", v1_14.CommentTypeDeleteBranchUseOldRef),
newMigration(170, "Add Dismissed to Review table", v1_14.AddDismissedReviewColumn),
newMigration(171, "Add Sorting to ProjectBoard table", v1_14.AddSortingColToProjectBoard),
newMigration(172, "Add sessions table for go-chi/session", v1_14.AddSessionTable),
newMigration(173, "Add time_id column to Comment", v1_14.AddTimeIDCommentColumn),
newMigration(174, "Create repo transfer table", v1_14.AddRepoTransfer),
newMigration(175, "Fix Postgres ID Sequences broken by recreate-table", v1_14.FixPostgresIDSequences),
newMigration(176, "Remove invalid labels from comments", v1_14.RemoveInvalidLabels),
newMigration(177, "Delete orphaned IssueLabels", v1_14.DeleteOrphanedIssueLabels),
// Gitea 1.14.0 ends at v178
// Gitea 1.14.0 ends at database version 178
// v178 -> v179
NewMigration("Add LFS columns to Mirror", v1_15.AddLFSMirrorColumns),
// v179 -> v180
NewMigration("Convert avatar url to text", v1_15.ConvertAvatarURLToText),
// v180 -> v181
NewMigration("Delete credentials from past migrations", v1_15.DeleteMigrationCredentials),
// v181 -> v182
NewMigration("Always save primary email on email address table", v1_15.AddPrimaryEmail2EmailAddress),
// v182 -> v183
NewMigration("Add issue resource index table", v1_15.AddIssueResourceIndexTable),
// v183 -> v184
NewMigration("Create PushMirror table", v1_15.CreatePushMirrorTable),
// v184 -> v185
NewMigration("Rename Task errors to message", v1_15.RenameTaskErrorsToMessage),
// v185 -> v186
NewMigration("Add new table repo_archiver", v1_15.AddRepoArchiver),
// v186 -> v187
NewMigration("Create protected tag table", v1_15.CreateProtectedTagTable),
// v187 -> v188
NewMigration("Drop unneeded webhook related columns", v1_15.DropWebhookColumns),
// v188 -> v189
NewMigration("Add key is verified to gpg key", v1_15.AddKeyIsVerified),
newMigration(178, "Add LFS columns to Mirror", v1_15.AddLFSMirrorColumns),
newMigration(179, "Convert avatar url to text", v1_15.ConvertAvatarURLToText),
newMigration(180, "Delete credentials from past migrations", v1_15.DeleteMigrationCredentials),
newMigration(181, "Always save primary email on email address table", v1_15.AddPrimaryEmail2EmailAddress),
newMigration(182, "Add issue resource index table", v1_15.AddIssueResourceIndexTable),
newMigration(183, "Create PushMirror table", v1_15.CreatePushMirrorTable),
newMigration(184, "Rename Task errors to message", v1_15.RenameTaskErrorsToMessage),
newMigration(185, "Add new table repo_archiver", v1_15.AddRepoArchiver),
newMigration(186, "Create protected tag table", v1_15.CreateProtectedTagTable),
newMigration(187, "Drop unneeded webhook related columns", v1_15.DropWebhookColumns),
newMigration(188, "Add key is verified to gpg key", v1_15.AddKeyIsVerified),
// Gitea 1.15.0 ends at v189
// Gitea 1.15.0 ends at database version 189
// v189 -> v190
NewMigration("Unwrap ldap.Sources", v1_16.UnwrapLDAPSourceCfg),
// v190 -> v191
NewMigration("Add agit flow pull request support", v1_16.AddAgitFlowPullRequest),
// v191 -> v192
NewMigration("Alter issue/comment table TEXT fields to LONGTEXT", v1_16.AlterIssueAndCommentTextFieldsToLongText),
// v192 -> v193
NewMigration("RecreateIssueResourceIndexTable to have a primary key instead of an unique index", v1_16.RecreateIssueResourceIndexTable),
// v193 -> v194
NewMigration("Add repo id column for attachment table", v1_16.AddRepoIDForAttachment),
// v194 -> v195
NewMigration("Add Branch Protection Unprotected Files Column", v1_16.AddBranchProtectionUnprotectedFilesColumn),
// v195 -> v196
NewMigration("Add table commit_status_index", v1_16.AddTableCommitStatusIndex),
// v196 -> v197
NewMigration("Add Color to ProjectBoard table", v1_16.AddColorColToProjectBoard),
// v197 -> v198
NewMigration("Add renamed_branch table", v1_16.AddRenamedBranchTable),
// v198 -> v199
NewMigration("Add issue content history table", v1_16.AddTableIssueContentHistory),
// v199 -> v200
NewMigration("No-op (remote version is using AppState now)", noopMigration),
// v200 -> v201
NewMigration("Add table app_state", v1_16.AddTableAppState),
// v201 -> v202
NewMigration("Drop table remote_version (if exists)", v1_16.DropTableRemoteVersion),
// v202 -> v203
NewMigration("Create key/value table for user settings", v1_16.CreateUserSettingsTable),
// v203 -> v204
NewMigration("Add Sorting to ProjectIssue table", v1_16.AddProjectIssueSorting),
// v204 -> v205
NewMigration("Add key is verified to ssh key", v1_16.AddSSHKeyIsVerified),
// v205 -> v206
NewMigration("Migrate to higher varchar on user struct", v1_16.MigrateUserPasswordSalt),
// v206 -> v207
NewMigration("Add authorize column to team_unit table", v1_16.AddAuthorizeColForTeamUnit),
// v207 -> v208
NewMigration("Add webauthn table and migrate u2f data to webauthn - NO-OPED", v1_16.AddWebAuthnCred),
// v208 -> v209
NewMigration("Use base32.HexEncoding instead of base64 encoding for cred ID as it is case insensitive - NO-OPED", v1_16.UseBase32HexForCredIDInWebAuthnCredential),
// v209 -> v210
NewMigration("Increase WebAuthentication CredentialID size to 410 - NO-OPED", v1_16.IncreaseCredentialIDTo410),
// v210 -> v211
NewMigration("v208 was completely broken - remigrate", v1_16.RemigrateU2FCredentials),
newMigration(189, "Unwrap ldap.Sources", v1_16.UnwrapLDAPSourceCfg),
newMigration(190, "Add agit flow pull request support", v1_16.AddAgitFlowPullRequest),
newMigration(191, "Alter issue/comment table TEXT fields to LONGTEXT", v1_16.AlterIssueAndCommentTextFieldsToLongText),
newMigration(192, "RecreateIssueResourceIndexTable to have a primary key instead of an unique index", v1_16.RecreateIssueResourceIndexTable),
newMigration(193, "Add repo id column for attachment table", v1_16.AddRepoIDForAttachment),
newMigration(194, "Add Branch Protection Unprotected Files Column", v1_16.AddBranchProtectionUnprotectedFilesColumn),
newMigration(195, "Add table commit_status_index", v1_16.AddTableCommitStatusIndex),
newMigration(196, "Add Color to ProjectBoard table", v1_16.AddColorColToProjectBoard),
newMigration(197, "Add renamed_branch table", v1_16.AddRenamedBranchTable),
newMigration(198, "Add issue content history table", v1_16.AddTableIssueContentHistory),
newMigration(199, "No-op (remote version is using AppState now)", noopMigration),
newMigration(200, "Add table app_state", v1_16.AddTableAppState),
newMigration(201, "Drop table remote_version (if exists)", v1_16.DropTableRemoteVersion),
newMigration(202, "Create key/value table for user settings", v1_16.CreateUserSettingsTable),
newMigration(203, "Add Sorting to ProjectIssue table", v1_16.AddProjectIssueSorting),
newMigration(204, "Add key is verified to ssh key", v1_16.AddSSHKeyIsVerified),
newMigration(205, "Migrate to higher varchar on user struct", v1_16.MigrateUserPasswordSalt),
newMigration(206, "Add authorize column to team_unit table", v1_16.AddAuthorizeColForTeamUnit),
newMigration(207, "Add webauthn table and migrate u2f data to webauthn - NO-OPED", v1_16.AddWebAuthnCred),
newMigration(208, "Use base32.HexEncoding instead of base64 encoding for cred ID as it is case insensitive - NO-OPED", v1_16.UseBase32HexForCredIDInWebAuthnCredential),
newMigration(209, "Increase WebAuthentication CredentialID size to 410 - NO-OPED", v1_16.IncreaseCredentialIDTo410),
newMigration(210, "v208 was completely broken - remigrate", v1_16.RemigrateU2FCredentials),
// Gitea 1.16.2 ends at v211
// Gitea 1.16.2 ends at database version 211
// v211 -> v212
NewMigration("Create ForeignReference table", v1_17.CreateForeignReferenceTable),
// v212 -> v213
NewMigration("Add package tables", v1_17.AddPackageTables),
// v213 -> v214
NewMigration("Add allow edits from maintainers to PullRequest table", v1_17.AddAllowMaintainerEdit),
// v214 -> v215
NewMigration("Add auto merge table", v1_17.AddAutoMergeTable),
// v215 -> v216
NewMigration("allow to view files in PRs", v1_17.AddReviewViewedFiles),
// v216 -> v217
NewMigration("No-op (Improve Action table indices v1)", noopMigration),
// v217 -> v218
NewMigration("Alter hook_task table TEXT fields to LONGTEXT", v1_17.AlterHookTaskTextFieldsToLongText),
// v218 -> v219
NewMigration("Improve Action table indices v2", v1_17.ImproveActionTableIndices),
// v219 -> v220
NewMigration("Add sync_on_commit column to push_mirror table", v1_17.AddSyncOnCommitColForPushMirror),
// v220 -> v221
NewMigration("Add container repository property", v1_17.AddContainerRepositoryProperty),
// v221 -> v222
NewMigration("Store WebAuthentication CredentialID as bytes and increase size to at least 1024", v1_17.StoreWebauthnCredentialIDAsBytes),
// v222 -> v223
NewMigration("Drop old CredentialID column", v1_17.DropOldCredentialIDColumn),
// v223 -> v224
NewMigration("Rename CredentialIDBytes column to CredentialID", v1_17.RenameCredentialIDBytes),
newMigration(211, "Create ForeignReference table", v1_17.CreateForeignReferenceTable),
newMigration(212, "Add package tables", v1_17.AddPackageTables),
newMigration(213, "Add allow edits from maintainers to PullRequest table", v1_17.AddAllowMaintainerEdit),
newMigration(214, "Add auto merge table", v1_17.AddAutoMergeTable),
newMigration(215, "allow to view files in PRs", v1_17.AddReviewViewedFiles),
newMigration(216, "No-op (Improve Action table indices v1)", noopMigration),
newMigration(217, "Alter hook_task table TEXT fields to LONGTEXT", v1_17.AlterHookTaskTextFieldsToLongText),
newMigration(218, "Improve Action table indices v2", v1_17.ImproveActionTableIndices),
newMigration(219, "Add sync_on_commit column to push_mirror table", v1_17.AddSyncOnCommitColForPushMirror),
newMigration(220, "Add container repository property", v1_17.AddContainerRepositoryProperty),
newMigration(221, "Store WebAuthentication CredentialID as bytes and increase size to at least 1024", v1_17.StoreWebauthnCredentialIDAsBytes),
newMigration(222, "Drop old CredentialID column", v1_17.DropOldCredentialIDColumn),
newMigration(223, "Rename CredentialIDBytes column to CredentialID", v1_17.RenameCredentialIDBytes),
// Gitea 1.17.0 ends at v224
// Gitea 1.17.0 ends at database version 224
// v224 -> v225
NewMigration("Add badges to users", v1_18.CreateUserBadgesTable),
// v225 -> v226
NewMigration("Alter gpg_key/public_key content TEXT fields to MEDIUMTEXT", v1_18.AlterPublicGPGKeyContentFieldsToMediumText),
// v226 -> v227
NewMigration("Conan and generic packages do not need to be semantically versioned", v1_18.FixPackageSemverField),
// v227 -> v228
NewMigration("Create key/value table for system settings", v1_18.CreateSystemSettingsTable),
// v228 -> v229
NewMigration("Add TeamInvite table", v1_18.AddTeamInviteTable),
// v229 -> v230
NewMigration("Update counts of all open milestones", v1_18.UpdateOpenMilestoneCounts),
// v230 -> v231
NewMigration("Add ConfidentialClient column (default true) to OAuth2Application table", v1_18.AddConfidentialClientColumnToOAuth2ApplicationTable),
newMigration(224, "Add badges to users", v1_18.CreateUserBadgesTable),
newMigration(225, "Alter gpg_key/public_key content TEXT fields to MEDIUMTEXT", v1_18.AlterPublicGPGKeyContentFieldsToMediumText),
newMigration(226, "Conan and generic packages do not need to be semantically versioned", v1_18.FixPackageSemverField),
newMigration(227, "Create key/value table for system settings", v1_18.CreateSystemSettingsTable),
newMigration(228, "Add TeamInvite table", v1_18.AddTeamInviteTable),
newMigration(229, "Update counts of all open milestones", v1_18.UpdateOpenMilestoneCounts),
newMigration(230, "Add ConfidentialClient column (default true) to OAuth2Application table", v1_18.AddConfidentialClientColumnToOAuth2ApplicationTable),
// Gitea 1.18.0 ends at v231
// Gitea 1.18.0 ends at database version 231
// v231 -> v232
NewMigration("Add index for hook_task", v1_19.AddIndexForHookTask),
// v232 -> v233
NewMigration("Alter package_version.metadata_json to LONGTEXT", v1_19.AlterPackageVersionMetadataToLongText),
// v233 -> v234
NewMigration("Add header_authorization_encrypted column to webhook table", v1_19.AddHeaderAuthorizationEncryptedColWebhook),
// v234 -> v235
NewMigration("Add package cleanup rule table", v1_19.CreatePackageCleanupRuleTable),
// v235 -> v236
NewMigration("Add index for access_token", v1_19.AddIndexForAccessToken),
// v236 -> v237
NewMigration("Create secrets table", v1_19.CreateSecretsTable),
// v237 -> v238
NewMigration("Drop ForeignReference table", v1_19.DropForeignReferenceTable),
// v238 -> v239
NewMigration("Add updated unix to LFSMetaObject", v1_19.AddUpdatedUnixToLFSMetaObject),
// v239 -> v240
NewMigration("Add scope for access_token", v1_19.AddScopeForAccessTokens),
// v240 -> v241
NewMigration("Add actions tables", v1_19.AddActionsTables),
// v241 -> v242
NewMigration("Add card_type column to project table", v1_19.AddCardTypeToProjectTable),
// v242 -> v243
NewMigration("Alter gpg_key_import content TEXT field to MEDIUMTEXT", v1_19.AlterPublicGPGKeyImportContentFieldToMediumText),
// v243 -> v244
NewMigration("Add exclusive label", v1_19.AddExclusiveLabel),
newMigration(231, "Add index for hook_task", v1_19.AddIndexForHookTask),
newMigration(232, "Alter package_version.metadata_json to LONGTEXT", v1_19.AlterPackageVersionMetadataToLongText),
newMigration(233, "Add header_authorization_encrypted column to webhook table", v1_19.AddHeaderAuthorizationEncryptedColWebhook),
newMigration(234, "Add package cleanup rule table", v1_19.CreatePackageCleanupRuleTable),
newMigration(235, "Add index for access_token", v1_19.AddIndexForAccessToken),
newMigration(236, "Create secrets table", v1_19.CreateSecretsTable),
newMigration(237, "Drop ForeignReference table", v1_19.DropForeignReferenceTable),
newMigration(238, "Add updated unix to LFSMetaObject", v1_19.AddUpdatedUnixToLFSMetaObject),
newMigration(239, "Add scope for access_token", v1_19.AddScopeForAccessTokens),
newMigration(240, "Add actions tables", v1_19.AddActionsTables),
newMigration(241, "Add card_type column to project table", v1_19.AddCardTypeToProjectTable),
newMigration(242, "Alter gpg_key_import content TEXT field to MEDIUMTEXT", v1_19.AlterPublicGPGKeyImportContentFieldToMediumText),
newMigration(243, "Add exclusive label", v1_19.AddExclusiveLabel),
// Gitea 1.19.0 ends at v244
// Gitea 1.19.0 ends at database version 244
// v244 -> v245
NewMigration("Add NeedApproval to actions tables", v1_20.AddNeedApprovalToActionRun),
// v245 -> v246
NewMigration("Rename Webhook org_id to owner_id", v1_20.RenameWebhookOrgToOwner),
// v246 -> v247
NewMigration("Add missed column owner_id for project table", v1_20.AddNewColumnForProject),
// v247 -> v248
NewMigration("Fix incorrect project type", v1_20.FixIncorrectProjectType),
// v248 -> v249
NewMigration("Add version column to action_runner table", v1_20.AddVersionToActionRunner),
// v249 -> v250
NewMigration("Improve Action table indices v3", v1_20.ImproveActionTableIndices),
// v250 -> v251
NewMigration("Change Container Metadata", v1_20.ChangeContainerMetadataMultiArch),
// v251 -> v252
NewMigration("Fix incorrect owner team unit access mode", v1_20.FixIncorrectOwnerTeamUnitAccessMode),
// v252 -> v253
NewMigration("Fix incorrect admin team unit access mode", v1_20.FixIncorrectAdminTeamUnitAccessMode),
// v253 -> v254
NewMigration("Fix ExternalTracker and ExternalWiki accessMode in owner and admin team", v1_20.FixExternalTrackerAndExternalWikiAccessModeInOwnerAndAdminTeam),
// v254 -> v255
NewMigration("Add ActionTaskOutput table", v1_20.AddActionTaskOutputTable),
// v255 -> v256
NewMigration("Add ArchivedUnix Column", v1_20.AddArchivedUnixToRepository),
// v256 -> v257
NewMigration("Add is_internal column to package", v1_20.AddIsInternalColumnToPackage),
// v257 -> v258
NewMigration("Add Actions Artifact table", v1_20.CreateActionArtifactTable),
// v258 -> v259
NewMigration("Add PinOrder Column", v1_20.AddPinOrderToIssue),
// v259 -> v260
NewMigration("Convert scoped access tokens", v1_20.ConvertScopedAccessTokens),
newMigration(244, "Add NeedApproval to actions tables", v1_20.AddNeedApprovalToActionRun),
newMigration(245, "Rename Webhook org_id to owner_id", v1_20.RenameWebhookOrgToOwner),
newMigration(246, "Add missed column owner_id for project table", v1_20.AddNewColumnForProject),
newMigration(247, "Fix incorrect project type", v1_20.FixIncorrectProjectType),
newMigration(248, "Add version column to action_runner table", v1_20.AddVersionToActionRunner),
newMigration(249, "Improve Action table indices v3", v1_20.ImproveActionTableIndices),
newMigration(250, "Change Container Metadata", v1_20.ChangeContainerMetadataMultiArch),
newMigration(251, "Fix incorrect owner team unit access mode", v1_20.FixIncorrectOwnerTeamUnitAccessMode),
newMigration(252, "Fix incorrect admin team unit access mode", v1_20.FixIncorrectAdminTeamUnitAccessMode),
newMigration(253, "Fix ExternalTracker and ExternalWiki accessMode in owner and admin team", v1_20.FixExternalTrackerAndExternalWikiAccessModeInOwnerAndAdminTeam),
newMigration(254, "Add ActionTaskOutput table", v1_20.AddActionTaskOutputTable),
newMigration(255, "Add ArchivedUnix Column", v1_20.AddArchivedUnixToRepository),
newMigration(256, "Add is_internal column to package", v1_20.AddIsInternalColumnToPackage),
newMigration(257, "Add Actions Artifact table", v1_20.CreateActionArtifactTable),
newMigration(258, "Add PinOrder Column", v1_20.AddPinOrderToIssue),
newMigration(259, "Convert scoped access tokens", v1_20.ConvertScopedAccessTokens),
// Gitea 1.20.0 ends at v260
// Gitea 1.20.0 ends at database version 260
// v260 -> v261
NewMigration("Drop custom_labels column of action_runner table", v1_21.DropCustomLabelsColumnOfActionRunner),
// v261 -> v262
NewMigration("Add variable table", v1_21.CreateVariableTable),
// v262 -> v263
NewMigration("Add TriggerEvent to action_run table", v1_21.AddTriggerEventToActionRun),
// v263 -> v264
NewMigration("Add git_size and lfs_size columns to repository table", v1_21.AddGitSizeAndLFSSizeToRepositoryTable),
// v264 -> v265
NewMigration("Add branch table", v1_21.AddBranchTable),
// v265 -> v266
NewMigration("Alter Actions Artifact table", v1_21.AlterActionArtifactTable),
// v266 -> v267
NewMigration("Reduce commit status", v1_21.ReduceCommitStatus),
// v267 -> v268
NewMigration("Add action_tasks_version table", v1_21.CreateActionTasksVersionTable),
// v268 -> v269
NewMigration("Update Action Ref", v1_21.UpdateActionsRefIndex),
// v269 -> v270
NewMigration("Drop deleted branch table", v1_21.DropDeletedBranchTable),
// v270 -> v271
NewMigration("Fix PackageProperty typo", v1_21.FixPackagePropertyTypo),
// v271 -> v272
NewMigration("Allow archiving labels", v1_21.AddArchivedUnixColumInLabelTable),
// v272 -> v273
NewMigration("Add Version to ActionRun table", v1_21.AddVersionToActionRunTable),
// v273 -> v274
NewMigration("Add Action Schedule Table", v1_21.AddActionScheduleTable),
// v274 -> v275
NewMigration("Add Actions artifacts expiration date", v1_21.AddExpiredUnixColumnInActionArtifactTable),
// v275 -> v276
NewMigration("Add ScheduleID for ActionRun", v1_21.AddScheduleIDForActionRun),
// v276 -> v277
NewMigration("Add RemoteAddress to mirrors", v1_21.AddRemoteAddressToMirrors),
// v277 -> v278
NewMigration("Add Index to issue_user.issue_id", v1_21.AddIndexToIssueUserIssueID),
// v278 -> v279
NewMigration("Add Index to comment.dependent_issue_id", v1_21.AddIndexToCommentDependentIssueID),
// v279 -> v280
NewMigration("Add Index to action.user_id", v1_21.AddIndexToActionUserID),
newMigration(260, "Drop custom_labels column of action_runner table", v1_21.DropCustomLabelsColumnOfActionRunner),
newMigration(261, "Add variable table", v1_21.CreateVariableTable),
newMigration(262, "Add TriggerEvent to action_run table", v1_21.AddTriggerEventToActionRun),
newMigration(263, "Add git_size and lfs_size columns to repository table", v1_21.AddGitSizeAndLFSSizeToRepositoryTable),
newMigration(264, "Add branch table", v1_21.AddBranchTable),
newMigration(265, "Alter Actions Artifact table", v1_21.AlterActionArtifactTable),
newMigration(266, "Reduce commit status", v1_21.ReduceCommitStatus),
newMigration(267, "Add action_tasks_version table", v1_21.CreateActionTasksVersionTable),
newMigration(268, "Update Action Ref", v1_21.UpdateActionsRefIndex),
newMigration(269, "Drop deleted branch table", v1_21.DropDeletedBranchTable),
newMigration(270, "Fix PackageProperty typo", v1_21.FixPackagePropertyTypo),
newMigration(271, "Allow archiving labels", v1_21.AddArchivedUnixColumInLabelTable),
newMigration(272, "Add Version to ActionRun table", v1_21.AddVersionToActionRunTable),
newMigration(273, "Add Action Schedule Table", v1_21.AddActionScheduleTable),
newMigration(274, "Add Actions artifacts expiration date", v1_21.AddExpiredUnixColumnInActionArtifactTable),
newMigration(275, "Add ScheduleID for ActionRun", v1_21.AddScheduleIDForActionRun),
newMigration(276, "Add RemoteAddress to mirrors", v1_21.AddRemoteAddressToMirrors),
newMigration(277, "Add Index to issue_user.issue_id", v1_21.AddIndexToIssueUserIssueID),
newMigration(278, "Add Index to comment.dependent_issue_id", v1_21.AddIndexToCommentDependentIssueID),
newMigration(279, "Add Index to action.user_id", v1_21.AddIndexToActionUserID),
// Gitea 1.21.0 ends at 280
// Gitea 1.21.0 ends at database version 280
// v280 -> v281
NewMigration("Rename user themes", v1_22.RenameUserThemes),
// v281 -> v282
NewMigration("Add auth_token table", v1_22.CreateAuthTokenTable),
// v282 -> v283
NewMigration("Add Index to pull_auto_merge.doer_id", v1_22.AddIndexToPullAutoMergeDoerID),
// v283 -> v284
NewMigration("Add combined Index to issue_user.uid and issue_id", v1_22.AddCombinedIndexToIssueUser),
// v284 -> v285
NewMigration("Add ignore stale approval column on branch table", v1_22.AddIgnoreStaleApprovalsColumnToProtectedBranchTable),
// v285 -> v286
NewMigration("Add PreviousDuration to ActionRun", v1_22.AddPreviousDurationToActionRun),
// v286 -> v287
NewMigration("Add support for SHA256 git repositories", v1_22.AdjustDBForSha256),
// v287 -> v288
NewMigration("Use Slug instead of ID for Badges", v1_22.UseSlugInsteadOfIDForBadges),
// v288 -> v289
NewMigration("Add user_blocking table", v1_22.AddUserBlockingTable),
// v289 -> v290
NewMigration("Add default_wiki_branch to repository table", v1_22.AddDefaultWikiBranch),
// v290 -> v291
NewMigration("Add PayloadVersion to HookTask", v1_22.AddPayloadVersionToHookTaskTable),
// v291 -> v292
NewMigration("Add Index to attachment.comment_id", v1_22.AddCommentIDIndexofAttachment),
// v292 -> v293
NewMigration("Ensure every project has exactly one default column - No Op", noopMigration),
// v293 -> v294
NewMigration("Ensure every project has exactly one default column", v1_22.CheckProjectColumnsConsistency),
newMigration(280, "Rename user themes", v1_22.RenameUserThemes),
newMigration(281, "Add auth_token table", v1_22.CreateAuthTokenTable),
newMigration(282, "Add Index to pull_auto_merge.doer_id", v1_22.AddIndexToPullAutoMergeDoerID),
newMigration(283, "Add combined Index to issue_user.uid and issue_id", v1_22.AddCombinedIndexToIssueUser),
newMigration(284, "Add ignore stale approval column on branch table", v1_22.AddIgnoreStaleApprovalsColumnToProtectedBranchTable),
newMigration(285, "Add PreviousDuration to ActionRun", v1_22.AddPreviousDurationToActionRun),
newMigration(286, "Add support for SHA256 git repositories", v1_22.AdjustDBForSha256),
newMigration(287, "Use Slug instead of ID for Badges", v1_22.UseSlugInsteadOfIDForBadges),
newMigration(288, "Add user_blocking table", v1_22.AddUserBlockingTable),
newMigration(289, "Add default_wiki_branch to repository table", v1_22.AddDefaultWikiBranch),
newMigration(290, "Add PayloadVersion to HookTask", v1_22.AddPayloadVersionToHookTaskTable),
newMigration(291, "Add Index to attachment.comment_id", v1_22.AddCommentIDIndexofAttachment),
newMigration(292, "Ensure every project has exactly one default column - No Op", noopMigration),
newMigration(293, "Ensure every project has exactly one default column", v1_22.CheckProjectColumnsConsistency),
// Gitea 1.22.0-rc0 ends at 294
// Gitea 1.22.0-rc0 ends at database version 294
// v294 -> v295
NewMigration("Add unique index for project issue table", v1_22.AddUniqueIndexForProjectIssue),
// v295 -> v296
NewMigration("Add commit status summary table", v1_22.AddCommitStatusSummary),
// v296 -> v297
NewMigration("Add missing field of commit status summary table", v1_22.AddCommitStatusSummary2),
// v297 -> v298
NewMigration("Add everyone_access_mode for repo_unit", v1_22.AddRepoUnitEveryoneAccessMode),
// v298 -> v299
NewMigration("Drop wrongly created table o_auth2_application", v1_22.DropWronglyCreatedTable),
newMigration(294, "Add unique index for project issue table", v1_22.AddUniqueIndexForProjectIssue),
newMigration(295, "Add commit status summary table", v1_22.AddCommitStatusSummary),
newMigration(296, "Add missing field of commit status summary table", v1_22.AddCommitStatusSummary2),
newMigration(297, "Add everyone_access_mode for repo_unit", v1_22.AddRepoUnitEveryoneAccessMode),
newMigration(298, "Drop wrongly created table o_auth2_application", v1_22.DropWronglyCreatedTable),
// Gitea 1.22.0-rc1 ends at 299
// Gitea 1.22.0-rc1 ends at migration ID number 298 (database version 299)
// v299 -> v300
NewMigration("Add content version to issue and comment table", v1_23.AddContentVersionToIssueAndComment),
// v300 -> v301
NewMigration("Add force-push branch protection support", v1_23.AddForcePushBranchProtection),
// v301 -> v302
NewMigration("Add skip_secondary_authorization option to oauth2 application table", v1_23.AddSkipSecondaryAuthColumnToOAuth2ApplicationTable),
// v302 -> v303
NewMigration("Add index to action_task stopped log_expired", v1_23.AddIndexToActionTaskStoppedLogExpired),
// v303 -> v304
NewMigration("Add metadata column for comment table", v1_23.AddCommentMetaDataColumn),
// v304 -> v305
NewMigration("Add index for release sha1", v1_23.AddIndexForReleaseSha1),
// v305 -> v306
NewMigration("Add Repository Licenses", v1_23.AddRepositoryLicenses),
// v306 -> v307
NewMigration("Add BlockAdminMergeOverride to ProtectedBranch", v1_23.AddBlockAdminMergeOverrideBranchProtection),
newMigration(299, "Add content version to issue and comment table", v1_23.AddContentVersionToIssueAndComment),
newMigration(300, "Add force-push branch protection support", v1_23.AddForcePushBranchProtection),
newMigration(301, "Add skip_secondary_authorization option to oauth2 application table", v1_23.AddSkipSecondaryAuthColumnToOAuth2ApplicationTable),
newMigration(302, "Add index to action_task stopped log_expired", v1_23.AddIndexToActionTaskStoppedLogExpired),
newMigration(303, "Add metadata column for comment table", v1_23.AddCommentMetaDataColumn),
newMigration(304, "Add index for release sha1", v1_23.AddIndexForReleaseSha1),
newMigration(305, "Add Repository Licenses", v1_23.AddRepositoryLicenses),
newMigration(306, "Add BlockAdminMergeOverride to ProtectedBranch", v1_23.AddBlockAdminMergeOverrideBranchProtection),
newMigration(307, "Fix milestone deadline_unix when there is no due date", v1_23.FixMilestoneNoDueDate),
newMigration(308, "Add index(user_id, is_deleted) for action table", v1_23.AddNewIndexForUserDashboard),
}
return preparedMigrations
}
// GetCurrentDBVersion returns the current db version
@ -624,9 +387,20 @@ func GetCurrentDBVersion(x *xorm.Engine) (int64, error) {
return currentVersion.Version, nil
}
// ExpectedVersion returns the expected db version
func ExpectedVersion() int64 {
return int64(minDBVersion + len(migrations))
func calcDBVersion(migrations []*migration) int64 {
dbVer := int64(minDBVersion + len(migrations))
if migrations[0].idNumber != minDBVersion {
panic("migrations should start at minDBVersion")
}
if dbVer != migrations[len(migrations)-1].idNumber+1 {
panic("migrations are not in order")
}
return dbVer
}
// ExpectedDBVersion returns the expected db version
func ExpectedDBVersion() int64 {
return calcDBVersion(prepareMigrationTasks())
}
// EnsureUpToDate will check if the db is at the correct version
@ -637,24 +411,35 @@ func EnsureUpToDate(x *xorm.Engine) error {
}
if currentDB < 0 {
return fmt.Errorf("Database has not been initialized")
return fmt.Errorf("database has not been initialized")
}
if minDBVersion > currentDB {
return fmt.Errorf("DB version %d (<= %d) is too old for auto-migration. Upgrade to Gitea 1.6.4 first then upgrade to this version", currentDB, minDBVersion)
}
expected := ExpectedVersion()
expectedDB := ExpectedDBVersion()
if currentDB != expected {
return fmt.Errorf(`Current database version %d is not equal to the expected version %d. Please run "gitea [--config /path/to/app.ini] migrate" to update the database version`, currentDB, expected)
if currentDB != expectedDB {
return fmt.Errorf(`current database version %d is not equal to the expected version %d. Please run "gitea [--config /path/to/app.ini] migrate" to update the database version`, currentDB, expectedDB)
}
return nil
}
func getPendingMigrations(curDBVer int64, migrations []*migration) []*migration {
return migrations[curDBVer-minDBVersion:]
}
func migrationIDNumberToDBVersion(idNumber int64) int64 {
return idNumber + 1
}
// Migrate database to current version
func Migrate(x *xorm.Engine) error {
migrations := prepareMigrationTasks()
maxDBVer := calcDBVersion(migrations)
// Set a new clean the default mapper to GonicMapper as that is the default for Gitea.
x.SetMapper(names.GonicMapper{})
if err := x.Sync(new(Version)); err != nil {
@ -666,29 +451,29 @@ func Migrate(x *xorm.Engine) error {
if err != nil {
return fmt.Errorf("get: %w", err)
} else if !has {
// If the version record does not exist we think
// it is a fresh installation and we can skip all migrations.
// If the version record does not exist, it is a fresh installation, and we can skip all migrations.
// XORM model framework will create all tables when initializing.
currentVersion.ID = 0
currentVersion.Version = int64(minDBVersion + len(migrations))
currentVersion.Version = maxDBVer
if _, err = x.InsertOne(currentVersion); err != nil {
return fmt.Errorf("insert: %w", err)
}
}
v := currentVersion.Version
if minDBVersion > v {
curDBVer := currentVersion.Version
// Outdated Gitea database version is not supported
if curDBVer < minDBVersion {
log.Fatal(`Gitea no longer supports auto-migration from your previously installed version.
Please try upgrading to a lower version first (suggested v1.6.4), then upgrade to this version.`)
return nil
}
// Downgrading Gitea's database version not supported
if int(v-minDBVersion) > len(migrations) {
msg := fmt.Sprintf("Your database (migration version: %d) is for a newer Gitea, you can not use the newer database for this old Gitea release (%d).", v, minDBVersion+len(migrations))
if maxDBVer < curDBVer {
msg := fmt.Sprintf("Your database (migration version: %d) is for a newer Gitea, you can not use the newer database for this old Gitea release (%d).", curDBVer, maxDBVer)
msg += "\nGitea will exit to keep your database safe and unchanged. Please use the correct Gitea release, do not change the migration version manually (incorrect manual operation may lose data)."
if !setting.IsProd {
msg += fmt.Sprintf("\nIf you are in development and really know what you're doing, you can force changing the migration version by executing: UPDATE version SET version=%d WHERE id=1;", minDBVersion+len(migrations))
msg += fmt.Sprintf("\nIf you are in development and really know what you're doing, you can force changing the migration version by executing: UPDATE version SET version=%d WHERE id=1;", maxDBVer)
}
log.Fatal("Migration Error: %s", msg)
return nil
@ -702,14 +487,14 @@ Please try upgrading to a lower version first (suggested v1.6.4), then upgrade t
}
// Migrate
for i, m := range migrations[v-minDBVersion:] {
log.Info("Migration[%d]: %s", v+int64(i), m.Description())
for _, m := range getPendingMigrations(curDBVer, migrations) {
log.Info("Migration[%d]: %s", m.idNumber, m.description)
// Reset the mapper between each migration - migrations are not supposed to depend on each other
x.SetMapper(names.GonicMapper{})
if err = m.Migrate(x); err != nil {
return fmt.Errorf("migration[%d]: %s failed: %w", v+int64(i), m.Description(), err)
return fmt.Errorf("migration[%d]: %s failed: %w", m.idNumber, m.description, err)
}
currentVersion.Version = v + int64(i) + 1
currentVersion.Version = migrationIDNumberToDBVersion(m.idNumber)
if _, err = x.ID(1).Update(currentVersion); err != nil {
return err
}

View File

@ -0,0 +1,28 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package migrations
import (
"testing"
"code.gitea.io/gitea/modules/test"
"github.com/stretchr/testify/assert"
)
func TestMigrations(t *testing.T) {
defer test.MockVariableValue(&preparedMigrations)()
preparedMigrations = []*migration{
{idNumber: 70},
{idNumber: 71},
}
assert.EqualValues(t, 72, calcDBVersion(preparedMigrations))
assert.EqualValues(t, 72, ExpectedDBVersion())
assert.EqualValues(t, 71, migrationIDNumberToDBVersion(70))
assert.EqualValues(t, []*migration{{idNumber: 70}, {idNumber: 71}}, getPendingMigrations(70, preparedMigrations))
assert.EqualValues(t, []*migration{{idNumber: 71}}, getPendingMigrations(71, preparedMigrations))
assert.EqualValues(t, []*migration{}, getPendingMigrations(72, preparedMigrations))
}

View File

@ -12,6 +12,7 @@ import (
"code.gitea.io/gitea/modules/git"
giturl "code.gitea.io/gitea/modules/git/url"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
"xorm.io/xorm"
)
@ -163,7 +164,9 @@ func migratePushMirrors(x *xorm.Engine) error {
func getRemoteAddress(ownerName, repoName, remoteName string) (string, error) {
repoPath := filepath.Join(setting.RepoRootPath, strings.ToLower(ownerName), strings.ToLower(repoName)+".git")
if exist, _ := util.IsExist(repoPath); !exist {
return "", nil
}
remoteURL, err := git.GetRemoteAddress(context.Background(), repoPath, remoteName)
if err != nil {
return "", fmt.Errorf("get remote %s's address of %s/%s failed: %v", remoteName, ownerName, repoName, err)

View File

@ -0,0 +1,21 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package v1_23 //nolint
import (
"code.gitea.io/gitea/modules/timeutil"
"xorm.io/xorm"
)
func FixMilestoneNoDueDate(x *xorm.Engine) error {
type Milestone struct {
DeadlineUnix timeutil.TimeStamp
}
// Wednesday, December 1, 9999 12:00:00 AM GMT+00:00
_, err := x.Table("milestone").Where("deadline_unix > 253399622400").
Cols("deadline_unix").
Update(&Milestone{DeadlineUnix: 0})
return err
}

View File

@ -0,0 +1,52 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package v1_23 //nolint
import (
"code.gitea.io/gitea/modules/timeutil"
"xorm.io/xorm"
"xorm.io/xorm/schemas"
)
type improveActionTableIndicesAction struct {
ID int64 `xorm:"pk autoincr"`
UserID int64 `xorm:"INDEX"` // Receiver user id.
OpType int
ActUserID int64 // Action user id.
RepoID int64
CommentID int64 `xorm:"INDEX"`
IsDeleted bool `xorm:"NOT NULL DEFAULT false"`
RefName string
IsPrivate bool `xorm:"NOT NULL DEFAULT false"`
Content string `xorm:"TEXT"`
CreatedUnix timeutil.TimeStamp `xorm:"created"`
}
// TableName sets the name of this table
func (*improveActionTableIndicesAction) TableName() string {
return "action"
}
func (a *improveActionTableIndicesAction) TableIndices() []*schemas.Index {
repoIndex := schemas.NewIndex("r_u_d", schemas.IndexType)
repoIndex.AddColumn("repo_id", "user_id", "is_deleted")
actUserIndex := schemas.NewIndex("au_r_c_u_d", schemas.IndexType)
actUserIndex.AddColumn("act_user_id", "repo_id", "created_unix", "user_id", "is_deleted")
cudIndex := schemas.NewIndex("c_u_d", schemas.IndexType)
cudIndex.AddColumn("created_unix", "user_id", "is_deleted")
cuIndex := schemas.NewIndex("c_u", schemas.IndexType)
cuIndex.AddColumn("user_id", "is_deleted")
indices := []*schemas.Index{actUserIndex, repoIndex, cudIndex, cuIndex}
return indices
}
func AddNewIndexForUserDashboard(x *xorm.Engine) error {
return x.Sync(new(improveActionTableIndicesAction))
}

View File

@ -22,6 +22,7 @@ import (
"code.gitea.io/gitea/modules/util"
"xorm.io/builder"
"xorm.io/xorm"
)
// ________ .__ __ .__
@ -141,8 +142,9 @@ func (org *Organization) LoadTeams(ctx context.Context) ([]*Team, error) {
}
// GetMembers returns all members of organization.
func (org *Organization) GetMembers(ctx context.Context) (user_model.UserList, map[int64]bool, error) {
func (org *Organization) GetMembers(ctx context.Context, doer *user_model.User) (user_model.UserList, map[int64]bool, error) {
return FindOrgMembers(ctx, &FindOrgMembersOpts{
Doer: doer,
OrgID: org.ID,
})
}
@ -195,16 +197,39 @@ func (org *Organization) CanCreateRepo() bool {
// FindOrgMembersOpts represensts find org members conditions
type FindOrgMembersOpts struct {
db.ListOptions
Doer *user_model.User
IsDoerMember bool
OrgID int64
PublicOnly bool
}
func (opts FindOrgMembersOpts) PublicOnly() bool {
return opts.Doer == nil || !(opts.IsDoerMember || opts.Doer.IsAdmin)
}
// applyTeamMatesOnlyFilter make sure restricted users only see public team members and there own team mates
func (opts FindOrgMembersOpts) applyTeamMatesOnlyFilter(sess *xorm.Session) {
if opts.Doer != nil && opts.IsDoerMember && opts.Doer.IsRestricted {
teamMates := builder.Select("DISTINCT team_user.uid").
From("team_user").
Where(builder.In("team_user.team_id", getUserTeamIDsQueryBuilder(opts.OrgID, opts.Doer.ID))).
And(builder.Eq{"team_user.org_id": opts.OrgID})
sess.And(
builder.In("org_user.uid", teamMates).
Or(builder.Eq{"org_user.is_public": true}),
)
}
}
// CountOrgMembers counts the organization's members
func CountOrgMembers(ctx context.Context, opts *FindOrgMembersOpts) (int64, error) {
sess := db.GetEngine(ctx).Where("org_id=?", opts.OrgID)
if opts.PublicOnly {
sess.And("is_public = ?", true)
if opts.PublicOnly() {
sess = sess.And("is_public = ?", true)
} else {
opts.applyTeamMatesOnlyFilter(sess)
}
return sess.Count(new(OrgUser))
}
@ -525,9 +550,12 @@ func GetOrgsCanCreateRepoByUserID(ctx context.Context, userID int64) ([]*Organiz
// GetOrgUsersByOrgID returns all organization-user relations by organization ID.
func GetOrgUsersByOrgID(ctx context.Context, opts *FindOrgMembersOpts) ([]*OrgUser, error) {
sess := db.GetEngine(ctx).Where("org_id=?", opts.OrgID)
if opts.PublicOnly {
sess.And("is_public = ?", true)
if opts.PublicOnly() {
sess = sess.And("is_public = ?", true)
} else {
opts.applyTeamMatesOnlyFilter(sess)
}
if opts.ListOptions.PageSize > 0 {
sess = db.SetSessionPagination(sess, opts)
@ -656,6 +684,15 @@ func (org *Organization) getUserTeamIDs(ctx context.Context, userID int64) ([]in
Find(&teamIDs)
}
func getUserTeamIDsQueryBuilder(orgID, userID int64) *builder.Builder {
return builder.Select("team.id").From("team").
InnerJoin("team_user", "team_user.team_id = team.id").
Where(builder.Eq{
"team_user.org_id": orgID,
"team_user.uid": userID,
})
}
// TeamsWithAccessToRepo returns all teams that have given access level to the repository.
func (org *Organization) TeamsWithAccessToRepo(ctx context.Context, repoID int64, mode perm.AccessMode) ([]*Team, error) {
return GetTeamsWithAccessToRepo(ctx, org.ID, repoID, mode)

View File

@ -4,6 +4,8 @@
package organization_test
import (
"slices"
"sort"
"testing"
"code.gitea.io/gitea/models/db"
@ -103,7 +105,7 @@ func TestUser_GetTeams(t *testing.T) {
func TestUser_GetMembers(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
org := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 3})
members, _, err := org.GetMembers(db.DefaultContext)
members, _, err := org.GetMembers(db.DefaultContext, &user_model.User{IsAdmin: true})
assert.NoError(t, err)
if assert.Len(t, members, 3) {
assert.Equal(t, int64(2), members[0].ID)
@ -180,6 +182,75 @@ func TestIsPublicMembership(t *testing.T) {
test(unittest.NonexistentID, unittest.NonexistentID, false)
}
func TestRestrictedUserOrgMembers(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
restrictedUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{
ID: 29,
IsRestricted: true,
})
if !assert.True(t, restrictedUser.IsRestricted) {
return // ensure fixtures return restricted user
}
testCases := []struct {
name string
opts *organization.FindOrgMembersOpts
expectedUIDs []int64
}{
{
name: "restricted user sees public members and teammates",
opts: &organization.FindOrgMembersOpts{
OrgID: 17, // org17 where user29 is in team9
Doer: restrictedUser,
IsDoerMember: true,
},
expectedUIDs: []int64{2, 15, 20, 29}, // Public members (2) + teammates in team9 (15, 20, 29)
},
{
name: "restricted user sees only public members when not member",
opts: &organization.FindOrgMembersOpts{
OrgID: 3, // org3 where user29 is not a member
Doer: restrictedUser,
},
expectedUIDs: []int64{2, 28}, // Only public members
},
{
name: "non logged in only shows public members",
opts: &organization.FindOrgMembersOpts{
OrgID: 3,
},
expectedUIDs: []int64{2, 28}, // Only public members
},
{
name: "non restricted user sees all members",
opts: &organization.FindOrgMembersOpts{
OrgID: 17,
Doer: unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 15}),
IsDoerMember: true,
},
expectedUIDs: []int64{2, 15, 18, 20, 29}, // All members
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
count, err := organization.CountOrgMembers(db.DefaultContext, tc.opts)
assert.NoError(t, err)
assert.EqualValues(t, len(tc.expectedUIDs), count)
members, err := organization.GetOrgUsersByOrgID(db.DefaultContext, tc.opts)
assert.NoError(t, err)
memberUIDs := make([]int64, 0, len(members))
for _, member := range members {
memberUIDs = append(memberUIDs, member.UID)
}
slices.Sort(memberUIDs)
assert.EqualValues(t, tc.expectedUIDs, memberUIDs)
})
}
}
func TestFindOrgs(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
@ -210,37 +281,42 @@ func TestFindOrgs(t *testing.T) {
func TestGetOrgUsersByOrgID(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
orgUsers, err := organization.GetOrgUsersByOrgID(db.DefaultContext, &organization.FindOrgMembersOpts{
ListOptions: db.ListOptions{},
opts := &organization.FindOrgMembersOpts{
Doer: &user_model.User{IsAdmin: true},
OrgID: 3,
PublicOnly: false,
})
}
assert.False(t, opts.PublicOnly())
orgUsers, err := organization.GetOrgUsersByOrgID(db.DefaultContext, opts)
assert.NoError(t, err)
if assert.Len(t, orgUsers, 3) {
assert.Equal(t, organization.OrgUser{
ID: orgUsers[0].ID,
sort.Slice(orgUsers, func(i, j int) bool {
return orgUsers[i].ID < orgUsers[j].ID
})
assert.EqualValues(t, []*organization.OrgUser{{
ID: 1,
OrgID: 3,
UID: 2,
IsPublic: true,
}, *orgUsers[0])
assert.Equal(t, organization.OrgUser{
ID: orgUsers[1].ID,
}, {
ID: 2,
OrgID: 3,
UID: 4,
IsPublic: false,
}, *orgUsers[1])
assert.Equal(t, organization.OrgUser{
ID: orgUsers[2].ID,
}, {
ID: 9,
OrgID: 3,
UID: 28,
IsPublic: true,
}, *orgUsers[2])
}
}}, orgUsers)
opts = &organization.FindOrgMembersOpts{OrgID: 3}
assert.True(t, opts.PublicOnly())
orgUsers, err = organization.GetOrgUsersByOrgID(db.DefaultContext, opts)
assert.NoError(t, err)
assert.Len(t, orgUsers, 2)
orgUsers, err = organization.GetOrgUsersByOrgID(db.DefaultContext, &organization.FindOrgMembersOpts{
ListOptions: db.ListOptions{},
OrgID: unittest.NonexistentID,
PublicOnly: false,
})
assert.NoError(t, err)
assert.Len(t, orgUsers, 0)

View File

@ -94,7 +94,7 @@ func TestUserListIsPublicMember(t *testing.T) {
func testUserListIsPublicMember(t *testing.T, orgID int64, expected map[int64]bool) {
org, err := organization.GetOrgByID(db.DefaultContext, orgID)
assert.NoError(t, err)
_, membersIsPublic, err := org.GetMembers(db.DefaultContext)
_, membersIsPublic, err := org.GetMembers(db.DefaultContext, &user_model.User{IsAdmin: true})
assert.NoError(t, err)
assert.Equal(t, expected, membersIsPublic)
}
@ -121,7 +121,7 @@ func TestUserListIsUserOrgOwner(t *testing.T) {
func testUserListIsUserOrgOwner(t *testing.T, orgID int64, expected map[int64]bool) {
org, err := organization.GetOrgByID(db.DefaultContext, orgID)
assert.NoError(t, err)
members, _, err := org.GetMembers(db.DefaultContext)
members, _, err := org.GetMembers(db.DefaultContext, &user_model.User{IsAdmin: true})
assert.NoError(t, err)
assert.Equal(t, expected, organization.IsUserOrgOwner(db.DefaultContext, members, orgID))
}

View File

@ -75,26 +75,27 @@ func ExistPackages(ctx context.Context, opts *PackageSearchOptions) (bool, error
}
// SearchPackages gets the packages matching the search options
func SearchPackages(ctx context.Context, opts *PackageSearchOptions, iter func(*packages.PackageFileDescriptor)) error {
return db.GetEngine(ctx).
func SearchPackages(ctx context.Context, opts *PackageSearchOptions) ([]*packages.PackageFileDescriptor, error) {
var pkgFiles []*packages.PackageFile
err := db.GetEngine(ctx).
Table("package_file").
Select("package_file.*").
Join("INNER", "package_version", "package_version.id = package_file.version_id").
Join("INNER", "package", "package.id = package_version.package_id").
Where(opts.toCond()).
Asc("package.lower_name", "package_version.created_unix").
Iterate(new(packages.PackageFile), func(_ int, bean any) error {
pf := bean.(*packages.PackageFile)
Asc("package.lower_name", "package_version.created_unix").Find(&pkgFiles)
if err != nil {
return nil, err
}
pfds := make([]*packages.PackageFileDescriptor, 0, len(pkgFiles))
for _, pf := range pkgFiles {
pfd, err := packages.GetPackageFileDescriptor(ctx, pf)
if err != nil {
return err
return nil, err
}
iter(pfd)
return nil
})
pfds = append(pfds, pfd)
}
return pfds, nil
}
// GetDistributions gets all available distributions

View File

@ -60,3 +60,6 @@ func ParseAccessMode(permission string, allowed ...AccessMode) AccessMode {
}
return util.Iif(slices.Contains(allowed, m), m, AccessModeNone)
}
// ErrInvalidAccessMode is returned when an invalid access mode is used
var ErrInvalidAccessMode = util.NewInvalidArgumentErrorf("Invalid access mode")

View File

@ -110,6 +110,10 @@ func GetRepoAssignees(ctx context.Context, repo *Repository) (_ []*user_model.Us
return nil, err
}
uniqueUserIDs := make(container.Set[int64])
uniqueUserIDs.AddMultiple(userIDs...)
if repo.Owner.IsOrganization() {
additionalUserIDs := make([]int64, 0, 10)
if err = e.Table("team_user").
Join("INNER", "team_repo", "`team_repo`.team_id = `team_user`.team_id").
@ -121,15 +125,13 @@ func GetRepoAssignees(ctx context.Context, repo *Repository) (_ []*user_model.Us
Find(&additionalUserIDs); err != nil {
return nil, err
}
uniqueUserIDs := make(container.Set[int64])
uniqueUserIDs.AddMultiple(userIDs...)
uniqueUserIDs.AddMultiple(additionalUserIDs...)
}
// Leave a seat for owner itself to append later, but if owner is an organization
// and just waste 1 unit is cheaper than re-allocate memory once.
users := make([]*user_model.User, 0, len(uniqueUserIDs)+1)
if len(userIDs) > 0 {
if len(uniqueUserIDs) > 0 {
if err = e.In("id", uniqueUserIDs.Values()).
Where(builder.Eq{"`user`.is_active": true}).
OrderBy(user_model.GetOrderByName()).

View File

@ -80,6 +80,27 @@ var (
TypePullRequests,
}
// DefaultMirrorRepoUnits contains the default unit types for mirrors
DefaultMirrorRepoUnits = []Type{
TypeCode,
TypeIssues,
TypeReleases,
TypeWiki,
TypeProjects,
TypePackages,
}
// DefaultTemplateRepoUnits contains the default unit types for templates
DefaultTemplateRepoUnits = []Type{
TypeCode,
TypeIssues,
TypePullRequests,
TypeReleases,
TypeWiki,
TypeProjects,
TypePackages,
}
// NotAllowedDefaultRepoUnits contains units that can't be default
NotAllowedDefaultRepoUnits = []Type{
TypeExternalWiki,
@ -147,6 +168,7 @@ func LoadUnitConfig() error {
if len(DefaultRepoUnits) == 0 {
return errors.New("no default repository units found")
}
// default fork repo units
setDefaultForkRepoUnits, invalidKeys := FindUnitTypes(setting.Repository.DefaultForkRepoUnits...)
if len(invalidKeys) > 0 {
log.Warn("Invalid keys in default fork repo units: %s", strings.Join(invalidKeys, ", "))
@ -155,6 +177,24 @@ func LoadUnitConfig() error {
if len(DefaultForkRepoUnits) == 0 {
return errors.New("no default fork repository units found")
}
// default mirror repo units
setDefaultMirrorRepoUnits, invalidKeys := FindUnitTypes(setting.Repository.DefaultMirrorRepoUnits...)
if len(invalidKeys) > 0 {
log.Warn("Invalid keys in default mirror repo units: %s", strings.Join(invalidKeys, ", "))
}
DefaultMirrorRepoUnits = validateDefaultRepoUnits(DefaultMirrorRepoUnits, setDefaultMirrorRepoUnits)
if len(DefaultMirrorRepoUnits) == 0 {
return errors.New("no default mirror repository units found")
}
// default template repo units
setDefaultTemplateRepoUnits, invalidKeys := FindUnitTypes(setting.Repository.DefaultTemplateRepoUnits...)
if len(invalidKeys) > 0 {
log.Warn("Invalid keys in default template repo units: %s", strings.Join(invalidKeys, ", "))
}
DefaultTemplateRepoUnits = validateDefaultRepoUnits(DefaultTemplateRepoUnits, setDefaultTemplateRepoUnits)
if len(DefaultTemplateRepoUnits) == 0 {
return errors.New("no default template repository units found")
}
return nil
}

View File

@ -25,7 +25,7 @@ func GetXORMEngine(engine ...*xorm.Engine) (x *xorm.Engine) {
if len(engine) == 1 {
return engine[0]
}
return db.DefaultContext.(*db.Context).Engine().(*xorm.Engine)
return db.GetEngine(db.DefaultContext).(*xorm.Engine)
}
// InitFixtures initialize test fixtures for a test database

View File

@ -147,6 +147,9 @@ func StringsToInt64s(strs []string) ([]int64, error) {
}
ints := make([]int64, 0, len(strs))
for _, s := range strs {
if s == "" {
continue
}
n, err := strconv.ParseInt(s, 10, 64)
if err != nil {
return nil, err

View File

@ -152,6 +152,7 @@ func TestStringsToInt64s(t *testing.T) {
}
testSuccess(nil, nil)
testSuccess([]string{}, []int64{})
testSuccess([]string{""}, []int64{})
testSuccess([]string{"-1234"}, []int64{-1234})
testSuccess([]string{"1", "4", "16", "64", "256"}, []int64{1, 4, 16, 64, 256})

View File

@ -40,14 +40,12 @@ func TestMaybeRemoveBOM(t *testing.T) {
func TestToUTF8(t *testing.T) {
resetDefaultCharsetsOrder()
var res string
var err error
// Note: golang compiler seems so behave differently depending on the current
// locale, so some conversions might behave differently. For that reason, we don't
// depend on particular conversions but in expected behaviors.
res, err = ToUTF8([]byte{0x41, 0x42, 0x43}, ConvertOpts{})
res, err := ToUTF8([]byte{0x41, 0x42, 0x43}, ConvertOpts{})
assert.NoError(t, err)
assert.Equal(t, "ABC", res)

View File

@ -31,8 +31,8 @@ func (s Set[T]) AddMultiple(values ...T) {
}
}
// Contains determines whether a set contains the specified elements.
// Returns true if the set contains the specified element; otherwise, false.
// Contains determines whether a set contains all these elements.
// Returns true if the set contains all these elements; otherwise, false.
func (s Set[T]) Contains(values ...T) bool {
ret := true
for _, value := range values {

View File

@ -18,7 +18,9 @@ func TestSet(t *testing.T) {
assert.True(t, s.Contains("key1"))
assert.True(t, s.Contains("key2"))
assert.True(t, s.Contains("key1", "key2"))
assert.False(t, s.Contains("key3"))
assert.False(t, s.Contains("key1", "key3"))
assert.True(t, s.Remove("key2"))
assert.False(t, s.Contains("key2"))

View File

@ -146,9 +146,8 @@ func catFileBatch(ctx context.Context, repoPath string) (WriteCloserError, *bufi
}
// ReadBatchLine reads the header line from cat-file --batch
// We expect:
// <sha> SP <type> SP <size> LF
// sha is a hex encoded here
// We expect: <oid> SP <type> SP <size> LF
// then leaving the rest of the stream "<contents> LF" to be read
func ReadBatchLine(rd *bufio.Reader) (sha []byte, typ string, size int64, err error) {
typ, err = rd.ReadString('\n')
if err != nil {

View File

@ -4,28 +4,14 @@
package git
import (
"context"
"errors"
"fmt"
"strings"
"time"
"code.gitea.io/gitea/modules/util"
)
// ErrExecTimeout error when exec timed out
type ErrExecTimeout struct {
Duration time.Duration
}
// IsErrExecTimeout if some error is ErrExecTimeout
func IsErrExecTimeout(err error) bool {
_, ok := err.(ErrExecTimeout)
return ok
}
func (err ErrExecTimeout) Error() string {
return fmt.Sprintf("execution is timeout [duration: %v]", err.Duration)
}
// ErrNotExist commit not exist error
type ErrNotExist struct {
ID string
@ -62,21 +48,6 @@ func IsErrBadLink(err error) bool {
return ok
}
// ErrUnsupportedVersion error when required git version not matched
type ErrUnsupportedVersion struct {
Required string
}
// IsErrUnsupportedVersion if some error is ErrUnsupportedVersion
func IsErrUnsupportedVersion(err error) bool {
_, ok := err.(ErrUnsupportedVersion)
return ok
}
func (err ErrUnsupportedVersion) Error() string {
return fmt.Sprintf("Operation requires higher version [required: %s]", err.Required)
}
// ErrBranchNotExist represents a "BranchNotExist" kind of error.
type ErrBranchNotExist struct {
Name string
@ -185,3 +156,10 @@ func IsErrMoreThanOne(err error) bool {
func (err *ErrMoreThanOne) Error() string {
return fmt.Sprintf("ErrMoreThanOne Error: %v: %s\n%s", err.Err, err.StdErr, err.StdOut)
}
func IsErrCanceledOrKilled(err error) bool {
// When "cancel()" a git command's context, the returned error of "Run()" could be one of them:
// - context.Canceled
// - *exec.ExitError: "signal: killed"
return err != nil && (errors.Is(err, context.Canceled) || err.Error() == "signal: killed")
}

View File

@ -166,9 +166,7 @@ func (c *CheckAttributeReader) Run() error {
Stdout: c.stdOut,
Stderr: stdErr,
})
if err != nil && // If there is an error we need to return but:
c.ctx.Err() != err && // 1. Ignore the context error if the context is cancelled or exceeds the deadline (RunWithContext could return c.ctx.Err() which is Canceled or DeadlineExceeded)
err.Error() != "signal: killed" { // 2. We should not pass up errors due to the program being killed
if err != nil && !IsErrCanceledOrKilled(err) {
return fmt.Errorf("failed to run attr-check. Error: %w\nStderr: %s", err, stdErr.String())
}
return nil

View File

@ -50,25 +50,35 @@ func (repo *Repository) readTreeToIndex(id ObjectID, indexFilename ...string) er
}
// ReadTreeToTemporaryIndex reads a treeish to a temporary index file
func (repo *Repository) ReadTreeToTemporaryIndex(treeish string) (filename, tmpDir string, cancel context.CancelFunc, err error) {
tmpDir, err = os.MkdirTemp("", "index")
if err != nil {
return filename, tmpDir, cancel, err
func (repo *Repository) ReadTreeToTemporaryIndex(treeish string) (tmpIndexFilename, tmpDir string, cancel context.CancelFunc, err error) {
defer func() {
// if error happens and there is a cancel function, do clean up
if err != nil && cancel != nil {
cancel()
cancel = nil
}
}()
removeDirFn := func(dir string) func() { // it can't use the return value "tmpDir" directly because it is empty when error occurs
return func() {
if err := util.RemoveAll(dir); err != nil {
log.Error("failed to remove tmp index dir: %v", err)
}
}
}
filename = filepath.Join(tmpDir, ".tmp-index")
cancel = func() {
err := util.RemoveAll(tmpDir)
tmpDir, err = os.MkdirTemp("", "index")
if err != nil {
log.Error("failed to remove tmp index file: %v", err)
return "", "", nil, err
}
}
err = repo.ReadTreeToIndex(treeish, filename)
tmpIndexFilename = filepath.Join(tmpDir, ".tmp-index")
cancel = removeDirFn(tmpDir)
err = repo.ReadTreeToIndex(treeish, tmpIndexFilename)
if err != nil {
defer cancel()
return "", "", func() {}, err
return "", "", cancel, err
}
return filename, tmpDir, cancel, err
return tmpIndexFilename, tmpDir, cancel, err
}
// EmptyIndex empties the index

View File

@ -32,7 +32,7 @@ func GetCommitGraph(r *git.Repository, page, maxAllowedColors int, hidePRRefs bo
graphCmd.AddArguments("--all")
}
graphCmd.AddArguments("-C", "-M", "--date=iso").
graphCmd.AddArguments("-C", "-M", "--date=iso-strict").
AddOptionFormat("-n %d", setting.UI.GraphMaxCommitNum*page).
AddOptionFormat("--pretty=format:%s", format)

View File

@ -8,6 +8,7 @@ import (
"context"
"fmt"
"strings"
"time"
asymkey_model "code.gitea.io/gitea/models/asymkey"
"code.gitea.io/gitea/models/db"
@ -192,6 +193,14 @@ var RelationCommit = &Commit{
Row: -1,
}
func parseGitTime(timeStr string) time.Time {
t, err := time.Parse(time.RFC3339, timeStr)
if err != nil {
return time.Unix(0, 0)
}
return t
}
// NewCommit creates a new commit from a provided line
func NewCommit(row, column int, line []byte) (*Commit, error) {
data := bytes.SplitN(line, []byte("|"), 5)
@ -206,7 +215,7 @@ func NewCommit(row, column int, line []byte) (*Commit, error) {
// 1 matches git log --pretty=format:%H => commit hash
Rev: string(data[1]),
// 2 matches git log --pretty=format:%ad => author date (format respects --date= option)
Date: string(data[2]),
Date: parseGitTime(string(data[2])),
// 3 matches git log --pretty=format:%h => abbreviated commit hash
ShortRev: string(data[3]),
// 4 matches git log --pretty=format:%s => subject
@ -245,7 +254,7 @@ type Commit struct {
Column int
Refs []git.Reference
Rev string
Date string
Date time.Time
ShortRev string
Subject string
}

View File

@ -11,6 +11,7 @@ import (
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
)
type Repository interface {
@ -59,15 +60,11 @@ func repositoryFromContext(ctx context.Context, repo Repository) *git.Repository
return nil
}
type nopCloser func()
func (nopCloser) Close() error { return nil }
// RepositoryFromContextOrOpen attempts to get the repository from the context or just opens it
func RepositoryFromContextOrOpen(ctx context.Context, repo Repository) (*git.Repository, io.Closer, error) {
gitRepo := repositoryFromContext(ctx, repo)
if gitRepo != nil {
return gitRepo, nopCloser(nil), nil
return gitRepo, util.NopCloser{}, nil
}
gitRepo, err := OpenRepository(ctx, repo)
@ -95,7 +92,7 @@ func repositoryFromContextPath(ctx context.Context, path string) *git.Repository
func RepositoryFromContextOrOpenPath(ctx context.Context, path string) (*git.Repository, io.Closer, error) {
gitRepo := repositoryFromContextPath(ctx, path)
if gitRepo != nil {
return gitRepo, nopCloser(nil), nil
return gitRepo, util.NopCloser{}, nil
}
gitRepo, err := git.OpenRepository(ctx, path)

View File

@ -31,6 +31,7 @@ import (
"github.com/blevesearch/bleve/v2/analysis/token/camelcase"
"github.com/blevesearch/bleve/v2/analysis/token/lowercase"
"github.com/blevesearch/bleve/v2/analysis/token/unicodenorm"
"github.com/blevesearch/bleve/v2/analysis/tokenizer/letter"
"github.com/blevesearch/bleve/v2/analysis/tokenizer/unicode"
"github.com/blevesearch/bleve/v2/mapping"
"github.com/blevesearch/bleve/v2/search/query"
@ -69,7 +70,7 @@ const (
filenameIndexerAnalyzer = "filenameIndexerAnalyzer"
filenameIndexerTokenizer = "filenameIndexerTokenizer"
repoIndexerDocType = "repoIndexerDocType"
repoIndexerLatestVersion = 7
repoIndexerLatestVersion = 8
)
// generateBleveIndexMapping generates a bleve index mapping for the repo indexer
@ -105,7 +106,7 @@ func generateBleveIndexMapping() (mapping.IndexMapping, error) {
} else if err := mapping.AddCustomAnalyzer(repoIndexerAnalyzer, map[string]any{
"type": analyzer_custom.Name,
"char_filters": []string{},
"tokenizer": unicode.Name,
"tokenizer": letter.Name,
"token_filters": []string{unicodeNormalizeName, camelcase.Name, lowercase.Name},
}); err != nil {
return nil, err

View File

@ -30,7 +30,7 @@ import (
)
const (
esRepoIndexerLatestVersion = 2
esRepoIndexerLatestVersion = 3
// multi-match-types, currently only 2 types are used
// Reference: https://www.elastic.co/guide/en/elasticsearch/reference/7.0/query-dsl-multi-match-query.html#multi-match-types
esMultiMatchTypeBestFields = "best_fields"
@ -60,6 +60,10 @@ const (
"settings": {
"analysis": {
"analyzer": {
"content_analyzer": {
"tokenizer": "content_tokenizer",
"filter" : ["lowercase"]
},
"filename_path_analyzer": {
"tokenizer": "path_tokenizer"
},
@ -68,6 +72,10 @@ const (
}
},
"tokenizer": {
"content_tokenizer": {
"type": "simple_pattern_split",
"pattern": "[^a-zA-Z0-9]"
},
"path_tokenizer": {
"type": "path_hierarchy",
"delimiter": "/"
@ -104,7 +112,8 @@ const (
"content": {
"type": "text",
"term_vector": "with_positions_offsets",
"index": true
"index": true,
"analyzer": "content_analyzer"
},
"commit_id": {
"type": "keyword",

View File

@ -181,6 +181,55 @@ func testIndexer(name string, t *testing.T, indexer internal.Indexer) {
},
},
},
// Search for matches on the contents of files regardless of case.
{
RepoIDs: nil,
Keyword: "dESCRIPTION",
Langs: 1,
Results: []codeSearchResult{
{
Filename: "README.md",
Content: "# repo1\n\nDescription for repo1",
},
},
},
// Search for an exact match on the filename within the repo '62' (case insenstive).
// This scenario yields a single result (the file avocado.md on the repo '62')
{
RepoIDs: []int64{62},
Keyword: "AVOCADO.MD",
Langs: 1,
Results: []codeSearchResult{
{
Filename: "avocado.md",
Content: "# repo1\n\npineaple pie of cucumber juice",
},
},
},
// Search for matches on the contents of files when the criteria is a expression.
{
RepoIDs: []int64{62},
Keyword: "console.log",
Langs: 1,
Results: []codeSearchResult{
{
Filename: "example-file.js",
Content: "console.log(\"Hello, World!\")",
},
},
},
// Search for matches on the contents of files when the criteria is part of a expression.
{
RepoIDs: []int64{62},
Keyword: "log",
Langs: 1,
Results: []codeSearchResult{
{
Filename: "example-file.js",
Content: "console.log(\"Hello, World!\")",
},
},
},
}
for _, kw := range keywords {

View File

@ -6,12 +6,13 @@ package bleve
import (
"errors"
"os"
"unicode"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/util"
"github.com/blevesearch/bleve/v2"
"github.com/blevesearch/bleve/v2/analysis/tokenizer/unicode"
unicode_tokenizer "github.com/blevesearch/bleve/v2/analysis/tokenizer/unicode"
"github.com/blevesearch/bleve/v2/index/upsidedown"
"github.com/ethantkoenig/rupture"
)
@ -57,7 +58,7 @@ func openIndexer(path string, latestVersion int) (bleve.Index, int, error) {
// may be different on two string and they still be considered equivalent.
// Given a phrasse, its shortest word determines its fuzziness. If a phrase uses CJK (eg: `갃갃갃` `啊啊啊`), the fuzziness is zero.
func GuessFuzzinessByKeyword(s string) int {
tokenizer := unicode.NewUnicodeTokenizer()
tokenizer := unicode_tokenizer.NewUnicodeTokenizer()
tokens := tokenizer.Tokenize([]byte(s))
if len(tokens) > 0 {
@ -77,8 +78,10 @@ func guessFuzzinessByKeyword(s string) int {
// according to https://github.com/blevesearch/bleve/issues/1563, the supported max fuzziness is 2
// magic number 4 was chosen to determine the levenshtein distance per each character of a keyword
// BUT, when using CJK (eg: `갃갃갃` `啊啊啊`), it mismatches a lot.
// Likewise, queries whose terms contains characters that are *not* letters should not use fuzziness
for _, r := range s {
if r >= 128 {
if r >= 128 || !unicode.IsLetter(r) {
return 0
}
}

View File

@ -35,6 +35,14 @@ func TestBleveGuessFuzzinessByKeyword(t *testing.T) {
Input: "갃갃갃",
Fuzziness: 0,
},
{
Input: "repo1",
Fuzziness: 0,
},
{
Input: "avocado.md",
Fuzziness: 0,
},
}
for _, scenario := range scenarios {

View File

@ -12,7 +12,7 @@ import (
// Indexer represents a basic meilisearch indexer implementation
type Indexer struct {
Client *meilisearch.Client
Client meilisearch.ServiceManager
url, apiKey string
indexName string
@ -40,11 +40,7 @@ func (i *Indexer) Init(_ context.Context) (bool, error) {
return false, fmt.Errorf("indexer is already initialized")
}
i.Client = meilisearch.NewClient(meilisearch.ClientConfig{
Host: i.url,
APIKey: i.apiKey,
})
i.Client = meilisearch.New(i.url, meilisearch.WithAPIKey(i.apiKey))
_, err := i.Client.GetIndex(i.VersionedIndexName())
if err == nil {
return true, nil

View File

@ -16,9 +16,10 @@ import (
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/proxy"
)
"code.gitea.io/gitea/modules/setting"
const httpBatchSize = 20
"golang.org/x/sync/errgroup"
)
// HTTPClient is used to communicate with the LFS server
// https://github.com/git-lfs/git-lfs/blob/main/docs/api/batch.md
@ -30,7 +31,7 @@ type HTTPClient struct {
// BatchSize returns the preferred size of batchs to process
func (c *HTTPClient) BatchSize() int {
return httpBatchSize
return setting.LFSClient.BatchSize
}
func newHTTPClient(endpoint *url.URL, httpTransport *http.Transport) *HTTPClient {
@ -114,6 +115,7 @@ func (c *HTTPClient) Upload(ctx context.Context, objects []Pointer, callback Upl
return c.performOperation(ctx, objects, nil, callback)
}
// performOperation takes a slice of LFS object pointers, batches them, and performs the upload/download operations concurrently in each batch
func (c *HTTPClient) performOperation(ctx context.Context, objects []Pointer, dc DownloadCallback, uc UploadCallback) error {
if len(objects) == 0 {
return nil
@ -134,30 +136,51 @@ func (c *HTTPClient) performOperation(ctx context.Context, objects []Pointer, dc
return fmt.Errorf("TransferAdapter not found: %s", result.Transfer)
}
if setting.LFSClient.BatchOperationConcurrency <= 0 {
panic("BatchOperationConcurrency must be greater than 0, forgot to init?")
}
errGroup, groupCtx := errgroup.WithContext(ctx)
errGroup.SetLimit(setting.LFSClient.BatchOperationConcurrency)
for _, object := range result.Objects {
errGroup.Go(func() error {
return performSingleOperation(groupCtx, object, dc, uc, transferAdapter)
})
}
// only the first error is returned, preserving legacy behavior before concurrency
return errGroup.Wait()
}
// performSingleOperation performs an LFS upload or download operation on a single object
func performSingleOperation(ctx context.Context, object *ObjectResponse, dc DownloadCallback, uc UploadCallback, transferAdapter TransferAdapter) error {
// the response from a lfs batch api request for this specific object id contained an error
if object.Error != nil {
log.Trace("Error on object %v: %v", object.Pointer, object.Error)
// this was an 'upload' request inside the batch request
if uc != nil {
if _, err := uc(object.Pointer, object.Error); err != nil {
return err
}
} else {
// this was NOT an 'upload' request inside the batch request, meaning it must be a 'download' request
if err := dc(object.Pointer, nil, object.Error); err != nil {
return err
}
}
continue
// if the callback returns no err, then the error could be ignored, and the operations should continue
return nil
}
// the response from an lfs batch api request contained necessary upload/download fields to act upon
if uc != nil {
if len(object.Actions) == 0 {
log.Trace("%v already present on server", object.Pointer)
continue
return nil
}
link, ok := object.Actions["upload"]
if !ok {
log.Debug("%+v", object)
return errors.New("missing action 'upload'")
}
@ -197,8 +220,6 @@ func (c *HTTPClient) performOperation(ctx context.Context, objects []Pointer, dc
return err
}
}
}
return nil
}

View File

@ -12,6 +12,8 @@ import (
"testing"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/test"
"github.com/stretchr/testify/assert"
)
@ -183,71 +185,61 @@ func TestHTTPClientDownload(t *testing.T) {
cases := []struct {
endpoint string
expectederror string
expectedError string
}{
// case 0
{
endpoint: "https://status-not-ok.io",
expectederror: io.ErrUnexpectedEOF.Error(),
expectedError: io.ErrUnexpectedEOF.Error(),
},
// case 1
{
endpoint: "https://invalid-json-response.io",
expectederror: "invalid json",
expectedError: "invalid json",
},
// case 2
{
endpoint: "https://valid-batch-request-download.io",
expectederror: "",
expectedError: "",
},
// case 3
{
endpoint: "https://response-no-objects.io",
expectederror: "",
expectedError: "",
},
// case 4
{
endpoint: "https://unknown-transfer-adapter.io",
expectederror: "TransferAdapter not found: ",
expectedError: "TransferAdapter not found: ",
},
// case 5
{
endpoint: "https://error-in-response-objects.io",
expectederror: "Object not found",
expectedError: "Object not found",
},
// case 6
{
endpoint: "https://empty-actions-map.io",
expectederror: "missing action 'download'",
expectedError: "missing action 'download'",
},
// case 7
{
endpoint: "https://download-actions-map.io",
expectederror: "",
expectedError: "",
},
// case 8
{
endpoint: "https://upload-actions-map.io",
expectederror: "missing action 'download'",
expectedError: "missing action 'download'",
},
// case 9
{
endpoint: "https://verify-actions-map.io",
expectederror: "missing action 'download'",
expectedError: "missing action 'download'",
},
// case 10
{
endpoint: "https://unknown-actions-map.io",
expectederror: "missing action 'download'",
expectedError: "missing action 'download'",
},
// case 11
{
endpoint: "https://legacy-batch-request-download.io",
expectederror: "",
expectedError: "",
},
}
for n, c := range cases {
defer test.MockVariableValue(&setting.LFSClient.BatchOperationConcurrency, 8)()
for _, c := range cases {
t.Run(c.endpoint, func(t *testing.T) {
client := &HTTPClient{
client: hc,
endpoint: c.endpoint,
@ -265,11 +257,12 @@ func TestHTTPClientDownload(t *testing.T) {
assert.Equal(t, []byte("dummy"), b)
return nil
})
if len(c.expectederror) > 0 {
assert.True(t, strings.Contains(err.Error(), c.expectederror), "case %d: '%s' should contain '%s'", n, err.Error(), c.expectederror)
if c.expectedError != "" {
assert.ErrorContains(t, err, c.expectedError)
} else {
assert.NoError(t, err, "case %d", n)
assert.NoError(t, err)
}
})
}
}
@ -296,66 +289,57 @@ func TestHTTPClientUpload(t *testing.T) {
cases := []struct {
endpoint string
expectederror string
expectedError string
}{
// case 0
{
endpoint: "https://status-not-ok.io",
expectederror: io.ErrUnexpectedEOF.Error(),
expectedError: io.ErrUnexpectedEOF.Error(),
},
// case 1
{
endpoint: "https://invalid-json-response.io",
expectederror: "invalid json",
expectedError: "invalid json",
},
// case 2
{
endpoint: "https://valid-batch-request-upload.io",
expectederror: "",
expectedError: "",
},
// case 3
{
endpoint: "https://response-no-objects.io",
expectederror: "",
expectedError: "",
},
// case 4
{
endpoint: "https://unknown-transfer-adapter.io",
expectederror: "TransferAdapter not found: ",
expectedError: "TransferAdapter not found: ",
},
// case 5
{
endpoint: "https://error-in-response-objects.io",
expectederror: "Object not found",
expectedError: "Object not found",
},
// case 6
{
endpoint: "https://empty-actions-map.io",
expectederror: "",
expectedError: "",
},
// case 7
{
endpoint: "https://download-actions-map.io",
expectederror: "missing action 'upload'",
expectedError: "missing action 'upload'",
},
// case 8
{
endpoint: "https://upload-actions-map.io",
expectederror: "",
expectedError: "",
},
// case 9
{
endpoint: "https://verify-actions-map.io",
expectederror: "missing action 'upload'",
expectedError: "missing action 'upload'",
},
// case 10
{
endpoint: "https://unknown-actions-map.io",
expectederror: "missing action 'upload'",
expectedError: "missing action 'upload'",
},
}
for n, c := range cases {
defer test.MockVariableValue(&setting.LFSClient.BatchOperationConcurrency, 8)()
for _, c := range cases {
t.Run(c.endpoint, func(t *testing.T) {
client := &HTTPClient{
client: hc,
endpoint: c.endpoint,
@ -367,10 +351,11 @@ func TestHTTPClientUpload(t *testing.T) {
err := client.Upload(context.Background(), []Pointer{p}, func(p Pointer, objectError error) (io.ReadCloser, error) {
return io.NopCloser(new(bytes.Buffer)), objectError
})
if len(c.expectederror) > 0 {
assert.True(t, strings.Contains(err.Error(), c.expectederror), "case %d: '%s' should contain '%s'", n, err.Error(), c.expectederror)
if c.expectedError != "" {
assert.ErrorContains(t, err, c.expectedError)
} else {
assert.NoError(t, err, "case %d", n)
}
assert.NoError(t, err)
}
})
}
}

View File

@ -36,8 +36,8 @@ type GiteaBackend struct {
ctx context.Context
server *url.URL
op string
token string
itoken string
authToken string
internalAuth string
logger transfer.Logger
}
@ -48,7 +48,7 @@ func New(ctx context.Context, repo, op, token string, logger transfer.Logger) (t
return nil, err
}
server = server.JoinPath("api/internal/repo", repo, "info/lfs")
return &GiteaBackend{ctx: ctx, server: server, op: op, token: token, itoken: fmt.Sprintf("Bearer %s", setting.InternalToken), logger: logger}, nil
return &GiteaBackend{ctx: ctx, server: server, op: op, authToken: token, internalAuth: fmt.Sprintf("Bearer %s", setting.InternalToken), logger: logger}, nil
}
// Batch implements transfer.Backend
@ -73,8 +73,8 @@ func (g *GiteaBackend) Batch(_ string, pointers []transfer.BatchItem, args trans
}
url := g.server.JoinPath("objects/batch").String()
headers := map[string]string{
headerAuthorisation: g.itoken,
headerAuthX: g.token,
headerAuthorization: g.authToken,
headerGiteaInternalAuth: g.internalAuth,
headerAccept: mimeGitLFS,
headerContentType: mimeGitLFS,
}
@ -119,7 +119,7 @@ func (g *GiteaBackend) Batch(_ string, pointers []transfer.BatchItem, args trans
}
idMapStr := base64.StdEncoding.EncodeToString(idMapBytes)
item.Args[argID] = idMapStr
if authHeader, ok := action.Header[headerAuthorisation]; ok {
if authHeader, ok := action.Header[headerAuthorization]; ok {
authHeaderB64 := base64.StdEncoding.EncodeToString([]byte(authHeader))
item.Args[argToken] = authHeaderB64
}
@ -142,7 +142,7 @@ func (g *GiteaBackend) Batch(_ string, pointers []transfer.BatchItem, args trans
}
idMapStr := base64.StdEncoding.EncodeToString(idMapBytes)
item.Args[argID] = idMapStr
if authHeader, ok := action.Header[headerAuthorisation]; ok {
if authHeader, ok := action.Header[headerAuthorization]; ok {
authHeaderB64 := base64.StdEncoding.EncodeToString([]byte(authHeader))
item.Args[argToken] = authHeaderB64
}
@ -183,8 +183,8 @@ func (g *GiteaBackend) Download(oid string, args transfer.Args) (io.ReadCloser,
}
url := action.Href
headers := map[string]string{
headerAuthorisation: g.itoken,
headerAuthX: g.token,
headerAuthorization: g.authToken,
headerGiteaInternalAuth: g.internalAuth,
headerAccept: mimeOctetStream,
}
req := newInternalRequest(g.ctx, url, http.MethodGet, headers, nil)
@ -229,8 +229,8 @@ func (g *GiteaBackend) Upload(oid string, size int64, r io.Reader, args transfer
}
url := action.Href
headers := map[string]string{
headerAuthorisation: g.itoken,
headerAuthX: g.token,
headerAuthorization: g.authToken,
headerGiteaInternalAuth: g.internalAuth,
headerContentType: mimeOctetStream,
headerContentLength: strconv.FormatInt(size, 10),
}
@ -279,8 +279,8 @@ func (g *GiteaBackend) Verify(oid string, size int64, args transfer.Args) (trans
}
url := action.Href
headers := map[string]string{
headerAuthorisation: g.itoken,
headerAuthX: g.token,
headerAuthorization: g.authToken,
headerGiteaInternalAuth: g.internalAuth,
headerAccept: mimeGitLFS,
headerContentType: mimeGitLFS,
}

View File

@ -24,14 +24,14 @@ type giteaLockBackend struct {
ctx context.Context
g *GiteaBackend
server *url.URL
token string
itoken string
authToken string
internalAuth string
logger transfer.Logger
}
func newGiteaLockBackend(g *GiteaBackend) transfer.LockBackend {
server := g.server.JoinPath("locks")
return &giteaLockBackend{ctx: g.ctx, g: g, server: server, token: g.token, itoken: g.itoken, logger: g.logger}
return &giteaLockBackend{ctx: g.ctx, g: g, server: server, authToken: g.authToken, internalAuth: g.internalAuth, logger: g.logger}
}
// Create implements transfer.LockBackend
@ -45,8 +45,8 @@ func (g *giteaLockBackend) Create(path, refname string) (transfer.Lock, error) {
}
url := g.server.String()
headers := map[string]string{
headerAuthorisation: g.itoken,
headerAuthX: g.token,
headerAuthorization: g.authToken,
headerGiteaInternalAuth: g.internalAuth,
headerAccept: mimeGitLFS,
headerContentType: mimeGitLFS,
}
@ -97,8 +97,8 @@ func (g *giteaLockBackend) Unlock(lock transfer.Lock) error {
}
url := g.server.JoinPath(lock.ID(), "unlock").String()
headers := map[string]string{
headerAuthorisation: g.itoken,
headerAuthX: g.token,
headerAuthorization: g.authToken,
headerGiteaInternalAuth: g.internalAuth,
headerAccept: mimeGitLFS,
headerContentType: mimeGitLFS,
}
@ -180,8 +180,8 @@ func (g *giteaLockBackend) queryLocks(v url.Values) ([]transfer.Lock, string, er
urlq.RawQuery = v.Encode()
url := urlq.String()
headers := map[string]string{
headerAuthorisation: g.itoken,
headerAuthX: g.token,
headerAuthorization: g.authToken,
headerGiteaInternalAuth: g.internalAuth,
headerAccept: mimeGitLFS,
headerContentType: mimeGitLFS,
}

View File

@ -21,8 +21,8 @@ import (
// HTTP headers
const (
headerAccept = "Accept"
headerAuthorisation = "Authorization"
headerAuthX = "X-Auth"
headerAuthorization = "Authorization"
headerGiteaInternalAuth = "X-Gitea-Internal-Auth"
headerContentType = "Content-Type"
headerContentLength = "Content-Length"
)

View File

@ -4,8 +4,9 @@
package log
import (
"io"
"os"
"code.gitea.io/gitea/modules/util"
)
type WriterConsoleOption struct {
@ -18,19 +19,13 @@ type eventWriterConsole struct {
var _ EventWriter = (*eventWriterConsole)(nil)
type nopCloser struct {
io.Writer
}
func (nopCloser) Close() error { return nil }
func NewEventWriterConsole(name string, mode WriterMode) EventWriter {
w := &eventWriterConsole{EventWriterBaseImpl: NewEventWriterBase(name, "console", mode)}
opt := mode.WriterOption.(WriterConsoleOption)
if opt.Stderr {
w.OutputWriteCloser = nopCloser{os.Stderr}
w.OutputWriteCloser = util.NopCloser{Writer: os.Stderr}
} else {
w.OutputWriteCloser = nopCloser{os.Stdout}
w.OutputWriteCloser = util.NopCloser{Writer: os.Stdout}
}
return w
}

View File

@ -6,6 +6,7 @@ package log
import (
"io"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/util/rotatingfilewriter"
)
@ -42,7 +43,7 @@ func NewEventWriterFile(name string, mode WriterMode) EventWriter {
// if the log file can't be opened, what should it do? panic/exit? ignore logs? fallback to stderr?
// it seems that "fallback to stderr" is slightly better than others ....
FallbackErrorf("unable to open log file %q: %v", opt.FileName, err)
w.fileWriter = nopCloser{Writer: LoggerToWriter(FallbackErrorf)}
w.fileWriter = util.NopCloser{Writer: LoggerToWriter(FallbackErrorf)}
}
w.OutputWriteCloser = w.fileWriter
return w

View File

@ -6,25 +6,12 @@ package markup
import (
"bytes"
"io"
"net/url"
"path"
"path/filepath"
"regexp"
"slices"
"strings"
"sync"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/emoji"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/markup/common"
"code.gitea.io/gitea/modules/references"
"code.gitea.io/gitea/modules/regexplru"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/templates/vars"
"code.gitea.io/gitea/modules/translation"
"code.gitea.io/gitea/modules/util"
"golang.org/x/net/html"
"golang.org/x/net/html/atom"
@ -451,55 +438,14 @@ func createKeyword(content string) *html.Node {
return span
}
func createEmoji(content, class, name string) *html.Node {
span := &html.Node{
Type: html.ElementNode,
Data: atom.Span.String(),
Attr: []html.Attribute{},
}
if class != "" {
span.Attr = append(span.Attr, html.Attribute{Key: "class", Val: class})
}
if name != "" {
span.Attr = append(span.Attr, html.Attribute{Key: "aria-label", Val: name})
}
text := &html.Node{
Type: html.TextNode,
Data: content,
}
span.AppendChild(text)
return span
}
func createCustomEmoji(alias string) *html.Node {
span := &html.Node{
Type: html.ElementNode,
Data: atom.Span.String(),
Attr: []html.Attribute{},
}
span.Attr = append(span.Attr, html.Attribute{Key: "class", Val: "emoji"})
span.Attr = append(span.Attr, html.Attribute{Key: "aria-label", Val: alias})
img := &html.Node{
Type: html.ElementNode,
DataAtom: atom.Img,
Data: "img",
Attr: []html.Attribute{},
}
img.Attr = append(img.Attr, html.Attribute{Key: "alt", Val: ":" + alias + ":"})
img.Attr = append(img.Attr, html.Attribute{Key: "src", Val: setting.StaticURLPrefix + "/assets/img/emoji/" + alias + ".png"})
span.AppendChild(img)
return span
}
func createLink(href, content, class string) *html.Node {
a := &html.Node{
Type: html.ElementNode,
Data: atom.A.String(),
Attr: []html.Attribute{{Key: "href", Val: href}},
Attr: []html.Attribute{
{Key: "href", Val: href},
{Key: "data-markdown-generated-content"},
},
}
if class != "" {
@ -515,33 +461,6 @@ func createLink(href, content, class string) *html.Node {
return a
}
func createCodeLink(href, content, class string) *html.Node {
a := &html.Node{
Type: html.ElementNode,
Data: atom.A.String(),
Attr: []html.Attribute{{Key: "href", Val: href}},
}
if class != "" {
a.Attr = append(a.Attr, html.Attribute{Key: "class", Val: class})
}
text := &html.Node{
Type: html.TextNode,
Data: content,
}
code := &html.Node{
Type: html.ElementNode,
Data: atom.Code.String(),
Attr: []html.Attribute{{Key: "class", Val: "nohighlight"}},
}
code.AppendChild(text)
a.AppendChild(code)
return a
}
// replaceContent takes text node, and in its content it replaces a section of
// it with the specified newNode.
func replaceContent(node *html.Node, i, j int, newNode *html.Node) {
@ -573,676 +492,3 @@ func replaceContentList(node *html.Node, i, j int, newNodes []*html.Node) {
}, nextSibling)
}
}
func mentionProcessor(ctx *RenderContext, node *html.Node) {
start := 0
nodeStop := node.NextSibling
for node != nodeStop {
found, loc := references.FindFirstMentionBytes(util.UnsafeStringToBytes(node.Data[start:]))
if !found {
node = node.NextSibling
start = 0
continue
}
loc.Start += start
loc.End += start
mention := node.Data[loc.Start:loc.End]
teams, ok := ctx.Metas["teams"]
// FIXME: util.URLJoin may not be necessary here:
// - setting.AppURL is defined to have a terminal '/' so unless mention[1:]
// is an AppSubURL link we can probably fallback to concatenation.
// team mention should follow @orgName/teamName style
if ok && strings.Contains(mention, "/") {
mentionOrgAndTeam := strings.Split(mention, "/")
if mentionOrgAndTeam[0][1:] == ctx.Metas["org"] && strings.Contains(teams, ","+strings.ToLower(mentionOrgAndTeam[1])+",") {
replaceContent(node, loc.Start, loc.End, createLink(util.URLJoin(ctx.Links.Prefix(), "org", ctx.Metas["org"], "teams", mentionOrgAndTeam[1]), mention, "mention"))
node = node.NextSibling.NextSibling
start = 0
continue
}
start = loc.End
continue
}
mentionedUsername := mention[1:]
if DefaultProcessorHelper.IsUsernameMentionable != nil && DefaultProcessorHelper.IsUsernameMentionable(ctx.Ctx, mentionedUsername) {
replaceContent(node, loc.Start, loc.End, createLink(util.URLJoin(ctx.Links.Prefix(), mentionedUsername), mention, "mention"))
node = node.NextSibling.NextSibling
start = 0
} else {
start = loc.End
}
}
}
func shortLinkProcessor(ctx *RenderContext, node *html.Node) {
next := node.NextSibling
for node != nil && node != next {
m := shortLinkPattern.FindStringSubmatchIndex(node.Data)
if m == nil {
return
}
content := node.Data[m[2]:m[3]]
tail := node.Data[m[4]:m[5]]
props := make(map[string]string)
// MediaWiki uses [[link|text]], while GitHub uses [[text|link]]
// It makes page handling terrible, but we prefer GitHub syntax
// And fall back to MediaWiki only when it is obvious from the look
// Of text and link contents
sl := strings.Split(content, "|")
for _, v := range sl {
if equalPos := strings.IndexByte(v, '='); equalPos == -1 {
// There is no equal in this argument; this is a mandatory arg
if props["name"] == "" {
if IsFullURLString(v) {
// If we clearly see it is a link, we save it so
// But first we need to ensure, that if both mandatory args provided
// look like links, we stick to GitHub syntax
if props["link"] != "" {
props["name"] = props["link"]
}
props["link"] = strings.TrimSpace(v)
} else {
props["name"] = v
}
} else {
props["link"] = strings.TrimSpace(v)
}
} else {
// There is an equal; optional argument.
sep := strings.IndexByte(v, '=')
key, val := v[:sep], html.UnescapeString(v[sep+1:])
// When parsing HTML, x/net/html will change all quotes which are
// not used for syntax into UTF-8 quotes. So checking val[0] won't
// be enough, since that only checks a single byte.
if len(val) > 1 {
if (strings.HasPrefix(val, "“") && strings.HasSuffix(val, "”")) ||
(strings.HasPrefix(val, "") && strings.HasSuffix(val, "")) {
const lenQuote = len("")
val = val[lenQuote : len(val)-lenQuote]
} else if (strings.HasPrefix(val, "\"") && strings.HasSuffix(val, "\"")) ||
(strings.HasPrefix(val, "'") && strings.HasSuffix(val, "'")) {
val = val[1 : len(val)-1]
} else if strings.HasPrefix(val, "'") && strings.HasSuffix(val, "") {
const lenQuote = len("")
val = val[1 : len(val)-lenQuote]
}
}
props[key] = val
}
}
var name, link string
if props["link"] != "" {
link = props["link"]
} else if props["name"] != "" {
link = props["name"]
}
if props["title"] != "" {
name = props["title"]
} else if props["name"] != "" {
name = props["name"]
} else {
name = link
}
name += tail
image := false
ext := filepath.Ext(link)
switch ext {
// fast path: empty string, ignore
case "":
// leave image as false
case ".jpg", ".jpeg", ".png", ".tif", ".tiff", ".webp", ".gif", ".bmp", ".ico", ".svg":
image = true
}
childNode := &html.Node{}
linkNode := &html.Node{
FirstChild: childNode,
LastChild: childNode,
Type: html.ElementNode,
Data: "a",
DataAtom: atom.A,
}
childNode.Parent = linkNode
absoluteLink := IsFullURLString(link)
if !absoluteLink {
if image {
link = strings.ReplaceAll(link, " ", "+")
} else {
link = strings.ReplaceAll(link, " ", "-") // FIXME: it should support dashes in the link, eg: "the-dash-support.-"
}
if !strings.Contains(link, "/") {
link = url.PathEscape(link) // FIXME: it doesn't seem right and it might cause double-escaping
}
}
if image {
if !absoluteLink {
link = util.URLJoin(ctx.Links.ResolveMediaLink(ctx.IsWiki), link)
}
title := props["title"]
if title == "" {
title = props["alt"]
}
if title == "" {
title = path.Base(name)
}
alt := props["alt"]
if alt == "" {
alt = name
}
// make the childNode an image - if we can, we also place the alt
childNode.Type = html.ElementNode
childNode.Data = "img"
childNode.DataAtom = atom.Img
childNode.Attr = []html.Attribute{
{Key: "src", Val: link},
{Key: "title", Val: title},
{Key: "alt", Val: alt},
}
if alt == "" {
childNode.Attr = childNode.Attr[:2]
}
} else {
link, _ = ResolveLink(ctx, link, "")
childNode.Type = html.TextNode
childNode.Data = name
}
linkNode.Attr = []html.Attribute{{Key: "href", Val: link}}
replaceContent(node, m[0], m[1], linkNode)
node = node.NextSibling.NextSibling
}
}
func fullIssuePatternProcessor(ctx *RenderContext, node *html.Node) {
if ctx.Metas == nil {
return
}
next := node.NextSibling
for node != nil && node != next {
m := getIssueFullPattern().FindStringSubmatchIndex(node.Data)
if m == nil {
return
}
mDiffView := getFilesChangedFullPattern().FindStringSubmatchIndex(node.Data)
// leave it as it is if the link is from "Files Changed" tab in PR Diff View https://domain/org/repo/pulls/27/files
if mDiffView != nil {
return
}
link := node.Data[m[0]:m[1]]
text := "#" + node.Data[m[2]:m[3]]
// if m[4] and m[5] is not -1, then link is to a comment
// indicate that in the text by appending (comment)
if m[4] != -1 && m[5] != -1 {
if locale, ok := ctx.Ctx.Value(translation.ContextKey).(translation.Locale); ok {
text += " " + locale.TrString("repo.from_comment")
} else {
text += " (comment)"
}
}
// extract repo and org name from matched link like
// http://localhost:3000/gituser/myrepo/issues/1
linkParts := strings.Split(link, "/")
matchOrg := linkParts[len(linkParts)-4]
matchRepo := linkParts[len(linkParts)-3]
if matchOrg == ctx.Metas["user"] && matchRepo == ctx.Metas["repo"] {
replaceContent(node, m[0], m[1], createLink(link, text, "ref-issue"))
} else {
text = matchOrg + "/" + matchRepo + text
replaceContent(node, m[0], m[1], createLink(link, text, "ref-issue"))
}
node = node.NextSibling.NextSibling
}
}
func issueIndexPatternProcessor(ctx *RenderContext, node *html.Node) {
if ctx.Metas == nil {
return
}
// FIXME: the use of "mode" is quite dirty and hacky, for example: what is a "document"? how should it be rendered?
// The "mode" approach should be refactored to some other more clear&reliable way.
crossLinkOnly := ctx.Metas["mode"] == "document" && !ctx.IsWiki
var (
found bool
ref *references.RenderizableReference
)
next := node.NextSibling
for node != nil && node != next {
_, hasExtTrackFormat := ctx.Metas["format"]
// Repos with external issue trackers might still need to reference local PRs
// We need to concern with the first one that shows up in the text, whichever it is
isNumericStyle := ctx.Metas["style"] == "" || ctx.Metas["style"] == IssueNameStyleNumeric
foundNumeric, refNumeric := references.FindRenderizableReferenceNumeric(node.Data, hasExtTrackFormat && !isNumericStyle, crossLinkOnly)
switch ctx.Metas["style"] {
case "", IssueNameStyleNumeric:
found, ref = foundNumeric, refNumeric
case IssueNameStyleAlphanumeric:
found, ref = references.FindRenderizableReferenceAlphanumeric(node.Data)
case IssueNameStyleRegexp:
pattern, err := regexplru.GetCompiled(ctx.Metas["regexp"])
if err != nil {
return
}
found, ref = references.FindRenderizableReferenceRegexp(node.Data, pattern)
}
// Repos with external issue trackers might still need to reference local PRs
// We need to concern with the first one that shows up in the text, whichever it is
if hasExtTrackFormat && !isNumericStyle && refNumeric != nil {
// If numeric (PR) was found, and it was BEFORE the non-numeric pattern, use that
// Allow a free-pass when non-numeric pattern wasn't found.
if found && (ref == nil || refNumeric.RefLocation.Start < ref.RefLocation.Start) {
found = foundNumeric
ref = refNumeric
}
}
if !found {
return
}
var link *html.Node
reftext := node.Data[ref.RefLocation.Start:ref.RefLocation.End]
if hasExtTrackFormat && !ref.IsPull {
ctx.Metas["index"] = ref.Issue
res, err := vars.Expand(ctx.Metas["format"], ctx.Metas)
if err != nil {
// here we could just log the error and continue the rendering
log.Error("unable to expand template vars for ref %s, err: %v", ref.Issue, err)
}
link = createLink(res, reftext, "ref-issue ref-external-issue")
} else {
// Path determines the type of link that will be rendered. It's unknown at this point whether
// the linked item is actually a PR or an issue. Luckily it's of no real consequence because
// Gitea will redirect on click as appropriate.
issuePath := util.Iif(ref.IsPull, "pulls", "issues")
if ref.Owner == "" {
link = createLink(util.URLJoin(ctx.Links.Prefix(), ctx.Metas["user"], ctx.Metas["repo"], issuePath, ref.Issue), reftext, "ref-issue")
} else {
link = createLink(util.URLJoin(ctx.Links.Prefix(), ref.Owner, ref.Name, issuePath, ref.Issue), reftext, "ref-issue")
}
}
if ref.Action == references.XRefActionNone {
replaceContent(node, ref.RefLocation.Start, ref.RefLocation.End, link)
node = node.NextSibling.NextSibling
continue
}
// Decorate action keywords if actionable
var keyword *html.Node
if references.IsXrefActionable(ref, hasExtTrackFormat) {
keyword = createKeyword(node.Data[ref.ActionLocation.Start:ref.ActionLocation.End])
} else {
keyword = &html.Node{
Type: html.TextNode,
Data: node.Data[ref.ActionLocation.Start:ref.ActionLocation.End],
}
}
spaces := &html.Node{
Type: html.TextNode,
Data: node.Data[ref.ActionLocation.End:ref.RefLocation.Start],
}
replaceContentList(node, ref.ActionLocation.Start, ref.RefLocation.End, []*html.Node{keyword, spaces, link})
node = node.NextSibling.NextSibling.NextSibling.NextSibling
}
}
func commitCrossReferencePatternProcessor(ctx *RenderContext, node *html.Node) {
next := node.NextSibling
for node != nil && node != next {
found, ref := references.FindRenderizableCommitCrossReference(node.Data)
if !found {
return
}
reftext := ref.Owner + "/" + ref.Name + "@" + base.ShortSha(ref.CommitSha)
link := createLink(util.URLJoin(ctx.Links.Prefix(), ref.Owner, ref.Name, "commit", ref.CommitSha), reftext, "commit")
replaceContent(node, ref.RefLocation.Start, ref.RefLocation.End, link)
node = node.NextSibling.NextSibling
}
}
type anyHashPatternResult struct {
PosStart int
PosEnd int
FullURL string
CommitID string
SubPath string
QueryHash string
}
func anyHashPatternExtract(s string) (ret anyHashPatternResult, ok bool) {
m := anyHashPattern.FindStringSubmatchIndex(s)
if m == nil {
return ret, false
}
ret.PosStart, ret.PosEnd = m[0], m[1]
ret.FullURL = s[ret.PosStart:ret.PosEnd]
if strings.HasSuffix(ret.FullURL, ".") {
// if url ends in '.', it's very likely that it is not part of the actual url but used to finish a sentence.
ret.PosEnd--
ret.FullURL = ret.FullURL[:len(ret.FullURL)-1]
for i := 0; i < len(m); i++ {
m[i] = min(m[i], ret.PosEnd)
}
}
ret.CommitID = s[m[2]:m[3]]
if m[5] > 0 {
ret.SubPath = s[m[4]:m[5]]
}
lastStart, lastEnd := m[len(m)-2], m[len(m)-1]
if lastEnd > 0 {
ret.QueryHash = s[lastStart:lastEnd][1:]
}
return ret, true
}
// fullHashPatternProcessor renders SHA containing URLs
func fullHashPatternProcessor(ctx *RenderContext, node *html.Node) {
if ctx.Metas == nil {
return
}
nodeStop := node.NextSibling
for node != nodeStop {
if node.Type != html.TextNode {
node = node.NextSibling
continue
}
ret, ok := anyHashPatternExtract(node.Data)
if !ok {
node = node.NextSibling
continue
}
text := base.ShortSha(ret.CommitID)
if ret.SubPath != "" {
text += ret.SubPath
}
if ret.QueryHash != "" {
text += " (" + ret.QueryHash + ")"
}
replaceContent(node, ret.PosStart, ret.PosEnd, createCodeLink(ret.FullURL, text, "commit"))
node = node.NextSibling.NextSibling
}
}
func comparePatternProcessor(ctx *RenderContext, node *html.Node) {
if ctx.Metas == nil {
return
}
nodeStop := node.NextSibling
for node != nodeStop {
if node.Type != html.TextNode {
node = node.NextSibling
continue
}
m := comparePattern.FindStringSubmatchIndex(node.Data)
if m == nil || slices.Contains(m[:8], -1) { // ensure that every group (m[0]...m[7]) has a match
node = node.NextSibling
continue
}
urlFull := node.Data[m[0]:m[1]]
text1 := base.ShortSha(node.Data[m[2]:m[3]])
textDots := base.ShortSha(node.Data[m[4]:m[5]])
text2 := base.ShortSha(node.Data[m[6]:m[7]])
hash := ""
if m[9] > 0 {
hash = node.Data[m[8]:m[9]][1:]
}
start := m[0]
end := m[1]
// If url ends in '.', it's very likely that it is not part of the
// actual url but used to finish a sentence.
if strings.HasSuffix(urlFull, ".") {
end--
urlFull = urlFull[:len(urlFull)-1]
if hash != "" {
hash = hash[:len(hash)-1]
} else if text2 != "" {
text2 = text2[:len(text2)-1]
}
}
text := text1 + textDots + text2
if hash != "" {
text += " (" + hash + ")"
}
replaceContent(node, start, end, createCodeLink(urlFull, text, "compare"))
node = node.NextSibling.NextSibling
}
}
// emojiShortCodeProcessor for rendering text like :smile: into emoji
func emojiShortCodeProcessor(ctx *RenderContext, node *html.Node) {
start := 0
next := node.NextSibling
for node != nil && node != next && start < len(node.Data) {
m := emojiShortCodeRegex.FindStringSubmatchIndex(node.Data[start:])
if m == nil {
return
}
m[0] += start
m[1] += start
start = m[1]
alias := node.Data[m[0]:m[1]]
alias = strings.ReplaceAll(alias, ":", "")
converted := emoji.FromAlias(alias)
if converted == nil {
// check if this is a custom reaction
if _, exist := setting.UI.CustomEmojisMap[alias]; exist {
replaceContent(node, m[0], m[1], createCustomEmoji(alias))
node = node.NextSibling.NextSibling
start = 0
continue
}
continue
}
replaceContent(node, m[0], m[1], createEmoji(converted.Emoji, "emoji", converted.Description))
node = node.NextSibling.NextSibling
start = 0
}
}
// emoji processor to match emoji and add emoji class
func emojiProcessor(ctx *RenderContext, node *html.Node) {
start := 0
next := node.NextSibling
for node != nil && node != next && start < len(node.Data) {
m := emoji.FindEmojiSubmatchIndex(node.Data[start:])
if m == nil {
return
}
m[0] += start
m[1] += start
codepoint := node.Data[m[0]:m[1]]
start = m[1]
val := emoji.FromCode(codepoint)
if val != nil {
replaceContent(node, m[0], m[1], createEmoji(codepoint, "emoji", val.Description))
node = node.NextSibling.NextSibling
start = 0
}
}
}
// hashCurrentPatternProcessor renders SHA1 strings to corresponding links that
// are assumed to be in the same repository.
func hashCurrentPatternProcessor(ctx *RenderContext, node *html.Node) {
if ctx.Metas == nil || ctx.Metas["user"] == "" || ctx.Metas["repo"] == "" || (ctx.Repo == nil && ctx.GitRepo == nil) {
return
}
start := 0
next := node.NextSibling
if ctx.ShaExistCache == nil {
ctx.ShaExistCache = make(map[string]bool)
}
for node != nil && node != next && start < len(node.Data) {
m := hashCurrentPattern.FindStringSubmatchIndex(node.Data[start:])
if m == nil {
return
}
m[2] += start
m[3] += start
hash := node.Data[m[2]:m[3]]
// The regex does not lie, it matches the hash pattern.
// However, a regex cannot know if a hash actually exists or not.
// We could assume that a SHA1 hash should probably contain alphas AND numerics
// but that is not always the case.
// Although unlikely, deadbeef and 1234567 are valid short forms of SHA1 hash
// as used by git and github for linking and thus we have to do similar.
// Because of this, we check to make sure that a matched hash is actually
// a commit in the repository before making it a link.
// check cache first
exist, inCache := ctx.ShaExistCache[hash]
if !inCache {
if ctx.GitRepo == nil {
var err error
var closer io.Closer
ctx.GitRepo, closer, err = gitrepo.RepositoryFromContextOrOpen(ctx.Ctx, ctx.Repo)
if err != nil {
log.Error("unable to open repository: %s Error: %v", gitrepo.RepoGitURL(ctx.Repo), err)
return
}
ctx.AddCancel(func() {
_ = closer.Close()
ctx.GitRepo = nil
})
}
// Don't use IsObjectExist since it doesn't support short hashs with gogit edition.
exist = ctx.GitRepo.IsReferenceExist(hash)
ctx.ShaExistCache[hash] = exist
}
if !exist {
start = m[3]
continue
}
link := util.URLJoin(ctx.Links.Prefix(), ctx.Metas["user"], ctx.Metas["repo"], "commit", hash)
replaceContent(node, m[2], m[3], createCodeLink(link, base.ShortSha(hash), "commit"))
start = 0
node = node.NextSibling.NextSibling
}
}
// emailAddressProcessor replaces raw email addresses with a mailto: link.
func emailAddressProcessor(ctx *RenderContext, node *html.Node) {
next := node.NextSibling
for node != nil && node != next {
m := emailRegex.FindStringSubmatchIndex(node.Data)
if m == nil {
return
}
mail := node.Data[m[2]:m[3]]
replaceContent(node, m[2], m[3], createLink("mailto:"+mail, mail, "mailto"))
node = node.NextSibling.NextSibling
}
}
// linkProcessor creates links for any HTTP or HTTPS URL not captured by
// markdown.
func linkProcessor(ctx *RenderContext, node *html.Node) {
next := node.NextSibling
for node != nil && node != next {
m := common.LinkRegex.FindStringIndex(node.Data)
if m == nil {
return
}
uri := node.Data[m[0]:m[1]]
replaceContent(node, m[0], m[1], createLink(uri, uri, "link"))
node = node.NextSibling.NextSibling
}
}
func genDefaultLinkProcessor(defaultLink string) processor {
return func(ctx *RenderContext, node *html.Node) {
ch := &html.Node{
Parent: node,
Type: html.TextNode,
Data: node.Data,
}
node.Type = html.ElementNode
node.Data = "a"
node.DataAtom = atom.A
node.Attr = []html.Attribute{
{Key: "href", Val: defaultLink},
{Key: "class", Val: "default-link muted"},
}
node.FirstChild, node.LastChild = ch, ch
}
}
// descriptionLinkProcessor creates links for DescriptionHTML
func descriptionLinkProcessor(ctx *RenderContext, node *html.Node) {
next := node.NextSibling
for node != nil && node != next {
m := common.LinkRegex.FindStringIndex(node.Data)
if m == nil {
return
}
uri := node.Data[m[0]:m[1]]
replaceContent(node, m[0], m[1], createDescriptionLink(uri, uri))
node = node.NextSibling.NextSibling
}
}
func createDescriptionLink(href, content string) *html.Node {
textNode := &html.Node{
Type: html.TextNode,
Data: content,
}
linkNode := &html.Node{
FirstChild: textNode,
LastChild: textNode,
Type: html.ElementNode,
Data: "a",
DataAtom: atom.A,
Attr: []html.Attribute{
{Key: "href", Val: href},
{Key: "target", Val: "_blank"},
{Key: "rel", Val: "noopener noreferrer"},
},
}
textNode.Parent = linkNode
return linkNode
}

View File

@ -30,5 +30,5 @@ func TestRenderCodePreview(t *testing.T) {
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
}
test("http://localhost:3000/owner/repo/src/commit/0123456789/foo/bar.md#L10-L20", "<p><div>code preview</div></p>")
test("http://other/owner/repo/src/commit/0123456789/foo/bar.md#L10-L20", `<p><a href="http://other/owner/repo/src/commit/0123456789/foo/bar.md#L10-L20" rel="nofollow">http://other/owner/repo/src/commit/0123456789/foo/bar.md#L10-L20</a></p>`)
test("http://other/owner/repo/src/commit/0123456789/foo/bar.md#L10-L20", `<p><a href="http://other/owner/repo/src/commit/0123456789/foo/bar.md#L10-L20" data-markdown-generated-content="" rel="nofollow">http://other/owner/repo/src/commit/0123456789/foo/bar.md#L10-L20</a></p>`)
}

View File

@ -0,0 +1,225 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package markup
import (
"io"
"slices"
"strings"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/util"
"golang.org/x/net/html"
"golang.org/x/net/html/atom"
)
type anyHashPatternResult struct {
PosStart int
PosEnd int
FullURL string
CommitID string
SubPath string
QueryHash string
}
func createCodeLink(href, content, class string) *html.Node {
a := &html.Node{
Type: html.ElementNode,
Data: atom.A.String(),
Attr: []html.Attribute{{Key: "href", Val: href}},
}
if class != "" {
a.Attr = append(a.Attr, html.Attribute{Key: "class", Val: class})
}
text := &html.Node{
Type: html.TextNode,
Data: content,
}
code := &html.Node{
Type: html.ElementNode,
Data: atom.Code.String(),
Attr: []html.Attribute{{Key: "class", Val: "nohighlight"}},
}
code.AppendChild(text)
a.AppendChild(code)
return a
}
func anyHashPatternExtract(s string) (ret anyHashPatternResult, ok bool) {
m := anyHashPattern.FindStringSubmatchIndex(s)
if m == nil {
return ret, false
}
ret.PosStart, ret.PosEnd = m[0], m[1]
ret.FullURL = s[ret.PosStart:ret.PosEnd]
if strings.HasSuffix(ret.FullURL, ".") {
// if url ends in '.', it's very likely that it is not part of the actual url but used to finish a sentence.
ret.PosEnd--
ret.FullURL = ret.FullURL[:len(ret.FullURL)-1]
for i := 0; i < len(m); i++ {
m[i] = min(m[i], ret.PosEnd)
}
}
ret.CommitID = s[m[2]:m[3]]
if m[5] > 0 {
ret.SubPath = s[m[4]:m[5]]
}
lastStart, lastEnd := m[len(m)-2], m[len(m)-1]
if lastEnd > 0 {
ret.QueryHash = s[lastStart:lastEnd][1:]
}
return ret, true
}
// fullHashPatternProcessor renders SHA containing URLs
func fullHashPatternProcessor(ctx *RenderContext, node *html.Node) {
if ctx.Metas == nil {
return
}
nodeStop := node.NextSibling
for node != nodeStop {
if node.Type != html.TextNode {
node = node.NextSibling
continue
}
ret, ok := anyHashPatternExtract(node.Data)
if !ok {
node = node.NextSibling
continue
}
text := base.ShortSha(ret.CommitID)
if ret.SubPath != "" {
text += ret.SubPath
}
if ret.QueryHash != "" {
text += " (" + ret.QueryHash + ")"
}
replaceContent(node, ret.PosStart, ret.PosEnd, createCodeLink(ret.FullURL, text, "commit"))
node = node.NextSibling.NextSibling
}
}
func comparePatternProcessor(ctx *RenderContext, node *html.Node) {
if ctx.Metas == nil {
return
}
nodeStop := node.NextSibling
for node != nodeStop {
if node.Type != html.TextNode {
node = node.NextSibling
continue
}
m := comparePattern.FindStringSubmatchIndex(node.Data)
if m == nil || slices.Contains(m[:8], -1) { // ensure that every group (m[0]...m[7]) has a match
node = node.NextSibling
continue
}
urlFull := node.Data[m[0]:m[1]]
text1 := base.ShortSha(node.Data[m[2]:m[3]])
textDots := base.ShortSha(node.Data[m[4]:m[5]])
text2 := base.ShortSha(node.Data[m[6]:m[7]])
hash := ""
if m[9] > 0 {
hash = node.Data[m[8]:m[9]][1:]
}
start := m[0]
end := m[1]
// If url ends in '.', it's very likely that it is not part of the
// actual url but used to finish a sentence.
if strings.HasSuffix(urlFull, ".") {
end--
urlFull = urlFull[:len(urlFull)-1]
if hash != "" {
hash = hash[:len(hash)-1]
} else if text2 != "" {
text2 = text2[:len(text2)-1]
}
}
text := text1 + textDots + text2
if hash != "" {
text += " (" + hash + ")"
}
replaceContent(node, start, end, createCodeLink(urlFull, text, "compare"))
node = node.NextSibling.NextSibling
}
}
// hashCurrentPatternProcessor renders SHA1 strings to corresponding links that
// are assumed to be in the same repository.
func hashCurrentPatternProcessor(ctx *RenderContext, node *html.Node) {
if ctx.Metas == nil || ctx.Metas["user"] == "" || ctx.Metas["repo"] == "" || (ctx.Repo == nil && ctx.GitRepo == nil) {
return
}
start := 0
next := node.NextSibling
if ctx.ShaExistCache == nil {
ctx.ShaExistCache = make(map[string]bool)
}
for node != nil && node != next && start < len(node.Data) {
m := hashCurrentPattern.FindStringSubmatchIndex(node.Data[start:])
if m == nil {
return
}
m[2] += start
m[3] += start
hash := node.Data[m[2]:m[3]]
// The regex does not lie, it matches the hash pattern.
// However, a regex cannot know if a hash actually exists or not.
// We could assume that a SHA1 hash should probably contain alphas AND numerics
// but that is not always the case.
// Although unlikely, deadbeef and 1234567 are valid short forms of SHA1 hash
// as used by git and github for linking and thus we have to do similar.
// Because of this, we check to make sure that a matched hash is actually
// a commit in the repository before making it a link.
// check cache first
exist, inCache := ctx.ShaExistCache[hash]
if !inCache {
if ctx.GitRepo == nil {
var err error
var closer io.Closer
ctx.GitRepo, closer, err = gitrepo.RepositoryFromContextOrOpen(ctx.Ctx, ctx.Repo)
if err != nil {
log.Error("unable to open repository: %s Error: %v", gitrepo.RepoGitURL(ctx.Repo), err)
return
}
ctx.AddCancel(func() {
_ = closer.Close()
ctx.GitRepo = nil
})
}
// Don't use IsObjectExist since it doesn't support short hashs with gogit edition.
exist = ctx.GitRepo.IsReferenceExist(hash)
ctx.ShaExistCache[hash] = exist
}
if !exist {
start = m[3]
continue
}
link := util.URLJoin(ctx.Links.Prefix(), ctx.Metas["user"], ctx.Metas["repo"], "commit", hash)
replaceContent(node, m[2], m[3], createCodeLink(link, base.ShortSha(hash), "commit"))
start = 0
node = node.NextSibling.NextSibling
}
}

View File

@ -0,0 +1,21 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package markup
import "golang.org/x/net/html"
// emailAddressProcessor replaces raw email addresses with a mailto: link.
func emailAddressProcessor(ctx *RenderContext, node *html.Node) {
next := node.NextSibling
for node != nil && node != next {
m := emailRegex.FindStringSubmatchIndex(node.Data)
if m == nil {
return
}
mail := node.Data[m[2]:m[3]]
replaceContent(node, m[2], m[3], createLink("mailto:"+mail, mail, "mailto"))
node = node.NextSibling.NextSibling
}
}

View File

@ -0,0 +1,115 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package markup
import (
"strings"
"code.gitea.io/gitea/modules/emoji"
"code.gitea.io/gitea/modules/setting"
"golang.org/x/net/html"
"golang.org/x/net/html/atom"
)
func createEmoji(content, class, name string) *html.Node {
span := &html.Node{
Type: html.ElementNode,
Data: atom.Span.String(),
Attr: []html.Attribute{},
}
if class != "" {
span.Attr = append(span.Attr, html.Attribute{Key: "class", Val: class})
}
if name != "" {
span.Attr = append(span.Attr, html.Attribute{Key: "aria-label", Val: name})
}
text := &html.Node{
Type: html.TextNode,
Data: content,
}
span.AppendChild(text)
return span
}
func createCustomEmoji(alias string) *html.Node {
span := &html.Node{
Type: html.ElementNode,
Data: atom.Span.String(),
Attr: []html.Attribute{},
}
span.Attr = append(span.Attr, html.Attribute{Key: "class", Val: "emoji"})
span.Attr = append(span.Attr, html.Attribute{Key: "aria-label", Val: alias})
img := &html.Node{
Type: html.ElementNode,
DataAtom: atom.Img,
Data: "img",
Attr: []html.Attribute{},
}
img.Attr = append(img.Attr, html.Attribute{Key: "alt", Val: ":" + alias + ":"})
img.Attr = append(img.Attr, html.Attribute{Key: "src", Val: setting.StaticURLPrefix + "/assets/img/emoji/" + alias + ".png"})
span.AppendChild(img)
return span
}
// emojiShortCodeProcessor for rendering text like :smile: into emoji
func emojiShortCodeProcessor(ctx *RenderContext, node *html.Node) {
start := 0
next := node.NextSibling
for node != nil && node != next && start < len(node.Data) {
m := emojiShortCodeRegex.FindStringSubmatchIndex(node.Data[start:])
if m == nil {
return
}
m[0] += start
m[1] += start
start = m[1]
alias := node.Data[m[0]:m[1]]
alias = strings.ReplaceAll(alias, ":", "")
converted := emoji.FromAlias(alias)
if converted == nil {
// check if this is a custom reaction
if _, exist := setting.UI.CustomEmojisMap[alias]; exist {
replaceContent(node, m[0], m[1], createCustomEmoji(alias))
node = node.NextSibling.NextSibling
start = 0
continue
}
continue
}
replaceContent(node, m[0], m[1], createEmoji(converted.Emoji, "emoji", converted.Description))
node = node.NextSibling.NextSibling
start = 0
}
}
// emoji processor to match emoji and add emoji class
func emojiProcessor(ctx *RenderContext, node *html.Node) {
start := 0
next := node.NextSibling
for node != nil && node != next && start < len(node.Data) {
m := emoji.FindEmojiSubmatchIndex(node.Data[start:])
if m == nil {
return
}
m[0] += start
m[1] += start
codepoint := node.Data[m[0]:m[1]]
start = m[1]
val := emoji.FromCode(codepoint)
if val != nil {
replaceContent(node, m[0], m[1], createEmoji(codepoint, "emoji", val.Description))
node = node.NextSibling.NextSibling
start = 0
}
}
}

View File

@ -33,11 +33,9 @@ func numericIssueLink(baseURL, class string, index int, marker string) string {
// link an HTML link
func link(href, class, contents string) string {
if class != "" {
class = " class=\"" + class + "\""
}
return fmt.Sprintf("<a href=\"%s\"%s>%s</a>", href, class, contents)
extra := ` data-markdown-generated-content=""`
extra += util.Iif(class != "", ` class="`+class+`"`, "")
return fmt.Sprintf(`<a href="%s"%s>%s</a>`, href, extra, contents)
}
var numericMetas = map[string]string{
@ -353,7 +351,9 @@ func TestRender_FullIssueURLs(t *testing.T) {
Metas: localMetas,
}, []processor{fullIssuePatternProcessor}, strings.NewReader(input), &result)
assert.NoError(t, err)
assert.Equal(t, expected, result.String())
actual := result.String()
actual = strings.ReplaceAll(actual, ` data-markdown-generated-content=""`, "")
assert.Equal(t, expected, actual)
}
test("Here is a link https://git.osgeo.org/gogs/postgis/postgis/pulls/6",
"Here is a link https://git.osgeo.org/gogs/postgis/postgis/pulls/6")

View File

@ -0,0 +1,180 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package markup
import (
"strings"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/references"
"code.gitea.io/gitea/modules/regexplru"
"code.gitea.io/gitea/modules/templates/vars"
"code.gitea.io/gitea/modules/translation"
"code.gitea.io/gitea/modules/util"
"golang.org/x/net/html"
)
func fullIssuePatternProcessor(ctx *RenderContext, node *html.Node) {
if ctx.Metas == nil {
return
}
next := node.NextSibling
for node != nil && node != next {
m := getIssueFullPattern().FindStringSubmatchIndex(node.Data)
if m == nil {
return
}
mDiffView := getFilesChangedFullPattern().FindStringSubmatchIndex(node.Data)
// leave it as it is if the link is from "Files Changed" tab in PR Diff View https://domain/org/repo/pulls/27/files
if mDiffView != nil {
return
}
link := node.Data[m[0]:m[1]]
text := "#" + node.Data[m[2]:m[3]]
// if m[4] and m[5] is not -1, then link is to a comment
// indicate that in the text by appending (comment)
if m[4] != -1 && m[5] != -1 {
if locale, ok := ctx.Ctx.Value(translation.ContextKey).(translation.Locale); ok {
text += " " + locale.TrString("repo.from_comment")
} else {
text += " (comment)"
}
}
// extract repo and org name from matched link like
// http://localhost:3000/gituser/myrepo/issues/1
linkParts := strings.Split(link, "/")
matchOrg := linkParts[len(linkParts)-4]
matchRepo := linkParts[len(linkParts)-3]
if matchOrg == ctx.Metas["user"] && matchRepo == ctx.Metas["repo"] {
replaceContent(node, m[0], m[1], createLink(link, text, "ref-issue"))
} else {
text = matchOrg + "/" + matchRepo + text
replaceContent(node, m[0], m[1], createLink(link, text, "ref-issue"))
}
node = node.NextSibling.NextSibling
}
}
func issueIndexPatternProcessor(ctx *RenderContext, node *html.Node) {
if ctx.Metas == nil {
return
}
// FIXME: the use of "mode" is quite dirty and hacky, for example: what is a "document"? how should it be rendered?
// The "mode" approach should be refactored to some other more clear&reliable way.
crossLinkOnly := ctx.Metas["mode"] == "document" && !ctx.IsWiki
var (
found bool
ref *references.RenderizableReference
)
next := node.NextSibling
for node != nil && node != next {
_, hasExtTrackFormat := ctx.Metas["format"]
// Repos with external issue trackers might still need to reference local PRs
// We need to concern with the first one that shows up in the text, whichever it is
isNumericStyle := ctx.Metas["style"] == "" || ctx.Metas["style"] == IssueNameStyleNumeric
foundNumeric, refNumeric := references.FindRenderizableReferenceNumeric(node.Data, hasExtTrackFormat && !isNumericStyle, crossLinkOnly)
switch ctx.Metas["style"] {
case "", IssueNameStyleNumeric:
found, ref = foundNumeric, refNumeric
case IssueNameStyleAlphanumeric:
found, ref = references.FindRenderizableReferenceAlphanumeric(node.Data)
case IssueNameStyleRegexp:
pattern, err := regexplru.GetCompiled(ctx.Metas["regexp"])
if err != nil {
return
}
found, ref = references.FindRenderizableReferenceRegexp(node.Data, pattern)
}
// Repos with external issue trackers might still need to reference local PRs
// We need to concern with the first one that shows up in the text, whichever it is
if hasExtTrackFormat && !isNumericStyle && refNumeric != nil {
// If numeric (PR) was found, and it was BEFORE the non-numeric pattern, use that
// Allow a free-pass when non-numeric pattern wasn't found.
if found && (ref == nil || refNumeric.RefLocation.Start < ref.RefLocation.Start) {
found = foundNumeric
ref = refNumeric
}
}
if !found {
return
}
var link *html.Node
reftext := node.Data[ref.RefLocation.Start:ref.RefLocation.End]
if hasExtTrackFormat && !ref.IsPull {
ctx.Metas["index"] = ref.Issue
res, err := vars.Expand(ctx.Metas["format"], ctx.Metas)
if err != nil {
// here we could just log the error and continue the rendering
log.Error("unable to expand template vars for ref %s, err: %v", ref.Issue, err)
}
link = createLink(res, reftext, "ref-issue ref-external-issue")
} else {
// Path determines the type of link that will be rendered. It's unknown at this point whether
// the linked item is actually a PR or an issue. Luckily it's of no real consequence because
// Gitea will redirect on click as appropriate.
issuePath := util.Iif(ref.IsPull, "pulls", "issues")
if ref.Owner == "" {
link = createLink(util.URLJoin(ctx.Links.Prefix(), ctx.Metas["user"], ctx.Metas["repo"], issuePath, ref.Issue), reftext, "ref-issue")
} else {
link = createLink(util.URLJoin(ctx.Links.Prefix(), ref.Owner, ref.Name, issuePath, ref.Issue), reftext, "ref-issue")
}
}
if ref.Action == references.XRefActionNone {
replaceContent(node, ref.RefLocation.Start, ref.RefLocation.End, link)
node = node.NextSibling.NextSibling
continue
}
// Decorate action keywords if actionable
var keyword *html.Node
if references.IsXrefActionable(ref, hasExtTrackFormat) {
keyword = createKeyword(node.Data[ref.ActionLocation.Start:ref.ActionLocation.End])
} else {
keyword = &html.Node{
Type: html.TextNode,
Data: node.Data[ref.ActionLocation.Start:ref.ActionLocation.End],
}
}
spaces := &html.Node{
Type: html.TextNode,
Data: node.Data[ref.ActionLocation.End:ref.RefLocation.Start],
}
replaceContentList(node, ref.ActionLocation.Start, ref.RefLocation.End, []*html.Node{keyword, spaces, link})
node = node.NextSibling.NextSibling.NextSibling.NextSibling
}
}
func commitCrossReferencePatternProcessor(ctx *RenderContext, node *html.Node) {
next := node.NextSibling
for node != nil && node != next {
found, ref := references.FindRenderizableCommitCrossReference(node.Data)
if !found {
return
}
reftext := ref.Owner + "/" + ref.Name + "@" + base.ShortSha(ref.CommitSha)
link := createLink(util.URLJoin(ctx.Links.Prefix(), ref.Owner, ref.Name, "commit", ref.CommitSha), reftext, "commit")
replaceContent(node, ref.RefLocation.Start, ref.RefLocation.End, link)
node = node.NextSibling.NextSibling
}
}

View File

@ -4,7 +4,16 @@
package markup
import (
"net/url"
"path"
"path/filepath"
"strings"
"code.gitea.io/gitea/modules/markup/common"
"code.gitea.io/gitea/modules/util"
"golang.org/x/net/html"
"golang.org/x/net/html/atom"
)
func ResolveLink(ctx *RenderContext, link, userContentAnchorPrefix string) (result string, resolved bool) {
@ -27,3 +36,221 @@ func ResolveLink(ctx *RenderContext, link, userContentAnchorPrefix string) (resu
}
return link, resolved
}
func shortLinkProcessor(ctx *RenderContext, node *html.Node) {
next := node.NextSibling
for node != nil && node != next {
m := shortLinkPattern.FindStringSubmatchIndex(node.Data)
if m == nil {
return
}
content := node.Data[m[2]:m[3]]
tail := node.Data[m[4]:m[5]]
props := make(map[string]string)
// MediaWiki uses [[link|text]], while GitHub uses [[text|link]]
// It makes page handling terrible, but we prefer GitHub syntax
// And fall back to MediaWiki only when it is obvious from the look
// Of text and link contents
sl := strings.Split(content, "|")
for _, v := range sl {
if equalPos := strings.IndexByte(v, '='); equalPos == -1 {
// There is no equal in this argument; this is a mandatory arg
if props["name"] == "" {
if IsFullURLString(v) {
// If we clearly see it is a link, we save it so
// But first we need to ensure, that if both mandatory args provided
// look like links, we stick to GitHub syntax
if props["link"] != "" {
props["name"] = props["link"]
}
props["link"] = strings.TrimSpace(v)
} else {
props["name"] = v
}
} else {
props["link"] = strings.TrimSpace(v)
}
} else {
// There is an equal; optional argument.
sep := strings.IndexByte(v, '=')
key, val := v[:sep], html.UnescapeString(v[sep+1:])
// When parsing HTML, x/net/html will change all quotes which are
// not used for syntax into UTF-8 quotes. So checking val[0] won't
// be enough, since that only checks a single byte.
if len(val) > 1 {
if (strings.HasPrefix(val, "“") && strings.HasSuffix(val, "”")) ||
(strings.HasPrefix(val, "") && strings.HasSuffix(val, "")) {
const lenQuote = len("")
val = val[lenQuote : len(val)-lenQuote]
} else if (strings.HasPrefix(val, "\"") && strings.HasSuffix(val, "\"")) ||
(strings.HasPrefix(val, "'") && strings.HasSuffix(val, "'")) {
val = val[1 : len(val)-1]
} else if strings.HasPrefix(val, "'") && strings.HasSuffix(val, "") {
const lenQuote = len("")
val = val[1 : len(val)-lenQuote]
}
}
props[key] = val
}
}
var name, link string
if props["link"] != "" {
link = props["link"]
} else if props["name"] != "" {
link = props["name"]
}
if props["title"] != "" {
name = props["title"]
} else if props["name"] != "" {
name = props["name"]
} else {
name = link
}
name += tail
image := false
ext := filepath.Ext(link)
switch ext {
// fast path: empty string, ignore
case "":
// leave image as false
case ".jpg", ".jpeg", ".png", ".tif", ".tiff", ".webp", ".gif", ".bmp", ".ico", ".svg":
image = true
}
childNode := &html.Node{}
linkNode := &html.Node{
FirstChild: childNode,
LastChild: childNode,
Type: html.ElementNode,
Data: "a",
DataAtom: atom.A,
}
childNode.Parent = linkNode
absoluteLink := IsFullURLString(link)
if !absoluteLink {
if image {
link = strings.ReplaceAll(link, " ", "+")
} else {
link = strings.ReplaceAll(link, " ", "-") // FIXME: it should support dashes in the link, eg: "the-dash-support.-"
}
if !strings.Contains(link, "/") {
link = url.PathEscape(link) // FIXME: it doesn't seem right and it might cause double-escaping
}
}
if image {
if !absoluteLink {
link = util.URLJoin(ctx.Links.ResolveMediaLink(ctx.IsWiki), link)
}
title := props["title"]
if title == "" {
title = props["alt"]
}
if title == "" {
title = path.Base(name)
}
alt := props["alt"]
if alt == "" {
alt = name
}
// make the childNode an image - if we can, we also place the alt
childNode.Type = html.ElementNode
childNode.Data = "img"
childNode.DataAtom = atom.Img
childNode.Attr = []html.Attribute{
{Key: "src", Val: link},
{Key: "title", Val: title},
{Key: "alt", Val: alt},
}
if alt == "" {
childNode.Attr = childNode.Attr[:2]
}
} else {
link, _ = ResolveLink(ctx, link, "")
childNode.Type = html.TextNode
childNode.Data = name
}
linkNode.Attr = []html.Attribute{{Key: "href", Val: link}}
replaceContent(node, m[0], m[1], linkNode)
node = node.NextSibling.NextSibling
}
}
// linkProcessor creates links for any HTTP or HTTPS URL not captured by
// markdown.
func linkProcessor(ctx *RenderContext, node *html.Node) {
next := node.NextSibling
for node != nil && node != next {
m := common.LinkRegex.FindStringIndex(node.Data)
if m == nil {
return
}
uri := node.Data[m[0]:m[1]]
replaceContent(node, m[0], m[1], createLink(uri, uri, "link"))
node = node.NextSibling.NextSibling
}
}
func genDefaultLinkProcessor(defaultLink string) processor {
return func(ctx *RenderContext, node *html.Node) {
ch := &html.Node{
Parent: node,
Type: html.TextNode,
Data: node.Data,
}
node.Type = html.ElementNode
node.Data = "a"
node.DataAtom = atom.A
node.Attr = []html.Attribute{
{Key: "href", Val: defaultLink},
{Key: "class", Val: "default-link muted"},
}
node.FirstChild, node.LastChild = ch, ch
}
}
// descriptionLinkProcessor creates links for DescriptionHTML
func descriptionLinkProcessor(ctx *RenderContext, node *html.Node) {
next := node.NextSibling
for node != nil && node != next {
m := common.LinkRegex.FindStringIndex(node.Data)
if m == nil {
return
}
uri := node.Data[m[0]:m[1]]
replaceContent(node, m[0], m[1], createDescriptionLink(uri, uri))
node = node.NextSibling.NextSibling
}
}
func createDescriptionLink(href, content string) *html.Node {
textNode := &html.Node{
Type: html.TextNode,
Data: content,
}
linkNode := &html.Node{
FirstChild: textNode,
LastChild: textNode,
Type: html.ElementNode,
Data: "a",
DataAtom: atom.A,
Attr: []html.Attribute{
{Key: "href", Val: href},
{Key: "target", Val: "_blank"},
{Key: "rel", Val: "noopener noreferrer"},
},
}
textNode.Parent = linkNode
return linkNode
}

View File

@ -0,0 +1,54 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package markup
import (
"strings"
"code.gitea.io/gitea/modules/references"
"code.gitea.io/gitea/modules/util"
"golang.org/x/net/html"
)
func mentionProcessor(ctx *RenderContext, node *html.Node) {
start := 0
nodeStop := node.NextSibling
for node != nodeStop {
found, loc := references.FindFirstMentionBytes(util.UnsafeStringToBytes(node.Data[start:]))
if !found {
node = node.NextSibling
start = 0
continue
}
loc.Start += start
loc.End += start
mention := node.Data[loc.Start:loc.End]
teams, ok := ctx.Metas["teams"]
// FIXME: util.URLJoin may not be necessary here:
// - setting.AppURL is defined to have a terminal '/' so unless mention[1:]
// is an AppSubURL link we can probably fallback to concatenation.
// team mention should follow @orgName/teamName style
if ok && strings.Contains(mention, "/") {
mentionOrgAndTeam := strings.Split(mention, "/")
if mentionOrgAndTeam[0][1:] == ctx.Metas["org"] && strings.Contains(teams, ","+strings.ToLower(mentionOrgAndTeam[1])+",") {
replaceContent(node, loc.Start, loc.End, createLink(util.URLJoin(ctx.Links.Prefix(), "org", ctx.Metas["org"], "teams", mentionOrgAndTeam[1]), mention, "mention"))
node = node.NextSibling.NextSibling
start = 0
continue
}
start = loc.End
continue
}
mentionedUsername := mention[1:]
if DefaultProcessorHelper.IsUsernameMentionable != nil && DefaultProcessorHelper.IsUsernameMentionable(ctx.Ctx, mentionedUsername) {
replaceContent(node, loc.Start, loc.End, createLink(util.URLJoin(ctx.Links.Prefix(), mentionedUsername), mention, "mention"))
node = node.NextSibling.NextSibling
start = 0
} else {
start = loc.End
}
}
}

View File

@ -116,7 +116,9 @@ func TestRender_CrossReferences(t *testing.T) {
Metas: localMetas,
}, input)
assert.NoError(t, err)
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
actual := strings.TrimSpace(buffer)
actual = strings.ReplaceAll(actual, ` data-markdown-generated-content=""`, "")
assert.Equal(t, strings.TrimSpace(expected), actual)
}
test(
@ -156,7 +158,9 @@ func TestRender_links(t *testing.T) {
},
}, input)
assert.NoError(t, err)
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer))
actual := strings.TrimSpace(buffer)
actual = strings.ReplaceAll(actual, ` data-markdown-generated-content=""`, "")
assert.Equal(t, strings.TrimSpace(expected), actual)
}
oldCustomURLSchemes := setting.Markdown.CustomURLSchemes
@ -267,7 +271,9 @@ func TestRender_email(t *testing.T) {
},
}, input)
assert.NoError(t, err)
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(res))
actual := strings.TrimSpace(res)
actual = strings.ReplaceAll(actual, ` data-markdown-generated-content=""`, "")
assert.Equal(t, strings.TrimSpace(expected), actual)
}
// Text that should be turned into email link
@ -616,7 +622,9 @@ func TestPostProcess_RenderDocument(t *testing.T) {
Metas: localMetas,
}, strings.NewReader(input), &res)
assert.NoError(t, err)
assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(res.String()))
actual := strings.TrimSpace(res.String())
actual = strings.ReplaceAll(actual, ` data-markdown-generated-content=""`, "")
assert.Equal(t, strings.TrimSpace(expected), actual)
}
// Issue index shouldn't be post processing in a document.

View File

@ -45,7 +45,7 @@ func (g *ASTTransformer) Transform(node *ast.Document, reader text.Reader, pc pa
ctx := pc.Get(renderContextKey).(*markup.RenderContext)
rc := pc.Get(renderConfigKey).(*RenderConfig)
tocList := make([]markup.Header, 0, 20)
tocList := make([]Header, 0, 20)
if rc.yamlNode != nil {
metaNode := rc.toMetaNode()
if metaNode != nil {
@ -213,8 +213,7 @@ func (r *HTMLRenderer) renderIcon(w util.BufWriter, source []byte, node ast.Node
return ast.WalkContinue, nil
}
var err error
_, err = w.WriteString(fmt.Sprintf(`<i class="icon %s"></i>`, name))
_, err := w.WriteString(fmt.Sprintf(`<i class="icon %s"></i>`, name))
if err != nil {
return ast.WalkStop, err
}

View File

@ -311,7 +311,8 @@ func TestTotal_RenderWiki(t *testing.T) {
IsWiki: true,
}, sameCases[i])
assert.NoError(t, err)
assert.Equal(t, template.HTML(answers[i]), line)
actual := strings.ReplaceAll(string(line), ` data-markdown-generated-content=""`, "")
assert.Equal(t, answers[i], actual)
}
testCases := []string{
@ -336,7 +337,8 @@ func TestTotal_RenderWiki(t *testing.T) {
IsWiki: true,
}, testCases[i])
assert.NoError(t, err)
assert.Equal(t, template.HTML(testCases[i+1]), line)
actual := strings.ReplaceAll(string(line), ` data-markdown-generated-content=""`, "")
assert.EqualValues(t, testCases[i+1], actual)
}
}
@ -356,7 +358,8 @@ func TestTotal_RenderString(t *testing.T) {
Metas: localMetas,
}, sameCases[i])
assert.NoError(t, err)
assert.Equal(t, template.HTML(answers[i]), line)
actual := strings.ReplaceAll(string(line), ` data-markdown-generated-content=""`, "")
assert.Equal(t, answers[i], actual)
}
testCases := []string{}
@ -996,7 +999,8 @@ space</p>
for i, c := range cases {
result, err := markdown.RenderString(&markup.RenderContext{Ctx: context.Background(), Links: c.Links, IsWiki: c.IsWiki}, input)
assert.NoError(t, err, "Unexpected error in testcase: %v", i)
assert.Equal(t, c.Expected, string(result), "Unexpected result in testcase %v", i)
actual := strings.ReplaceAll(string(result), ` data-markdown-generated-content=""`, "")
assert.Equal(t, c.Expected, actual, "Unexpected result in testcase %v", i)
}
}

View File

@ -7,13 +7,19 @@ import (
"fmt"
"net/url"
"code.gitea.io/gitea/modules/markup"
"code.gitea.io/gitea/modules/translation"
"github.com/yuin/goldmark/ast"
)
func createTOCNode(toc []markup.Header, lang string, detailsAttrs map[string]string) ast.Node {
// Header holds the data about a header.
type Header struct {
Level int
Text string
ID string
}
func createTOCNode(toc []Header, lang string, detailsAttrs map[string]string) ast.Node {
details := NewDetails()
summary := NewSummary()

View File

@ -45,7 +45,7 @@ func (g *ASTTransformer) extractBlockquoteAttentionEmphasis(firstParagraph ast.N
if !ok {
return "", nil
}
val1 := string(node1.Text(reader.Source()))
val1 := string(node1.Text(reader.Source())) //nolint:staticcheck
attentionType := strings.ToLower(val1)
if g.attentionTypes.Contains(attentionType) {
return attentionType, []ast.Node{node1}

View File

@ -69,7 +69,7 @@ func cssColorHandler(value string) bool {
}
func (g *ASTTransformer) transformCodeSpan(_ *markup.RenderContext, v *ast.CodeSpan, reader text.Reader) {
colorContent := v.Text(reader.Source())
colorContent := v.Text(reader.Source()) //nolint:staticcheck
if cssColorHandler(string(colorContent)) {
v.AppendChild(v, NewColorPreview(colorContent))
}

View File

@ -13,14 +13,14 @@ import (
"github.com/yuin/goldmark/text"
)
func (g *ASTTransformer) transformHeading(_ *markup.RenderContext, v *ast.Heading, reader text.Reader, tocList *[]markup.Header) {
func (g *ASTTransformer) transformHeading(_ *markup.RenderContext, v *ast.Heading, reader text.Reader, tocList *[]Header) {
for _, attr := range v.Attributes() {
if _, ok := attr.Value.([]byte); !ok {
v.SetAttribute(attr.Name, []byte(fmt.Sprintf("%v", attr.Value)))
}
}
txt := v.Text(reader.Source())
header := markup.Header{
txt := v.Text(reader.Source()) //nolint:staticcheck
header := Header{
Text: util.UnsafeBytesToString(txt),
Level: v.Level,
}

View File

@ -46,7 +46,7 @@ func (r *stripRenderer) Render(w io.Writer, source []byte, doc ast.Node) error {
coalesce := prevSibIsText
r.processString(
w,
v.Text(source),
v.Text(source), //nolint:staticcheck
coalesce)
if v.SoftLineBreak() {
r.doubleSpace(w)

226
modules/markup/render.go Normal file
View File

@ -0,0 +1,226 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package markup
import (
"context"
"errors"
"fmt"
"io"
"net/url"
"path/filepath"
"strings"
"sync"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
"github.com/yuin/goldmark/ast"
)
type RenderMetaMode string
const (
RenderMetaAsDetails RenderMetaMode = "details" // default
RenderMetaAsNone RenderMetaMode = "none"
RenderMetaAsTable RenderMetaMode = "table"
)
// RenderContext represents a render context
type RenderContext struct {
Ctx context.Context
RelativePath string // relative path from tree root of the branch
Type string
IsWiki bool
Links Links
Metas map[string]string // user, repo, mode(comment/document)
DefaultLink string
GitRepo *git.Repository
Repo gitrepo.Repository
ShaExistCache map[string]bool
cancelFn func()
SidebarTocNode ast.Node
RenderMetaAs RenderMetaMode
InStandalonePage bool // used by external render. the router "/org/repo/render/..." will output the rendered content in a standalone page
}
// Cancel runs any cleanup functions that have been registered for this Ctx
func (ctx *RenderContext) Cancel() {
if ctx == nil {
return
}
ctx.ShaExistCache = map[string]bool{}
if ctx.cancelFn == nil {
return
}
ctx.cancelFn()
}
// AddCancel adds the provided fn as a Cleanup for this Ctx
func (ctx *RenderContext) AddCancel(fn func()) {
if ctx == nil {
return
}
oldCancelFn := ctx.cancelFn
if oldCancelFn == nil {
ctx.cancelFn = fn
return
}
ctx.cancelFn = func() {
defer oldCancelFn()
fn()
}
}
// Render renders markup file to HTML with all specific handling stuff.
func Render(ctx *RenderContext, input io.Reader, output io.Writer) error {
if ctx.Type != "" {
return renderByType(ctx, input, output)
} else if ctx.RelativePath != "" {
return renderFile(ctx, input, output)
}
return errors.New("render options both filename and type missing")
}
// RenderString renders Markup string to HTML with all specific handling stuff and return string
func RenderString(ctx *RenderContext, content string) (string, error) {
var buf strings.Builder
if err := Render(ctx, strings.NewReader(content), &buf); err != nil {
return "", err
}
return buf.String(), nil
}
func renderIFrame(ctx *RenderContext, output io.Writer) error {
// set height="0" ahead, otherwise the scrollHeight would be max(150, realHeight)
// at the moment, only "allow-scripts" is allowed for sandbox mode.
// "allow-same-origin" should never be used, it leads to XSS attack, and it makes the JS in iframe can access parent window's config and CSRF token
// TODO: when using dark theme, if the rendered content doesn't have proper style, the default text color is black, which is not easy to read
_, err := io.WriteString(output, fmt.Sprintf(`
<iframe src="%s/%s/%s/render/%s/%s"
name="giteaExternalRender"
onload="this.height=giteaExternalRender.document.documentElement.scrollHeight"
width="100%%" height="0" scrolling="no" frameborder="0" style="overflow: hidden"
sandbox="allow-scripts"
></iframe>`,
setting.AppSubURL,
url.PathEscape(ctx.Metas["user"]),
url.PathEscape(ctx.Metas["repo"]),
ctx.Metas["BranchNameSubURL"],
url.PathEscape(ctx.RelativePath),
))
return err
}
func render(ctx *RenderContext, renderer Renderer, input io.Reader, output io.Writer) error {
var wg sync.WaitGroup
var err error
pr, pw := io.Pipe()
defer func() {
_ = pr.Close()
_ = pw.Close()
}()
var pr2 io.ReadCloser
var pw2 io.WriteCloser
var sanitizerDisabled bool
if r, ok := renderer.(ExternalRenderer); ok {
sanitizerDisabled = r.SanitizerDisabled()
}
if !sanitizerDisabled {
pr2, pw2 = io.Pipe()
defer func() {
_ = pr2.Close()
_ = pw2.Close()
}()
wg.Add(1)
go func() {
err = SanitizeReader(pr2, renderer.Name(), output)
_ = pr2.Close()
wg.Done()
}()
} else {
pw2 = util.NopCloser{Writer: output}
}
wg.Add(1)
go func() {
if r, ok := renderer.(PostProcessRenderer); ok && r.NeedPostProcess() {
err = PostProcess(ctx, pr, pw2)
} else {
_, err = io.Copy(pw2, pr)
}
_ = pr.Close()
_ = pw2.Close()
wg.Done()
}()
if err1 := renderer.Render(ctx, input, pw); err1 != nil {
return err1
}
_ = pw.Close()
wg.Wait()
return err
}
func renderByType(ctx *RenderContext, input io.Reader, output io.Writer) error {
if renderer, ok := renderers[ctx.Type]; ok {
return render(ctx, renderer, input, output)
}
return fmt.Errorf("unsupported render type: %s", ctx.Type)
}
// ErrUnsupportedRenderExtension represents the error when extension doesn't supported to render
type ErrUnsupportedRenderExtension struct {
Extension string
}
func IsErrUnsupportedRenderExtension(err error) bool {
_, ok := err.(ErrUnsupportedRenderExtension)
return ok
}
func (err ErrUnsupportedRenderExtension) Error() string {
return fmt.Sprintf("Unsupported render extension: %s", err.Extension)
}
func renderFile(ctx *RenderContext, input io.Reader, output io.Writer) error {
extension := strings.ToLower(filepath.Ext(ctx.RelativePath))
if renderer, ok := extRenderers[extension]; ok {
if r, ok := renderer.(ExternalRenderer); ok && r.DisplayInIFrame() {
if !ctx.InStandalonePage {
// for an external render, it could only output its content in a standalone page
// otherwise, a <iframe> should be outputted to embed the external rendered page
return renderIFrame(ctx, output)
}
}
return render(ctx, renderer, input, output)
}
return ErrUnsupportedRenderExtension{extension}
}
// Init initializes the render global variables
func Init(ph *ProcessorHelper) {
if ph != nil {
DefaultProcessorHelper = *ph
}
if len(setting.Markdown.CustomURLSchemes) > 0 {
CustomLinkURLSchemes(setting.Markdown.CustomURLSchemes)
}
// since setting maybe changed extensions, this will reload all renderer extensions mapping
extRenderers = make(map[string]Renderer)
for _, renderer := range renderers {
for _, ext := range renderer.Extensions() {
extRenderers[strings.ToLower(ext)] = renderer
}
}
}

View File

@ -0,0 +1,21 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package markup
import (
"context"
"html/template"
)
// ProcessorHelper is a helper for the rendering processors (it could be renamed to RenderHelper in the future).
// The main purpose of this helper is to decouple some functions which are not directly available in this package.
type ProcessorHelper struct {
IsUsernameMentionable func(ctx context.Context, username string) bool
ElementDir string // the direction of the elements, eg: "ltr", "rtl", "auto", default to no direction attribute
RenderRepoFileCodePreview func(ctx context.Context, options RenderCodePreviewOptions) (template.HTML, error)
}
var DefaultProcessorHelper ProcessorHelper

View File

@ -0,0 +1,56 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package markup
import (
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
)
type Links struct {
AbsolutePrefix bool // add absolute URL prefix to auto-resolved links like "#issue", but not for pre-provided links and medias
Base string // base prefix for pre-provided links and medias (images, videos)
BranchPath string // actually it is the ref path, eg: "branch/features/feat-12", "tag/v1.0"
TreePath string // the dir of the file, eg: "doc" if the file "doc/CHANGE.md" is being rendered
}
func (l *Links) Prefix() string {
if l.AbsolutePrefix {
return setting.AppURL
}
return setting.AppSubURL
}
func (l *Links) HasBranchInfo() bool {
return l.BranchPath != ""
}
func (l *Links) SrcLink() string {
return util.URLJoin(l.Base, "src", l.BranchPath, l.TreePath)
}
func (l *Links) MediaLink() string {
return util.URLJoin(l.Base, "media", l.BranchPath, l.TreePath)
}
func (l *Links) RawLink() string {
return util.URLJoin(l.Base, "raw", l.BranchPath, l.TreePath)
}
func (l *Links) WikiLink() string {
return util.URLJoin(l.Base, "wiki")
}
func (l *Links) WikiRawLink() string {
return util.URLJoin(l.Base, "wiki/raw")
}
func (l *Links) ResolveMediaLink(isWiki bool) string {
if isWiki {
return l.WikiRawLink()
} else if l.HasBranchInfo() {
return l.MediaLink()
}
return l.Base
}

View File

@ -5,161 +5,13 @@ package markup
import (
"bytes"
"context"
"errors"
"fmt"
"html/template"
"io"
"net/url"
"path/filepath"
"strings"
"sync"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/gitrepo"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
"github.com/yuin/goldmark/ast"
)
type RenderMetaMode string
const (
RenderMetaAsDetails RenderMetaMode = "details" // default
RenderMetaAsNone RenderMetaMode = "none"
RenderMetaAsTable RenderMetaMode = "table"
)
type ProcessorHelper struct {
IsUsernameMentionable func(ctx context.Context, username string) bool
ElementDir string // the direction of the elements, eg: "ltr", "rtl", "auto", default to no direction attribute
RenderRepoFileCodePreview func(ctx context.Context, options RenderCodePreviewOptions) (template.HTML, error)
}
var DefaultProcessorHelper ProcessorHelper
// Init initialize regexps for markdown parsing
func Init(ph *ProcessorHelper) {
if ph != nil {
DefaultProcessorHelper = *ph
}
if len(setting.Markdown.CustomURLSchemes) > 0 {
CustomLinkURLSchemes(setting.Markdown.CustomURLSchemes)
}
// since setting maybe changed extensions, this will reload all renderer extensions mapping
extRenderers = make(map[string]Renderer)
for _, renderer := range renderers {
for _, ext := range renderer.Extensions() {
extRenderers[strings.ToLower(ext)] = renderer
}
}
}
// Header holds the data about a header.
type Header struct {
Level int
Text string
ID string
}
// RenderContext represents a render context
type RenderContext struct {
Ctx context.Context
RelativePath string // relative path from tree root of the branch
Type string
IsWiki bool
Links Links
Metas map[string]string // user, repo, mode(comment/document)
DefaultLink string
GitRepo *git.Repository
Repo gitrepo.Repository
ShaExistCache map[string]bool
cancelFn func()
SidebarTocNode ast.Node
RenderMetaAs RenderMetaMode
InStandalonePage bool // used by external render. the router "/org/repo/render/..." will output the rendered content in a standalone page
}
type Links struct {
AbsolutePrefix bool // add absolute URL prefix to auto-resolved links like "#issue", but not for pre-provided links and medias
Base string // base prefix for pre-provided links and medias (images, videos)
BranchPath string // actually it is the ref path, eg: "branch/features/feat-12", "tag/v1.0"
TreePath string // the dir of the file, eg: "doc" if the file "doc/CHANGE.md" is being rendered
}
func (l *Links) Prefix() string {
if l.AbsolutePrefix {
return setting.AppURL
}
return setting.AppSubURL
}
func (l *Links) HasBranchInfo() bool {
return l.BranchPath != ""
}
func (l *Links) SrcLink() string {
return util.URLJoin(l.Base, "src", l.BranchPath, l.TreePath)
}
func (l *Links) MediaLink() string {
return util.URLJoin(l.Base, "media", l.BranchPath, l.TreePath)
}
func (l *Links) RawLink() string {
return util.URLJoin(l.Base, "raw", l.BranchPath, l.TreePath)
}
func (l *Links) WikiLink() string {
return util.URLJoin(l.Base, "wiki")
}
func (l *Links) WikiRawLink() string {
return util.URLJoin(l.Base, "wiki/raw")
}
func (l *Links) ResolveMediaLink(isWiki bool) string {
if isWiki {
return l.WikiRawLink()
} else if l.HasBranchInfo() {
return l.MediaLink()
}
return l.Base
}
// Cancel runs any cleanup functions that have been registered for this Ctx
func (ctx *RenderContext) Cancel() {
if ctx == nil {
return
}
ctx.ShaExistCache = map[string]bool{}
if ctx.cancelFn == nil {
return
}
ctx.cancelFn()
}
// AddCancel adds the provided fn as a Cleanup for this Ctx
func (ctx *RenderContext) AddCancel(fn func()) {
if ctx == nil {
return
}
oldCancelFn := ctx.cancelFn
if oldCancelFn == nil {
ctx.cancelFn = fn
return
}
ctx.cancelFn = func() {
defer oldCancelFn()
fn()
}
}
// Renderer defines an interface for rendering markup file to HTML
type Renderer interface {
Name() string // markup format name
@ -173,7 +25,7 @@ type PostProcessRenderer interface {
NeedPostProcess() bool
}
// PostProcessRenderer defines an interface for external renderers
// ExternalRenderer defines an interface for external renderers
type ExternalRenderer interface {
// SanitizerDisabled disabled sanitize if return true
SanitizerDisabled() bool
@ -207,11 +59,6 @@ func GetRendererByFileName(filename string) Renderer {
return extRenderers[extension]
}
// GetRendererByType returns a renderer according type
func GetRendererByType(tp string) Renderer {
return renderers[tp]
}
// DetectRendererType detects the markup type of the content
func DetectRendererType(filename string, input io.Reader) string {
buf, err := io.ReadAll(input)
@ -226,152 +73,6 @@ func DetectRendererType(filename string, input io.Reader) string {
return ""
}
// Render renders markup file to HTML with all specific handling stuff.
func Render(ctx *RenderContext, input io.Reader, output io.Writer) error {
if ctx.Type != "" {
return renderByType(ctx, input, output)
} else if ctx.RelativePath != "" {
return renderFile(ctx, input, output)
}
return errors.New("Render options both filename and type missing")
}
// RenderString renders Markup string to HTML with all specific handling stuff and return string
func RenderString(ctx *RenderContext, content string) (string, error) {
var buf strings.Builder
if err := Render(ctx, strings.NewReader(content), &buf); err != nil {
return "", err
}
return buf.String(), nil
}
type nopCloser struct {
io.Writer
}
func (nopCloser) Close() error { return nil }
func renderIFrame(ctx *RenderContext, output io.Writer) error {
// set height="0" ahead, otherwise the scrollHeight would be max(150, realHeight)
// at the moment, only "allow-scripts" is allowed for sandbox mode.
// "allow-same-origin" should never be used, it leads to XSS attack, and it makes the JS in iframe can access parent window's config and CSRF token
// TODO: when using dark theme, if the rendered content doesn't have proper style, the default text color is black, which is not easy to read
_, err := io.WriteString(output, fmt.Sprintf(`
<iframe src="%s/%s/%s/render/%s/%s"
name="giteaExternalRender"
onload="this.height=giteaExternalRender.document.documentElement.scrollHeight"
width="100%%" height="0" scrolling="no" frameborder="0" style="overflow: hidden"
sandbox="allow-scripts"
></iframe>`,
setting.AppSubURL,
url.PathEscape(ctx.Metas["user"]),
url.PathEscape(ctx.Metas["repo"]),
ctx.Metas["BranchNameSubURL"],
url.PathEscape(ctx.RelativePath),
))
return err
}
func render(ctx *RenderContext, renderer Renderer, input io.Reader, output io.Writer) error {
var wg sync.WaitGroup
var err error
pr, pw := io.Pipe()
defer func() {
_ = pr.Close()
_ = pw.Close()
}()
var pr2 io.ReadCloser
var pw2 io.WriteCloser
var sanitizerDisabled bool
if r, ok := renderer.(ExternalRenderer); ok {
sanitizerDisabled = r.SanitizerDisabled()
}
if !sanitizerDisabled {
pr2, pw2 = io.Pipe()
defer func() {
_ = pr2.Close()
_ = pw2.Close()
}()
wg.Add(1)
go func() {
err = SanitizeReader(pr2, renderer.Name(), output)
_ = pr2.Close()
wg.Done()
}()
} else {
pw2 = nopCloser{output}
}
wg.Add(1)
go func() {
if r, ok := renderer.(PostProcessRenderer); ok && r.NeedPostProcess() {
err = PostProcess(ctx, pr, pw2)
} else {
_, err = io.Copy(pw2, pr)
}
_ = pr.Close()
_ = pw2.Close()
wg.Done()
}()
if err1 := renderer.Render(ctx, input, pw); err1 != nil {
return err1
}
_ = pw.Close()
wg.Wait()
return err
}
// ErrUnsupportedRenderType represents
type ErrUnsupportedRenderType struct {
Type string
}
func (err ErrUnsupportedRenderType) Error() string {
return fmt.Sprintf("Unsupported render type: %s", err.Type)
}
func renderByType(ctx *RenderContext, input io.Reader, output io.Writer) error {
if renderer, ok := renderers[ctx.Type]; ok {
return render(ctx, renderer, input, output)
}
return ErrUnsupportedRenderType{ctx.Type}
}
// ErrUnsupportedRenderExtension represents the error when extension doesn't supported to render
type ErrUnsupportedRenderExtension struct {
Extension string
}
func IsErrUnsupportedRenderExtension(err error) bool {
_, ok := err.(ErrUnsupportedRenderExtension)
return ok
}
func (err ErrUnsupportedRenderExtension) Error() string {
return fmt.Sprintf("Unsupported render extension: %s", err.Extension)
}
func renderFile(ctx *RenderContext, input io.Reader, output io.Writer) error {
extension := strings.ToLower(filepath.Ext(ctx.RelativePath))
if renderer, ok := extRenderers[extension]; ok {
if r, ok := renderer.(ExternalRenderer); ok && r.DisplayInIFrame() {
if !ctx.InStandalonePage {
// for an external render, it could only output its content in a standalone page
// otherwise, a <iframe> should be outputted to embed the external rendered page
return renderIFrame(ctx, output)
}
}
return render(ctx, renderer, input, output)
}
return ErrUnsupportedRenderExtension{extension}
}
// DetectMarkupTypeByFileName returns the possible markup format type via the filename
func DetectMarkupTypeByFileName(filename string) string {
if parser := GetRendererByFileName(filename); parser != nil {

View File

@ -107,6 +107,7 @@ func (st *Sanitizer) createDefaultPolicy() *bluemonday.Policy {
"start", "summary", "tabindex", "target",
"title", "type", "usemap", "valign", "value",
"vspace", "width", "itemprop",
"data-markdown-generated-content",
}
generalSafeElements := []string{

View File

@ -136,8 +136,16 @@ func parsePackage(r io.Reader) (*Package, error) {
dependencies := make([]*Dependency, 0, len(meta.Deps))
for _, dep := range meta.Deps {
// https://doc.rust-lang.org/cargo/reference/registry-web-api.html#publish
// It is a string of the new package name if the dependency is renamed, otherwise empty
name := dep.ExplicitNameInToml
pkg := &dep.Name
if name == "" {
name = dep.Name
pkg = nil
}
dependencies = append(dependencies, &Dependency{
Name: dep.Name,
Name: name,
Req: dep.VersionReq,
Features: dep.Features,
Optional: dep.Optional,
@ -145,6 +153,7 @@ func parsePackage(r io.Reader) (*Package, error) {
Target: dep.Target,
Kind: dep.Kind,
Registry: dep.Registry,
Package: pkg,
})
}

View File

@ -13,16 +13,16 @@ import (
"github.com/stretchr/testify/assert"
)
func TestParsePackage(t *testing.T) {
const (
description = "Package Description"
author = "KN4CK3R"
homepage = "https://gitea.io/"
license = "MIT"
payload = "gitea test dummy payload" // a fake payload for test only
)
func TestParsePackage(t *testing.T) {
createPackage := func(name, version string) io.Reader {
metadata := `{
makeDefaultPackageMeta := func(name, version string) string {
return `{
"name":"` + name + `",
"vers":"` + version + `",
"description":"` + description + `",
@ -36,18 +36,19 @@ func TestParsePackage(t *testing.T) {
"homepage":"` + homepage + `",
"license":"` + license + `"
}`
}
createPackage := func(metadata string) io.Reader {
var buf bytes.Buffer
binary.Write(&buf, binary.LittleEndian, uint32(len(metadata)))
buf.WriteString(metadata)
binary.Write(&buf, binary.LittleEndian, uint32(4))
buf.WriteString("test")
binary.Write(&buf, binary.LittleEndian, uint32(len(payload)))
buf.WriteString(payload)
return &buf
}
t.Run("InvalidName", func(t *testing.T) {
for _, name := range []string{"", "0test", "-test", "_test", strings.Repeat("a", 65)} {
data := createPackage(name, "1.0.0")
data := createPackage(makeDefaultPackageMeta(name, "1.0.0"))
cp, err := ParsePackage(data)
assert.Nil(t, cp)
@ -57,7 +58,7 @@ func TestParsePackage(t *testing.T) {
t.Run("InvalidVersion", func(t *testing.T) {
for _, version := range []string{"", "1.", "-1.0", "1.0.0/1"} {
data := createPackage("test", version)
data := createPackage(makeDefaultPackageMeta("test", version))
cp, err := ParsePackage(data)
assert.Nil(t, cp)
@ -66,7 +67,7 @@ func TestParsePackage(t *testing.T) {
})
t.Run("Valid", func(t *testing.T) {
data := createPackage("test", "1.0.0")
data := createPackage(makeDefaultPackageMeta("test", "1.0.0"))
cp, err := ParsePackage(data)
assert.NotNil(t, cp)
@ -78,9 +79,34 @@ func TestParsePackage(t *testing.T) {
assert.Equal(t, []string{author}, cp.Metadata.Authors)
assert.Len(t, cp.Metadata.Dependencies, 1)
assert.Equal(t, "dep", cp.Metadata.Dependencies[0].Name)
assert.Nil(t, cp.Metadata.Dependencies[0].Package)
assert.Equal(t, homepage, cp.Metadata.ProjectURL)
assert.Equal(t, license, cp.Metadata.License)
content, _ := io.ReadAll(cp.Content)
assert.Equal(t, "test", string(content))
assert.Equal(t, payload, string(content))
})
t.Run("Renamed", func(t *testing.T) {
data := createPackage(`{
"name":"test-pkg",
"vers":"1.0",
"description":"test-desc",
"authors": ["test-author"],
"deps":[
{
"name":"dep-renamed",
"explicit_name_in_toml":"dep-explicit",
"version_req":"1.0"
}
],
"homepage":"https://gitea.io/",
"license":"MIT"
}`)
cp, err := ParsePackage(data)
assert.NoError(t, err)
assert.Equal(t, "test-pkg", cp.Name)
assert.Equal(t, "https://gitea.io/", cp.Metadata.ProjectURL)
assert.Equal(t, "dep-explicit", cp.Metadata.Dependencies[0].Name)
assert.Equal(t, "dep-renamed", *cp.Metadata.Dependencies[0].Package)
})
}

View File

@ -37,8 +37,8 @@ func (s *ContentStore) ShouldServeDirect() bool {
return setting.Packages.Storage.ServeDirect()
}
func (s *ContentStore) GetServeDirectURL(key BlobHash256Key, filename string) (*url.URL, error) {
return s.store.URL(KeyToRelativePath(key), filename)
func (s *ContentStore) GetServeDirectURL(key BlobHash256Key, filename string, reqParams url.Values) (*url.URL, error) {
return s.store.URL(KeyToRelativePath(key), filename, reqParams)
}
// FIXME: Workaround to be removed in v1.20

View File

@ -10,6 +10,7 @@ import (
"io"
"testing"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/zstd"
"github.com/blakesmith/ar"
@ -77,7 +78,7 @@ func TestParsePackage(t *testing.T) {
{
Extension: "",
WriterFactory: func(w io.Writer) io.WriteCloser {
return nopCloser{w}
return util.NopCloser{Writer: w}
},
},
{
@ -129,14 +130,6 @@ func TestParsePackage(t *testing.T) {
})
}
type nopCloser struct {
io.Writer
}
func (nopCloser) Close() error {
return nil
}
func TestParseControlFile(t *testing.T) {
buildContent := func(name, version, architecture string) *bytes.Buffer {
var buf bytes.Buffer

View File

@ -43,7 +43,7 @@ Ensure you are running in the correct environment or set the correct configurati
req := httplib.NewRequest(url, method).
SetContext(ctx).
Header("X-Real-IP", getClientIP()).
Header("Authorization", fmt.Sprintf("Bearer %s", setting.InternalToken)).
Header("X-Gitea-Internal-Auth", fmt.Sprintf("Bearer %s", setting.InternalToken)).
SetTLSClientConfig(&tls.Config{
InsecureSkipVerify: true,
ServerName: setting.Domain,

View File

@ -1,48 +0,0 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package repository
import (
"context"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/perm"
access_model "code.gitea.io/gitea/models/perm/access"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"xorm.io/builder"
)
func AddCollaborator(ctx context.Context, repo *repo_model.Repository, u *user_model.User) error {
if err := repo.LoadOwner(ctx); err != nil {
return err
}
if user_model.IsUserBlockedBy(ctx, u, repo.OwnerID) || user_model.IsUserBlockedBy(ctx, repo.Owner, u.ID) {
return user_model.ErrBlockedUser
}
return db.WithTx(ctx, func(ctx context.Context) error {
has, err := db.Exist[repo_model.Collaboration](ctx, builder.Eq{
"repo_id": repo.ID,
"user_id": u.ID,
})
if err != nil {
return err
} else if has {
return nil
}
if err = db.Insert(ctx, &repo_model.Collaboration{
RepoID: repo.ID,
UserID: u.ID,
Mode: perm.AccessModeWrite,
}); err != nil {
return err
}
return access_model.RecalculateUserAccess(ctx, repo, u.ID)
})
}

View File

@ -1,280 +0,0 @@
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package repository
import (
"testing"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/organization"
perm_model "code.gitea.io/gitea/models/perm"
access_model "code.gitea.io/gitea/models/perm/access"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
"github.com/stretchr/testify/assert"
)
func TestRepository_AddCollaborator(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
testSuccess := func(repoID, userID int64) {
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repoID})
assert.NoError(t, repo.LoadOwner(db.DefaultContext))
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: userID})
assert.NoError(t, AddCollaborator(db.DefaultContext, repo, user))
unittest.CheckConsistencyFor(t, &repo_model.Repository{ID: repoID}, &user_model.User{ID: userID})
}
testSuccess(1, 4)
testSuccess(1, 4)
testSuccess(3, 4)
}
func TestRepoPermissionPublicNonOrgRepo(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
// public non-organization repo
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4})
assert.NoError(t, repo.LoadUnits(db.DefaultContext))
// plain user
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
perm, err := access_model.GetUserRepoPermission(db.DefaultContext, repo, user)
assert.NoError(t, err)
for _, unit := range repo.Units {
assert.True(t, perm.CanRead(unit.Type))
assert.False(t, perm.CanWrite(unit.Type))
}
// change to collaborator
assert.NoError(t, AddCollaborator(db.DefaultContext, repo, user))
perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, user)
assert.NoError(t, err)
for _, unit := range repo.Units {
assert.True(t, perm.CanRead(unit.Type))
assert.True(t, perm.CanWrite(unit.Type))
}
// collaborator
collaborator := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4})
perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, collaborator)
assert.NoError(t, err)
for _, unit := range repo.Units {
assert.True(t, perm.CanRead(unit.Type))
assert.True(t, perm.CanWrite(unit.Type))
}
// owner
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5})
perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, owner)
assert.NoError(t, err)
for _, unit := range repo.Units {
assert.True(t, perm.CanRead(unit.Type))
assert.True(t, perm.CanWrite(unit.Type))
}
// admin
admin := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, admin)
assert.NoError(t, err)
for _, unit := range repo.Units {
assert.True(t, perm.CanRead(unit.Type))
assert.True(t, perm.CanWrite(unit.Type))
}
}
func TestRepoPermissionPrivateNonOrgRepo(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
// private non-organization repo
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2})
assert.NoError(t, repo.LoadUnits(db.DefaultContext))
// plain user
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4})
perm, err := access_model.GetUserRepoPermission(db.DefaultContext, repo, user)
assert.NoError(t, err)
for _, unit := range repo.Units {
assert.False(t, perm.CanRead(unit.Type))
assert.False(t, perm.CanWrite(unit.Type))
}
// change to collaborator to default write access
assert.NoError(t, AddCollaborator(db.DefaultContext, repo, user))
perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, user)
assert.NoError(t, err)
for _, unit := range repo.Units {
assert.True(t, perm.CanRead(unit.Type))
assert.True(t, perm.CanWrite(unit.Type))
}
assert.NoError(t, repo_model.ChangeCollaborationAccessMode(db.DefaultContext, repo, user.ID, perm_model.AccessModeRead))
perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, user)
assert.NoError(t, err)
for _, unit := range repo.Units {
assert.True(t, perm.CanRead(unit.Type))
assert.False(t, perm.CanWrite(unit.Type))
}
// owner
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, owner)
assert.NoError(t, err)
for _, unit := range repo.Units {
assert.True(t, perm.CanRead(unit.Type))
assert.True(t, perm.CanWrite(unit.Type))
}
// admin
admin := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, admin)
assert.NoError(t, err)
for _, unit := range repo.Units {
assert.True(t, perm.CanRead(unit.Type))
assert.True(t, perm.CanWrite(unit.Type))
}
}
func TestRepoPermissionPublicOrgRepo(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
// public organization repo
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 32})
assert.NoError(t, repo.LoadUnits(db.DefaultContext))
// plain user
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5})
perm, err := access_model.GetUserRepoPermission(db.DefaultContext, repo, user)
assert.NoError(t, err)
for _, unit := range repo.Units {
assert.True(t, perm.CanRead(unit.Type))
assert.False(t, perm.CanWrite(unit.Type))
}
// change to collaborator to default write access
assert.NoError(t, AddCollaborator(db.DefaultContext, repo, user))
perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, user)
assert.NoError(t, err)
for _, unit := range repo.Units {
assert.True(t, perm.CanRead(unit.Type))
assert.True(t, perm.CanWrite(unit.Type))
}
assert.NoError(t, repo_model.ChangeCollaborationAccessMode(db.DefaultContext, repo, user.ID, perm_model.AccessModeRead))
perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, user)
assert.NoError(t, err)
for _, unit := range repo.Units {
assert.True(t, perm.CanRead(unit.Type))
assert.False(t, perm.CanWrite(unit.Type))
}
// org member team owner
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, owner)
assert.NoError(t, err)
for _, unit := range repo.Units {
assert.True(t, perm.CanRead(unit.Type))
assert.True(t, perm.CanWrite(unit.Type))
}
// org member team tester
member := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 15})
perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, member)
assert.NoError(t, err)
for _, unit := range repo.Units {
assert.True(t, perm.CanRead(unit.Type))
}
assert.True(t, perm.CanWrite(unit.TypeIssues))
assert.False(t, perm.CanWrite(unit.TypeCode))
// admin
admin := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, admin)
assert.NoError(t, err)
for _, unit := range repo.Units {
assert.True(t, perm.CanRead(unit.Type))
assert.True(t, perm.CanWrite(unit.Type))
}
}
func TestRepoPermissionPrivateOrgRepo(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
// private organization repo
repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 24})
assert.NoError(t, repo.LoadUnits(db.DefaultContext))
// plain user
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5})
perm, err := access_model.GetUserRepoPermission(db.DefaultContext, repo, user)
assert.NoError(t, err)
for _, unit := range repo.Units {
assert.False(t, perm.CanRead(unit.Type))
assert.False(t, perm.CanWrite(unit.Type))
}
// change to collaborator to default write access
assert.NoError(t, AddCollaborator(db.DefaultContext, repo, user))
perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, user)
assert.NoError(t, err)
for _, unit := range repo.Units {
assert.True(t, perm.CanRead(unit.Type))
assert.True(t, perm.CanWrite(unit.Type))
}
assert.NoError(t, repo_model.ChangeCollaborationAccessMode(db.DefaultContext, repo, user.ID, perm_model.AccessModeRead))
perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, user)
assert.NoError(t, err)
for _, unit := range repo.Units {
assert.True(t, perm.CanRead(unit.Type))
assert.False(t, perm.CanWrite(unit.Type))
}
// org member team owner
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 15})
perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, owner)
assert.NoError(t, err)
for _, unit := range repo.Units {
assert.True(t, perm.CanRead(unit.Type))
assert.True(t, perm.CanWrite(unit.Type))
}
// update team information and then check permission
team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 5})
err = organization.UpdateTeamUnits(db.DefaultContext, team, nil)
assert.NoError(t, err)
perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, owner)
assert.NoError(t, err)
for _, unit := range repo.Units {
assert.True(t, perm.CanRead(unit.Type))
assert.True(t, perm.CanWrite(unit.Type))
}
// org member team tester
tester := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, tester)
assert.NoError(t, err)
assert.True(t, perm.CanWrite(unit.TypeIssues))
assert.False(t, perm.CanWrite(unit.TypeCode))
assert.False(t, perm.CanRead(unit.TypeCode))
// org member team reviewer
reviewer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 20})
perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, reviewer)
assert.NoError(t, err)
assert.False(t, perm.CanRead(unit.TypeIssues))
assert.False(t, perm.CanWrite(unit.TypeCode))
assert.True(t, perm.CanRead(unit.TypeCode))
// admin
admin := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, admin)
assert.NoError(t, err)
for _, unit := range repo.Units {
assert.True(t, perm.CanRead(unit.Type))
assert.True(t, perm.CanWrite(unit.Type))
}
}

View File

@ -42,8 +42,8 @@ func NewPushCommits() *PushCommits {
return &PushCommits{}
}
// toAPIPayloadCommit converts a single PushCommit to an api.PayloadCommit object.
func (pc *PushCommits) toAPIPayloadCommit(ctx context.Context, emailUsers map[string]*user_model.User, repoPath, repoLink string, commit *PushCommit) (*api.PayloadCommit, error) {
// ToAPIPayloadCommit converts a single PushCommit to an api.PayloadCommit object.
func ToAPIPayloadCommit(ctx context.Context, emailUsers map[string]*user_model.User, repoPath, repoLink string, commit *PushCommit) (*api.PayloadCommit, error) {
var err error
authorUsername := ""
author, ok := emailUsers[commit.AuthorEmail]
@ -105,7 +105,7 @@ func (pc *PushCommits) ToAPIPayloadCommits(ctx context.Context, repoPath, repoLi
emailUsers := make(map[string]*user_model.User)
for i, commit := range pc.Commits {
apiCommit, err := pc.toAPIPayloadCommit(ctx, emailUsers, repoPath, repoLink, commit)
apiCommit, err := ToAPIPayloadCommit(ctx, emailUsers, repoPath, repoLink, commit)
if err != nil {
return nil, nil, err
}
@ -117,7 +117,7 @@ func (pc *PushCommits) ToAPIPayloadCommits(ctx context.Context, repoPath, repoLi
}
if pc.HeadCommit != nil && headCommit == nil {
var err error
headCommit, err = pc.toAPIPayloadCommit(ctx, emailUsers, repoPath, repoLink, pc.HeadCommit)
headCommit, err = ToAPIPayloadCommit(ctx, emailUsers, repoPath, repoLink, pc.HeadCommit)
if err != nil {
return nil, nil, err
}

View File

@ -11,160 +11,17 @@ import (
"path/filepath"
"strings"
"code.gitea.io/gitea/models"
activities_model "code.gitea.io/gitea/models/activities"
"code.gitea.io/gitea/models/db"
git_model "code.gitea.io/gitea/models/git"
"code.gitea.io/gitea/models/organization"
"code.gitea.io/gitea/models/perm"
access_model "code.gitea.io/gitea/models/perm/access"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/models/webhook"
issue_indexer "code.gitea.io/gitea/modules/indexer/issues"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
)
// CreateRepositoryByExample creates a repository for the user/organization.
func CreateRepositoryByExample(ctx context.Context, doer, u *user_model.User, repo *repo_model.Repository, overwriteOrAdopt, isFork bool) (err error) {
if err = repo_model.IsUsableRepoName(repo.Name); err != nil {
return err
}
has, err := repo_model.IsRepositoryModelExist(ctx, u, repo.Name)
if err != nil {
return fmt.Errorf("IsRepositoryExist: %w", err)
} else if has {
return repo_model.ErrRepoAlreadyExist{
Uname: u.Name,
Name: repo.Name,
}
}
repoPath := repo_model.RepoPath(u.Name, repo.Name)
isExist, err := util.IsExist(repoPath)
if err != nil {
log.Error("Unable to check if %s exists. Error: %v", repoPath, err)
return err
}
if !overwriteOrAdopt && isExist {
log.Error("Files already exist in %s and we are not going to adopt or delete.", repoPath)
return repo_model.ErrRepoFilesAlreadyExist{
Uname: u.Name,
Name: repo.Name,
}
}
if err = db.Insert(ctx, repo); err != nil {
return err
}
if err = repo_model.DeleteRedirect(ctx, u.ID, repo.Name); err != nil {
return err
}
// insert units for repo
defaultUnits := unit.DefaultRepoUnits
if isFork {
defaultUnits = unit.DefaultForkRepoUnits
}
units := make([]repo_model.RepoUnit, 0, len(defaultUnits))
for _, tp := range defaultUnits {
if tp == unit.TypeIssues {
units = append(units, repo_model.RepoUnit{
RepoID: repo.ID,
Type: tp,
Config: &repo_model.IssuesConfig{
EnableTimetracker: setting.Service.DefaultEnableTimetracking,
AllowOnlyContributorsToTrackTime: setting.Service.DefaultAllowOnlyContributorsToTrackTime,
EnableDependencies: setting.Service.DefaultEnableDependencies,
},
})
} else if tp == unit.TypePullRequests {
units = append(units, repo_model.RepoUnit{
RepoID: repo.ID,
Type: tp,
Config: &repo_model.PullRequestsConfig{
AllowMerge: true, AllowRebase: true, AllowRebaseMerge: true, AllowSquash: true, AllowFastForwardOnly: true,
DefaultMergeStyle: repo_model.MergeStyle(setting.Repository.PullRequest.DefaultMergeStyle),
AllowRebaseUpdate: true,
},
})
} else if tp == unit.TypeProjects {
units = append(units, repo_model.RepoUnit{
RepoID: repo.ID,
Type: tp,
Config: &repo_model.ProjectsConfig{ProjectsMode: repo_model.ProjectsModeAll},
})
} else {
units = append(units, repo_model.RepoUnit{
RepoID: repo.ID,
Type: tp,
})
}
}
if err = db.Insert(ctx, units); err != nil {
return err
}
// Remember visibility preference.
u.LastRepoVisibility = repo.IsPrivate
if err = user_model.UpdateUserCols(ctx, u, "last_repo_visibility"); err != nil {
return fmt.Errorf("UpdateUserCols: %w", err)
}
if err = user_model.IncrUserRepoNum(ctx, u.ID); err != nil {
return fmt.Errorf("IncrUserRepoNum: %w", err)
}
u.NumRepos++
// Give access to all members in teams with access to all repositories.
if u.IsOrganization() {
teams, err := organization.FindOrgTeams(ctx, u.ID)
if err != nil {
return fmt.Errorf("FindOrgTeams: %w", err)
}
for _, t := range teams {
if t.IncludesAllRepositories {
if err := models.AddRepository(ctx, t, repo); err != nil {
return fmt.Errorf("AddRepository: %w", err)
}
}
}
if isAdmin, err := access_model.IsUserRepoAdmin(ctx, repo, doer); err != nil {
return fmt.Errorf("IsUserRepoAdmin: %w", err)
} else if !isAdmin {
// Make creator repo admin if it wasn't assigned automatically
if err = AddCollaborator(ctx, repo, doer); err != nil {
return fmt.Errorf("AddCollaborator: %w", err)
}
if err = repo_model.ChangeCollaborationAccessMode(ctx, repo, doer.ID, perm.AccessModeAdmin); err != nil {
return fmt.Errorf("ChangeCollaborationAccessModeCtx: %w", err)
}
}
} else if err = access_model.RecalculateAccesses(ctx, repo); err != nil {
// Organization automatically called this in AddRepository method.
return fmt.Errorf("RecalculateAccesses: %w", err)
}
if setting.Service.AutoWatchNewRepos {
if err = repo_model.WatchRepo(ctx, doer, repo, true); err != nil {
return fmt.Errorf("WatchRepo: %w", err)
}
}
if err = webhook.CopyDefaultWebhooksToRepo(ctx, repo.ID); err != nil {
return fmt.Errorf("CopyDefaultWebhooksToRepo: %w", err)
}
return nil
}
const notRegularFileMode = os.ModeSymlink | os.ModeNamedPipe | os.ModeSocket | os.ModeDevice | os.ModeCharDevice | os.ModeIrregular
// getDirectorySize returns the disk consumption for a given path

View File

@ -8,6 +8,7 @@ import (
"code.gitea.io/gitea/models/unittest"
_ "code.gitea.io/gitea/models"
_ "code.gitea.io/gitea/models/actions"
)

View File

@ -181,11 +181,12 @@ func StoreMissingLfsObjectsInRepository(ctx context.Context, repo *repo_model.Re
downloadObjects := func(pointers []lfs.Pointer) error {
err := lfsClient.Download(ctx, pointers, func(p lfs.Pointer, content io.ReadCloser, objectError error) error {
if objectError != nil {
if errors.Is(objectError, lfs.ErrObjectNotExist) {
log.Warn("Repo[%-v]: Ignore missing LFS object %-v: %v", repo, p, objectError)
log.Warn("Ignoring missing upstream LFS object %-v: %v", p, objectError)
return nil
}
if objectError != nil {
return objectError
}
@ -340,9 +341,10 @@ func pullMirrorReleaseSync(ctx context.Context, repo *repo_model.Repository, git
for _, tag := range updates {
if _, err := db.GetEngine(ctx).Where("repo_id = ? AND lower_tag_name = ?", repo.ID, strings.ToLower(tag.Name)).
Cols("sha1").
Cols("sha1", "created_unix").
Update(&repo_model.Release{
Sha1: tag.Object.String(),
CreatedUnix: timeutil.TimeStamp(tag.Tagger.When.Unix()),
}); err != nil {
return fmt.Errorf("unable to update tag %s for pull-mirror Repo[%d:%s/%s]: %w", tag.Name, repo.ID, repo.OwnerName, repo.Name, err)
}

View File

@ -10,7 +10,10 @@ import (
"code.gitea.io/gitea/modules/generate"
)
// LFS represents the configuration for Git LFS
// LFS represents the server-side configuration for Git LFS.
// Ideally these options should be in a section like "[lfs_server]",
// but they are in "[server]" section due to historical reasons.
// Could be refactored in the future while keeping backwards compatibility.
var LFS = struct {
StartServer bool `ini:"LFS_START_SERVER"`
AllowPureSSH bool `ini:"LFS_ALLOW_PURE_SSH"`
@ -18,15 +21,22 @@ var LFS = struct {
HTTPAuthExpiry time.Duration `ini:"LFS_HTTP_AUTH_EXPIRY"`
MaxFileSize int64 `ini:"LFS_MAX_FILE_SIZE"`
LocksPagingNum int `ini:"LFS_LOCKS_PAGING_NUM"`
MaxBatchSize int `ini:"LFS_MAX_BATCH_SIZE"`
Storage *Storage
}{}
// LFSClient represents configuration for Gitea's LFS clients, for example: mirroring upstream Git LFS
var LFSClient = struct {
BatchSize int `ini:"BATCH_SIZE"`
BatchOperationConcurrency int `ini:"BATCH_OPERATION_CONCURRENCY"`
}{}
func loadLFSFrom(rootCfg ConfigProvider) error {
mustMapSetting(rootCfg, "lfs_client", &LFSClient)
mustMapSetting(rootCfg, "server", &LFS)
sec := rootCfg.Section("server")
if err := sec.MapTo(&LFS); err != nil {
return fmt.Errorf("failed to map LFS settings: %v", err)
}
lfsSec, _ := rootCfg.GetSection("lfs")
@ -53,6 +63,15 @@ func loadLFSFrom(rootCfg ConfigProvider) error {
LFS.LocksPagingNum = 50
}
if LFSClient.BatchSize < 1 {
LFSClient.BatchSize = 20
}
if LFSClient.BatchOperationConcurrency < 1 {
// match the default git-lfs's `lfs.concurrenttransfers` https://github.com/git-lfs/git-lfs/blob/main/docs/man/git-lfs-config.adoc#upload-and-download-transfer-settings
LFSClient.BatchOperationConcurrency = 8
}
LFS.HTTPAuthExpiry = sec.Key("LFS_HTTP_AUTH_EXPIRY").MustDuration(24 * time.Hour)
if !LFS.StartServer || !InstallLock {

View File

@ -99,3 +99,32 @@ STORAGE_TYPE = minio
assert.EqualValues(t, "gitea", LFS.Storage.MinioConfig.Bucket)
assert.EqualValues(t, "lfs/", LFS.Storage.MinioConfig.BasePath)
}
func Test_LFSClientServerConfigs(t *testing.T) {
iniStr := `
[server]
LFS_MAX_BATCH_SIZE = 100
[lfs_client]
# will default to 20
BATCH_SIZE = 0
`
cfg, err := NewConfigProviderFromData(iniStr)
assert.NoError(t, err)
assert.NoError(t, loadLFSFrom(cfg))
assert.EqualValues(t, 100, LFS.MaxBatchSize)
assert.EqualValues(t, 20, LFSClient.BatchSize)
assert.EqualValues(t, 8, LFSClient.BatchOperationConcurrency)
iniStr = `
[lfs_client]
BATCH_SIZE = 50
BATCH_OPERATION_CONCURRENCY = 10
`
cfg, err = NewConfigProviderFromData(iniStr)
assert.NoError(t, err)
assert.NoError(t, loadLFSFrom(cfg))
assert.EqualValues(t, 50, LFSClient.BatchSize)
assert.EqualValues(t, 10, LFSClient.BatchOperationConcurrency)
}

View File

@ -43,6 +43,8 @@ var (
DisabledRepoUnits []string
DefaultRepoUnits []string
DefaultForkRepoUnits []string
DefaultMirrorRepoUnits []string
DefaultTemplateRepoUnits []string
PrefixArchiveFiles bool
DisableMigrations bool
DisableStars bool `ini:"DISABLE_STARS"`
@ -161,6 +163,8 @@ var (
DisabledRepoUnits: []string{},
DefaultRepoUnits: []string{},
DefaultForkRepoUnits: []string{},
DefaultMirrorRepoUnits: []string{},
DefaultTemplateRepoUnits: []string{},
PrefixArchiveFiles: true,
DisableMigrations: false,
DisableStars: false,

View File

@ -247,7 +247,7 @@ func (a *AzureBlobStorage) Delete(path string) error {
}
// URL gets the redirect URL to a file. The presigned link is valid for 5 minutes.
func (a *AzureBlobStorage) URL(path, name string) (*url.URL, error) {
func (a *AzureBlobStorage) URL(path, name string, reqParams url.Values) (*url.URL, error) {
blobClient := a.getBlobClient(path)
startTime := time.Now()

View File

@ -30,7 +30,7 @@ func (s discardStorage) Delete(_ string) error {
return fmt.Errorf("%s", s)
}
func (s discardStorage) URL(_, _ string) (*url.URL, error) {
func (s discardStorage) URL(_, _ string, _ url.Values) (*url.URL, error) {
return nil, fmt.Errorf("%s", s)
}

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