diff --git a/docs/static/_redirects b/docs/static/_redirects
index 2114ae933f..676181b61f 100644
--- a/docs/static/_redirects
+++ b/docs/static/_redirects
@@ -10,3 +10,7 @@ https://gitea-docs.netlify.com/* https://docs.gitea.io/:splat 302!
/en-us/ci-cd/ /en-us/integrations/ 302!
/en-us/third-party-tools/ /en-us/integrations/ 302!
/en-us/make/ /en-us/hacking-on-gitea/ 302!
+/en-us/upgrade/ /en-us/upgrade-from-gitea/ 302!
+/fr-fr/upgrade/ /fr-fr/upgrade-from-gitea/ 302!
+/zh-cn/upgrade/ /zh-cn/upgrade-from-gitea/ 302!
+/zh-tw/upgrade/ /zh-tw/upgrade-from-gitea/ 302!
diff --git a/docs/static/open-in-gitpod.svg b/docs/static/open-in-gitpod.svg
new file mode 100644
index 0000000000..b97cd29487
--- /dev/null
+++ b/docs/static/open-in-gitpod.svg
@@ -0,0 +1,23 @@
+
\ No newline at end of file
diff --git a/go.mod b/go.mod
index 78495cc6a2..5bafaf4142 100644
--- a/go.mod
+++ b/go.mod
@@ -5,102 +5,105 @@ go 1.18
require (
code.gitea.io/gitea-vet v0.2.2-0.20220122151748-48ebc902541b
code.gitea.io/sdk/gitea v0.15.1
+ codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570
gitea.com/go-chi/binding v0.0.0-20220309004920-114340dabecb
gitea.com/go-chi/cache v0.2.0
gitea.com/go-chi/captcha v0.0.0-20211013065431-70641c1a35d5
gitea.com/go-chi/session v0.0.0-20211218221615-e3605d8b28b8
- gitea.com/lunny/levelqueue v0.4.1
+ gitea.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96
+ gitea.com/lunny/levelqueue v0.4.2-0.20220729054728-f020868cc2f7
github.com/42wim/sshsig v0.0.0-20211121163825-841cf5bbc121
github.com/NYTimes/gziphandler v1.1.1
github.com/PuerkitoBio/goquery v1.8.0
- github.com/alecthomas/chroma v0.10.0
- github.com/blevesearch/bleve/v2 v2.3.2
- github.com/buildkite/terminal-to-html/v3 v3.6.1
- github.com/caddyserver/certmagic v0.16.1
+ github.com/alecthomas/chroma/v2 v2.3.0
+ github.com/blevesearch/bleve/v2 v2.3.4
+ github.com/buildkite/terminal-to-html/v3 v3.7.0
+ github.com/caddyserver/certmagic v0.17.2
github.com/chi-middleware/proxy v1.1.1
- github.com/denisenkom/go-mssqldb v0.12.0
+ github.com/denisenkom/go-mssqldb v0.12.2
github.com/djherbis/buffer v1.2.0
github.com/djherbis/nio/v3 v3.0.1
- github.com/duo-labs/webauthn v0.0.0-20220330035159-03696f3d4499
+ github.com/duo-labs/webauthn v0.0.0-20220815211337-00c9fb5711f5
github.com/dustin/go-humanize v1.0.0
- github.com/editorconfig/editorconfig-core-go/v2 v2.4.4
+ github.com/editorconfig/editorconfig-core-go/v2 v2.4.5
github.com/emirpasic/gods v1.18.1
github.com/ethantkoenig/rupture v1.0.1
- github.com/felixge/fgprof v0.9.2
- github.com/gliderlabs/ssh v0.3.4
- github.com/go-ap/activitypub v0.0.0-20220615144428-48208c70483b
- github.com/go-ap/jsonld v0.0.0-20220615144122-1d862b15410d
+ github.com/felixge/fgprof v0.9.3
+ github.com/fsnotify/fsnotify v1.5.4
+ github.com/gliderlabs/ssh v0.3.5
+ github.com/go-ap/activitypub v0.0.0-20220917143152-e4e7018838c0
+ github.com/go-ap/jsonld v0.0.0-20220917142617-76bf51585778
github.com/go-chi/chi/v5 v5.0.7
github.com/go-chi/cors v1.2.1
- github.com/go-enry/go-enry/v2 v2.8.2
+ github.com/go-enry/go-enry/v2 v2.8.3
github.com/go-fed/httpsig v1.1.1-0.20201223112313-55836744818e
github.com/go-git/go-billy/v5 v5.3.1
- github.com/go-git/go-git/v5 v5.4.3-0.20210630082519-b4368b2a2ca4
- github.com/go-ldap/ldap/v3 v3.4.3
+ github.com/go-git/go-git/v5 v5.4.3-0.20220529141257-bc1f419cebcf
+ github.com/go-ldap/ldap/v3 v3.4.4
github.com/go-redis/redis/v8 v8.11.5
github.com/go-sql-driver/mysql v1.6.0
- github.com/go-swagger/go-swagger v0.29.0
- github.com/go-testfixtures/testfixtures/v3 v3.6.1
+ github.com/go-swagger/go-swagger v0.30.3
+ github.com/go-testfixtures/testfixtures/v3 v3.8.1
github.com/gobwas/glob v0.2.3
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f
github.com/gogs/cron v0.0.0-20171120032916-9f6c956d3e14
github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85
- github.com/golang-jwt/jwt/v4 v4.4.1
- github.com/google/go-github/v45 v45.0.0
- github.com/google/pprof v0.0.0-20220509035851-59ca7ad80af3
+ github.com/golang-jwt/jwt/v4 v4.4.2
+ github.com/google/go-github/v45 v45.2.0
+ github.com/google/pprof v0.0.0-20220829040838-70bd9ae97f40
github.com/google/uuid v1.3.0
github.com/gorilla/feeds v1.1.1
github.com/gorilla/sessions v1.2.1
- github.com/hashicorp/go-version v1.4.0
+ github.com/hashicorp/go-version v1.6.0
github.com/hashicorp/golang-lru v0.5.4
github.com/huandu/xstrings v1.3.2
github.com/jaytaylor/html2text v0.0.0-20211105163654-bc68cce691ba
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.15.3
- github.com/klauspost/cpuid/v2 v2.0.12
- github.com/lib/pq v1.10.5
- github.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96
- github.com/markbates/goth v1.72.0
- github.com/mattn/go-isatty v0.0.14
- github.com/mattn/go-sqlite3 v1.14.12
+ github.com/klauspost/compress v1.15.11
+ github.com/klauspost/cpuid/v2 v2.1.1
+ github.com/lib/pq v1.10.7
+ github.com/markbates/goth v1.73.0
+ github.com/mattn/go-isatty v0.0.16
+ github.com/mattn/go-sqlite3 v1.14.15
github.com/mholt/archiver/v3 v3.5.1
- github.com/microcosm-cc/bluemonday v1.0.19
- github.com/minio/minio-go/v7 v7.0.26
- github.com/msteinert/pam v1.0.0
+ github.com/microcosm-cc/bluemonday v1.0.20
+ github.com/minio/minio-go/v7 v7.0.39
+ github.com/msteinert/pam v1.1.0
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
- github.com/niklasfasching/go-org v1.6.2
+ github.com/niklasfasching/go-org v1.6.5
github.com/oliamb/cutter v0.2.2
github.com/olivere/elastic/v7 v7.0.32
github.com/pkg/errors v0.9.1
github.com/pquerna/otp v1.3.0
- github.com/prometheus/client_golang v1.12.1
+ github.com/prometheus/client_golang v1.13.0
github.com/quasoft/websspi v1.1.2
- github.com/santhosh-tekuri/jsonschema/v5 v5.0.0
+ github.com/santhosh-tekuri/jsonschema/v5 v5.0.1
github.com/sergi/go-diff v1.2.0
github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546
- github.com/stretchr/testify v1.7.1
+ github.com/stretchr/testify v1.8.0
github.com/syndtr/goleveldb v1.0.0
github.com/tstranex/u2f v1.0.0
- github.com/unrolled/render v1.4.1
- github.com/urfave/cli v1.22.9
- github.com/xanzy/go-gitlab v0.64.0
+ github.com/unrolled/render v1.5.0
+ github.com/urfave/cli v1.22.10
+ github.com/xanzy/go-gitlab v0.73.1
github.com/yohcop/openid-go v1.0.0
- github.com/yuin/goldmark v1.4.13
- github.com/yuin/goldmark-highlighting v0.0.0-20220208100518-594be1970594
+ github.com/yuin/goldmark v1.5.2
+ github.com/yuin/goldmark-highlighting/v2 v2.0.0-20220924101305-151362477c87
github.com/yuin/goldmark-meta v1.1.0
go.jolheiser.com/hcaptcha v0.0.4
go.jolheiser.com/pwn v0.0.3
- golang.org/x/crypto v0.0.0-20220507011949-2cf3adece122
- golang.org/x/net v0.0.0-20220630215102-69896b714898
- golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5
- golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a
- golang.org/x/text v0.3.7
- golang.org/x/tools v0.1.10
+ golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be
+ golang.org/x/net v0.0.0-20220927171203-f486391704dc
+ golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1
+ golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec
+ golang.org/x/text v0.3.8
+ golang.org/x/tools v0.1.12
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
- gopkg.in/ini.v1 v1.66.4
+ gopkg.in/ini.v1 v1.67.0
gopkg.in/yaml.v2 v2.4.0
+ gopkg.in/yaml.v3 v3.0.1
mvdan.cc/xurls/v2 v2.4.0
strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251
xorm.io/builder v0.3.11
@@ -108,14 +111,15 @@ require (
)
require (
- cloud.google.com/go v0.99.0 // indirect
- git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20200411073322-f0bcc40f0bf2 // indirect
- github.com/Azure/go-ntlmssp v0.0.0-20211209120228-48547f28849e // indirect
- github.com/Microsoft/go-winio v0.5.2 // indirect
- github.com/ProtonMail/go-crypto v0.0.0-20220407094043-a94812496cf5 // indirect
- github.com/PuerkitoBio/purell v1.1.1 // indirect
- github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
- github.com/RoaringBitmap/roaring v0.9.4 // indirect
+ cloud.google.com/go/compute v1.7.0 // indirect
+ git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078 // indirect
+ github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e // indirect
+ github.com/Masterminds/goutils v1.1.1 // indirect
+ github.com/Masterminds/semver/v3 v3.1.1 // indirect
+ github.com/Masterminds/sprig/v3 v3.2.2 // indirect
+ github.com/Microsoft/go-winio v0.6.0 // indirect
+ github.com/ProtonMail/go-crypto v0.0.0-20220930113650-c6815a8c17ad // indirect
+ github.com/RoaringBitmap/roaring v1.2.1 // indirect
github.com/acomagu/bufpipe v1.0.3 // indirect
github.com/andybalholm/brotli v1.0.4 // indirect
github.com/andybalholm/cascadia v1.3.1 // indirect
@@ -124,26 +128,28 @@ require (
github.com/aymerick/douceur v0.2.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bgentry/speakeasy v0.1.0 // indirect
- github.com/bits-and-blooms/bitset v1.2.2 // indirect
- github.com/blevesearch/bleve_index_api v1.0.1 // indirect
+ github.com/bits-and-blooms/bitset v1.3.3 // indirect
+ github.com/blevesearch/bleve_index_api v1.0.3 // indirect
+ github.com/blevesearch/geo v0.1.14 // 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.3 // indirect
- github.com/blevesearch/scorch_segment_api/v2 v2.1.0 // indirect
+ github.com/blevesearch/mmap-go v1.0.4 // indirect
+ github.com/blevesearch/scorch_segment_api/v2 v2.1.2 // indirect
github.com/blevesearch/segment v0.9.0 // indirect
github.com/blevesearch/snowballstem v0.9.0 // indirect
github.com/blevesearch/upsidedown_store_api v1.0.1 // indirect
- github.com/blevesearch/vellum v1.0.7 // indirect
- github.com/blevesearch/zapx/v11 v11.3.3 // indirect
- github.com/blevesearch/zapx/v12 v12.3.3 // indirect
- github.com/blevesearch/zapx/v13 v13.3.3 // indirect
- github.com/blevesearch/zapx/v14 v14.3.3 // indirect
- github.com/blevesearch/zapx/v15 v15.3.3 // indirect
+ github.com/blevesearch/vellum v1.0.8 // indirect
+ github.com/blevesearch/zapx/v11 v11.3.5 // indirect
+ github.com/blevesearch/zapx/v12 v12.3.5 // indirect
+ github.com/blevesearch/zapx/v13 v13.3.5 // indirect
+ github.com/blevesearch/zapx/v14 v14.3.5 // indirect
+ github.com/blevesearch/zapx/v15 v15.3.5 // indirect
github.com/boombuler/barcode v1.0.1 // indirect
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b // indirect
github.com/census-instrumentation/opencensus-proto v0.3.0 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/cloudflare/cfssl v1.6.1 // indirect
+ github.com/cloudflare/circl v1.2.0 // indirect
github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4 // indirect
github.com/cncf/xds/go v0.0.0-20211130200136-a8f946100490 // indirect
github.com/coreos/go-semver v0.3.0 // indirect
@@ -154,35 +160,34 @@ require (
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
- github.com/dlclark/regexp2 v1.4.0 // indirect
+ github.com/dlclark/regexp2 v1.7.0 // indirect
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect
- github.com/envoyproxy/go-control-plane v0.10.1 // indirect
+ github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1 // indirect
github.com/envoyproxy/protoc-gen-validate v0.6.2 // indirect
- github.com/felixge/httpsnoop v1.0.2 // indirect
+ github.com/felixge/httpsnoop v1.0.3 // indirect
github.com/form3tech-oss/jwt-go v3.2.3+incompatible // indirect
- github.com/fsnotify/fsnotify v1.5.4 // indirect
github.com/fullstorydev/grpcurl v1.8.1 // indirect
github.com/fxamacker/cbor/v2 v2.4.0 // indirect
- github.com/go-ap/errors v0.0.0-20220615144307-e8bc4a40ae9f // indirect
+ github.com/go-ap/errors v0.0.0-20220917143055-4283ea5dae18 // indirect
github.com/go-asn1-ber/asn1-ber v1.5.4 // indirect
github.com/go-enry/go-oniguruma v1.2.1 // indirect
github.com/go-git/gcfg v1.5.0 // indirect
- github.com/go-openapi/analysis v0.21.2 // indirect
- github.com/go-openapi/errors v0.20.2 // indirect
+ github.com/go-openapi/analysis v0.21.4 // indirect
+ github.com/go-openapi/errors v0.20.3 // indirect
github.com/go-openapi/inflect v0.19.0 // indirect
github.com/go-openapi/jsonpointer v0.19.5 // indirect
- github.com/go-openapi/jsonreference v0.19.6 // indirect
- github.com/go-openapi/loads v0.21.0 // indirect
- github.com/go-openapi/runtime v0.21.1 // indirect
- github.com/go-openapi/spec v0.20.4 // indirect
- github.com/go-openapi/strfmt v0.21.1 // indirect
- github.com/go-openapi/swag v0.19.15 // indirect
- github.com/go-openapi/validate v0.20.3 // indirect
- github.com/go-stack/stack v1.8.1 // indirect
- github.com/goccy/go-json v0.9.7 // indirect
+ github.com/go-openapi/jsonreference v0.20.0 // indirect
+ github.com/go-openapi/loads v0.21.2 // indirect
+ github.com/go-openapi/runtime v0.24.1 // indirect
+ github.com/go-openapi/spec v0.20.7 // indirect
+ github.com/go-openapi/strfmt v0.21.3 // indirect
+ github.com/go-openapi/swag v0.22.3 // indirect
+ github.com/go-openapi/validate v0.22.0 // indirect
+ github.com/goccy/go-json v0.9.11 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
- github.com/golang-sql/sqlexp v0.0.0-20170517235910-f1bb20e5a188 // indirect
+ github.com/golang-sql/sqlexp v0.1.0 // indirect
+ github.com/golang/geo v0.0.0-20210211234256-740aa86cb551 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/mock v1.6.0 // indirect
github.com/golang/protobuf v1.5.2 // indirect
@@ -201,7 +206,7 @@ require (
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-retryablehttp v0.7.1 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
- github.com/imdario/mergo v0.3.12 // indirect
+ github.com/imdario/mergo v0.3.13 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/jessevdk/go-flags v1.5.0 // indirect
@@ -213,17 +218,18 @@ require (
github.com/kr/pretty v0.3.0 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/libdns/libdns v0.2.1 // indirect
- github.com/magiconair/properties v1.8.5 // indirect
+ github.com/magiconair/properties v1.8.6 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/markbates/going v1.0.0 // indirect
- github.com/mattn/go-runewidth v0.0.13 // indirect
+ github.com/mattn/go-runewidth v0.0.14 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
- github.com/mholt/acmez v1.0.2 // indirect
- github.com/miekg/dns v1.1.48 // indirect
+ github.com/mholt/acmez v1.0.4 // indirect
+ github.com/miekg/dns v1.1.50 // indirect
github.com/minio/md5-simd v1.1.2 // indirect
github.com/minio/sha256-simd v1.0.0 // indirect
- github.com/mitchellh/go-homedir v1.1.0 // indirect
- github.com/mitchellh/mapstructure v1.4.3 // indirect
+ github.com/mitchellh/copystructure v1.2.0 // indirect
+ github.com/mitchellh/mapstructure v1.5.0 // indirect
+ github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/mrjones/oauth v0.0.0-20190623134757-126b35219450 // indirect
@@ -231,69 +237,67 @@ require (
github.com/nwaples/rardecode v1.1.3 // indirect
github.com/oklog/ulid v1.3.1 // indirect
github.com/olekukonko/tablewriter v0.0.5 // indirect
- github.com/pelletier/go-toml v1.9.4 // indirect
- github.com/pierrec/lz4/v4 v4.1.14 // indirect
+ github.com/pelletier/go-toml v1.9.5 // indirect
+ github.com/pelletier/go-toml/v2 v2.0.1 // indirect
+ github.com/pierrec/lz4/v4 v4.1.17 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_model v0.2.0 // indirect
- github.com/prometheus/common v0.32.1 // indirect
- github.com/prometheus/procfs v0.7.3 // indirect
- github.com/rivo/uniseg v0.2.0 // indirect
- github.com/rogpeppe/go-internal v1.8.1 // indirect
+ github.com/prometheus/common v0.37.0 // indirect
+ github.com/prometheus/procfs v0.8.0 // indirect
+ github.com/rivo/uniseg v0.4.2 // indirect
+ github.com/rogpeppe/go-internal v1.9.0 // indirect
github.com/rs/xid v1.4.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
+ github.com/shopspring/decimal v1.2.0 // indirect
github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 // indirect
- github.com/sirupsen/logrus v1.8.1 // indirect
+ github.com/sirupsen/logrus v1.9.0 // indirect
github.com/soheilhy/cmux v0.1.5 // indirect
- github.com/spf13/afero v1.8.0 // indirect
- github.com/spf13/cast v1.4.1 // indirect
- github.com/spf13/cobra v1.3.0 // indirect
+ github.com/spf13/afero v1.8.2 // indirect
+ github.com/spf13/cast v1.5.0 // indirect
+ github.com/spf13/cobra v1.5.0 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
- github.com/spf13/viper v1.10.1 // indirect
+ github.com/spf13/viper v1.12.0 // indirect
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect
- github.com/subosito/gotenv v1.2.0 // indirect
+ github.com/subosito/gotenv v1.3.0 // indirect
github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802 // indirect
github.com/toqueteos/webbrowser v1.2.0 // indirect
github.com/ulikunitz/xz v0.5.10 // indirect
github.com/unknwon/com v1.0.1 // indirect
github.com/valyala/fastjson v1.6.3 // indirect
github.com/x448/float16 v0.8.4 // indirect
- github.com/xanzy/ssh-agent v0.3.1 // indirect
+ github.com/xanzy/ssh-agent v0.3.2 // indirect
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 // indirect
go.etcd.io/bbolt v1.3.6 // indirect
- go.etcd.io/etcd/api/v3 v3.5.1 // indirect
- go.etcd.io/etcd/client/pkg/v3 v3.5.1 // indirect
- go.etcd.io/etcd/client/v2 v2.305.1 // indirect
- go.etcd.io/etcd/client/v3 v3.5.0-alpha.0 // indirect
+ go.etcd.io/etcd/api/v3 v3.5.4 // indirect
+ go.etcd.io/etcd/client/pkg/v3 v3.5.4 // indirect
+ go.etcd.io/etcd/client/v2 v2.305.4 // indirect
+ go.etcd.io/etcd/client/v3 v3.5.4 // indirect
go.etcd.io/etcd/etcdctl/v3 v3.5.0-alpha.0 // indirect
go.etcd.io/etcd/pkg/v3 v3.5.0-alpha.0 // indirect
go.etcd.io/etcd/raft/v3 v3.5.0-alpha.0 // indirect
go.etcd.io/etcd/server/v3 v3.5.0-alpha.0 // indirect
go.etcd.io/etcd/tests/v3 v3.5.0-alpha.0 // indirect
go.etcd.io/etcd/v3 v3.5.0-alpha.0 // indirect
- go.mongodb.org/mongo-driver v1.8.2 // indirect
- go.uber.org/atomic v1.9.0 // indirect
+ go.mongodb.org/mongo-driver v1.10.1 // indirect
+ go.uber.org/atomic v1.10.0 // indirect
go.uber.org/multierr v1.8.0 // indirect
- go.uber.org/zap v1.21.0 // indirect
- golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect
- golang.org/x/time v0.0.0-20220411224347-583f2d630306 // indirect
- golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f // indirect
+ go.uber.org/zap v1.23.0 // indirect
+ golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
+ golang.org/x/time v0.0.0-20220922220347-f3bd1da661af // indirect
google.golang.org/appengine v1.6.7 // indirect
- google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa // indirect
- google.golang.org/grpc v1.43.0 // indirect
- google.golang.org/protobuf v1.28.0 // indirect
+ google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90 // indirect
+ google.golang.org/grpc v1.47.0 // indirect
+ google.golang.org/protobuf v1.28.1 // indirect
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
gopkg.in/cheggaaa/pb.v1 v1.0.28 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
- gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
sigs.k8s.io/yaml v1.2.0 // indirect
)
replace github.com/hashicorp/go-version => github.com/6543/go-version v1.3.1
-replace github.com/markbates/goth v1.68.0 => github.com/zeripath/goth v1.68.1-0.20220109111530-754359885dce
-
replace github.com/shurcooL/vfsgen => github.com/lunny/vfsgen v0.0.0-20220105142115-2c99e1ffdfa0
replace github.com/satori/go.uuid v1.2.0 => github.com/gofrs/uuid v4.2.0+incompatible
diff --git a/go.sum b/go.sum
index dca68d9a8e..b53732f6a5 100644
--- a/go.sum
+++ b/go.sum
@@ -32,19 +32,26 @@ cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aD
cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI=
cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4=
cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc=
-cloud.google.com/go v0.98.0/go.mod h1:ua6Ush4NALrHk5QXDWnjvZHN93OuF0HfuEPq9I1X0cM=
-cloud.google.com/go v0.99.0 h1:y/cM2iqGgGi5D5DQZl6D9STN/3dR/Vx5Mp8s752oJTY=
cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA=
+cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A=
+cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
+cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow=
+cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM=
+cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M=
+cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s=
+cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU=
+cloud.google.com/go/compute v1.7.0 h1:v/k9Eueb8aAJ0vZuxKMrgm6kPhCLZU9HxFU+AFDs9Uk=
+cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
-cloud.google.com/go/firestore v1.6.1/go.mod h1:asNXNOzBdyVQmEU+ggO8UPodTkEVFW5Qx+rwHnAz+EY=
+cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
@@ -56,12 +63,15 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
+cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y=
code.gitea.io/gitea-vet v0.2.1/go.mod h1:zcNbT/aJEmivCAhfmkHOlT645KNOf9W2KnkLgFjGGfE=
code.gitea.io/gitea-vet v0.2.2-0.20220122151748-48ebc902541b h1:uv9a8eGSdQ8Dr4HyUcuHFfDsk/QuwO+wf+Y99RYdxY0=
code.gitea.io/gitea-vet v0.2.2-0.20220122151748-48ebc902541b/go.mod h1:zcNbT/aJEmivCAhfmkHOlT645KNOf9W2KnkLgFjGGfE=
code.gitea.io/sdk/gitea v0.11.3/go.mod h1:z3uwDV/b9Ls47NGukYM9XhnHtqPh/J+t40lsUrR6JDY=
code.gitea.io/sdk/gitea v0.15.1 h1:WJreC7YYuxbn0UDaPuWIe/mtiNKTvLN8MLkaw71yx/M=
code.gitea.io/sdk/gitea v0.15.1/go.mod h1:klY2LVI3s3NChzIk/MzMn7G1FHrfU7qd63iSMVoHRBA=
+codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570 h1:TXbikPqa7YRtfU9vS6QJBg77pUvbEb6StRdZO8t1bEY=
+codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570/go.mod h1:IIAjsijsd8q1isWX8MACefDEgTQslQ4stk2AeeTt3kM=
contrib.go.opencensus.io/exporter/aws v0.0.0-20181029163544-2befc13012d0/go.mod h1:uu1P0UCM/6RbsMrgPa98ll8ZcHM858i/AD06a9aLRCA=
contrib.go.opencensus.io/exporter/ocagent v0.5.0/go.mod h1:ImxhfLRpxoYiSq891pBrLVhN+qmP8BTVvdH2YLs7Gl0=
contrib.go.opencensus.io/exporter/stackdriver v0.12.1/go.mod h1:iwB6wGarfphGGe/e5CWqyUk/cLzKnWsOKPVW3no6OTw=
@@ -69,8 +79,8 @@ contrib.go.opencensus.io/exporter/stackdriver v0.13.5/go.mod h1:aXENhDJ1Y4lIg4EU
contrib.go.opencensus.io/integrations/ocsql v0.1.4/go.mod h1:8DsSdjz3F+APR+0z0WkU1aRorQCFfRxvqjUUPMbF3fE=
contrib.go.opencensus.io/resource v0.1.1/go.mod h1:F361eGI91LCmW1I/Saf+rX0+OFcigGlFvXwEGEnkRLA=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
-git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20200411073322-f0bcc40f0bf2 h1:2OrsyJYZp7J6nyAsKi2q1SELYRaIc0aQmcQ/EQqPfk8=
-git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20200411073322-f0bcc40f0bf2/go.mod h1:g/V2Hjas6Z1UHUp4yIx6bATpNzJ7DYtD0FG3+xARWxs=
+git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078 h1:cliQ4HHsCo6xi2oWZYKWW4bly/Ory9FuTpFPRxj/mAg=
+git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078/go.mod h1:g/V2Hjas6Z1UHUp4yIx6bATpNzJ7DYtD0FG3+xARWxs=
gitea.com/go-chi/binding v0.0.0-20220309004920-114340dabecb h1:Yy0Bxzc8R2wxiwXoG/rECGplJUSpXqCsog9PuJFgiHs=
gitea.com/go-chi/binding v0.0.0-20220309004920-114340dabecb/go.mod h1:77TZu701zMXWJFvB8gvTbQ92zQ3DQq/H7l5wAEjQRKc=
gitea.com/go-chi/cache v0.0.0-20210110083709-82c4c9ce2d5e/go.mod h1:k2V/gPDEtXGjjMGuBJiapffAXTv76H4snSmlJRLUhH0=
@@ -80,8 +90,10 @@ gitea.com/go-chi/captcha v0.0.0-20211013065431-70641c1a35d5 h1:J/1i8u40TbcLP/w2w
gitea.com/go-chi/captcha v0.0.0-20211013065431-70641c1a35d5/go.mod h1:hQ9SYHKdOX968wJglb/NMQ+UqpOKwW4L+EYdvkWjHSo=
gitea.com/go-chi/session v0.0.0-20211218221615-e3605d8b28b8 h1:tJQRXgZigkLeeW9LPlps9G9aMoE6LAmqigLA+wxmd1Q=
gitea.com/go-chi/session v0.0.0-20211218221615-e3605d8b28b8/go.mod h1:fc/pjt5EqNKgqQXYzcas1Z5L5whkZHyOvTA7OzWVJck=
-gitea.com/lunny/levelqueue v0.4.1 h1:RZ+AFx5gBsZuyqCvofhAkPQ9uaVDPJnsULoJZIYaJNw=
-gitea.com/lunny/levelqueue v0.4.1/go.mod h1:HBqmLbz56JWpfEGG0prskAV97ATNRoj5LDmPicD22hU=
+gitea.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96 h1:+wWBi6Qfruqu7xJgjOIrKVQGiLUZdpKYCZewJ4clqhw=
+gitea.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96/go.mod h1:VyMQP6ue6MKHM8UsOXfNfuMKD0oSAWZdXVcpHIN2yaY=
+gitea.com/lunny/levelqueue v0.4.2-0.20220729054728-f020868cc2f7 h1:Zc3RQWC2xOVglLciQH/ZIC5IqSk3Jn96LflGQLv18Rg=
+gitea.com/lunny/levelqueue v0.4.2-0.20220729054728-f020868cc2f7/go.mod h1:HBqmLbz56JWpfEGG0prskAV97ATNRoj5LDmPicD22hU=
gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:lSA0F4e9A2NcQSqGqTOXqu2aRi/XEQxDCBwM8yJtE6s=
gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:EXuID2Zs0pAQhH8yz+DNjUbjppKQzKFAn28TMYPB6IU=
gitee.com/travelliu/dm v1.8.11192/go.mod h1:DHTzyhCrM843x9VdKVbZ+GKXGRbKM2sJ4LxihRxShkE=
@@ -99,64 +111,66 @@ github.com/Azure/azure-sdk-for-go/sdk/internal v0.7.0/go.mod h1:yqy467j36fJxcRV2
github.com/Azure/azure-service-bus-go v0.9.1/go.mod h1:yzBx6/BUGfjfeqbRZny9AQIbIe3AcV9WZbAdpkoXOa0=
github.com/Azure/azure-storage-blob-go v0.8.0/go.mod h1:lPI3aLPpuLTeUwh1sViKXFxwl2B6teiRqI0deQUvsw0=
github.com/Azure/go-autorest v12.0.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
-github.com/Azure/go-ntlmssp v0.0.0-20211209120228-48547f28849e h1:ZU22z/2YRFLyf/P4ZwUYSdNCWsMEI0VeyrFoI2rAhJQ=
-github.com/Azure/go-ntlmssp v0.0.0-20211209120228-48547f28849e/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
+github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e h1:NeAW1fUYUEWhft7pkxDf6WoUvEZJ/uOKsvtpjLnn8MU=
+github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
-github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
github.com/GeertJohan/go.incremental v1.0.0/go.mod h1:6fAjUhbVuX1KcMD3c8TEgVUqmo4seqhv0i0kdATSkM0=
github.com/GeertJohan/go.rice v1.0.2/go.mod h1:af5vUNlDNkCjOZeSGFgIJxDje9qdjsO6hshx0gTmZt4=
github.com/GoogleCloudPlatform/cloudsql-proxy v0.0.0-20191009163259-e802c2cb94ae/go.mod h1:mjwGPas4yKduTyubHvD1Atl9r1rUq8DfVy+gkVvZ+oo=
github.com/Julusian/godocdown v0.0.0-20170816220326-6d19f8ff2df8/go.mod h1:INZr5t32rG59/5xeltqoCJoNY7e5x/3xoY9WSWVWg74=
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
+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 v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
github.com/Masterminds/semver/v3 v3.0.3/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
github.com/Masterminds/semver/v3 v3.1.0/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
+github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc=
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
github.com/Masterminds/sprig v2.15.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o=
github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o=
-github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
-github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=
+github.com/Masterminds/sprig/v3 v3.2.2 h1:17jRggJu518dr3QaafizSXOjKYp94wKfABxUmyxvxX8=
+github.com/Masterminds/sprig/v3 v3.2.2/go.mod h1:UoaO7Yp8KlPnJIYWTFkMaqPUYKTfGFPhxNuwnnxkKlk=
github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
-github.com/Microsoft/go-winio v0.5.2 h1:a9IhgEQBCUEk6QCdml9CiJGhAws+YwffDHEMp1VMrpA=
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
+github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg=
+github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE=
github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I=
github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo=
-github.com/ProtonMail/go-crypto v0.0.0-20220407094043-a94812496cf5 h1:cSHEbLj0GZeHM1mWG84qEnGFojNEQ83W7cwaPRjcwXU=
-github.com/ProtonMail/go-crypto v0.0.0-20220407094043-a94812496cf5/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo=
+github.com/ProtonMail/go-crypto v0.0.0-20220930113650-c6815a8c17ad h1:QeeqI2zxxgZVe11UrYFXXx6gVxPVF40ygekjBzEg4XY=
+github.com/ProtonMail/go-crypto v0.0.0-20220930113650-c6815a8c17ad/go.mod h1:UBYPn8k0D56RtnR8RFQMjmh4KrZzWJ5o7Z9SYjossQ8=
github.com/PuerkitoBio/goquery v1.8.0 h1:PJTF7AmFCFKk1N6V6jmKfrNH9tV5pNE6lZMkG0gta/U=
github.com/PuerkitoBio/goquery v1.8.0/go.mod h1:ypIiRMtY7COPGk+I/YbZLbxsxn9g5ejnI2HSMtkjZvI=
-github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
-github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
-github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
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 v0.9.4 h1:ckvZSX5gwCRaJYBNe7syNawCU5oruY9gQmjXlp4riwo=
github.com/RoaringBitmap/roaring v0.9.4/go.mod h1:icnadbWcNyfEHlYdr+tDlOTih1Bf/h+rzPpv4sbomAA=
+github.com/RoaringBitmap/roaring v1.2.1 h1:58/LJlg/81wfEHd5L9qsHduznOIhyv4qb1yWcSvVq9A=
+github.com/RoaringBitmap/roaring v1.2.1/go.mod h1:icnadbWcNyfEHlYdr+tDlOTih1Bf/h+rzPpv4sbomAA=
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
github.com/acomagu/bufpipe v1.0.3 h1:fxAGrHZTgQ9w5QqVItgzwj235/uYZYgbXitB+dLupOk=
github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4=
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
-github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c=
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
-github.com/alecthomas/chroma v0.10.0 h1:7XDcGkCQopCNKjZHfYrNLraA+M7e0fMiJ/Mfikbfjek=
-github.com/alecthomas/chroma v0.10.0/go.mod h1:jtJATyUxlIORhUOFNA9NZDWGAQ8wpxQQqNSB4rjA/1s=
+github.com/alecthomas/chroma/v2 v2.2.0/go.mod h1:vf4zrexSH54oEjJ7EdB65tGNHmH3pGZmVkgTP5RHvAs=
+github.com/alecthomas/chroma/v2 v2.3.0 h1:83xfxrnjv8eK+Cf8qZDzNo3PPF9IbTWHs7z28GY6D0U=
+github.com/alecthomas/chroma/v2 v2.3.0/go.mod h1:mZxeWZlxP2Dy+/8cBob2PYd8O2DwNAzave5AY7A2eQw=
github.com/alecthomas/kingpin v2.2.6+incompatible/go.mod h1:59OFYbFVLKQKq+mqrL6Rw5bR0c3ACQaawgXx0QYndlE=
+github.com/alecthomas/repr v0.0.0-20220113201626-b1b626ac65ae/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8=
+github.com/alecthomas/repr v0.1.0 h1:ENn2e1+J3k09gyj2shc0dHr/yjaWSHRlrJ4DPMevDqE=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
-github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
@@ -177,16 +191,10 @@ github.com/aphistic/sweet v0.2.0/go.mod h1:fWDlIh/isSE9n6EPsRmC0det+whmX6dJid3st
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
-github.com/armon/go-metrics v0.3.10/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
-github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
-github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
-github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
-github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg=
-github.com/asaskevich/govalidator v0.0.0-20200428143746-21a406dcc535/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg=
github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ=
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
@@ -198,7 +206,6 @@ github.com/aws/aws-sdk-go v1.20.6/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN
github.com/aws/aws-sdk-go v1.23.20/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go v1.25.11/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
-github.com/aws/aws-sdk-go v1.34.28/go.mod h1:H7NKnBqNVzoTJpGfLrQkkD+ytBA93eiDYi/+8rV9s48=
github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
github.com/aybabtme/rgbterm v0.0.0-20170906152045-cc83f3b3ce59/go.mod h1:q/89r3U2H7sSsE2t6Kca0lfwTK8JdoNGS/yzM/4iH5I=
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
@@ -213,28 +220,31 @@ github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQ
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
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.2.2 h1:J5gbX05GpMdBjCvQ9MteIg2KKDExr7DrgK+Yc15FvIk=
-github.com/bits-and-blooms/bitset v1.2.2/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=
+github.com/bits-and-blooms/bitset v1.3.3 h1:R1XWiopGiXf66xygsiLpzLo67xEYvMkHw3w+rCOSAwg=
+github.com/bits-and-blooms/bitset v1.3.3/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
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.3.2 h1:BJUnMhi2nrkl+vboHmKfW+9l+tJSj39HeWa5c3BN3/Y=
-github.com/blevesearch/bleve/v2 v2.3.2/go.mod h1:96+xE5pZUOsr3Y4vHzV1cBC837xZCpwLlX0hrrxnvIg=
+github.com/blevesearch/bleve/v2 v2.3.4 h1:SSb7/cwGzo85LWX1jchIsXM8ZiNNMX3shT5lROM63ew=
+github.com/blevesearch/bleve/v2 v2.3.4/go.mod h1:Ot0zYum8XQRfPcwhae8bZmNyYubynsoMjVvl1jPqL30=
github.com/blevesearch/bleve_index_api v1.0.0/go.mod h1:fiwKS0xLEm+gBRgv5mumf0dhgFr2mDgZah1pqv1c1M4=
-github.com/blevesearch/bleve_index_api v1.0.1 h1:nx9++0hnyiGOHJwQQYfsUGzpRdEVE5LsylmmngQvaFk=
-github.com/blevesearch/bleve_index_api v1.0.1/go.mod h1:fiwKS0xLEm+gBRgv5mumf0dhgFr2mDgZah1pqv1c1M4=
-github.com/blevesearch/go-metrics v0.0.0-20190826022208-cac0b30c2563/go.mod h1:9eJDeqxJ3E7WnLebQUlPD7ZjSce7AnDb9vjGmMCbD0A=
+github.com/blevesearch/bleve_index_api v1.0.3 h1:DDSWaPXOZZJ2BB73ZTWjKxydAugjwywcqU+91AAqcAg=
+github.com/blevesearch/bleve_index_api v1.0.3/go.mod h1:fiwKS0xLEm+gBRgv5mumf0dhgFr2mDgZah1pqv1c1M4=
+github.com/blevesearch/geo v0.1.13/go.mod h1:cRIvqCdk3cgMhGeHNNe6yPzb+w56otxbfo1FBJfR2Pc=
+github.com/blevesearch/geo v0.1.14 h1:TTDpJN6l9ck/cUYbXSn4aCElNls0Whe44rcQKsB7EfU=
+github.com/blevesearch/geo v0.1.14/go.mod h1:cRIvqCdk3cgMhGeHNNe6yPzb+w56otxbfo1FBJfR2Pc=
+github.com/blevesearch/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:9eJDeqxJ3E7WnLebQUlPD7ZjSce7AnDb9vjGmMCbD0A=
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/goleveldb v1.0.1/go.mod h1:WrU8ltZbIp0wAoig/MHbrPCXSOLpe79nz5lv5nqfYrQ=
github.com/blevesearch/gtreap v0.1.1 h1:2JWigFrzDMR+42WGIN/V2p0cUvn4UP3C4Q5nmaZGW8Y=
github.com/blevesearch/gtreap v0.1.1/go.mod h1:QaQyDRAT51sotthUWAH4Sj08awFSSWzgYICSZ3w0tYk=
github.com/blevesearch/mmap-go v1.0.2/go.mod h1:ol2qBqYaOUsGdm7aRMRrYGgPvnwLe6Y+7LMvAB5IbSA=
-github.com/blevesearch/mmap-go v1.0.3 h1:7QkALgFNooSq3a46AE+pWeKASAZc9SiNFJhDGF1NDx4=
-github.com/blevesearch/mmap-go v1.0.3/go.mod h1:pYvKl/grLQrBxuaRYgoTssa4rVujYYeenDp++2E+yvs=
+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.1.0 h1:NFwteOpZEvJk5Vg0H6gD0hxupsG3JYocE4DBvsA2GZI=
-github.com/blevesearch/scorch_segment_api/v2 v2.1.0/go.mod h1:uch7xyyO/Alxkuxa+CGs79vw0QY8BENSBjg6Mw5L5DE=
+github.com/blevesearch/scorch_segment_api/v2 v2.1.2 h1:TAte9VZLWda5WAVlZTTZ+GCzEHqGJb4iB2aiZSA6Iv8=
+github.com/blevesearch/scorch_segment_api/v2 v2.1.2/go.mod h1:rvoQXZGq8drq7vXbNeyiRzdEOwZkjkiYGf1822i6CRA=
github.com/blevesearch/segment v0.9.0 h1:5lG7yBCx98or7gK2cHMKPukPZ/31Kag7nONpoBt22Ac=
github.com/blevesearch/segment v0.9.0/go.mod h1:9PfHYUdQCgHktBgvtUOF4x+pc4/l8rdH0u5spnW85UQ=
github.com/blevesearch/snowball v0.6.1/go.mod h1:ZF0IBg5vgpeoUhnMza2v0A/z8m1cWPlwhke08LpNusg=
@@ -244,34 +254,36 @@ github.com/blevesearch/upsidedown_store_api v1.0.1 h1:1SYRwyoFLwG3sj0ed89RLtM15a
github.com/blevesearch/upsidedown_store_api v1.0.1/go.mod h1:MQDVGpHZrpe3Uy26zJBf/a8h0FZY6xJbthIMm8myH2Q=
github.com/blevesearch/vellum v1.0.3/go.mod h1:2u5ax02KeDuNWu4/C+hVQMD6uLN4txH1JbtpaDNLJRo=
github.com/blevesearch/vellum v1.0.4/go.mod h1:cMhywHI0de50f7Nj42YgvyD6bFJ2WkNRvNBlNMrEVgY=
-github.com/blevesearch/vellum v1.0.7 h1:+vn8rfyCRHxKVRgDLeR0FAXej2+6mEb5Q15aQE/XESQ=
-github.com/blevesearch/vellum v1.0.7/go.mod h1:doBZpmRhwTsASB4QdUZANlJvqVAUdUyX0ZK7QJCTeBE=
+github.com/blevesearch/vellum v1.0.8 h1:iMGh4lfxza4BnWO/UJTMPlI3HsK9YawjPv+TteVa9ck=
+github.com/blevesearch/vellum v1.0.8/go.mod h1:+cpRi/tqq49xUYSQN2P7A5zNSNrS+MscLeeaZ3J46UA=
github.com/blevesearch/zapx/v11 v11.2.0/go.mod h1:gN/a0alGw1FZt/YGTo1G6Z6XpDkeOfujX5exY9sCQQM=
-github.com/blevesearch/zapx/v11 v11.3.3 h1:8vQMO5hdA2qPCmicIMuKS+qcvUAEh6Vcb0uve4Nh8e4=
-github.com/blevesearch/zapx/v11 v11.3.3/go.mod h1:YzTfUm4kS3e8OmTXDHVV8OzC5MWPO/VPJZQgPNVb4Lc=
+github.com/blevesearch/zapx/v11 v11.3.5 h1:eBQWQ7huA+mzm0sAGnZDwgGGli7S45EO+N+ObFWssbI=
+github.com/blevesearch/zapx/v11 v11.3.5/go.mod h1:5UdIa/HRMdeRCiLQOyFESsnqBGiip7vQmYReA9toevU=
github.com/blevesearch/zapx/v12 v12.2.0/go.mod h1:fdjwvCwWWwJW/EYTYGtAp3gBA0geCYGLcVTtJEZnY6A=
-github.com/blevesearch/zapx/v12 v12.3.3 h1:MQO5YNI8MqdPz12ALCoXiJw5cl9QQamYZSp285Z/+Mo=
-github.com/blevesearch/zapx/v12 v12.3.3/go.mod h1:RMl6lOZqF+sTxKvhQDJ5yK2LT3Mu7E2p/jGdjAaiRxs=
+github.com/blevesearch/zapx/v12 v12.3.5 h1:5pX2hU+R1aZihT7ac1dNWh1n4wqkIM9pZzWp0ANED9s=
+github.com/blevesearch/zapx/v12 v12.3.5/go.mod h1:ANcthYRZQycpbRut/6ArF5gP5HxQyJqiFcuJCBju/ss=
github.com/blevesearch/zapx/v13 v13.2.0/go.mod h1:o5rAy/lRS5JpAbITdrOHBS/TugWYbkcYZTz6VfEinAQ=
-github.com/blevesearch/zapx/v13 v13.3.3 h1:TS4xpMK1ARPYHq+1WwuEOKMOiwvKpTK3RuWOkKlI7BE=
-github.com/blevesearch/zapx/v13 v13.3.3/go.mod h1:eppobNM35U4C22yDvTuxV9xPqo10pwfP/jugL4INWG4=
+github.com/blevesearch/zapx/v13 v13.3.5 h1:eJ3gbD+Nu8p36/O6lhfdvWQ4pxsGYSuTOBrLLPVWJ74=
+github.com/blevesearch/zapx/v13 v13.3.5/go.mod h1:FV+dRnScFgKnRDIp08RQL4JhVXt1x2HE3AOzqYa6fjo=
github.com/blevesearch/zapx/v14 v14.2.0/go.mod h1:GNgZusc1p4ot040cBQMRGEZobvwjCquiEKYh1xLFK9g=
-github.com/blevesearch/zapx/v14 v14.3.3 h1:dqqAzGphKl0yehHKKntDHKlEMhi9B/tJrD4OsWpY7YE=
-github.com/blevesearch/zapx/v14 v14.3.3/go.mod h1:zXNcVzukh0AvG57oUtT1T0ndi09H0kELNaNmekEy0jw=
+github.com/blevesearch/zapx/v14 v14.3.5 h1:hEvVjZaagFCvOUJrlFQ6/Z6Jjy0opM3g7TMEo58TwP4=
+github.com/blevesearch/zapx/v14 v14.3.5/go.mod h1:954A/eKFb+pg/ncIYWLWCKY+mIjReM9FGTGIO2Wu1cU=
github.com/blevesearch/zapx/v15 v15.2.0/go.mod h1:MmQceLpWfME4n1WrBFIwplhWmaQbQqLQARpaKUEOs/A=
-github.com/blevesearch/zapx/v15 v15.3.3 h1:60oE+qsJkveLenJmbc0eaH59GWYCbJJsPDV6Z5hEoYY=
-github.com/blevesearch/zapx/v15 v15.3.3/go.mod h1:C+f/97ZzTzK6vt/7sVlZdzZxKu+5+j4SrGCvr9dJzaY=
+github.com/blevesearch/zapx/v15 v15.3.5 h1:NVD0qq8vRk66ImJn1KloXT5ckqPDUZT7VbVJs9jKlac=
+github.com/blevesearch/zapx/v15 v15.3.5/go.mod h1:QMUh2hXCaYIWFKPYGavq/Iga2zbHWZ9DZAa9uFbWyvg=
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/bradfitz/gomemcache v0.0.0-20190329173943-551aad21a668/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b h1:L/QXpzIa3pOvUGt1D1lA5KjYhPBAN/3iWdP7xeFS9F0=
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=
-github.com/buildkite/terminal-to-html/v3 v3.6.1 h1:yHS+GXsPDXevb67YXjkVwZ4tolDCgPYa9RVOrzHlgGE=
-github.com/buildkite/terminal-to-html/v3 v3.6.1/go.mod h1:g0ME1XqbkBSgXR9YmlIHcJIjzaMyWW+HbsG0rPb5puo=
+github.com/buildkite/terminal-to-html/v3 v3.7.0 h1:chdLUSpiOj/A4v3dzxyOqixXI6aw7IDA6Dk77FXsvNU=
+github.com/buildkite/terminal-to-html/v3 v3.7.0/go.mod h1:g0ME1XqbkBSgXR9YmlIHcJIjzaMyWW+HbsG0rPb5puo=
+github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
+github.com/bwesterb/go-ristretto v1.2.1/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
github.com/caarlos0/ctrlc v1.0.0/go.mod h1:CdXpj4rmq0q/1Eb44M9zi2nKB0QraNKuRGYGrrHhcQw=
-github.com/caddyserver/certmagic v0.16.1 h1:rdSnjcUVJojmL4M0efJ+yHXErrrijS4YYg3FuwRdJkI=
-github.com/caddyserver/certmagic v0.16.1/go.mod h1:jKQ5n+ViHAr6DbPwEGLTSM2vDwTO6EvCKBblBRUvvuQ=
+github.com/caddyserver/certmagic v0.17.2 h1:o30seC1T/dBqBCNNGNHWwj2i5/I/FMjBbTAhjADP3nE=
+github.com/caddyserver/certmagic v0.17.2/go.mod h1:ouWUuC490GOLJzkyN35eXfV8bSbwMwSf4bdhkIxtdQE=
github.com/campoy/unique v0.0.0-20180121183637-88950e537e7e/go.mod h1:9IOqJGCPMSc6E5ydlp5NIonxObaeu/Iub/X03EKPVYo=
github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
github.com/cavaliercoder/go-cpio v0.0.0-20180626203310-925f9528c45e/go.mod h1:oDpT4efm8tSYHXV5tHSdRvBet/b/QzxZ+XyyPehvm3A=
@@ -292,13 +304,14 @@ github.com/chi-middleware/proxy v1.1.1/go.mod h1:jQwMEJct2tz9VmtCELxvnXoMfa+SOdi
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
-github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
-github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudflare/backoff v0.0.0-20161212185259-647f3cdfc87a/go.mod h1:rzgs2ZOiguV6/NpiDgADjRLPNyZlApIWxKpkT+X8SdY=
github.com/cloudflare/cfssl v1.6.1 h1:aIOUjpeuDJOpWjVJFP2ByplF53OgqG8I1S40Ggdlk3g=
github.com/cloudflare/cfssl v1.6.1/go.mod h1:ENhCj4Z17+bY2XikpxVmTHDg/C2IsG2Q0ZBeXpAqhCk=
+github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I=
+github.com/cloudflare/circl v1.2.0 h1:NheeISPSUcYftKlfrLuOo4T62FkmD4t4jviLfFFYaec=
+github.com/cloudflare/circl v1.2.0/go.mod h1:Ch2UgYr6ti2KTtlejELlROl0YIYj7SLjAC8M+INXlMk=
github.com/cloudflare/redoctober v0.0.0-20201013214028-99c99a8e7544/go.mod h1:6Se34jNoqrd8bTxrmJB2Bg2aoZ2CdSXonils9NsiNgo=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
@@ -352,7 +365,6 @@ github.com/couchbase/moss v0.2.0/go.mod h1:9MaHIaRuy9pvLPUJxB8sh8OrLfyDczECVL37g
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
-github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
@@ -368,8 +380,8 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.0-20210816181553-5444fa50b93d/go.mod h1:tmAIfUFEirG/Y8jhZ9M+h36obRZAk/1fcSpXwAVlfqE=
github.com/denisenkom/go-mssqldb v0.10.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
-github.com/denisenkom/go-mssqldb v0.12.0 h1:VtrkII767ttSPNRfFekePK3sctr+joXgO58stqQbtUA=
-github.com/denisenkom/go-mssqldb v0.12.0/go.mod h1:iiK0YP1ZeepvmBQk/QpLEhhTNJgfzrpArPY/aFvc9yU=
+github.com/denisenkom/go-mssqldb v0.12.2 h1:1OcPn5GBIobjWNd+8yjfHNIaFX14B1pWI3F9HZy5KXw=
+github.com/denisenkom/go-mssqldb v0.12.2/go.mod h1:lnIw1mZukFRZDJYQ0Pb833QS2IaC3l5HkEfra2LJ+sk=
github.com/devigned/tab v0.1.1/go.mod h1:XG9mPq0dFghrYvoBF3xdRrJzSTX1b7IQrvaL9mzjeJY=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
@@ -381,16 +393,16 @@ github.com/djherbis/buffer v1.2.0 h1:PH5Dd2ss0C7CRRhQCZ2u7MssF+No9ide8Ye71nPHcrQ
github.com/djherbis/buffer v1.2.0/go.mod h1:fjnebbZjCUpPinBRD+TDwXSOeNQ7fPQWLfGQqiAiUyE=
github.com/djherbis/nio/v3 v3.0.1 h1:6wxhnuppteMa6RHA4L81Dq7ThkZH8SwnDzXDYy95vB4=
github.com/djherbis/nio/v3 v3.0.1/go.mod h1:Ng4h80pbZFMla1yKzm61cF0tqqilXZYrogmWgZxOcmg=
-github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E=
github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
+github.com/dlclark/regexp2 v1.7.0 h1:7lJfhqlPssTb1WQx4yvTHN0uElPEv52sbaECrAQxjAo=
+github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=
-github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
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=
-github.com/duo-labs/webauthn v0.0.0-20220330035159-03696f3d4499 h1:jaQHuGKk9NVcfu9VbA7ygslr/7utxdYs47i4osBhZP8=
-github.com/duo-labs/webauthn v0.0.0-20220330035159-03696f3d4499/go.mod h1:UMk1JMDgQDcdI2vQz+WJOIUTSjIq07qSepAVgc93rUc=
+github.com/duo-labs/webauthn v0.0.0-20220815211337-00c9fb5711f5 h1:BaeJtFDlto/NjX9t730OebRRJf2P+t9YEDz3ur18824=
+github.com/duo-labs/webauthn v0.0.0-20220815211337-00c9fb5711f5/go.mod h1:Jcj7rFNlTknb18v9jpSA58BveX2LDhXqaoy+6YV1N9g=
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
@@ -398,8 +410,8 @@ github.com/dvyukov/go-fuzz v0.0.0-20210429054444-fca39067bc72/go.mod h1:11Gm+ccJ
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
-github.com/editorconfig/editorconfig-core-go/v2 v2.4.4 h1:FclmQqjEE8TqTFe6OGhaZY7MmbWVp9dBvv9vZkeC4Hk=
-github.com/editorconfig/editorconfig-core-go/v2 v2.4.4/go.mod h1:mz3wjBRdp75EfR6lp9qLmqyKp76AlT9I2Z13nALZeQs=
+github.com/editorconfig/editorconfig-core-go/v2 v2.4.5 h1:kTcVMyCvFGQmTk0S5+R7GF+y7wMHkWm4CYS5BwYWN8U=
+github.com/editorconfig/editorconfig-core-go/v2 v2.4.5/go.mod h1:rDB5UUleQsOI1HLbojaBmDNR8oUUe31InmNDTVzcDHY=
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
github.com/elazarl/go-bindata-assetfs v1.0.1/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4=
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
@@ -414,8 +426,8 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.m
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
-github.com/envoyproxy/go-control-plane v0.10.1 h1:cgDRLG7bs59Zd+apAWuzLQL95obVYAymNJek76W3mgw=
-github.com/envoyproxy/go-control-plane v0.10.1/go.mod h1:AY7fTTXNdv/aJ2O5jwpxAPOWUZ7hQAEvzN5Pf27BkQQ=
+github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1 h1:xvqufLtNVwAhN8NMyWklVgxnWohi+wtMGQMhtxexlm0=
+github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/envoyproxy/protoc-gen-validate v0.3.0-java/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/envoyproxy/protoc-gen-validate v0.6.1/go.mod h1:txg5va2Qkip90uYoSKH+nkAAmXrb2j3iq4FLwdrCbXQ=
@@ -427,12 +439,11 @@ github.com/ethantkoenig/rupture v1.0.1/go.mod h1:Sjqo/nbffZp1pVVXNGhpugIjsWmuS9K
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
-github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
-github.com/felixge/fgprof v0.9.2 h1:tAMHtWMyl6E0BimjVbFt7fieU6FpjttsZN7j0wT5blc=
-github.com/felixge/fgprof v0.9.2/go.mod h1:+VNi+ZXtHIQ6wIw6bUT8nXQRefQflWECoFyRealT5sg=
+github.com/felixge/fgprof v0.9.3 h1:VvyZxILNuCiUCSXtPtYmmtGvb65nqXh2QFWc0Wpf2/g=
+github.com/felixge/fgprof v0.9.3/go.mod h1:RdbpDgzqYVh/T9fPELJyV7EYJuHB55UTEULNun8eiPw=
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
-github.com/felixge/httpsnoop v1.0.2 h1:+nS9g82KMXccJ/wp0zyRW9ZBHFETmMGtkk+2CTTrW4o=
-github.com/felixge/httpsnoop v1.0.2/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
+github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk=
+github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
github.com/form3tech-oss/jwt-go v3.2.3+incompatible h1:7ZaBxOI7TMoYBfyA3cQHErNNyAWIKUMIwqxEtgHOs5c=
@@ -442,9 +453,9 @@ github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4=
github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
+github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
-github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI=
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
github.com/fullstorydev/grpcurl v1.8.0/go.mod h1:Mn2jWbdMrQGJQ8UD62uNyMumT2acsZUCkZIqFxsQf1o=
@@ -456,18 +467,16 @@ github.com/getsentry/raven-go v0.2.0 h1:no+xWJRb5ZI7eE8TWgIq1jLulQiIoLG0IfYxv5JY
github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
-github.com/gliderlabs/ssh v0.3.4 h1:+AXBtim7MTKaLVPgvE+3mhewYRawNLTd+jEEz/wExZw=
-github.com/gliderlabs/ssh v0.3.4/go.mod h1:ZSS+CUoKHDrqVakTfTWUlKSr9MtMFkC4UvtQKD7O914=
-github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
-github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
+github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY=
+github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4=
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-20220615144428-48208c70483b h1:+RjYfEfoZdM3wHFs752dlOpGaoRhwRRyQxjajg08LcQ=
-github.com/go-ap/activitypub v0.0.0-20220615144428-48208c70483b/go.mod h1:DE3vvc6Didgfd3k7M1Mos6qMDFNmMrxJmYVMHG9h9Io=
-github.com/go-ap/errors v0.0.0-20220615144307-e8bc4a40ae9f h1:kJhGo4NApJP0Lt9lkJnfmuTnRWVFbCynY0kiTxpPUR4=
-github.com/go-ap/errors v0.0.0-20220615144307-e8bc4a40ae9f/go.mod h1:KHkKFKZvc05lr79+RGoq/zG8YjWi3+FK60Bxd+mpCew=
-github.com/go-ap/jsonld v0.0.0-20220615144122-1d862b15410d h1:Z/oRXMlZHjvjIqDma1FrIGL3iE5YL7MUI0bwYEZ6qbA=
-github.com/go-ap/jsonld v0.0.0-20220615144122-1d862b15410d/go.mod h1:jyveZeGw5LaADntW+UEsMjl3IlIwk+DxlYNsbofQkGA=
+github.com/go-ap/activitypub v0.0.0-20220917143152-e4e7018838c0 h1:EUMB0x7u3de/ikGBtXiLxaJbmxgiqiAcM4yjW4whApM=
+github.com/go-ap/activitypub v0.0.0-20220917143152-e4e7018838c0/go.mod h1:OX9ajs2vU4UauC/DlghS/8M468Kn79r+y9kB6j7LuGM=
+github.com/go-ap/errors v0.0.0-20220917143055-4283ea5dae18 h1:A48SbkWKEciiJMbbcPzaRj9aizPUABzXFvCM3LtGGf8=
+github.com/go-ap/errors v0.0.0-20220917143055-4283ea5dae18/go.mod h1:dd3ZgjjloBsKPDpqA2kf2VWhF0A1eKUItOBh0/QcDWI=
+github.com/go-ap/jsonld v0.0.0-20220917142617-76bf51585778 h1:0tV3i8tE1NghMC4rXZXfD39KUbkKgIyLTsvOEmMOPCQ=
+github.com/go-ap/jsonld v0.0.0-20220917142617-76bf51585778/go.mod h1:jyveZeGw5LaADntW+UEsMjl3IlIwk+DxlYNsbofQkGA=
github.com/go-asn1-ber/asn1-ber v1.5.4 h1:vXT6d/FNDiELJnLb6hGNa309LMsrCoYFvpwHDF0+Y1A=
github.com/go-asn1-ber/asn1-ber v1.5.4/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
github.com/go-chi/chi/v5 v5.0.1/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
@@ -476,21 +485,20 @@ github.com/go-chi/chi/v5 v5.0.7 h1:rDTPXLDHGATaeHvVlLcR4Qe0zftYethFucbjVQ1PxU8=
github.com/go-chi/chi/v5 v5.0.7/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-enry/go-enry/v2 v2.8.2 h1:uiGmC+3K8sVd/6DOe2AOJEOihJdqda83nPyJNtMR8RI=
-github.com/go-enry/go-enry/v2 v2.8.2/go.mod h1:GVzIiAytiS5uT/QiuakK7TF1u4xDab87Y8V5EJRpsIQ=
+github.com/go-enry/go-enry/v2 v2.8.3 h1:BwvNrN58JqBJhyyVdZSl5QD3xoxEEGYUrRyPh31FGhw=
+github.com/go-enry/go-enry/v2 v2.8.3/go.mod h1:GVzIiAytiS5uT/QiuakK7TF1u4xDab87Y8V5EJRpsIQ=
github.com/go-enry/go-oniguruma v1.2.1 h1:k8aAMuJfMrqm/56SG2lV9Cfti6tC4x8673aHCcBk+eo=
github.com/go-enry/go-oniguruma v1.2.1/go.mod h1:bWDhYP+S6xZQgiRL7wlTScFYBe023B6ilRZbCAD5Hf4=
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.0 h1:Q5ViNfGF8zFgyJWPqYwA7qGFoMTEiBmdlkcfRmpIMa4=
github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E=
-github.com/go-git/go-billy/v5 v5.2.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
github.com/go-git/go-billy/v5 v5.3.1 h1:CPiOUAzKtMRvolEKw+bG1PLRpT7D3LIs3/3ey4Aiu34=
github.com/go-git/go-billy/v5 v5.3.1/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
-github.com/go-git/go-git-fixtures/v4 v4.2.1 h1:n9gGL1Ct/yIw+nfsfr8s4+sbhT+Ncu2SubfXjIWgci8=
-github.com/go-git/go-git-fixtures/v4 v4.2.1/go.mod h1:K8zd3kDUAykwTdDCr+I0per6Y6vMiRR/nnVTBtavnB0=
-github.com/go-git/go-git/v5 v5.4.3-0.20210630082519-b4368b2a2ca4 h1:1RSUwVK7VjTeA82kcLIqz1EU70QRwFdZUlJW58gP4GY=
-github.com/go-git/go-git/v5 v5.4.3-0.20210630082519-b4368b2a2ca4/go.mod h1:gQ1kArt6d+n+BGd+/B/I74HwRTLhth2+zti4ihgckDc=
+github.com/go-git/go-git-fixtures/v4 v4.3.1 h1:y5z6dd3qi8Hl+stezc8p3JxDkoTRqMAlKnXHuzrfjTQ=
+github.com/go-git/go-git-fixtures/v4 v4.3.1/go.mod h1:8LHG1a3SRW71ettAD/jW13h8c6AqjVSeL11RAdgaqpo=
+github.com/go-git/go-git/v5 v5.4.3-0.20220529141257-bc1f419cebcf h1:tmTaR5nN6RJs6G9+tmd3MRBNXSk6YTI9+8nv1WrIKzI=
+github.com/go-git/go-git/v5 v5.4.3-0.20220529141257-bc1f419cebcf/go.mod h1:cGzI9Lmtnh5wmJ5NjO/Cr3d6Iljyy588mEFUKs5vMvw=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
@@ -499,114 +507,51 @@ github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgOZ7o=
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
-github.com/go-ldap/ldap/v3 v3.4.3 h1:JCKUtJPIcyOuG7ctGabLKMgIlKnGumD/iGjuWeEruDI=
-github.com/go-ldap/ldap/v3 v3.4.3/go.mod h1:7LdHfVt6iIOESVEe3Bs4Jp2sHEKgDeduAhgM1/f9qmo=
+github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
+github.com/go-ldap/ldap/v3 v3.4.4 h1:qPjipEpt+qDa6SI/h1fzuGWoRUY+qqQ9sOZq67/PYUs=
+github.com/go-ldap/ldap/v3 v3.4.4/go.mod h1:fe1MsuN5eJJ1FeLT/LEBVdWfNWKh459R7aXgXtJC+aI=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
-github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI=
-github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
-github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
-github.com/go-openapi/analysis v0.19.2/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk=
-github.com/go-openapi/analysis v0.19.4/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk=
-github.com/go-openapi/analysis v0.19.5/go.mod h1:hkEAkxagaIvIP7VTn8ygJNkd4kAYON2rCu0v0ObL0AU=
-github.com/go-openapi/analysis v0.19.10/go.mod h1:qmhS3VNFxBlquFJ0RGoDtylO9y4pgTAUNE9AEEMdlJQ=
-github.com/go-openapi/analysis v0.19.16/go.mod h1:GLInF007N83Ad3m8a/CbQ5TPzdnGT7workfHwuVjNVk=
-github.com/go-openapi/analysis v0.20.0/go.mod h1:BMchjvaHDykmRMsK40iPtvyOfFdMMxlOmQr9FBZk+Og=
-github.com/go-openapi/analysis v0.20.1/go.mod h1:BMchjvaHDykmRMsK40iPtvyOfFdMMxlOmQr9FBZk+Og=
-github.com/go-openapi/analysis v0.21.2 h1:hXFrOYFHUAMQdu6zwAiKKJHJQ8kqZs1ux/ru1P1wLJU=
+github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
github.com/go-openapi/analysis v0.21.2/go.mod h1:HZwRk4RRisyG8vx2Oe6aqeSQcoxRp47Xkp3+K6q+LdY=
-github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
-github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
-github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94=
-github.com/go-openapi/errors v0.19.3/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94=
-github.com/go-openapi/errors v0.19.6/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M=
-github.com/go-openapi/errors v0.19.7/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M=
+github.com/go-openapi/analysis v0.21.4 h1:ZDFLvSNxpDaomuCueM0BlSXxpANBlFYiBvr+GXrvIHc=
+github.com/go-openapi/analysis v0.21.4/go.mod h1:4zQ35W4neeZTqh3ol0rv/O8JBbka9QyAgQRPp9y3pfo=
github.com/go-openapi/errors v0.19.8/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M=
github.com/go-openapi/errors v0.19.9/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M=
-github.com/go-openapi/errors v0.20.1/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M=
-github.com/go-openapi/errors v0.20.2 h1:dxy7PGTqEh94zj2E3h1cUmQQWiM1+aeCROfAr02EmK8=
github.com/go-openapi/errors v0.20.2/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M=
+github.com/go-openapi/errors v0.20.3 h1:rz6kiC84sqNQoqrtulzaL/VERgkoCyB6WdEkc2ujzUc=
+github.com/go-openapi/errors v0.20.3/go.mod h1:Z3FlZ4I8jEGxjUK+bugx3on2mIAk4txuAOhlsB1FSgk=
github.com/go-openapi/inflect v0.19.0 h1:9jCH9scKIbHeV9m12SmPilScz6krDxKRasNNSNPXu/4=
github.com/go-openapi/inflect v0.19.0/go.mod h1:lHpZVlpIQqLyKwJ4N+YSc9hchQy/i12fJykb83CRBH4=
-github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
-github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
-github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
-github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
-github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
-github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
-github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=
-github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg=
-github.com/go-openapi/jsonreference v0.19.6 h1:UBIxjkht+AWIgYzCDSv2GN+E/togfwXUJFRTWhl2Jjs=
github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns=
-github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
-github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
-github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
-github.com/go-openapi/loads v0.19.2/go.mod h1:QAskZPMX5V0C2gvfkGZzJlINuP7Hx/4+ix5jWFxsNPs=
-github.com/go-openapi/loads v0.19.3/go.mod h1:YVfqhUCdahYwR3f3iiwQLhicVRvLlU/WO5WPaZvcvSI=
-github.com/go-openapi/loads v0.19.5/go.mod h1:dswLCAdonkRufe/gSUC3gN8nTSaB9uaS2es0x5/IbjY=
-github.com/go-openapi/loads v0.19.6/go.mod h1:brCsvE6j8mnbmGBh103PT/QLHfbyDxA4hsKvYBNEGVc=
-github.com/go-openapi/loads v0.19.7/go.mod h1:brCsvE6j8mnbmGBh103PT/QLHfbyDxA4hsKvYBNEGVc=
-github.com/go-openapi/loads v0.20.0/go.mod h1:2LhKquiE513rN5xC6Aan6lYOSddlL8Mp20AW9kpviM4=
-github.com/go-openapi/loads v0.20.2/go.mod h1:hTVUotJ+UonAMMZsvakEgmWKgtulweO9vYP2bQYKA/o=
-github.com/go-openapi/loads v0.21.0 h1:jYtUO4wwP7psAweisP/MDoOpdzsYEESdoPcsWjHDR68=
-github.com/go-openapi/loads v0.21.0/go.mod h1:rHYve9nZrQ4CJhyeIIFJINGCg1tQpx2yJrrNo8sf1ws=
-github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA=
-github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64=
-github.com/go-openapi/runtime v0.19.4/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29gLDlFGtJ8NL4=
-github.com/go-openapi/runtime v0.19.15/go.mod h1:dhGWCTKRXlAfGnQG0ONViOZpjfg0m2gUt9nTQPQZuoo=
-github.com/go-openapi/runtime v0.19.16/go.mod h1:5P9104EJgYcizotuXhEuUrzVc+j1RiSjahULvYmlv98=
-github.com/go-openapi/runtime v0.19.24/go.mod h1:Lm9YGCeecBnUUkFTxPC4s1+lwrkJ0pthx8YvyjCfkgk=
-github.com/go-openapi/runtime v0.21.1 h1:/KIG00BzA2x2HRStX2tnhbqbQdPcFlkgsYCiNY20FZs=
-github.com/go-openapi/runtime v0.21.1/go.mod h1:aQg+kaIQEn+A2CRSY1TxbM8+sT9g2V3aLc1FbIAnbbs=
-github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
-github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
-github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY=
-github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
-github.com/go-openapi/spec v0.19.6/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk=
-github.com/go-openapi/spec v0.19.8/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk=
-github.com/go-openapi/spec v0.19.15/go.mod h1:+81FIL1JwC5P3/Iuuozq3pPE9dXdIEGxFutcFKaVbmU=
-github.com/go-openapi/spec v0.20.0/go.mod h1:+81FIL1JwC5P3/Iuuozq3pPE9dXdIEGxFutcFKaVbmU=
-github.com/go-openapi/spec v0.20.1/go.mod h1:93x7oh+d+FQsmsieroS4cmR3u0p/ywH649a3qwC9OsQ=
-github.com/go-openapi/spec v0.20.3/go.mod h1:gG4F8wdEDN+YPBMVnzE85Rbhf+Th2DTvA9nFPQ5AYEg=
-github.com/go-openapi/spec v0.20.4 h1:O8hJrt0UMnhHcluhIdUgCLRWyM2x7QkBXRvOs7m+O1M=
+github.com/go-openapi/jsonreference v0.20.0 h1:MYlu0sBgChmCfJxxUKZ8g1cPWFOB37YSZqewK7OKeyA=
+github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo=
+github.com/go-openapi/loads v0.21.1/go.mod h1:/DtAMXXneXFjbQMGEtbamCZb+4x7eGwkvZCvBmwUG+g=
+github.com/go-openapi/loads v0.21.2 h1:r2a/xFIYeZ4Qd2TnGpWDIQNcP80dIaZgf704za8enro=
+github.com/go-openapi/loads v0.21.2/go.mod h1:Jq58Os6SSGz0rzh62ptiu8Z31I+OTHqmULx5e/gJbNw=
+github.com/go-openapi/runtime v0.24.1 h1:Sml5cgQKGYQHF+M7yYSHaH1eOjvTykrddTE/KtQVjqo=
+github.com/go-openapi/runtime v0.24.1/go.mod h1:AKurw9fNre+h3ELZfk6ILsfvPN+bvvlaU/M9q/r9hpk=
github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I=
-github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
-github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
-github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY=
-github.com/go-openapi/strfmt v0.19.2/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU=
-github.com/go-openapi/strfmt v0.19.3/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU=
-github.com/go-openapi/strfmt v0.19.4/go.mod h1:eftuHTlB/dI8Uq8JJOyRlieZf+WkkxUuk0dgdHXr2Qk=
-github.com/go-openapi/strfmt v0.19.5/go.mod h1:eftuHTlB/dI8Uq8JJOyRlieZf+WkkxUuk0dgdHXr2Qk=
-github.com/go-openapi/strfmt v0.19.11/go.mod h1:UukAYgTaQfqJuAFlNxxMWNvMYiwiXtLsF2VwmoFtbtc=
-github.com/go-openapi/strfmt v0.20.0/go.mod h1:UukAYgTaQfqJuAFlNxxMWNvMYiwiXtLsF2VwmoFtbtc=
-github.com/go-openapi/strfmt v0.20.2/go.mod h1:43urheQI9dNtE5lTZQfuFJvjYJKPrxicATpEfZwHUNk=
+github.com/go-openapi/spec v0.20.6/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA=
+github.com/go-openapi/spec v0.20.7 h1:1Rlu/ZrOCCob0n+JKKJAWhNWMPW8bOZRg8FJaY+0SKI=
+github.com/go-openapi/spec v0.20.7/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA=
github.com/go-openapi/strfmt v0.21.0/go.mod h1:ZRQ409bWMj+SOgXofQAGTIo2Ebu72Gs+WaRADcS5iNg=
-github.com/go-openapi/strfmt v0.21.1 h1:G6s2t5V5kGCHLVbSdZ/6lI8Wm4OzoPFkc3/cjAsKQrM=
github.com/go-openapi/strfmt v0.21.1/go.mod h1:I/XVKeLc5+MM5oPNN7P6urMOpuLXEcNrCX/rPGuWb0k=
-github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
-github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
-github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
+github.com/go-openapi/strfmt v0.21.2/go.mod h1:I/XVKeLc5+MM5oPNN7P6urMOpuLXEcNrCX/rPGuWb0k=
+github.com/go-openapi/strfmt v0.21.3 h1:xwhj5X6CjXEZZHMWy1zKJxvW9AfHC9pkyUjLvHtKG7o=
+github.com/go-openapi/strfmt v0.21.3/go.mod h1:k+RzNO0Da+k3FrrynSNN8F7n/peCmQQqbbXjtDfvmGg=
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
-github.com/go-openapi/swag v0.19.7/go.mod h1:ao+8BpOPyKdpQz3AOJfbeEVpLmWAvlT1IfTe5McPyhY=
-github.com/go-openapi/swag v0.19.9/go.mod h1:ao+8BpOPyKdpQz3AOJfbeEVpLmWAvlT1IfTe5McPyhY=
-github.com/go-openapi/swag v0.19.12/go.mod h1:eFdyEBkTdoAf/9RXBvj4cr1nH7GD8Kzo5HTt47gr72M=
-github.com/go-openapi/swag v0.19.13/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
-github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
-github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM=
github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
-github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4=
-github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA=
-github.com/go-openapi/validate v0.19.3/go.mod h1:90Vh6jjkTn+OT1Eefm0ZixWNFjhtOH7vS9k0lo6zwJo=
-github.com/go-openapi/validate v0.19.10/go.mod h1:RKEZTUWDkxKQxN2jDT7ZnZi2bhZlbNMAuKvKB+IaGx8=
-github.com/go-openapi/validate v0.19.12/go.mod h1:Rzou8hA/CBw8donlS6WNEUQupNvUZ0waH08tGe6kAQ4=
-github.com/go-openapi/validate v0.19.15/go.mod h1:tbn/fdOwYHgrhPBzidZfJC2MIVvs9GA7monOmWBbeCI=
-github.com/go-openapi/validate v0.20.1/go.mod h1:b60iJT+xNNLfaQJUqLI7946tYiFEOuE9E4k54HpKcJ0=
-github.com/go-openapi/validate v0.20.3 h1:GZPPhhKSZrE8HjB4eEkoYAZmoWA4+tCemSgINH1/vKw=
-github.com/go-openapi/validate v0.20.3/go.mod h1:goDdqVGiigM3jChcrYJxD2joalke3ZXeftD16byIjA4=
+github.com/go-openapi/swag v0.21.1/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
+github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g=
+github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
+github.com/go-openapi/validate v0.21.0/go.mod h1:rjnrwK57VJ7A8xqfpAOEKRH8yQSGUriMu5/zuPSQ1hg=
+github.com/go-openapi/validate v0.22.0 h1:b0QecH6VslW/TxtpKgzpO1SNG7GU2FsaqKdP1E2T50Y=
+github.com/go-openapi/validate v0.22.0/go.mod h1:rjnrwK57VJ7A8xqfpAOEKRH8yQSGUriMu5/zuPSQ1hg=
github.com/go-redis/redis v6.15.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
github.com/go-redis/redis/v8 v8.4.0/go.mod h1:A1tbYoHSa1fXwN+//ljcCYYJeLmVrwL9hbQN45Jdy0M=
@@ -618,13 +563,12 @@ github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LB
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
-github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw=
github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4=
-github.com/go-swagger/go-swagger v0.29.0 h1:z3YoZtLvS1Y8TE/PCat1VypcZxM0IgKLt0NvZxQyNl8=
-github.com/go-swagger/go-swagger v0.29.0/go.mod h1:Z4GJzI+bHKKkGB2Ji1rawpi3/ldXX8CkzGIa9HAC5EE=
+github.com/go-swagger/go-swagger v0.30.3 h1:HuzvdMRed/9Q8vmzVcfNBQByZVtT79DNZxZ18OprdoI=
+github.com/go-swagger/go-swagger v0.30.3/go.mod h1:neDPes8r8PCz2JPvHRDj8BTULLh4VJUt7n6MpQqxhHM=
github.com/go-swagger/scan-repo-boundary v0.0.0-20180623220736-973b3573c013 h1:l9rI6sNaZgNC0LnF3MiE+qTmyBA/tZAg1rtyrGbUMK0=
-github.com/go-testfixtures/testfixtures/v3 v3.6.1 h1:n4Fv95Exp0D05G6l6CAZv22Ck1EJK0pa0TfPqE4ncSs=
-github.com/go-testfixtures/testfixtures/v3 v3.6.1/go.mod h1:Bsb2MoHAfHnNsPpSwAjtOs102mqDuM+1u3nE2OCi0N0=
+github.com/go-testfixtures/testfixtures/v3 v3.8.1 h1:uonwvepqRvSgddcrReZQhojTlWlmOlHkYAb9ZaOMWgU=
+github.com/go-testfixtures/testfixtures/v3 v3.8.1/go.mod h1:Kdu7YeMC0KRXVHdaQ91Vmx3pcjoTF63h4f1qTJDdXLA=
github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0=
github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY=
github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg=
@@ -654,8 +598,8 @@ github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJA
github.com/goccy/go-json v0.8.1/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/goccy/go-json v0.9.5/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/goccy/go-json v0.9.6/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
-github.com/goccy/go-json v0.9.7 h1:IcB+Aqpx/iMHu5Yooh7jEzJk1JZ7Pjtmys2ukPr7EeM=
-github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
+github.com/goccy/go-json v0.9.11 h1:/pAaQDLHEoCq/5FFmSKBswWmK6H0e8g4159Kc/X/nqk=
+github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
@@ -673,15 +617,16 @@ github.com/gogs/cron v0.0.0-20171120032916-9f6c956d3e14 h1:yXtpJr/LV6PFu4nTLgfjQ
github.com/gogs/cron v0.0.0-20171120032916-9f6c956d3e14/go.mod h1:jPoNZLWDAqA5N3G5amEoiNbhVrmM+ZQEcnQvNQ2KaZk=
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.1.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
-github.com/golang-jwt/jwt/v4 v4.4.1 h1:pC5DB52sCeK48Wlb9oPcdhnjkz1TKt1D/P7WKJ0kUcQ=
-github.com/golang-jwt/jwt/v4 v4.4.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
+github.com/golang-jwt/jwt/v4 v4.4.2 h1:rcc4lwaZgFMCZ5jxF9ABolDcIHdBytAFgqFPbSJQAYs=
+github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA=
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
-github.com/golang-sql/sqlexp v0.0.0-20170517235910-f1bb20e5a188 h1:+eHOFJl1BaXrQxKX+T06f78590z4qA2ZzBTqahsKSE4=
-github.com/golang-sql/sqlexp v0.0.0-20170517235910-f1bb20e5a188/go.mod h1:vXjM/+wXQnTPR4KqTKDgJukSZ6amVRtWMPEjE6sQoK8=
+github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A=
+github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI=
+github.com/golang/geo v0.0.0-20210211234256-740aa86cb551 h1:gtexQ/VGyN+VVFRXSFiguSNcXmS6rkKT+X7FdIrTtfo=
+github.com/golang/geo v0.0.0-20210211234256-740aa86cb551/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/glog v0.0.0-20210429001901-424d2337a529/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@@ -747,9 +692,10 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
+github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-github/v28 v28.1.1/go.mod h1:bsqJWQX05omyWVmc00nEUql9mhQyv38lDZ8kPZcQVoM=
-github.com/google/go-github/v45 v45.0.0 h1:LU0WBjYidxIVyx7PZeWb+FP4JZJ3Wh3FQgdumnGqiLs=
-github.com/google/go-github/v45 v45.0.0/go.mod h1:FObaZJEDSTa/WGCzZ2Z3eoCDXWJKMenWWTrd8jrta28=
+github.com/google/go-github/v45 v45.2.0 h1:5oRLszbrkvxDDqBCNj2hjDZMKmvexaZ1xw/FCD+K3FI=
+github.com/google/go-github/v45 v45.2.0/go.mod h1:FObaZJEDSTa/WGCzZ2Z3eoCDXWJKMenWWTrd8jrta28=
github.com/google/go-licenses v0.0.0-20210329231322-ce1d9163b77d/go.mod h1:+TYOmkVoJOpwnS0wfdsJCV9CoD5nJYsHoFk/0CrTK4M=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
@@ -757,6 +703,7 @@ github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17
github.com/google/go-replayers/grpcreplay v0.1.0/go.mod h1:8Ig2Idjpr6gifRd6pNVggX6TC1Zw6Jx74AKp7QNH2QE=
github.com/google/go-replayers/httpreplay v0.1.0/go.mod h1:YKZViNhiGgqdBlUbI2MwGpq4pXxNmhJLPHQ7cv2b5no=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/licenseclassifier v0.0.0-20210325184830-bb04aff29e72/go.mod h1:qsqn2hxC+vURpyBRygGUuinTO42MFRLcsmQ/P8v94+M=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian v2.1.1-0.20190517191504-25dcb96d9e51+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
@@ -780,8 +727,8 @@ github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLe
github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg=
-github.com/google/pprof v0.0.0-20220509035851-59ca7ad80af3 h1:vFrXU7L2gqtlP/ZGijSpaDIc16ZQrZI4FAuYtpQTyQc=
-github.com/google/pprof v0.0.0-20220509035851-59ca7ad80af3/go.mod h1:Pt31oes+eGImORns3McJn8zHefuQl2rG8l6xQjGYB4U=
+github.com/google/pprof v0.0.0-20220829040838-70bd9ae97f40 h1:ykKxL12NZd3JmWZnyqarJGsF73M9Xhtrik/FEtEeFRE=
+github.com/google/pprof v0.0.0-20220829040838-70bd9ae97f40/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/rpmpack v0.0.0-20191226140753-aa36bfddb3a0/go.mod h1:RaTPr0KUf2K7fnZYLNDrr8rxAamWs3iNywJLtQ2AzBg=
github.com/google/subcommands v1.0.1/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
@@ -795,11 +742,16 @@ github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/wire v0.3.0/go.mod h1:i1DMg/Lu8Sz5yYl25iOdmc5CT5qusaa+zmRWs16741s=
+github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8=
github.com/googleapis/gax-go v2.0.2+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0=
github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM=
+github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM=
+github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM=
+github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c=
+github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4=
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
@@ -848,30 +800,21 @@ github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE=
-github.com/hashicorp/consul/api v1.11.0/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M=
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
github.com/hashicorp/consul/sdk v0.3.0/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
-github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
-github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
-github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
-github.com/hashicorp/go-hclog v1.0.0 h1:bkKf0BeBXcSYa7f5Fyi9gMuQ8gNsxeiNpZjR6VxNZeo=
-github.com/hashicorp/go-hclog v1.0.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
+github.com/hashicorp/go-hclog v1.2.0 h1:La19f8d7WIlm4ogzNHB0JGqs5AUDAZ2UfCY4sJXcJdM=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
-github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
-github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
-github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
github.com/hashicorp/go-retryablehttp v0.6.4/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
github.com/hashicorp/go-retryablehttp v0.7.1 h1:sUiuQAnLlbvmExtFQs72iFW/HXeUn8Z1aJLQ4LJJbTQ=
github.com/hashicorp/go-retryablehttp v0.7.1/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
-github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
@@ -885,17 +828,12 @@ github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
-github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY=
-github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc=
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
-github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE=
-github.com/hashicorp/memberlist v0.3.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE=
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
-github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk=
-github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/huandu/xstrings v1.0.0/go.mod h1:4qWG/gcEcfX4z/mBDHJ++3ReCw9ibxbsNJbcucJdbSo=
github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4=
+github.com/huandu/xstrings v1.3.1/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw=
github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg=
@@ -907,8 +845,10 @@ github.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d/go.mod h1:
github.com/imdario/mergo v0.3.4/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
-github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
+github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
+github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk=
+github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
@@ -925,8 +865,8 @@ github.com/jackc/pgconn v1.5.0/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr
github.com/jackc/pgconn v1.5.1-0.20200601181101-fa742c524853/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI=
github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o=
github.com/jackc/pgconn v1.8.1/go.mod h1:JV6m6b6jhjdmzchES0drzCcYcAHS1OPD5xu3OZ/lE2g=
-github.com/jackc/pgconn v1.9.0 h1:gqibKSTJup/ahCsNKyMZAniPuZEfIqfXFc8FOWVYR+Q=
github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY=
+github.com/jackc/pgconn v1.12.1 h1:rsDFzIpRk7xT4B8FufgpCCeyjdNpKyghZeSefViE5W8=
github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE=
github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8=
github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE=
@@ -941,8 +881,8 @@ github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvW
github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
github.com/jackc/pgproto3/v2 v2.0.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
-github.com/jackc/pgproto3/v2 v2.1.1 h1:7PQ/4gLoqnl87ZxL7xjO0DR5gYuviDCZxQJsUlFW1eI=
github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
+github.com/jackc/pgproto3/v2 v2.3.0 h1:brH0pCGBDkBW07HWlN/oSBXrmo3WB0UvZd1pIuDcL8Y=
github.com/jackc/pgservicefile v0.0.0-20200307190119-3430c5407db8/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg=
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
@@ -950,24 +890,20 @@ github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01C
github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc=
github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw=
github.com/jackc/pgtype v1.2.0/go.mod h1:5m2OfMh1wTK7x+Fk952IDmI4nw3nPrvtQdM0ZT4WpC0=
-github.com/jackc/pgtype v1.3.0/go.mod h1:b0JqxHvPmljG+HQ5IsvQ0yqeSi4nGcDTVjFoiLDb0Ik=
github.com/jackc/pgtype v1.3.1-0.20200510190516-8cd94a14c75a/go.mod h1:vaogEUkALtxZMCH411K+tKzNpwzCKU+AnPzBKZ+I+Po=
github.com/jackc/pgtype v1.3.1-0.20200606141011-f6355165a91c/go.mod h1:cvk9Bgu/VzJ9/lxTO5R5sf80p0DiucVtN7ZxvaC4GmQ=
github.com/jackc/pgtype v1.7.0/go.mod h1:ZnHF+rMePVqDKaOfJVI4Q8IVvAQMryDlDkZnKOI75BE=
-github.com/jackc/pgtype v1.8.0 h1:iFVCcVhYlw0PulYCVoguRGm0SE9guIcPcccnLzHj8bA=
github.com/jackc/pgtype v1.8.0/go.mod h1:PqDKcEBtllAtk/2p6z6SHdXW5UB+MhE75tUol2OKexE=
-github.com/jackc/pgx v3.6.2+incompatible h1:2zP5OD7kiyR3xzRYMhOcXVvkDZsImVXfj+yIyTQf3/o=
-github.com/jackc/pgx v3.6.2+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I=
+github.com/jackc/pgtype v1.11.0 h1:u4uiGPz/1hryuXzyaBhSk6dnIyyG2683olG2OV+UUgs=
github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y=
github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM=
github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=
github.com/jackc/pgx/v4 v4.5.0/go.mod h1:EpAKPLdnTorwmPUUsqrPxy5fphV18j9q3wrfRXgo+kA=
-github.com/jackc/pgx/v4 v4.6.0/go.mod h1:vPh43ZzxijXUVJ+t/EmXBtFmbFVO72cuneCT9oAlxAg=
github.com/jackc/pgx/v4 v4.6.1-0.20200510190926-94ba730bb1e9/go.mod h1:t3/cdRQl6fOLDxqtlyhe9UWgfIi9R8+8v8GKV5TRA/o=
github.com/jackc/pgx/v4 v4.6.1-0.20200606145419-4e5062306904/go.mod h1:ZDaNWkt9sW1JMiNn0kdYBaLelIhw7Pg4qd+Vk6tw7Hg=
github.com/jackc/pgx/v4 v4.11.0/go.mod h1:i62xJgdrtVDsnL3U8ekyrQXEwGNTRoG7/8r+CIdYfcc=
-github.com/jackc/pgx/v4 v4.12.0 h1:xiP3TdnkwyslWNp77yE5XAPfxAsU9RMFDe0c1SwN8h4=
github.com/jackc/pgx/v4 v4.12.0/go.mod h1:fE547h6VulLPA3kySjfnSG/e2D861g/50JlVUa/ub60=
+github.com/jackc/pgx/v4 v4.16.1 h1:JzTglcal01DrghUqt+PmzWsZx/Yh7SC/CTQmSBMTd0Y=
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
@@ -987,12 +923,10 @@ github.com/jhump/protoreflect v1.8.2 h1:k2xE7wcUomeqwY0LDCYA16y4WWfyTcMx5mKhk0d4
github.com/jhump/protoreflect v1.8.2/go.mod h1:7GcYQDdMU/O/BBrl/cX6PNHpXh6cenjd8pneu5yW7Tg=
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
-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/jmhodges/clock v0.0.0-20160418191101-880ee4c33548/go.mod h1:hGT6jSUVzF6no3QaDSMLGLEHtHSBSefs+MgcDWnmhmo=
github.com/jmoiron/sqlx v1.3.3/go.mod h1:2BljVx/86SuTyjE+aPYlHCTNvZrnJXghYGpNiXLBMCQ=
-github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
+github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ=
github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=
@@ -1000,6 +934,7 @@ github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8Hm
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/jpillora/backoff v0.0.0-20180909062703-3050d21c67d7/go.mod h1:2iMrUgbbvHEiQClaW2NsSzMyGHqN+rDFqY705q49KG0=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
+github.com/json-iterator/go v0.0.0-20171115153421-f7279a603ede/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
@@ -1033,16 +968,15 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o
github.com/kisielk/sqlstruct v0.0.0-20201105191214-5f3e10d3ab46/go.mod h1:yyMNCyc/Ib3bDTKd379tNMpB/7/H5TjM2Y9QJ5THLbE=
github.com/kisom/goutils v1.4.3/go.mod h1:Lp5qrquG7yhYnWzZCI/68Pa/GpFynw//od6EkGnWpac=
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
-github.com/klauspost/compress v1.9.5/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.3 h1:wmfu2iqj9q22SyMINp1uQ8C2/V4M1phJdmH9fG4nba0=
-github.com/klauspost/compress v1.15.3/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
+github.com/klauspost/compress v1.15.11 h1:Lcadnb3RKGin4FYM/orgq0qde+nc15E5Cbqg4B9Sx9c=
+github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM=
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.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
-github.com/klauspost/cpuid/v2 v2.0.12 h1:p9dKCg8i4gmOxtv35DvrYoWqYzQrvEVdjQ762Y0OqZE=
-github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
+github.com/klauspost/cpuid/v2 v2.1.1 h1:t0wUqjowdm8ezddV5k0tLWVklVuvLJpoHeb4WBdydm0=
+github.com/klauspost/cpuid/v2 v2.1.1/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE=
github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
github.com/kljensen/snowball v0.6.0/go.mod h1:27N7E8fVU5H68RlUmnWwZCfxgt4POBJfENGMvNRhldw=
@@ -1052,12 +986,10 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxv
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
-github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
-github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
@@ -1078,14 +1010,12 @@ github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lib/pq v1.10.1/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
-github.com/lib/pq v1.10.5 h1:J+gdV2cUmX7ZqL2B0lFcW0m+egaHC2V3lpO8nWxyYiQ=
-github.com/lib/pq v1.10.5/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
+github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw=
+github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/libdns/libdns v0.2.1 h1:Wu59T7wSHRgtA0cfxC+n1c/e+O3upJGWytknkmFEDis=
github.com/libdns/libdns v0.2.1/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40=
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
-github.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96 h1:uNwtsDp7ci48vBTTxDuwcoTXz4lwtDTe7TjCQ0noaWY=
-github.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96/go.mod h1:mmIfjCSQlGYXmJ95jFN84AkQFnVABtKuJL8IrzwvUKQ=
github.com/lunny/log v0.0.0-20160921050905-7887c61bf0de/go.mod h1:3q8WtuPQsoRbatJuy3nvq/hRSvuBJrHHr+ybPPiNvHQ=
github.com/lunny/nodb v0.0.0-20160621015157-fc1ef06ad4af/go.mod h1:Cqz6pqow14VObJ7peltM+2n3PWOz7yTrfUuGbVFkzN0=
github.com/lunny/vfsgen v0.0.0-20220105142115-2c99e1ffdfa0 h1:F/3FfGmKdiKFa8kL3YrpZ7pe9H4l4AzA1pbaOUnRvPI=
@@ -1095,20 +1025,17 @@ github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc8
github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
-github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls=
-github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
-github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
-github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo=
+github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
-github.com/mailru/easyjson v0.7.1/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/markbates/going v1.0.0 h1:DQw0ZP7NbNlFGcKbcE/IVSOAFzScxRtLpd0rLMzLhq0=
github.com/markbates/going v1.0.0/go.mod h1:I6mnB4BPnEeqo85ynXIx1ZFLLbtiLHNXVgWeFO9OGOA=
-github.com/markbates/goth v1.72.0 h1:Vm9OE+GsB7FrrvBqKEYsRBiPg4LWJ6DT5zD0XN2Rl4U=
-github.com/markbates/goth v1.72.0/go.mod h1:X6xdNgpapSENS0O35iTBBcMHoJDQDfI9bJl+APCkYMc=
+github.com/markbates/goth v1.73.0 h1:X5QUUHLP5puJ4dhoPKkV3PhDIvvQEzsfVxsUmDNSJ28=
+github.com/markbates/goth v1.73.0/go.mod h1:X6xdNgpapSENS0O35iTBBcMHoJDQDfI9bJl+APCkYMc=
github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE=
github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0=
github.com/matryer/is v1.2.0 h1:92UTHpy8CDwaJ08GqLDzhhuixiBUUD1p3AU6PHddz4A=
@@ -1118,9 +1045,7 @@ github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcncea
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
-github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
-github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-ieproxy v0.0.0-20190610004146-91bb50d98149/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
@@ -1128,66 +1053,64 @@ github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
-github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
-github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
-github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
+github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
+github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
-github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
-github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
+github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
+github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-shellwords v1.0.10/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/mattn/go-sqlite3 v1.14.7/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
-github.com/mattn/go-sqlite3 v1.14.12 h1:TJ1bhYJPV44phC+IMu1u2K/i5RriLTPe+yc68XDJ1Z0=
-github.com/mattn/go-sqlite3 v1.14.12/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
+github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI=
+github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/mattn/go-zglob v0.0.1/go.mod h1:9fxibJccNxU2cnpIKLRRFA7zX7qhkJIQWBb449FYHOo=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
-github.com/mholt/acmez v1.0.2 h1:C8wsEBIUVi6e0DYoxqCcFuXtwc4AWXL/jgcDjF7mjVo=
-github.com/mholt/acmez v1.0.2/go.mod h1:8qnn8QA/Ewx8E3ZSsmscqsIjhhpxuy9vqdgbX2ceceM=
+github.com/mholt/acmez v1.0.4 h1:N3cE4Pek+dSolbsofIkAYz6H1d3pE+2G0os7QHslf80=
+github.com/mholt/acmez v1.0.4/go.mod h1:qFGLZ4u+ehWINeJZjzPlsnjJBCPAADWTcIqE/7DAYQY=
github.com/mholt/archiver/v3 v3.5.1 h1:rDjOBX9JSF5BvoJGvjqK479aL70qh9DIpZCl+k7Clwo=
github.com/mholt/archiver/v3 v3.5.1/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssnDhppzS1L4=
-github.com/microcosm-cc/bluemonday v1.0.19 h1:OI7hoF5FY4pFz2VA//RN8TfM0YJ2dJcl4P4APrCWy6c=
-github.com/microcosm-cc/bluemonday v1.0.19/go.mod h1:QNzV2UbLK2/53oIIwTOyLUSABMkjZ4tqiyC1g/DyqxE=
+github.com/microcosm-cc/bluemonday v1.0.20 h1:flpzsq4KU3QIYAYGV/szUat7H+GPOXR0B2JU5A1Wp8Y=
+github.com/microcosm-cc/bluemonday v1.0.20/go.mod h1:yfBmMi8mxvaZut3Yytv+jTXRY8mxyjJ0/kQBTElld50=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
-github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
-github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=
-github.com/miekg/dns v1.1.48 h1:Ucfr7IIVyMBz4lRE8qmGUuZ4Wt3/ZGu9hmcMT3Uu4tQ=
-github.com/miekg/dns v1.1.48/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
+github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA=
+github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME=
github.com/miekg/pkcs11 v1.0.2/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
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.26 h1:D0HK+8793etZfRY/vHhDmFaP+vmT41K3K4JV9vmZCBQ=
-github.com/minio/minio-go/v7 v7.0.26/go.mod h1:x81+AX5gHSfCSqw7jxRKHvxUXMlE5uKX0Vb75Xk5yYg=
+github.com/minio/minio-go/v7 v7.0.39 h1:upnbu1jCGOqEvrGSpRauSN9ZG7RCHK7VHxXS8Vmg2zk=
+github.com/minio/minio-go/v7 v7.0.39/go.mod h1:nCrRzjoSUQh8hgKKtu3Y708OLvRLtuASMg2/nvmbarw=
github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g=
github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
-github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI=
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
+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.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
-github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
-github.com/mitchellh/mapstructure v1.3.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
-github.com/mitchellh/mapstructure v1.4.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
-github.com/mitchellh/mapstructure v1.4.3 h1:OVowDSCllw/YjdLkam3/sm7wEtOy59d8ndGgCcyj8cs=
github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
+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.1/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=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@@ -1205,8 +1128,8 @@ github.com/mrjones/oauth v0.0.0-20190623134757-126b35219450/go.mod h1:skjdDftzkF
github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg=
github.com/mschoch/smat v0.2.0 h1:8imxQsjDm8yFEAVBe7azKmKSgzSkZXDuKkSq9374khM=
github.com/mschoch/smat v0.2.0/go.mod h1:kc9mz7DoBKqDyiRL7VZN8KvXQMWeTaVnttLRXOlotKw=
-github.com/msteinert/pam v1.0.0 h1:4XoXKtMCH3+e6GIkW41uxm6B37eYqci/DH3gzSq7ocg=
-github.com/msteinert/pam v1.0.0/go.mod h1:M4FPeAW8g2ITO68W8gACDz13NDJyOQM9IQsQhrR6TOI=
+github.com/msteinert/pam v1.1.0 h1:VhLun/0n0kQYxiRBJJvVpC2jR6d21SWJFjpvUVj20Kc=
+github.com/msteinert/pam v1.1.0/go.mod h1:M4FPeAW8g2ITO68W8gACDz13NDJyOQM9IQsQhrR6TOI=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mwitkow/go-proto-validators v0.0.0-20180403085117-0950a7990007/go.mod h1:m2XC9Qq0AlmmVksL6FktJCdTYyLk7V3fKyp0sl1yWQo=
@@ -1221,8 +1144,8 @@ github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OS
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
-github.com/niklasfasching/go-org v1.6.2 h1:kQBIZlfL4oRNApJCrBgaeNBfzxWzP6XlC7/b744Polk=
-github.com/niklasfasching/go-org v1.6.2/go.mod h1:wn76Xgu4/KRe43WZhsgZjxYMaloSrl3BSweGV74SwHs=
+github.com/niklasfasching/go-org v1.6.5 h1:5YAIqNTdl6lAOb7lD2AyQ1RuFGPVrAKvUexphk8PGbo=
+github.com/niklasfasching/go-org v1.6.5/go.mod h1:ybv0eGDnxylFUfFE+ySaQc734j/L3+/ChKZ/h63a2wM=
github.com/nishanths/predeclared v0.0.0-20200524104333-86fad755b4d3/go.mod h1:nt3d53pc1VYcphSCIaYAJtnPYnr3Zyn8fMq2wvPGPso=
github.com/nkovacs/streamquote v1.0.0/go.mod h1:BN+NaZ2CmdKqUuTUXUEm9j95B2TRbpOWpxbJYzzgUsc=
github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=
@@ -1272,22 +1195,22 @@ github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT9
github.com/otiai10/mint v1.3.1/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc=
github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
-github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
-github.com/pelletier/go-toml v1.4.0/go.mod h1:PN7xzY2wHTK0K9p34ErDQMlFxa51Fk0OUruD3k1mMwo=
github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE=
github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc=
-github.com/pelletier/go-toml v1.9.4 h1:tjENF6MfZAg8e4ZmZTeWaWiT2vXtsoO6+iuOjFhECwM=
-github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
+github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
+github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
+github.com/pelletier/go-toml/v2 v2.0.1 h1:8e3L2cCQzLFi2CR4g7vGFuFxX7Jl1kKX8gW+iV0GUKU=
+github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo=
github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac=
github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pierrec/lz4/v4 v4.1.2/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
-github.com/pierrec/lz4/v4 v4.1.14 h1:+fL8AQEZtz/ijeNnpduH0bROTu0O3NZAlPjQxGn8LwE=
-github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
+github.com/pierrec/lz4/v4 v4.1.17 h1:kV4Ip+/hUBC+8T6+2EgburRtkE9ef4nbY3f4dFhGjMc=
+github.com/pierrec/lz4/v4 v4.1.17/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@@ -1301,7 +1224,6 @@ github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
-github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
github.com/pquerna/otp v1.3.0 h1:oJV/SkzR33anKXwQU3Of42rL4wbrffP4uvUf1SvS5Xs=
github.com/pquerna/otp v1.3.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
@@ -1309,13 +1231,14 @@ github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og=
-github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
github.com/prometheus/client_golang v1.5.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
github.com/prometheus/client_golang v1.10.0/go.mod h1:WJM3cc3yu7XKBKa/I8WeZm+V3eltZnBwfENSU7mdogU=
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
-github.com/prometheus/client_golang v1.12.1 h1:ZiaPsmm9uiBeaSMRznKsCDNtPCS0T3JVDGF+06gjBzk=
+github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
+github.com/prometheus/client_golang v1.13.0 h1:b71QUfeo5M8gq2+evJdTPfZhYMAU0uKPkyPJ7TPsloU=
+github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
@@ -1333,8 +1256,9 @@ github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB8
github.com/prometheus/common v0.18.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s=
github.com/prometheus/common v0.24.0/go.mod h1:H6QK/N6XVT42whUeIdI3dp36w49c+/iMDk7UAI2qm7Q=
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
-github.com/prometheus/common v0.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4=
github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
+github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE=
+github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
@@ -1343,8 +1267,9 @@ github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+Gx
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
-github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU=
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
+github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo=
+github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/pseudomuto/protoc-gen-doc v1.4.1/go.mod h1:exDTOVwqpp30eV/EDPFLZy3Pwr2sn6hBC1WIYH/UbIg=
github.com/pseudomuto/protokit v0.2.0/go.mod h1:2PdH30hxVHsup8KpBTOXTBeMVhJZVio3Q8ViKSAXT0Q=
@@ -1355,8 +1280,9 @@ github.com/rcrowley/go-metrics v0.0.0-20190826022208-cac0b30c2563/go.mod h1:bCqn
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/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
-github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
+github.com/rivo/uniseg v0.4.2 h1:YwD0ulJSJytLpiaWua0sBDusfsCZohxjxzVTYjwxfV8=
+github.com/rivo/uniseg v0.4.2/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/robertkrimen/godocdown v0.0.0-20130622164427-0bfa04905481/go.mod h1:C9WhFzY47SzYBIvzFqSvHIR6ROgDo4TtdTuRaOMjF/s=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/fastuuid v1.1.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
@@ -1365,8 +1291,9 @@ github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR
github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
-github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg=
github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o=
+github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
+github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
github.com/rs/xid v1.4.0 h1:qd7wPTDkN6KQx2VmMBLrpHkiyQwgFXRnkOLacUiaSNY=
@@ -1378,10 +1305,9 @@ github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD
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/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
-github.com/sagikazarmark/crypt v0.3.0/go.mod h1:uD/D+6UF4SrIR1uGEv7bBNkNqLGqUr43MRiaGWX1Nig=
github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
-github.com/santhosh-tekuri/jsonschema/v5 v5.0.0 h1:TToq11gyfNlrMFZiYujSekIsPd9AmsA2Bj/iv+s4JHE=
-github.com/santhosh-tekuri/jsonschema/v5 v5.0.0/go.mod h1:FKdcjfQW6rpZSnxxUvEA5H/cDPdvJ/SZJQLWWXWGrZ0=
+github.com/santhosh-tekuri/jsonschema/v5 v5.0.1 h1:HNLA3HtUIROrQwG1cuu5EYuqk3UEoJ61Dr/9xkd6sok=
+github.com/santhosh-tekuri/jsonschema/v5 v5.0.1/go.mod h1:FKdcjfQW6rpZSnxxUvEA5H/cDPdvJ/SZJQLWWXWGrZ0=
github.com/sassoftware/go-rpmutils v0.0.0-20190420191620-a8f1baeba37b/go.mod h1:am+Fp8Bt506lA3Rk3QCmSqmYmLMnPDhdDUcosQCAx+I=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
@@ -1390,6 +1316,7 @@ github.com/sergi/go-diff v1.2.0 h1:XU+rvMAioB0UC3q1MFrIQy4Vo5/4VsRDQQXHsEya6xQ=
github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
+github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ=
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 h1:bUGsEnyNbVPw06Bs80sCeARAlK8lhwqGyi6UT8ymuGk=
github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
@@ -1405,8 +1332,9 @@ github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMB
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
-github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
+github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
+github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/assertions v1.0.0/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM=
@@ -1427,18 +1355,19 @@ github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B
github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4=
github.com/spf13/afero v1.3.4/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
-github.com/spf13/afero v1.8.0 h1:5MmtuhAgYeU6qpa7w7bP0dv6MBYuup0vekhSpSkoq60=
-github.com/spf13/afero v1.8.0/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo=
+github.com/spf13/afero v1.8.2 h1:xehSyVa0YnHWsJ49JFljMpg1HX19V6NDZ1fkm1Xznbo=
+github.com/spf13/afero v1.8.2/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
-github.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA=
-github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
+github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
+github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
+github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU=
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI=
github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo=
-github.com/spf13/cobra v1.3.0 h1:R7cSvGu+Vv+qX0gW5R/85dx2kmmJT5z5NM8ifdYjdn0=
-github.com/spf13/cobra v1.3.0/go.mod h1:BrRVncBjOJa/eUcVVm9CE+oC6as8k+VYr4NY7WCi9V4=
+github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU=
+github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
@@ -1449,9 +1378,8 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
-github.com/spf13/viper v1.10.0/go.mod h1:SoyBPwAtKDzypXNDFKN5kzH7ppppbGZtls1UpIy5AsM=
-github.com/spf13/viper v1.10.1 h1:nuJZuYpG7gTj/XqiUwg8bA0cp1+M2mC3J4g5luUYBKk=
-github.com/spf13/viper v1.10.1/go.mod h1:IGlFPqhNAPKRxohIzWpI5QEy4kuI7tcl5WvR+8qy1rU=
+github.com/spf13/viper v1.12.0 h1:CZ7eSOd3kZoaYDLbXnmzgQI5RlciuXBMA+18HwHRfZQ=
+github.com/spf13/viper v1.12.0/go.mod h1:b6COn30jlNxbm/V2IqWiNWkJ+vZNiMNksliPCiuKtSI=
github.com/src-d/gcfg v1.4.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI=
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf h1:pvbZ0lM0XWPBqUKqFU8cmavspvIl9nulOYwdy6IFRRo=
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf/go.mod h1:RJID2RhlZKId02nZ62WenDCkgHFerpIOmW0iT7GKmXM=
@@ -1463,6 +1391,7 @@ github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5J
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
+github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/testify v0.0.0-20170130113145-4d4bfba8f1d1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
@@ -1470,10 +1399,13 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
+github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
+github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
+github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
+github.com/subosito/gotenv v1.3.0 h1:mjC+YW8QpAdXibNi+vNWgzmgBH4+5l5dCXv8cNysBLI=
+github.com/subosito/gotenv v1.3.0/go.mod h1:YzJjq/33h7nrwdY+iHMhEOEEbW0ovIz0tB6t6PwAXzs=
github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE=
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
@@ -1493,7 +1425,6 @@ github.com/toqueteos/webbrowser v1.2.0 h1:tVP/gpK69Fx+qMJKsLE7TD8LuGWPnEV71wBN9r
github.com/toqueteos/webbrowser v1.2.0/go.mod h1:XWoZq4cyp9WeUeak7w7LXRUQf1F1ATJMir8RTqb4ayM=
github.com/tstranex/u2f v1.0.0 h1:HhJkSzDDlVSVIVt7pDJwCHQj67k7A5EeBgPmeD+pVsQ=
github.com/tstranex/u2f v1.0.0/go.mod h1:eahSLaqAS0zsIEv80+vXT7WanXs7MQQDg3j3wGBSayo=
-github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
@@ -1505,37 +1436,36 @@ github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0o
github.com/unknwon/com v0.0.0-20190804042917-757f69c95f3e/go.mod h1:tOOxU81rwgoCLoOVVPHb6T/wt8HZygqH5id+GNnlCXM=
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/unrolled/render v1.4.1 h1:VdpMc2YkAOWzbmC/P2yoHhRDXgsaCQHcTJ1KK6SNCA4=
-github.com/unrolled/render v1.4.1/go.mod h1:cK4RSTTVdND5j9EYEc0LAMOvdG11JeiKjyjfyZRvV2w=
+github.com/unrolled/render v1.5.0 h1:uNTHMvVoI9pyyXfgoDHHycIqFONNY2p4eQR9ty+NsxM=
+github.com/unrolled/render v1.5.0/go.mod h1:eLTosBkQqEPEk7pRfkCRApXd++lm++nCsVlFOHpeedw=
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
-github.com/urfave/cli v1.22.9 h1:cv3/KhXGBGjEXLC4bH0sLuJ9BewaAbpk5oyMOveu4pw=
-github.com/urfave/cli v1.22.9/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
+github.com/urfave/cli v1.22.10 h1:p8Fspmz3iTctJstry1PYS3HVdllxnEzTEsgIgtxTrCk=
+github.com/urfave/cli v1.22.10/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/urfave/cli/v2 v2.2.0/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fastjson v1.6.3 h1:tAKFnnwmeMGPbwJ7IwxcTPCNr3uIzoIj3/Fh90ra4xc=
github.com/valyala/fastjson v1.6.3/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY=
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
-github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw=
github.com/weppos/publicsuffix-go v0.13.1-0.20210123135404-5fd73613514e/go.mod h1:HYux0V0Zi04bHNwOHy4cXJVz/TQjYonnF6aoYhj+3QE=
github.com/weppos/publicsuffix-go v0.15.1-0.20210511084619-b1f36a2d6c0b/go.mod h1:HYux0V0Zi04bHNwOHy4cXJVz/TQjYonnF6aoYhj+3QE=
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.31.0/go.mod h1:sPLojNBn68fMUWSxIJtdVVIP8uSBYqesTfDUseX11Ug=
-github.com/xanzy/go-gitlab v0.64.0 h1:rMgQdW9S1w3qvNAH2LYpFd2xh7KNLk+JWJd7sorNuTc=
-github.com/xanzy/go-gitlab v0.64.0/go.mod h1:F0QEXwmqiBUxCgJm8fE9S+1veX4XC9Z4cfaAbqwk4YM=
+github.com/xanzy/go-gitlab v0.73.1 h1:UMagqUZLJdjss1SovIC+kJCH4k2AZWXl58gJd38Y/hI=
+github.com/xanzy/go-gitlab v0.73.1/go.mod h1:d/a0vswScO7Agg1CZNz15Ic6SSvBG9vfw8egL99t4kA=
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
-github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0=
-github.com/xanzy/ssh-agent v0.3.1 h1:AmzO1SSWxw73zxFZPRwaMN1MohDw8UyHnmuxyceTEGo=
github.com/xanzy/ssh-agent v0.3.1/go.mod h1:QIE4lCeL7nkC25x+yA3LBIYfwCc1TFziCtG7cBAac6w=
+github.com/xanzy/ssh-agent v0.3.2 h1:eKj4SX2Fe7mui28ZgnFW5fmTz1EIr7ugo5s6wDxdHBM=
+github.com/xanzy/ssh-agent v0.3.2/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs=
+github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g=
github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM=
-github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I=
-github.com/xdg/stringprep v0.0.0-20180714160509-73f8eece6fdc/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y=
+github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8=
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo=
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8=
@@ -1549,11 +1479,11 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
-github.com/yuin/goldmark v1.4.5/go.mod h1:rmuwmfZ0+bvzB24eSC//bk1R1Zp3hM0OXYv/G2LIilg=
-github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE=
-github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
-github.com/yuin/goldmark-highlighting v0.0.0-20220208100518-594be1970594 h1:yHfZyN55+5dp1wG7wDKv8HQ044moxkyGq12KFFMFDxg=
-github.com/yuin/goldmark-highlighting v0.0.0-20220208100518-594be1970594/go.mod h1:U9ihbh+1ZN7fR5Se3daSPoz1CGF9IYtSvWwVQtnzGHU=
+github.com/yuin/goldmark v1.4.15/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
+github.com/yuin/goldmark v1.5.2 h1:ALmeCk/px5FSm1MAcFBAsVKZjDuMVj8Tm7FFIlMJnqU=
+github.com/yuin/goldmark v1.5.2/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
+github.com/yuin/goldmark-highlighting/v2 v2.0.0-20220924101305-151362477c87 h1:Py16JEzkSdKAtEFJjiaYLYBOWGXc1r/xHj/Q/5lA37k=
+github.com/yuin/goldmark-highlighting/v2 v2.0.0-20220924101305-151362477c87/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/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
@@ -1570,15 +1500,16 @@ go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU=
go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4=
go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
go.etcd.io/etcd/api/v3 v3.5.0-alpha.0/go.mod h1:mPcW6aZJukV6Aa81LSKpBjQXTWlXB5r74ymPoSWa3Sw=
-go.etcd.io/etcd/api/v3 v3.5.1 h1:v28cktvBq+7vGyJXF8G+rWJmj+1XUmMtqcLnH8hDocM=
-go.etcd.io/etcd/api/v3 v3.5.1/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
-go.etcd.io/etcd/client/pkg/v3 v3.5.1 h1:XIQcHCFSG53bJETYeRJtIxdLv2EWRGxcfzR8lSnTH4E=
-go.etcd.io/etcd/client/pkg/v3 v3.5.1/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
+go.etcd.io/etcd/api/v3 v3.5.4 h1:OHVyt3TopwtUQ2GKdd5wu3PmmipR4FTwCqoEjSyRdIc=
+go.etcd.io/etcd/api/v3 v3.5.4/go.mod h1:5GB2vv4A4AOn3yk7MftYGHkUfGtDHnEraIjym4dYz5A=
+go.etcd.io/etcd/client/pkg/v3 v3.5.4 h1:lrneYvz923dvC14R54XcA7FXoZ3mlGZAgmwhfm7HqOg=
+go.etcd.io/etcd/client/pkg/v3 v3.5.4/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
go.etcd.io/etcd/client/v2 v2.305.0-alpha.0/go.mod h1:kdV+xzCJ3luEBSIeQyB/OEKkWKd8Zkux4sbDeANrosU=
-go.etcd.io/etcd/client/v2 v2.305.1 h1:vtxYCKWA9x31w0WJj7DdqsHFNjhkigdAnziDtkZb/l4=
-go.etcd.io/etcd/client/v2 v2.305.1/go.mod h1:pMEacxZW7o8pg4CrFE7pquyCJJzZvkvdD2RibOCCCGs=
-go.etcd.io/etcd/client/v3 v3.5.0-alpha.0 h1:dr1EOILak2pu4Nf5XbRIOCNIBjcz6UmkQd7hHRXwxaM=
+go.etcd.io/etcd/client/v2 v2.305.4 h1:Dcx3/MYyfKcPNLpR4VVQUP5KgYrBeJtktBwEKkw08Ao=
+go.etcd.io/etcd/client/v2 v2.305.4/go.mod h1:Ud+VUwIi9/uQHOMA+4ekToJ12lTxlv0zB/+DHwTGEbU=
go.etcd.io/etcd/client/v3 v3.5.0-alpha.0/go.mod h1:wKt7jgDgf/OfKiYmCq5WFGxOFAkVMLxiiXgLDFhECr8=
+go.etcd.io/etcd/client/v3 v3.5.4 h1:p83BUL3tAYS0OT/r0qglgc3M1JjhM0diV8DSWAhVXv4=
+go.etcd.io/etcd/client/v3 v3.5.4/go.mod h1:ZaRkVgBZC+L+dLCjTcF1hRXpgZXQPOvnA/Ak/gq3kiY=
go.etcd.io/etcd/etcdctl/v3 v3.5.0-alpha.0 h1:odMFuQQCg0UmPd7Cyw6TViRYv9ybGuXuki4CusDSzqA=
go.etcd.io/etcd/etcdctl/v3 v3.5.0-alpha.0/go.mod h1:YPwSaBciV5G6Gpt435AasAG3ROetZsKNUzibRa/++oo=
go.etcd.io/etcd/pkg/v3 v3.5.0-alpha.0 h1:3yLUEC0nFCxw/RArImOyRUI4OAFbg4PFpBbAhSNzKNY=
@@ -1595,18 +1526,12 @@ go.jolheiser.com/hcaptcha v0.0.4 h1:RrDERcr/Tz/kWyJenjVtI+V09RtLinXxlAemiwN5F+I=
go.jolheiser.com/hcaptcha v0.0.4/go.mod h1:aw32WQOxnQZ6E06C0LypCf+sxNxPACyOnq+ZGnrIYho=
go.jolheiser.com/pwn v0.0.3 h1:MQowb3QvCL5r5NmHmCPxw93SdjfgJ0q6rAwYn4i1Hjg=
go.jolheiser.com/pwn v0.0.3/go.mod h1:/j5Dl8ftNqqJ8Dlx3YTrJV1wIR2lWOTyrNU3Qe7rk6I=
-go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
-go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM=
-go.mongodb.org/mongo-driver v1.3.0/go.mod h1:MSWZXKOynuguX+JSvwP8i+58jYCXxbia8HS3gZBapIE=
-go.mongodb.org/mongo-driver v1.3.4/go.mod h1:MSWZXKOynuguX+JSvwP8i+58jYCXxbia8HS3gZBapIE=
-go.mongodb.org/mongo-driver v1.4.3/go.mod h1:WcMNYLx/IlOxLe6JRJiv2uXuCz6zBLndR4SoGjYphSc=
-go.mongodb.org/mongo-driver v1.4.4/go.mod h1:WcMNYLx/IlOxLe6JRJiv2uXuCz6zBLndR4SoGjYphSc=
-go.mongodb.org/mongo-driver v1.4.6/go.mod h1:WcMNYLx/IlOxLe6JRJiv2uXuCz6zBLndR4SoGjYphSc=
-go.mongodb.org/mongo-driver v1.5.1/go.mod h1:gRXCHX4Jo7J0IJ1oDQyUxF7jfy19UfxniMS4xxMmUqw=
go.mongodb.org/mongo-driver v1.7.3/go.mod h1:NqaYOwnXWr5Pm7AOpO5QFxKJ503nbMse/R79oO62zWg=
go.mongodb.org/mongo-driver v1.7.5/go.mod h1:VXEWRZ6URJIkUq2SCAyapmhH0ZLRBP+FT4xhp5Zvxng=
-go.mongodb.org/mongo-driver v1.8.2 h1:8ssUXufb90ujcIvR6MyE1SchaNj0SFxsakiZgxIyrMk=
-go.mongodb.org/mongo-driver v1.8.2/go.mod h1:0sQWfOeY63QTntERDJJ/0SuKK0T1uVSgKCuAROlKEPY=
+go.mongodb.org/mongo-driver v1.8.3/go.mod h1:0sQWfOeY63QTntERDJJ/0SuKK0T1uVSgKCuAROlKEPY=
+go.mongodb.org/mongo-driver v1.10.0/go.mod h1:wsihk0Kdgv8Kqu1Anit4sfK+22vSFbUrAVEYRhCXrA8=
+go.mongodb.org/mongo-driver v1.10.1 h1:NujsPveKwHaWuKUer/ceo9DzEe7HIj1SlJ6uvXZG0S4=
+go.mongodb.org/mongo-driver v1.10.1/go.mod h1:z4XpeoU6w+9Vht+jAFyLgVrD+jGSQQe0+CBWFHNiHt8=
go.opencensus.io v0.15.0/go.mod h1:UffZAU+4sDEINUGP/B7UfBBkq4fqLu9zXAX7ke6CHW0=
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
@@ -1624,8 +1549,8 @@ go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
-go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
-go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
+go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ=
+go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=
go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
@@ -1639,11 +1564,11 @@ go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9E
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
-go.uber.org/zap v1.15.0/go.mod h1:Mb2vm2krFEG5DV0W9qcHBYFtp/Wku1cvYaqPsS/WYfc=
go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ=
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
-go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8=
go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
+go.uber.org/zap v1.23.0 h1:OjGQ5KQDEUawVHxNwQgPpiypGHOxo2mNZsOqTak4fFY=
+go.uber.org/zap v1.23.0/go.mod h1:D+nX8jyLsMHMYrln8A0rJjFt/T/9/bGgIhAqxv5URuY=
gocloud.dev v0.19.0/go.mod h1:SmKwiR8YwIMMJvQBKLsC3fHNyMwXLw3PMDO+VVteJMI=
golang.org/x/crypto v0.0.0-20180501155221-613d6eafa307/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
@@ -1651,25 +1576,21 @@ golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnf
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191117063200-497ca9f6d64f/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
@@ -1679,15 +1600,16 @@ golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f/go.mod h1:jdWPYTVW3xRLrWP
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
-golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
-golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/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-20220331220935-ae2d96664a29/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
-golang.org/x/crypto v0.0.0-20220507011949-2cf3adece122 h1:NvGWuYG8dkDHFSKksI1P9faiVJ9rayE6l0+ouWVIDs8=
-golang.org/x/crypto v0.0.0-20220507011949-2cf3adece122/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
+golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
+golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
+golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
+golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be h1:fmw3UbQh+nxngCAHrDCCztao/kbYFnWjoqop8dHx05A=
+golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -1725,13 +1647,11 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
-golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
-golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 h1:kQgndtyPBW/JIYERgdxfwMYh3AVStj88WQTlNDi2a+o=
-golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
+golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
+golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181108082009-03003ca0c849/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -1741,7 +1661,6 @@ golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73r
golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
@@ -1753,7 +1672,6 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191002035440-2ec189313ef0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191119073136-fc4aabc6c914/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -1770,7 +1688,6 @@ golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
-golang.org/x/net v0.0.0-20200602114024-627f9648deb9/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
@@ -1787,7 +1704,6 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
-golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8=
golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM=
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210510120150-4163338589ed/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
@@ -1798,8 +1714,15 @@ golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qx
golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
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-20220630215102-69896b714898 h1:K7wO6V1IrczY9QOQ2WkVpw4JQSwCd52UsxVEirZUfiw=
+golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
+golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
+golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
+golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
+golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20220630215102-69896b714898/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
+golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
+golang.org/x/net v0.0.0-20220927171203-f486391704dc h1:FxpXZdoBqT8RjqTy6i1E8nXHhW21wK7ptQ/EPIGxzPQ=
+golang.org/x/net v0.0.0-20220927171203-f486391704dc/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -1819,10 +1742,13 @@ golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ
golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5 h1:OSnWWcOd/CtWQC2cYSBgbTSJv3ciqd8r54ySIW2y3RE=
+golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
+golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
+golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE=
+golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1 h1:lxqLZaMad/dJHMFZH0NiNpiEZI/nhgWhe4wgzpE+MuA=
+golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -1834,8 +1760,9 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -1850,7 +1777,6 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -1859,7 +1785,6 @@ golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190620070143-6f217b454f45/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -1867,12 +1792,8 @@ golang.org/x/sys v0.0.0-20190730183949-1393eb018365/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191119060738-e882bf8e40c2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -1884,7 +1805,6 @@ golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -1912,7 +1832,6 @@ golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210309074719-68d13333faf2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -1924,11 +1843,9 @@ golang.org/x/sys v0.0.0-20210412220455-f1c623a9e750/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210511113859-b0526f3d8744/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -1939,19 +1856,33 @@ golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210902050250-f475640dd07b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20211210111614-af8b64212486/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-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220209214540-3681064d5158/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-20220315194320-039c03cc5b86/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k=
+golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/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-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec h1:BkDtF2Ih9xZ7le9ndzTA7KJow28VbQW3odyk/8drmuI=
+golang.org/x/sys v0.0.0-20220928140112-f11e5e49a4ec/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
-golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
+golang.org/x/term v0.0.0-20220722155259-a9ba230a4035 h1:Q5284mrmYTpACcm+eAKjKJH48BBwSyfJqmmGDTtT8Vc=
+golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -1960,22 +1891,22 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY=
+golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.0.0-20220411224347-583f2d630306 h1:+gHMid33q6pen7kv9xvT+JRinntgeXO2AeZVd0AWD3w=
-golang.org/x/time v0.0.0-20220411224347-583f2d630306/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20220922220347-f3bd1da661af h1:Yx9k8YCG3dvF87UAn2tu2HQLf2dt/eR1bXxpLMWeH+Y=
+golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
@@ -1991,14 +1922,11 @@ golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBn
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
-golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
-golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190729092621-ff9f1409240a/go.mod h1:jcCCGcm9btYwXyDqrUWc6MKQKKGJCWEQ3AfLSRIbEuI=
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191010075000-0337d82405ff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
@@ -2055,16 +1983,17 @@ golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
-golang.org/x/tools v0.1.10 h1:QjFRCZxdOhBJ/UNgnBZLbNV13DlbnK0quyivTnXJM20=
-golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
+golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
+golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
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=
-golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f h1:GGU+dLjvlC3qDwqYgL6UgRmHXhOOgns0bZu2Ty5mm6U=
golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
+golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.5.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
@@ -2100,9 +2029,16 @@ google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6
google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI=
-google.golang.org/api v0.59.0/go.mod h1:sT2boj7M9YJxZzgeZqXogmhfmRWDtPzT31xkieUbuZU=
google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I=
-google.golang.org/api v0.62.0/go.mod h1:dKmwPCydfsad4qCH08MSdgWjfHOyfpd4VtDGgRFdavw=
+google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo=
+google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g=
+google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA=
+google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8=
+google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs=
+google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA=
+google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw=
+google.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg=
+google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@@ -2162,6 +2098,7 @@ google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6D
google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
+google.golang.org/genproto v0.0.0-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
google.golang.org/genproto v0.0.0-20210331142528-b7513248f0ba/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
google.golang.org/genproto v0.0.0-20210413151531-c14fb6ef47c3/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A=
@@ -2182,14 +2119,28 @@ google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEc
google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
-google.golang.org/genproto v0.0.0-20211008145708-270636b82663/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
-google.golang.org/genproto v0.0.0-20211028162531-8db9c33dc351/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
-google.golang.org/genproto v0.0.0-20211129164237-f09f9a12af12/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
-google.golang.org/genproto v0.0.0-20211203200212-54befc351ae9/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
-google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa h1:I0YcKz0I7OAhddo7ya8kMnvprhcWM045PmkBdMO9zN0=
google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
+google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
+google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
+google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
+google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
+google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
+google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
+google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
+google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E=
+google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
+google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
+google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
+google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
+google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
+google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
+google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
+google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
+google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
+google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90 h1:4SPz2GL2CXJt28MTF8V6Ap/9ZiVbQlJeGSd9qtA7DLs=
+google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
google.golang.org/grpc v1.8.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
@@ -2223,9 +2174,12 @@ google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnD
google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
-google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
-google.golang.org/grpc v1.43.0 h1:Eeu7bZtDZ2DpRCsLhUlcrLnvYaMK1Gz86a+hMVvELmM=
-google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
+google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
+google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=
+google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
+google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
+google.golang.org/grpc v1.47.0 h1:9n77onPX5F3qfFCqjy9dhn8PbNQsIKeVU04J9G7umt8=
+google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
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=
@@ -2241,8 +2195,9 @@ google.golang.org/protobuf v1.25.1-0.20200805231151-a709e31e5d12/go.mod h1:9JNX7
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
-google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
+google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
+google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk=
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=
@@ -2264,9 +2219,8 @@ gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:a
gopkg.in/ini.v1 v1.44.2/go.mod h1:M3Cogqpuv0QCi3ExAY5V4uOt4qb/R3xZubo9m8lK5wg=
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
-gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
-gopkg.in/ini.v1 v1.66.4 h1:SsAcf+mM7mRZo2nJNGt8mZCjG8ZRaNGMURJw7BsIST4=
-gopkg.in/ini.v1 v1.66.4/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
+gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
+gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/src-d/go-billy.v4 v4.3.2/go.mod h1:nDjArDMp+XMs1aFAESLRjfGSgfvoYN0hDfzEk0GjC98=
gopkg.in/src-d/go-git-fixtures.v3 v3.5.0/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g=
@@ -2288,8 +2242,10 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
diff --git a/integrations/api_packages_generic_test.go b/integrations/api_packages_generic_test.go
deleted file mode 100644
index c507702eaa..0000000000
--- a/integrations/api_packages_generic_test.go
+++ /dev/null
@@ -1,109 +0,0 @@
-// Copyright 2021 The Gitea Authors. All rights reserved.
-// Use of this source code is governed by a MIT-style
-// license that can be found in the LICENSE file.
-
-package integrations
-
-import (
- "bytes"
- "fmt"
- "net/http"
- "testing"
-
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/models/packages"
- "code.gitea.io/gitea/models/unittest"
- user_model "code.gitea.io/gitea/models/user"
-
- "github.com/stretchr/testify/assert"
-)
-
-func TestPackageGeneric(t *testing.T) {
- defer prepareTestEnv(t)()
- user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
-
- packageName := "te-st_pac.kage"
- packageVersion := "1.0.3"
- filename := "fi-le_na.me"
- content := []byte{1, 2, 3}
-
- url := fmt.Sprintf("/api/packages/%s/generic/%s/%s/%s", user.Name, packageName, packageVersion, filename)
-
- t.Run("Upload", func(t *testing.T) {
- defer PrintCurrentTest(t)()
-
- req := NewRequestWithBody(t, "PUT", url, bytes.NewReader(content))
- AddBasicAuthHeader(req, user.Name)
- MakeRequest(t, req, http.StatusCreated)
-
- pvs, err := packages.GetVersionsByPackageType(db.DefaultContext, user.ID, packages.TypeGeneric)
- assert.NoError(t, err)
- assert.Len(t, pvs, 1)
-
- pd, err := packages.GetPackageDescriptor(db.DefaultContext, pvs[0])
- assert.NoError(t, err)
- assert.NotNil(t, pd.SemVer)
- assert.Nil(t, pd.Metadata)
- assert.Equal(t, packageName, pd.Package.Name)
- assert.Equal(t, packageVersion, pd.Version.Version)
-
- pfs, err := packages.GetFilesByVersionID(db.DefaultContext, pvs[0].ID)
- assert.NoError(t, err)
- assert.Len(t, pfs, 1)
- assert.Equal(t, filename, pfs[0].Name)
- assert.True(t, pfs[0].IsLead)
-
- pb, err := packages.GetBlobByID(db.DefaultContext, pfs[0].BlobID)
- assert.NoError(t, err)
- assert.Equal(t, int64(len(content)), pb.Size)
- })
-
- t.Run("UploadExists", func(t *testing.T) {
- defer PrintCurrentTest(t)()
-
- req := NewRequestWithBody(t, "PUT", url, bytes.NewReader(content))
- AddBasicAuthHeader(req, user.Name)
- MakeRequest(t, req, http.StatusBadRequest)
- })
-
- t.Run("Download", func(t *testing.T) {
- defer PrintCurrentTest(t)()
-
- req := NewRequest(t, "GET", url)
- resp := MakeRequest(t, req, http.StatusOK)
-
- assert.Equal(t, content, resp.Body.Bytes())
-
- pvs, err := packages.GetVersionsByPackageType(db.DefaultContext, user.ID, packages.TypeGeneric)
- assert.NoError(t, err)
- assert.Len(t, pvs, 1)
- assert.Equal(t, int64(1), pvs[0].DownloadCount)
- })
-
- t.Run("Delete", func(t *testing.T) {
- defer PrintCurrentTest(t)()
-
- req := NewRequest(t, "DELETE", url)
- AddBasicAuthHeader(req, user.Name)
- MakeRequest(t, req, http.StatusOK)
-
- pvs, err := packages.GetVersionsByPackageType(db.DefaultContext, user.ID, packages.TypeGeneric)
- assert.NoError(t, err)
- assert.Empty(t, pvs)
- })
-
- t.Run("DownloadNotExists", func(t *testing.T) {
- defer PrintCurrentTest(t)()
-
- req := NewRequest(t, "GET", url)
- MakeRequest(t, req, http.StatusNotFound)
- })
-
- t.Run("DeleteNotExists", func(t *testing.T) {
- defer PrintCurrentTest(t)()
-
- req := NewRequest(t, "DELETE", url)
- AddBasicAuthHeader(req, user.Name)
- MakeRequest(t, req, http.StatusNotFound)
- })
-}
diff --git a/integrations/api_packages_nuget_test.go b/integrations/api_packages_nuget_test.go
deleted file mode 100644
index e69dd0ff9b..0000000000
--- a/integrations/api_packages_nuget_test.go
+++ /dev/null
@@ -1,381 +0,0 @@
-// Copyright 2021 The Gitea Authors. All rights reserved.
-// Use of this source code is governed by a MIT-style
-// license that can be found in the LICENSE file.
-
-package integrations
-
-import (
- "archive/zip"
- "bytes"
- "encoding/base64"
- "fmt"
- "io"
- "net/http"
- "testing"
-
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/models/packages"
- "code.gitea.io/gitea/models/unittest"
- user_model "code.gitea.io/gitea/models/user"
- nuget_module "code.gitea.io/gitea/modules/packages/nuget"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/routers/api/packages/nuget"
-
- "github.com/stretchr/testify/assert"
-)
-
-func TestPackageNuGet(t *testing.T) {
- defer prepareTestEnv(t)()
- user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
-
- packageName := "test.package"
- packageVersion := "1.0.3"
- packageAuthors := "KN4CK3R"
- packageDescription := "Gitea Test Package"
- symbolFilename := "test.pdb"
- symbolID := "d910bb6948bd4c6cb40155bcf52c3c94"
-
- var buf bytes.Buffer
- archive := zip.NewWriter(&buf)
- w, _ := archive.Create("package.nuspec")
- w.Write([]byte(`
-
-
- ` + packageName + `
- ` + packageVersion + `
- ` + packageAuthors + `
- ` + packageDescription + `
-
-
-
-
- `))
- archive.Close()
- content := buf.Bytes()
-
- url := fmt.Sprintf("/api/packages/%s/nuget", user.Name)
-
- t.Run("ServiceIndex", func(t *testing.T) {
- defer PrintCurrentTest(t)()
-
- req := NewRequest(t, "GET", fmt.Sprintf("%s/index.json", url))
- req = AddBasicAuthHeader(req, user.Name)
- resp := MakeRequest(t, req, http.StatusOK)
-
- var result nuget.ServiceIndexResponse
- DecodeJSON(t, resp, &result)
-
- assert.Equal(t, "3.0.0", result.Version)
- assert.NotEmpty(t, result.Resources)
-
- root := setting.AppURL + url[1:]
- for _, r := range result.Resources {
- switch r.Type {
- case "SearchQueryService":
- fallthrough
- case "SearchQueryService/3.0.0-beta":
- fallthrough
- case "SearchQueryService/3.0.0-rc":
- assert.Equal(t, root+"/query", r.ID)
- case "RegistrationsBaseUrl":
- fallthrough
- case "RegistrationsBaseUrl/3.0.0-beta":
- fallthrough
- case "RegistrationsBaseUrl/3.0.0-rc":
- assert.Equal(t, root+"/registration", r.ID)
- case "PackageBaseAddress/3.0.0":
- assert.Equal(t, root+"/package", r.ID)
- case "PackagePublish/2.0.0":
- assert.Equal(t, root, r.ID)
- }
- }
- })
-
- t.Run("Upload", func(t *testing.T) {
- t.Run("DependencyPackage", func(t *testing.T) {
- defer PrintCurrentTest(t)()
-
- req := NewRequestWithBody(t, "PUT", url, bytes.NewReader(content))
- req = AddBasicAuthHeader(req, user.Name)
- MakeRequest(t, req, http.StatusCreated)
-
- pvs, err := packages.GetVersionsByPackageType(db.DefaultContext, user.ID, packages.TypeNuGet)
- assert.NoError(t, err)
- assert.Len(t, pvs, 1)
-
- pd, err := packages.GetPackageDescriptor(db.DefaultContext, pvs[0])
- assert.NoError(t, err)
- assert.NotNil(t, pd.SemVer)
- assert.IsType(t, &nuget_module.Metadata{}, pd.Metadata)
- assert.Equal(t, packageName, pd.Package.Name)
- assert.Equal(t, packageVersion, pd.Version.Version)
-
- pfs, err := packages.GetFilesByVersionID(db.DefaultContext, pvs[0].ID)
- assert.NoError(t, err)
- assert.Len(t, pfs, 1)
- assert.Equal(t, fmt.Sprintf("%s.%s.nupkg", packageName, packageVersion), pfs[0].Name)
- assert.True(t, pfs[0].IsLead)
-
- pb, err := packages.GetBlobByID(db.DefaultContext, pfs[0].BlobID)
- assert.NoError(t, err)
- assert.Equal(t, int64(len(content)), pb.Size)
-
- req = NewRequestWithBody(t, "PUT", url, bytes.NewReader(content))
- req = AddBasicAuthHeader(req, user.Name)
- MakeRequest(t, req, http.StatusBadRequest)
- })
-
- t.Run("SymbolPackage", func(t *testing.T) {
- defer PrintCurrentTest(t)()
-
- createPackage := func(id, packageType string) io.Reader {
- var buf bytes.Buffer
- archive := zip.NewWriter(&buf)
-
- w, _ := archive.Create("package.nuspec")
- w.Write([]byte(`
-
-
- ` + id + `
- ` + packageVersion + `
- ` + packageAuthors + `
- ` + packageDescription + `
-
-
- `))
-
- w, _ = archive.Create(symbolFilename)
- b, _ := base64.StdEncoding.DecodeString(`QlNKQgEAAQAAAAAADAAAAFBEQiB2MS4wAAAAAAAABgB8AAAAWAAAACNQZGIAAAAA1AAAAAgBAAAj
-fgAA3AEAAAQAAAAjU3RyaW5ncwAAAADgAQAABAAAACNVUwDkAQAAMAAAACNHVUlEAAAAFAIAACgB
-AAAjQmxvYgAAAGm7ENm9SGxMtAFVvPUsPJTF6PbtAAAAAFcVogEJAAAAAQAAAA==`)
- w.Write(b)
-
- archive.Close()
- return &buf
- }
-
- req := NewRequestWithBody(t, "PUT", fmt.Sprintf("%s/symbolpackage", url), createPackage("unknown-package", "SymbolsPackage"))
- req = AddBasicAuthHeader(req, user.Name)
- MakeRequest(t, req, http.StatusNotFound)
-
- req = NewRequestWithBody(t, "PUT", fmt.Sprintf("%s/symbolpackage", url), createPackage(packageName, "DummyPackage"))
- req = AddBasicAuthHeader(req, user.Name)
- MakeRequest(t, req, http.StatusBadRequest)
-
- req = NewRequestWithBody(t, "PUT", fmt.Sprintf("%s/symbolpackage", url), createPackage(packageName, "SymbolsPackage"))
- req = AddBasicAuthHeader(req, user.Name)
- MakeRequest(t, req, http.StatusCreated)
-
- pvs, err := packages.GetVersionsByPackageType(db.DefaultContext, user.ID, packages.TypeNuGet)
- assert.NoError(t, err)
- assert.Len(t, pvs, 1)
-
- pd, err := packages.GetPackageDescriptor(db.DefaultContext, pvs[0])
- assert.NoError(t, err)
- assert.NotNil(t, pd.SemVer)
- assert.IsType(t, &nuget_module.Metadata{}, pd.Metadata)
- assert.Equal(t, packageName, pd.Package.Name)
- assert.Equal(t, packageVersion, pd.Version.Version)
-
- pfs, err := packages.GetFilesByVersionID(db.DefaultContext, pvs[0].ID)
- assert.NoError(t, err)
- assert.Len(t, pfs, 3)
- for _, pf := range pfs {
- switch pf.Name {
- case fmt.Sprintf("%s.%s.nupkg", packageName, packageVersion):
- case fmt.Sprintf("%s.%s.snupkg", packageName, packageVersion):
- assert.False(t, pf.IsLead)
-
- pb, err := packages.GetBlobByID(db.DefaultContext, pf.BlobID)
- assert.NoError(t, err)
- assert.Equal(t, int64(616), pb.Size)
- case symbolFilename:
- assert.False(t, pf.IsLead)
-
- pb, err := packages.GetBlobByID(db.DefaultContext, pf.BlobID)
- assert.NoError(t, err)
- assert.Equal(t, int64(160), pb.Size)
-
- pps, err := packages.GetProperties(db.DefaultContext, packages.PropertyTypeFile, pf.ID)
- assert.NoError(t, err)
- assert.Len(t, pps, 1)
- assert.Equal(t, nuget_module.PropertySymbolID, pps[0].Name)
- assert.Equal(t, symbolID, pps[0].Value)
- default:
- assert.Fail(t, "unexpected file: %v", pf.Name)
- }
- }
-
- req = NewRequestWithBody(t, "PUT", fmt.Sprintf("%s/symbolpackage", url), createPackage(packageName, "SymbolsPackage"))
- req = AddBasicAuthHeader(req, user.Name)
- MakeRequest(t, req, http.StatusBadRequest)
- })
- })
-
- t.Run("Download", func(t *testing.T) {
- defer PrintCurrentTest(t)()
-
- checkDownloadCount := func(count int64) {
- pvs, err := packages.GetVersionsByPackageType(db.DefaultContext, user.ID, packages.TypeNuGet)
- assert.NoError(t, err)
- assert.Len(t, pvs, 1)
- assert.Equal(t, count, pvs[0].DownloadCount)
- }
-
- checkDownloadCount(0)
-
- req := NewRequest(t, "GET", fmt.Sprintf("%s/package/%s/%s/%s.%s.nupkg", url, packageName, packageVersion, packageName, packageVersion))
- req = AddBasicAuthHeader(req, user.Name)
- resp := MakeRequest(t, req, http.StatusOK)
-
- assert.Equal(t, content, resp.Body.Bytes())
-
- checkDownloadCount(1)
-
- req = NewRequest(t, "GET", fmt.Sprintf("%s/package/%s/%s/%s.%s.snupkg", url, packageName, packageVersion, packageName, packageVersion))
- req = AddBasicAuthHeader(req, user.Name)
- MakeRequest(t, req, http.StatusOK)
-
- checkDownloadCount(1)
-
- t.Run("Symbol", func(t *testing.T) {
- defer PrintCurrentTest(t)()
-
- req := NewRequest(t, "GET", fmt.Sprintf("%s/symbols/%s/%sFFFFFFFF/gitea.pdb", url, symbolFilename, symbolID))
- MakeRequest(t, req, http.StatusBadRequest)
-
- req = NewRequest(t, "GET", fmt.Sprintf("%s/symbols/%s/%sFFFFFFFF/%s", url, symbolFilename, "00000000000000000000000000000000", symbolFilename))
- req = AddBasicAuthHeader(req, user.Name)
- MakeRequest(t, req, http.StatusNotFound)
-
- req = NewRequest(t, "GET", fmt.Sprintf("%s/symbols/%s/%sFFFFFFFF/%s", url, symbolFilename, symbolID, symbolFilename))
- req = AddBasicAuthHeader(req, user.Name)
- MakeRequest(t, req, http.StatusOK)
-
- checkDownloadCount(1)
- })
- })
-
- t.Run("SearchService", func(t *testing.T) {
- defer PrintCurrentTest(t)()
-
- cases := []struct {
- Query string
- Skip int
- Take int
- ExpectedTotal int64
- ExpectedResults int
- }{
- {"", 0, 0, 1, 1},
- {"", 0, 10, 1, 1},
- {"gitea", 0, 10, 0, 0},
- {"test", 0, 10, 1, 1},
- {"test", 1, 10, 1, 0},
- }
-
- for i, c := range cases {
- req := NewRequest(t, "GET", fmt.Sprintf("%s/query?q=%s&skip=%d&take=%d", url, c.Query, c.Skip, c.Take))
- req = AddBasicAuthHeader(req, user.Name)
- resp := MakeRequest(t, req, http.StatusOK)
-
- var result nuget.SearchResultResponse
- DecodeJSON(t, resp, &result)
-
- assert.Equal(t, c.ExpectedTotal, result.TotalHits, "case %d: unexpected total hits", i)
- assert.Len(t, result.Data, c.ExpectedResults, "case %d: unexpected result count", i)
- }
- })
-
- t.Run("RegistrationService", func(t *testing.T) {
- indexURL := fmt.Sprintf("%s%s/registration/%s/index.json", setting.AppURL, url[1:], packageName)
- leafURL := fmt.Sprintf("%s%s/registration/%s/%s.json", setting.AppURL, url[1:], packageName, packageVersion)
- contentURL := fmt.Sprintf("%s%s/package/%s/%s/%s.%s.nupkg", setting.AppURL, url[1:], packageName, packageVersion, packageName, packageVersion)
-
- t.Run("RegistrationIndex", func(t *testing.T) {
- defer PrintCurrentTest(t)()
-
- req := NewRequest(t, "GET", fmt.Sprintf("%s/registration/%s/index.json", url, packageName))
- req = AddBasicAuthHeader(req, user.Name)
- resp := MakeRequest(t, req, http.StatusOK)
-
- var result nuget.RegistrationIndexResponse
- DecodeJSON(t, resp, &result)
-
- assert.Equal(t, indexURL, result.RegistrationIndexURL)
- assert.Equal(t, 1, result.Count)
- assert.Len(t, result.Pages, 1)
- assert.Equal(t, indexURL, result.Pages[0].RegistrationPageURL)
- assert.Equal(t, packageVersion, result.Pages[0].Lower)
- assert.Equal(t, packageVersion, result.Pages[0].Upper)
- assert.Equal(t, 1, result.Pages[0].Count)
- assert.Len(t, result.Pages[0].Items, 1)
- assert.Equal(t, packageName, result.Pages[0].Items[0].CatalogEntry.ID)
- assert.Equal(t, packageVersion, result.Pages[0].Items[0].CatalogEntry.Version)
- assert.Equal(t, packageAuthors, result.Pages[0].Items[0].CatalogEntry.Authors)
- assert.Equal(t, packageDescription, result.Pages[0].Items[0].CatalogEntry.Description)
- assert.Equal(t, leafURL, result.Pages[0].Items[0].CatalogEntry.CatalogLeafURL)
- assert.Equal(t, contentURL, result.Pages[0].Items[0].CatalogEntry.PackageContentURL)
- })
-
- t.Run("RegistrationLeaf", func(t *testing.T) {
- defer PrintCurrentTest(t)()
-
- req := NewRequest(t, "GET", fmt.Sprintf("%s/registration/%s/%s.json", url, packageName, packageVersion))
- req = AddBasicAuthHeader(req, user.Name)
- resp := MakeRequest(t, req, http.StatusOK)
-
- var result nuget.RegistrationLeafResponse
- DecodeJSON(t, resp, &result)
-
- assert.Equal(t, leafURL, result.RegistrationLeafURL)
- assert.Equal(t, contentURL, result.PackageContentURL)
- assert.Equal(t, indexURL, result.RegistrationIndexURL)
- })
- })
-
- t.Run("PackageService", func(t *testing.T) {
- defer PrintCurrentTest(t)()
-
- req := NewRequest(t, "GET", fmt.Sprintf("%s/package/%s/index.json", url, packageName))
- req = AddBasicAuthHeader(req, user.Name)
- resp := MakeRequest(t, req, http.StatusOK)
-
- var result nuget.PackageVersionsResponse
- DecodeJSON(t, resp, &result)
-
- assert.Len(t, result.Versions, 1)
- assert.Equal(t, packageVersion, result.Versions[0])
- })
-
- t.Run("Delete", func(t *testing.T) {
- defer PrintCurrentTest(t)()
-
- req := NewRequest(t, "DELETE", fmt.Sprintf("%s/%s/%s", url, packageName, packageVersion))
- req = AddBasicAuthHeader(req, user.Name)
- MakeRequest(t, req, http.StatusOK)
-
- pvs, err := packages.GetVersionsByPackageType(db.DefaultContext, user.ID, packages.TypeNuGet)
- assert.NoError(t, err)
- assert.Empty(t, pvs)
- })
-
- t.Run("DownloadNotExists", func(t *testing.T) {
- defer PrintCurrentTest(t)()
-
- req := NewRequest(t, "GET", fmt.Sprintf("%s/package/%s/%s/%s.%s.nupkg", url, packageName, packageVersion, packageName, packageVersion))
- req = AddBasicAuthHeader(req, user.Name)
- MakeRequest(t, req, http.StatusNotFound)
-
- req = NewRequest(t, "GET", fmt.Sprintf("%s/package/%s/%s/%s.%s.snupkg", url, packageName, packageVersion, packageName, packageVersion))
- req = AddBasicAuthHeader(req, user.Name)
- MakeRequest(t, req, http.StatusNotFound)
- })
-
- t.Run("DeleteNotExists", func(t *testing.T) {
- defer PrintCurrentTest(t)()
-
- req := NewRequest(t, "DELETE", fmt.Sprintf("%s/package/%s/%s", url, packageName, packageVersion))
- req = AddBasicAuthHeader(req, user.Name)
- MakeRequest(t, req, http.StatusNotFound)
- })
-}
diff --git a/jest.config.js b/jest.config.js
deleted file mode 100644
index d24333aa35..0000000000
--- a/jest.config.js
+++ /dev/null
@@ -1,12 +0,0 @@
-export default {
- rootDir: 'web_src',
- setupFilesAfterEnv: ['jest-extended/all'],
- testEnvironment: '@happy-dom/jest-environment',
- testMatch: ['
/**/*.test.js'],
- testTimeout: 20000,
- transform: {
- '\\.svg$': '/js/testUtils/jestRawLoader.js',
- },
- verbose: false,
-};
-
diff --git a/main.go b/main.go
index ac60f85a66..0e550f05eb 100644
--- a/main.go
+++ b/main.go
@@ -171,9 +171,9 @@ func setAppHelpTemplates() {
}
func adjustHelpTemplate(originalTemplate string) string {
- overrided := ""
+ overridden := ""
if _, ok := os.LookupEnv("GITEA_CUSTOM"); ok {
- overrided = "(GITEA_CUSTOM)"
+ overridden = "(GITEA_CUSTOM)"
}
return fmt.Sprintf(`%s
@@ -183,7 +183,7 @@ DEFAULT CONFIGURATION:
AppPath: %s
AppWorkPath: %s
-`, originalTemplate, setting.CustomPath, overrided, setting.CustomConf, setting.AppPath, setting.AppWorkPath)
+`, originalTemplate, setting.CustomPath, overridden, setting.CustomConf, setting.AppPath, setting.AppWorkPath)
}
func formatBuiltWith() string {
diff --git a/models/action.go b/models/activities/action.go
similarity index 93%
rename from models/action.go
rename to models/activities/action.go
index 14e021389a..3c8acb5ba8 100644
--- a/models/action.go
+++ b/models/activities/action.go
@@ -3,7 +3,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
-package models
+package activities
import (
"context"
@@ -98,7 +98,14 @@ func (a *Action) TableIndices() []*schemas.Index {
actUserIndex := schemas.NewIndex("au_r_c_u_d", schemas.IndexType)
actUserIndex.AddColumn("act_user_id", "repo_id", "created_unix", "user_id", "is_deleted")
- return []*schemas.Index{actUserIndex, repoIndex}
+ indices := []*schemas.Index{actUserIndex, repoIndex}
+ if setting.Database.UsePostgreSQL {
+ cudIndex := schemas.NewIndex("c_u_d", schemas.IndexType)
+ cudIndex.AddColumn("created_unix", "user_id", "is_deleted")
+ indices = append(indices, cudIndex)
+ }
+
+ return indices
}
// GetOpType gets the ActionType of this action.
@@ -211,19 +218,9 @@ func (a *Action) GetRepoLink() string {
return path.Join(setting.AppSubURL, "/", url.PathEscape(a.GetRepoUserName()), url.PathEscape(a.GetRepoName()))
}
-// GetRepositoryFromMatch returns a *repo_model.Repository from a username and repo strings
-func GetRepositoryFromMatch(ownerName, repoName string) (*repo_model.Repository, error) {
- var err error
- refRepo, err := repo_model.GetRepositoryByOwnerAndName(ownerName, repoName)
- if err != nil {
- if repo_model.IsErrRepoNotExist(err) {
- log.Warn("Repository referenced in commit but does not exist: %v", err)
- return nil, err
- }
- log.Error("repo_model.GetRepositoryByOwnerAndName: %v", err)
- return nil, err
- }
- return refRepo, nil
+// GetRepoAbsoluteLink returns the absolute link to action repository.
+func (a *Action) GetRepoAbsoluteLink() string {
+ return setting.AppURL + url.PathEscape(a.GetRepoUserName()) + "/" + url.PathEscape(a.GetRepoName())
}
// GetCommentLink returns link to action comment.
@@ -275,7 +272,7 @@ func (a *Action) GetRefLink() string {
return a.GetRepoLink() + "/src/branch/" + util.PathEscapeSegments(strings.TrimPrefix(a.RefName, git.BranchPrefix))
case strings.HasPrefix(a.RefName, git.TagPrefix):
return a.GetRepoLink() + "/src/tag/" + util.PathEscapeSegments(strings.TrimPrefix(a.RefName, git.TagPrefix))
- case len(a.RefName) == 40 && git.SHAPattern.MatchString(a.RefName):
+ case len(a.RefName) == 40 && git.IsValidSHAPattern(a.RefName):
return a.GetRepoLink() + "/src/commit/" + a.RefName
default:
// FIXME: we will just assume it's a branch - this was the old way - at some point we may want to enforce that there is always a ref here.
@@ -372,7 +369,8 @@ func GetFeeds(ctx context.Context, opts GetFeedsOptions) (ActionList, error) {
return actions, nil
}
-func activityReadable(user, doer *user_model.User) bool {
+// ActivityReadable return whether doer can read activities of user
+func ActivityReadable(user, doer *user_model.User) bool {
return !user.KeepActivityPrivate ||
doer != nil && (doer.IsAdmin || user.ID == doer.ID)
}
@@ -602,3 +600,23 @@ func DeleteIssueActions(ctx context.Context, repoID, issueID int64) error {
Delete(&Action{})
return err
}
+
+// CountActionCreatedUnixString count actions where created_unix is an empty string
+func CountActionCreatedUnixString() (int64, error) {
+ if setting.Database.UseSQLite3 {
+ return db.GetEngine(db.DefaultContext).Where(`created_unix = ""`).Count(new(Action))
+ }
+ return 0, nil
+}
+
+// FixActionCreatedUnixString set created_unix to zero if it is an empty string
+func FixActionCreatedUnixString() (int64, error) {
+ if setting.Database.UseSQLite3 {
+ res, err := db.GetEngine(db.DefaultContext).Exec(`UPDATE action SET created_unix = 0 WHERE created_unix = ""`)
+ if err != nil {
+ return 0, err
+ }
+ return res.RowsAffected()
+ }
+ return 0, nil
+}
diff --git a/models/action_list.go b/models/activities/action_list.go
similarity index 86%
rename from models/action_list.go
rename to models/activities/action_list.go
index d585ef0fc2..f349b94ac8 100644
--- a/models/action_list.go
+++ b/models/activities/action_list.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
-package models
+package activities
import (
"context"
@@ -18,13 +18,11 @@ import (
type ActionList []*Action
func (actions ActionList) getUserIDs() []int64 {
- userIDs := make(map[int64]struct{}, len(actions))
+ userIDs := make(container.Set[int64], len(actions))
for _, action := range actions {
- if _, ok := userIDs[action.ActUserID]; !ok {
- userIDs[action.ActUserID] = struct{}{}
- }
+ userIDs.Add(action.ActUserID)
}
- return container.KeysInt64(userIDs)
+ return userIDs.Values()
}
func (actions ActionList) loadUsers(ctx context.Context) (map[int64]*user_model.User, error) {
@@ -48,13 +46,11 @@ func (actions ActionList) loadUsers(ctx context.Context) (map[int64]*user_model.
}
func (actions ActionList) getRepoIDs() []int64 {
- repoIDs := make(map[int64]struct{}, len(actions))
+ repoIDs := make(container.Set[int64], len(actions))
for _, action := range actions {
- if _, ok := repoIDs[action.RepoID]; !ok {
- repoIDs[action.RepoID] = struct{}{}
- }
+ repoIDs.Add(action.RepoID)
}
- return container.KeysInt64(repoIDs)
+ return repoIDs.Values()
}
func (actions ActionList) loadRepositories(ctx context.Context) error {
diff --git a/models/action_test.go b/models/activities/action_test.go
similarity index 72%
rename from models/action_test.go
rename to models/activities/action_test.go
index 2d46bd3e80..ac2a3043a6 100644
--- a/models/action_test.go
+++ b/models/activities/action_test.go
@@ -2,13 +2,15 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
-package models
+package activities_test
import (
"path"
"testing"
+ activities_model "code.gitea.io/gitea/models/activities"
"code.gitea.io/gitea/models/db"
+ issue_model "code.gitea.io/gitea/models/issues"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
@@ -19,28 +21,31 @@ import (
func TestAction_GetRepoPath(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{}).(*repo_model.Repository)
- owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User)
- action := &Action{RepoID: repo.ID}
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
+ owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
+ action := &activities_model.Action{RepoID: repo.ID}
assert.Equal(t, path.Join(owner.Name, repo.Name), action.GetRepoPath())
}
func TestAction_GetRepoLink(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{}).(*repo_model.Repository)
- owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User)
- action := &Action{RepoID: repo.ID}
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
+ owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
+ comment := unittest.AssertExistsAndLoadBean(t, &issue_model.Comment{ID: 2})
+ action := &activities_model.Action{RepoID: repo.ID, CommentID: comment.ID}
setting.AppSubURL = "/suburl"
expected := path.Join(setting.AppSubURL, owner.Name, repo.Name)
assert.Equal(t, expected, action.GetRepoLink())
+ assert.Equal(t, repo.HTMLURL(), action.GetRepoAbsoluteLink())
+ assert.Equal(t, comment.HTMLURL(), action.GetCommentLink())
}
func TestGetFeeds(t *testing.T) {
// test with an individual user
assert.NoError(t, unittest.PrepareTestDatabase())
- user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
+ user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
- actions, err := GetFeeds(db.DefaultContext, GetFeedsOptions{
+ actions, err := activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{
RequestedUser: user,
Actor: user,
IncludePrivate: true,
@@ -53,7 +58,7 @@ func TestGetFeeds(t *testing.T) {
assert.EqualValues(t, user.ID, actions[0].UserID)
}
- actions, err = GetFeeds(db.DefaultContext, GetFeedsOptions{
+ actions, err = activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{
RequestedUser: user,
Actor: user,
IncludePrivate: false,
@@ -65,12 +70,12 @@ func TestGetFeeds(t *testing.T) {
func TestGetFeedsForRepos(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
- privRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}).(*repo_model.Repository)
- pubRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 8}).(*repo_model.Repository)
+ user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
+ privRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2})
+ pubRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 8})
// private repo & no login
- actions, err := GetFeeds(db.DefaultContext, GetFeedsOptions{
+ actions, err := activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{
RequestedRepo: privRepo,
IncludePrivate: true,
})
@@ -78,7 +83,7 @@ func TestGetFeedsForRepos(t *testing.T) {
assert.Len(t, actions, 0)
// public repo & no login
- actions, err = GetFeeds(db.DefaultContext, GetFeedsOptions{
+ actions, err = activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{
RequestedRepo: pubRepo,
IncludePrivate: true,
})
@@ -86,7 +91,7 @@ func TestGetFeedsForRepos(t *testing.T) {
assert.Len(t, actions, 1)
// private repo and login
- actions, err = GetFeeds(db.DefaultContext, GetFeedsOptions{
+ actions, err = activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{
RequestedRepo: privRepo,
IncludePrivate: true,
Actor: user,
@@ -95,7 +100,7 @@ func TestGetFeedsForRepos(t *testing.T) {
assert.Len(t, actions, 1)
// public repo & login
- actions, err = GetFeeds(db.DefaultContext, GetFeedsOptions{
+ actions, err = activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{
RequestedRepo: pubRepo,
IncludePrivate: true,
Actor: user,
@@ -107,10 +112,10 @@ func TestGetFeedsForRepos(t *testing.T) {
func TestGetFeeds2(t *testing.T) {
// test with an organization user
assert.NoError(t, unittest.PrepareTestDatabase())
- org := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3}).(*user_model.User)
- user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
+ org := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3})
+ user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
- actions, err := GetFeeds(db.DefaultContext, GetFeedsOptions{
+ actions, err := activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{
RequestedUser: org,
Actor: user,
IncludePrivate: true,
@@ -124,7 +129,7 @@ func TestGetFeeds2(t *testing.T) {
assert.EqualValues(t, org.ID, actions[0].UserID)
}
- actions, err = GetFeeds(db.DefaultContext, GetFeedsOptions{
+ actions, err = activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{
RequestedUser: org,
Actor: user,
IncludePrivate: false,
@@ -171,40 +176,40 @@ func TestActivityReadable(t *testing.T) {
result: true,
}}
for _, test := range tt {
- assert.Equal(t, test.result, activityReadable(test.user, test.doer), test.desc)
+ assert.Equal(t, test.result, activities_model.ActivityReadable(test.user, test.doer), test.desc)
}
}
func TestNotifyWatchers(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- action := &Action{
+ action := &activities_model.Action{
ActUserID: 8,
RepoID: 1,
- OpType: ActionStarRepo,
+ OpType: activities_model.ActionStarRepo,
}
- assert.NoError(t, NotifyWatchers(action))
+ assert.NoError(t, activities_model.NotifyWatchers(action))
// One watchers are inactive, thus action is only created for user 8, 1, 4, 11
- unittest.AssertExistsAndLoadBean(t, &Action{
+ unittest.AssertExistsAndLoadBean(t, &activities_model.Action{
ActUserID: action.ActUserID,
UserID: 8,
RepoID: action.RepoID,
OpType: action.OpType,
})
- unittest.AssertExistsAndLoadBean(t, &Action{
+ unittest.AssertExistsAndLoadBean(t, &activities_model.Action{
ActUserID: action.ActUserID,
UserID: 1,
RepoID: action.RepoID,
OpType: action.OpType,
})
- unittest.AssertExistsAndLoadBean(t, &Action{
+ unittest.AssertExistsAndLoadBean(t, &activities_model.Action{
ActUserID: action.ActUserID,
UserID: 4,
RepoID: action.RepoID,
OpType: action.OpType,
})
- unittest.AssertExistsAndLoadBean(t, &Action{
+ unittest.AssertExistsAndLoadBean(t, &activities_model.Action{
ActUserID: action.ActUserID,
UserID: 11,
RepoID: action.RepoID,
@@ -214,13 +219,13 @@ func TestNotifyWatchers(t *testing.T) {
func TestGetFeedsCorrupted(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User)
- unittest.AssertExistsAndLoadBean(t, &Action{
+ user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
+ unittest.AssertExistsAndLoadBean(t, &activities_model.Action{
ID: 8,
RepoID: 1700,
})
- actions, err := GetFeeds(db.DefaultContext, GetFeedsOptions{
+ actions, err := activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{
RequestedUser: user,
Actor: user,
IncludePrivate: true,
@@ -235,12 +240,12 @@ func TestConsistencyUpdateAction(t *testing.T) {
}
assert.NoError(t, unittest.PrepareTestDatabase())
id := 8
- unittest.AssertExistsAndLoadBean(t, &Action{
+ unittest.AssertExistsAndLoadBean(t, &activities_model.Action{
ID: int64(id),
})
_, err := db.GetEngine(db.DefaultContext).Exec(`UPDATE action SET created_unix = "" WHERE id = ?`, id)
assert.NoError(t, err)
- actions := make([]*Action, 0, 1)
+ actions := make([]*activities_model.Action, 0, 1)
//
// XORM returns an error when created_unix is a string
//
@@ -251,17 +256,17 @@ func TestConsistencyUpdateAction(t *testing.T) {
//
// Get rid of incorrectly set created_unix
//
- count, err := CountActionCreatedUnixString()
+ count, err := activities_model.CountActionCreatedUnixString()
assert.NoError(t, err)
assert.EqualValues(t, 1, count)
- count, err = FixActionCreatedUnixString()
+ count, err = activities_model.FixActionCreatedUnixString()
assert.NoError(t, err)
assert.EqualValues(t, 1, count)
- count, err = CountActionCreatedUnixString()
+ count, err = activities_model.CountActionCreatedUnixString()
assert.NoError(t, err)
assert.EqualValues(t, 0, count)
- count, err = FixActionCreatedUnixString()
+ count, err = activities_model.FixActionCreatedUnixString()
assert.NoError(t, err)
assert.EqualValues(t, 0, count)
@@ -269,5 +274,5 @@ func TestConsistencyUpdateAction(t *testing.T) {
// XORM must be happy now
//
assert.NoError(t, db.GetEngine(db.DefaultContext).Where("id = ?", id).Find(&actions))
- unittest.CheckConsistencyFor(t, &Action{})
+ unittest.CheckConsistencyFor(t, &activities_model.Action{})
}
diff --git a/models/admin/main_test.go b/models/activities/main_test.go
similarity index 86%
rename from models/admin/main_test.go
rename to models/activities/main_test.go
index 693b70fbf7..0a87f47600 100644
--- a/models/admin/main_test.go
+++ b/models/activities/main_test.go
@@ -2,18 +2,19 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
-package admin
+package activities_test
import (
"path/filepath"
"testing"
"code.gitea.io/gitea/models/unittest"
+
+ _ "code.gitea.io/gitea/models"
)
func TestMain(m *testing.M) {
unittest.MainTest(m, &unittest.TestOptions{
GiteaRootPath: filepath.Join("..", ".."),
- FixtureFiles: []string{"notice.yml"},
})
}
diff --git a/models/notification.go b/models/activities/notification.go
similarity index 95%
rename from models/notification.go
rename to models/activities/notification.go
index fdc4ffad13..0a61088167 100644
--- a/models/notification.go
+++ b/models/activities/notification.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
-package models
+package activities
import (
"context"
@@ -13,6 +13,7 @@ import (
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/organization"
+ 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"
@@ -199,7 +200,7 @@ func CreateOrUpdateIssueNotifications(issueID, commentID, notificationAuthorID,
func createOrUpdateIssueNotifications(ctx context.Context, issueID, commentID, notificationAuthorID, receiverID int64) error {
// init
- var toNotify map[int64]struct{}
+ var toNotify container.Set[int64]
notifications, err := getNotificationsByIssueID(ctx, issueID)
if err != nil {
return err
@@ -211,33 +212,27 @@ func createOrUpdateIssueNotifications(ctx context.Context, issueID, commentID, n
}
if receiverID > 0 {
- toNotify = make(map[int64]struct{}, 1)
- toNotify[receiverID] = struct{}{}
+ toNotify = make(container.Set[int64], 1)
+ toNotify.Add(receiverID)
} else {
- toNotify = make(map[int64]struct{}, 32)
+ toNotify = make(container.Set[int64], 32)
issueWatches, err := issues_model.GetIssueWatchersIDs(ctx, issueID, true)
if err != nil {
return err
}
- for _, id := range issueWatches {
- toNotify[id] = struct{}{}
- }
+ toNotify.AddMultiple(issueWatches...)
if !(issue.IsPull && issues_model.HasWorkInProgressPrefix(issue.Title)) {
repoWatches, err := repo_model.GetRepoWatchersIDs(ctx, issue.RepoID)
if err != nil {
return err
}
- for _, id := range repoWatches {
- toNotify[id] = struct{}{}
- }
+ toNotify.AddMultiple(repoWatches...)
}
issueParticipants, err := issue.GetParticipantIDsByIssue(ctx)
if err != nil {
return err
}
- for _, id := range issueParticipants {
- toNotify[id] = struct{}{}
- }
+ toNotify.AddMultiple(issueParticipants...)
// dont notify user who cause notification
delete(toNotify, notificationAuthorID)
@@ -247,7 +242,7 @@ func createOrUpdateIssueNotifications(ctx context.Context, issueID, commentID, n
return err
}
for _, id := range issueUnWatches {
- delete(toNotify, id)
+ toNotify.Remove(id)
}
}
@@ -267,10 +262,10 @@ func createOrUpdateIssueNotifications(ctx context.Context, issueID, commentID, n
return err
}
- if issue.IsPull && !CheckRepoUnitUser(ctx, issue.Repo, user, unit.TypePullRequests) {
+ if issue.IsPull && !access_model.CheckRepoUnitUser(ctx, issue.Repo, user, unit.TypePullRequests) {
continue
}
- if !issue.IsPull && !CheckRepoUnitUser(ctx, issue.Repo, user, unit.TypeIssues) {
+ if !issue.IsPull && !access_model.CheckRepoUnitUser(ctx, issue.Repo, user, unit.TypeIssues) {
continue
}
@@ -498,16 +493,14 @@ func (nl NotificationList) LoadAttributes() error {
}
func (nl NotificationList) getPendingRepoIDs() []int64 {
- ids := make(map[int64]struct{}, len(nl))
+ ids := make(container.Set[int64], len(nl))
for _, notification := range nl {
if notification.Repository != nil {
continue
}
- if _, ok := ids[notification.RepoID]; !ok {
- ids[notification.RepoID] = struct{}{}
- }
+ ids.Add(notification.RepoID)
}
- return container.KeysInt64(ids)
+ return ids.Values()
}
// LoadRepos loads repositories from database
@@ -574,16 +567,14 @@ func (nl NotificationList) LoadRepos() (repo_model.RepositoryList, []int, error)
}
func (nl NotificationList) getPendingIssueIDs() []int64 {
- ids := make(map[int64]struct{}, len(nl))
+ ids := make(container.Set[int64], len(nl))
for _, notification := range nl {
if notification.Issue != nil {
continue
}
- if _, ok := ids[notification.IssueID]; !ok {
- ids[notification.IssueID] = struct{}{}
- }
+ ids.Add(notification.IssueID)
}
- return container.KeysInt64(ids)
+ return ids.Values()
}
// LoadIssues loads issues from database
@@ -660,16 +651,14 @@ func (nl NotificationList) Without(failures []int) NotificationList {
}
func (nl NotificationList) getPendingCommentIDs() []int64 {
- ids := make(map[int64]struct{}, len(nl))
+ ids := make(container.Set[int64], len(nl))
for _, notification := range nl {
if notification.CommentID == 0 || notification.Comment != nil {
continue
}
- if _, ok := ids[notification.CommentID]; !ok {
- ids[notification.CommentID] = struct{}{}
- }
+ ids.Add(notification.CommentID)
}
- return container.KeysInt64(ids)
+ return ids.Values()
}
// LoadComments loads comments from database
@@ -817,7 +806,7 @@ func getNotificationByID(ctx context.Context, notificationID int64) (*Notificati
}
if !ok {
- return nil, db.ErrNotExist{ID: notificationID}
+ return nil, db.ErrNotExist{Resource: "notification", ID: notificationID}
}
return notification, nil
diff --git a/models/notification_test.go b/models/activities/notification_test.go
similarity index 52%
rename from models/notification_test.go
rename to models/activities/notification_test.go
index 16ff02d6c0..4ee16af076 100644
--- a/models/notification_test.go
+++ b/models/activities/notification_test.go
@@ -2,11 +2,12 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
-package models
+package activities_test
import (
"testing"
+ activities_model "code.gitea.io/gitea/models/activities"
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/unittest"
@@ -17,24 +18,24 @@ import (
func TestCreateOrUpdateIssueNotifications(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1}).(*issues_model.Issue)
+ issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1})
- assert.NoError(t, CreateOrUpdateIssueNotifications(issue.ID, 0, 2, 0))
+ assert.NoError(t, activities_model.CreateOrUpdateIssueNotifications(issue.ID, 0, 2, 0))
// User 9 is inactive, thus notifications for user 1 and 4 are created
- notf := unittest.AssertExistsAndLoadBean(t, &Notification{UserID: 1, IssueID: issue.ID}).(*Notification)
- assert.Equal(t, NotificationStatusUnread, notf.Status)
+ notf := unittest.AssertExistsAndLoadBean(t, &activities_model.Notification{UserID: 1, IssueID: issue.ID})
+ assert.Equal(t, activities_model.NotificationStatusUnread, notf.Status)
unittest.CheckConsistencyFor(t, &issues_model.Issue{ID: issue.ID})
- notf = unittest.AssertExistsAndLoadBean(t, &Notification{UserID: 4, IssueID: issue.ID}).(*Notification)
- assert.Equal(t, NotificationStatusUnread, notf.Status)
+ notf = unittest.AssertExistsAndLoadBean(t, &activities_model.Notification{UserID: 4, IssueID: issue.ID})
+ assert.Equal(t, activities_model.NotificationStatusUnread, notf.Status)
}
func TestNotificationsForUser(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
- statuses := []NotificationStatus{NotificationStatusRead, NotificationStatusUnread}
- notfs, err := NotificationsForUser(db.DefaultContext, user, statuses, 1, 10)
+ user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
+ statuses := []activities_model.NotificationStatus{activities_model.NotificationStatusRead, activities_model.NotificationStatusUnread}
+ notfs, err := activities_model.NotificationsForUser(db.DefaultContext, user, statuses, 1, 10)
assert.NoError(t, err)
if assert.Len(t, notfs, 3) {
assert.EqualValues(t, 5, notfs[0].ID)
@@ -48,7 +49,7 @@ func TestNotificationsForUser(t *testing.T) {
func TestNotification_GetRepo(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- notf := unittest.AssertExistsAndLoadBean(t, &Notification{RepoID: 1}).(*Notification)
+ notf := unittest.AssertExistsAndLoadBean(t, &activities_model.Notification{RepoID: 1})
repo, err := notf.GetRepo()
assert.NoError(t, err)
assert.Equal(t, repo, notf.Repository)
@@ -57,7 +58,7 @@ func TestNotification_GetRepo(t *testing.T) {
func TestNotification_GetIssue(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- notf := unittest.AssertExistsAndLoadBean(t, &Notification{RepoID: 1}).(*Notification)
+ notf := unittest.AssertExistsAndLoadBean(t, &activities_model.Notification{RepoID: 1})
issue, err := notf.GetIssue()
assert.NoError(t, err)
assert.Equal(t, issue, notf.Issue)
@@ -66,46 +67,46 @@ func TestNotification_GetIssue(t *testing.T) {
func TestGetNotificationCount(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User)
- cnt, err := GetNotificationCount(db.DefaultContext, user, NotificationStatusRead)
+ user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
+ cnt, err := activities_model.GetNotificationCount(db.DefaultContext, user, activities_model.NotificationStatusRead)
assert.NoError(t, err)
assert.EqualValues(t, 0, cnt)
- cnt, err = GetNotificationCount(db.DefaultContext, user, NotificationStatusUnread)
+ cnt, err = activities_model.GetNotificationCount(db.DefaultContext, user, activities_model.NotificationStatusUnread)
assert.NoError(t, err)
assert.EqualValues(t, 1, cnt)
}
func TestSetNotificationStatus(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
+ user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
notf := unittest.AssertExistsAndLoadBean(t,
- &Notification{UserID: user.ID, Status: NotificationStatusRead}).(*Notification)
- _, err := SetNotificationStatus(notf.ID, user, NotificationStatusPinned)
+ &activities_model.Notification{UserID: user.ID, Status: activities_model.NotificationStatusRead})
+ _, err := activities_model.SetNotificationStatus(notf.ID, user, activities_model.NotificationStatusPinned)
assert.NoError(t, err)
unittest.AssertExistsAndLoadBean(t,
- &Notification{ID: notf.ID, Status: NotificationStatusPinned})
+ &activities_model.Notification{ID: notf.ID, Status: activities_model.NotificationStatusPinned})
- _, err = SetNotificationStatus(1, user, NotificationStatusRead)
+ _, err = activities_model.SetNotificationStatus(1, user, activities_model.NotificationStatusRead)
assert.Error(t, err)
- _, err = SetNotificationStatus(unittest.NonexistentID, user, NotificationStatusRead)
+ _, err = activities_model.SetNotificationStatus(unittest.NonexistentID, user, activities_model.NotificationStatusRead)
assert.Error(t, err)
}
func TestUpdateNotificationStatuses(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
+ user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
notfUnread := unittest.AssertExistsAndLoadBean(t,
- &Notification{UserID: user.ID, Status: NotificationStatusUnread}).(*Notification)
+ &activities_model.Notification{UserID: user.ID, Status: activities_model.NotificationStatusUnread})
notfRead := unittest.AssertExistsAndLoadBean(t,
- &Notification{UserID: user.ID, Status: NotificationStatusRead}).(*Notification)
+ &activities_model.Notification{UserID: user.ID, Status: activities_model.NotificationStatusRead})
notfPinned := unittest.AssertExistsAndLoadBean(t,
- &Notification{UserID: user.ID, Status: NotificationStatusPinned}).(*Notification)
- assert.NoError(t, UpdateNotificationStatuses(user, NotificationStatusUnread, NotificationStatusRead))
+ &activities_model.Notification{UserID: user.ID, Status: activities_model.NotificationStatusPinned})
+ assert.NoError(t, activities_model.UpdateNotificationStatuses(user, activities_model.NotificationStatusUnread, activities_model.NotificationStatusRead))
unittest.AssertExistsAndLoadBean(t,
- &Notification{ID: notfUnread.ID, Status: NotificationStatusRead})
+ &activities_model.Notification{ID: notfUnread.ID, Status: activities_model.NotificationStatusRead})
unittest.AssertExistsAndLoadBean(t,
- &Notification{ID: notfRead.ID, Status: NotificationStatusRead})
+ &activities_model.Notification{ID: notfRead.ID, Status: activities_model.NotificationStatusRead})
unittest.AssertExistsAndLoadBean(t,
- &Notification{ID: notfPinned.ID, Status: NotificationStatusPinned})
+ &activities_model.Notification{ID: notfPinned.ID, Status: activities_model.NotificationStatusPinned})
}
diff --git a/models/repo_activity.go b/models/activities/repo_activity.go
similarity index 98%
rename from models/repo_activity.go
rename to models/activities/repo_activity.go
index 6a3636ab07..3052d65e83 100644
--- a/models/repo_activity.go
+++ b/models/activities/repo_activity.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
-package models
+package activities
import (
"context"
@@ -28,7 +28,7 @@ type ActivityAuthorData struct {
Commits int64 `json:"commits"`
}
-// ActivityStats represets issue and pull request information.
+// ActivityStats represents issue and pull request information.
type ActivityStats struct {
OpenedPRs issues_model.PullRequestList
OpenedPRAuthorCount int64
@@ -39,7 +39,7 @@ type ActivityStats struct {
ClosedIssues issues_model.IssueList
ClosedIssueAuthorCount int64
UnresolvedIssues issues_model.IssueList
- PublishedReleases []*Release
+ PublishedReleases []*repo_model.Release
PublishedReleaseAuthorCount int64
Code *git.CodeActivityStats
}
@@ -344,7 +344,7 @@ func (stats *ActivityStats) FillReleases(repoID int64, fromTime time.Time) error
// Published releases list
sess := releasesForActivityStatement(repoID, fromTime)
sess.OrderBy("release.created_unix DESC")
- stats.PublishedReleases = make([]*Release, 0)
+ stats.PublishedReleases = make([]*repo_model.Release, 0)
if err = sess.Find(&stats.PublishedReleases); err != nil {
return err
}
diff --git a/models/statistic.go b/models/activities/statistic.go
similarity index 97%
rename from models/statistic.go
rename to models/activities/statistic.go
index ec094b5f5b..ea785a3ee2 100644
--- a/models/statistic.go
+++ b/models/activities/statistic.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
-package models
+package activities
import (
asymkey_model "code.gitea.io/gitea/models/asymkey"
@@ -101,7 +101,7 @@ func GetStatistic() (stats Statistic) {
stats.Counter.Oauth = 0
stats.Counter.Follow, _ = e.Count(new(user_model.Follow))
stats.Counter.Mirror, _ = e.Count(new(repo_model.Mirror))
- stats.Counter.Release, _ = e.Count(new(Release))
+ stats.Counter.Release, _ = e.Count(new(repo_model.Release))
stats.Counter.AuthSource = auth.CountSources()
stats.Counter.Webhook, _ = e.Count(new(webhook.Webhook))
stats.Counter.Milestone, _ = e.Count(new(issues_model.Milestone))
diff --git a/models/user_heatmap.go b/models/activities/user_heatmap.go
similarity index 97%
rename from models/user_heatmap.go
rename to models/activities/user_heatmap.go
index e908837ae8..6e76be6c6b 100644
--- a/models/user_heatmap.go
+++ b/models/activities/user_heatmap.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.package models
-package models
+package activities
import (
"code.gitea.io/gitea/models/db"
@@ -31,7 +31,7 @@ func GetUserHeatmapDataByUserTeam(user *user_model.User, team *organization.Team
func getUserHeatmapData(user *user_model.User, team *organization.Team, doer *user_model.User) ([]*UserHeatmapData, error) {
hdata := make([]*UserHeatmapData, 0)
- if !activityReadable(user, doer) {
+ if !ActivityReadable(user, doer) {
return hdata, nil
}
diff --git a/models/user_heatmap_test.go b/models/activities/user_heatmap_test.go
similarity index 90%
rename from models/user_heatmap_test.go
rename to models/activities/user_heatmap_test.go
index 9361cb3452..a8a240f790 100644
--- a/models/user_heatmap_test.go
+++ b/models/activities/user_heatmap_test.go
@@ -2,13 +2,14 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.package models
-package models
+package activities_test
import (
"fmt"
"testing"
"time"
+ activities_model "code.gitea.io/gitea/models/activities"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
@@ -63,7 +64,7 @@ func TestGetUserHeatmapDataByUser(t *testing.T) {
defer timeutil.Unset()
for _, tc := range testCases {
- user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: tc.userID}).(*user_model.User)
+ user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: tc.userID})
doer := &user_model.User{ID: tc.doerID}
_, err := unittest.LoadBeanIfExists(doer)
@@ -73,7 +74,7 @@ func TestGetUserHeatmapDataByUser(t *testing.T) {
}
// get the action for comparison
- actions, err := GetFeeds(db.DefaultContext, GetFeedsOptions{
+ actions, err := activities_model.GetFeeds(db.DefaultContext, activities_model.GetFeedsOptions{
RequestedUser: user,
Actor: doer,
IncludePrivate: true,
@@ -83,7 +84,7 @@ func TestGetUserHeatmapDataByUser(t *testing.T) {
assert.NoError(t, err)
// Get the heatmap and compare
- heatmap, err := GetUserHeatmapDataByUser(user, doer)
+ heatmap, err := activities_model.GetUserHeatmapDataByUser(user, doer)
var contributions int
for _, hm := range heatmap {
contributions += int(hm.Contributions)
diff --git a/models/admin/notice_test.go b/models/admin/notice_test.go
deleted file mode 100644
index b4613db8e7..0000000000
--- a/models/admin/notice_test.go
+++ /dev/null
@@ -1,116 +0,0 @@
-// Copyright 2017 The Gitea Authors. All rights reserved.
-// Use of this source code is governed by a MIT-style
-// license that can be found in the LICENSE file.
-
-package admin
-
-import (
- "testing"
-
- "code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/models/unittest"
-
- "github.com/stretchr/testify/assert"
-)
-
-func TestNotice_TrStr(t *testing.T) {
- notice := &Notice{
- Type: NoticeRepository,
- Description: "test description",
- }
- assert.Equal(t, "admin.notices.type_1", notice.TrStr())
-}
-
-func TestCreateNotice(t *testing.T) {
- assert.NoError(t, unittest.PrepareTestDatabase())
-
- noticeBean := &Notice{
- Type: NoticeRepository,
- Description: "test description",
- }
- unittest.AssertNotExistsBean(t, noticeBean)
- assert.NoError(t, CreateNotice(db.DefaultContext, noticeBean.Type, noticeBean.Description))
- unittest.AssertExistsAndLoadBean(t, noticeBean)
-}
-
-func TestCreateRepositoryNotice(t *testing.T) {
- assert.NoError(t, unittest.PrepareTestDatabase())
-
- noticeBean := &Notice{
- Type: NoticeRepository,
- Description: "test description",
- }
- unittest.AssertNotExistsBean(t, noticeBean)
- assert.NoError(t, CreateRepositoryNotice(noticeBean.Description))
- unittest.AssertExistsAndLoadBean(t, noticeBean)
-}
-
-// TODO TestRemoveAllWithNotice
-
-func TestCountNotices(t *testing.T) {
- assert.NoError(t, unittest.PrepareTestDatabase())
- assert.Equal(t, int64(3), CountNotices())
-}
-
-func TestNotices(t *testing.T) {
- assert.NoError(t, unittest.PrepareTestDatabase())
-
- notices, err := Notices(1, 2)
- assert.NoError(t, err)
- if assert.Len(t, notices, 2) {
- assert.Equal(t, int64(3), notices[0].ID)
- assert.Equal(t, int64(2), notices[1].ID)
- }
-
- notices, err = Notices(2, 2)
- assert.NoError(t, err)
- if assert.Len(t, notices, 1) {
- assert.Equal(t, int64(1), notices[0].ID)
- }
-}
-
-func TestDeleteNotice(t *testing.T) {
- assert.NoError(t, unittest.PrepareTestDatabase())
-
- unittest.AssertExistsAndLoadBean(t, &Notice{ID: 3})
- assert.NoError(t, DeleteNotice(3))
- unittest.AssertNotExistsBean(t, &Notice{ID: 3})
-}
-
-func TestDeleteNotices(t *testing.T) {
- // delete a non-empty range
- assert.NoError(t, unittest.PrepareTestDatabase())
-
- unittest.AssertExistsAndLoadBean(t, &Notice{ID: 1})
- unittest.AssertExistsAndLoadBean(t, &Notice{ID: 2})
- unittest.AssertExistsAndLoadBean(t, &Notice{ID: 3})
- assert.NoError(t, DeleteNotices(1, 2))
- unittest.AssertNotExistsBean(t, &Notice{ID: 1})
- unittest.AssertNotExistsBean(t, &Notice{ID: 2})
- unittest.AssertExistsAndLoadBean(t, &Notice{ID: 3})
-}
-
-func TestDeleteNotices2(t *testing.T) {
- // delete an empty range
- assert.NoError(t, unittest.PrepareTestDatabase())
-
- unittest.AssertExistsAndLoadBean(t, &Notice{ID: 1})
- unittest.AssertExistsAndLoadBean(t, &Notice{ID: 2})
- unittest.AssertExistsAndLoadBean(t, &Notice{ID: 3})
- assert.NoError(t, DeleteNotices(3, 2))
- unittest.AssertExistsAndLoadBean(t, &Notice{ID: 1})
- unittest.AssertExistsAndLoadBean(t, &Notice{ID: 2})
- unittest.AssertExistsAndLoadBean(t, &Notice{ID: 3})
-}
-
-func TestDeleteNoticesByIDs(t *testing.T) {
- assert.NoError(t, unittest.PrepareTestDatabase())
-
- unittest.AssertExistsAndLoadBean(t, &Notice{ID: 1})
- unittest.AssertExistsAndLoadBean(t, &Notice{ID: 2})
- unittest.AssertExistsAndLoadBean(t, &Notice{ID: 3})
- assert.NoError(t, DeleteNoticesByIDs([]int64{1, 3}))
- unittest.AssertNotExistsBean(t, &Notice{ID: 1})
- unittest.AssertExistsAndLoadBean(t, &Notice{ID: 2})
- unittest.AssertNotExistsBean(t, &Notice{ID: 3})
-}
diff --git a/models/task.go b/models/admin/task.go
similarity index 98%
rename from models/task.go
rename to models/admin/task.go
index 67f04d9562..4fa0f10394 100644
--- a/models/task.go
+++ b/models/admin/task.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
-package models
+package admin
import (
"context"
@@ -167,6 +167,10 @@ func (err ErrTaskDoesNotExist) Error() string {
err.ID, err.RepoID, err.Type)
}
+func (err ErrTaskDoesNotExist) Unwrap() error {
+ return util.ErrNotExist
+}
+
// GetMigratingTask returns the migrating task by repo's id
func GetMigratingTask(repoID int64) (*Task, error) {
task := Task{
diff --git a/models/asymkey/error.go b/models/asymkey/error.go
index 5d2be1f289..3ddeb0498a 100644
--- a/models/asymkey/error.go
+++ b/models/asymkey/error.go
@@ -4,7 +4,11 @@
package asymkey
-import "fmt"
+import (
+ "fmt"
+
+ "code.gitea.io/gitea/modules/util"
+)
// ErrKeyUnableVerify represents a "KeyUnableVerify" kind of error.
type ErrKeyUnableVerify struct {
@@ -36,6 +40,10 @@ func (err ErrKeyNotExist) Error() string {
return fmt.Sprintf("public key does not exist [id: %d]", err.ID)
}
+func (err ErrKeyNotExist) Unwrap() error {
+ return util.ErrNotExist
+}
+
// ErrKeyAlreadyExist represents a "KeyAlreadyExist" kind of error.
type ErrKeyAlreadyExist struct {
OwnerID int64
@@ -54,6 +62,10 @@ func (err ErrKeyAlreadyExist) Error() string {
err.OwnerID, err.Fingerprint, err.Content)
}
+func (err ErrKeyAlreadyExist) Unwrap() error {
+ return util.ErrAlreadyExist
+}
+
// ErrKeyNameAlreadyUsed represents a "KeyNameAlreadyUsed" kind of error.
type ErrKeyNameAlreadyUsed struct {
OwnerID int64
@@ -70,6 +82,10 @@ func (err ErrKeyNameAlreadyUsed) Error() string {
return fmt.Sprintf("public key already exists [owner_id: %d, name: %s]", err.OwnerID, err.Name)
}
+func (err ErrKeyNameAlreadyUsed) Unwrap() error {
+ return util.ErrAlreadyExist
+}
+
// ErrGPGNoEmailFound represents a "ErrGPGNoEmailFound" kind of error.
type ErrGPGNoEmailFound struct {
FailedEmails []string
@@ -132,6 +148,10 @@ func (err ErrGPGKeyNotExist) Error() string {
return fmt.Sprintf("public gpg key does not exist [id: %d]", err.ID)
}
+func (err ErrGPGKeyNotExist) Unwrap() error {
+ return util.ErrNotExist
+}
+
// ErrGPGKeyImportNotExist represents a "GPGKeyImportNotExist" kind of error.
type ErrGPGKeyImportNotExist struct {
ID string
@@ -147,6 +167,10 @@ func (err ErrGPGKeyImportNotExist) Error() string {
return fmt.Sprintf("public gpg key import does not exist [id: %s]", err.ID)
}
+func (err ErrGPGKeyImportNotExist) Unwrap() error {
+ return util.ErrNotExist
+}
+
// ErrGPGKeyIDAlreadyUsed represents a "GPGKeyIDAlreadyUsed" kind of error.
type ErrGPGKeyIDAlreadyUsed struct {
KeyID string
@@ -162,6 +186,10 @@ func (err ErrGPGKeyIDAlreadyUsed) Error() string {
return fmt.Sprintf("public key already exists [key_id: %s]", err.KeyID)
}
+func (err ErrGPGKeyIDAlreadyUsed) Unwrap() error {
+ return util.ErrAlreadyExist
+}
+
// ErrGPGKeyAccessDenied represents a "GPGKeyAccessDenied" kind of Error.
type ErrGPGKeyAccessDenied struct {
UserID int64
@@ -180,6 +208,10 @@ func (err ErrGPGKeyAccessDenied) Error() string {
err.UserID, err.KeyID)
}
+func (err ErrGPGKeyAccessDenied) Unwrap() error {
+ return util.ErrPermissionDenied
+}
+
// ErrKeyAccessDenied represents a "KeyAccessDenied" kind of error.
type ErrKeyAccessDenied struct {
UserID int64
@@ -198,6 +230,10 @@ func (err ErrKeyAccessDenied) Error() string {
err.UserID, err.KeyID, err.Note)
}
+func (err ErrKeyAccessDenied) Unwrap() error {
+ return util.ErrPermissionDenied
+}
+
// ErrDeployKeyNotExist represents a "DeployKeyNotExist" kind of error.
type ErrDeployKeyNotExist struct {
ID int64
@@ -215,6 +251,10 @@ func (err ErrDeployKeyNotExist) Error() string {
return fmt.Sprintf("Deploy key does not exist [id: %d, key_id: %d, repo_id: %d]", err.ID, err.KeyID, err.RepoID)
}
+func (err ErrDeployKeyNotExist) Unwrap() error {
+ return util.ErrNotExist
+}
+
// ErrDeployKeyAlreadyExist represents a "DeployKeyAlreadyExist" kind of error.
type ErrDeployKeyAlreadyExist struct {
KeyID int64
@@ -231,6 +271,10 @@ func (err ErrDeployKeyAlreadyExist) Error() string {
return fmt.Sprintf("public key already exists [key_id: %d, repo_id: %d]", err.KeyID, err.RepoID)
}
+func (err ErrDeployKeyAlreadyExist) Unwrap() error {
+ return util.ErrAlreadyExist
+}
+
// ErrDeployKeyNameAlreadyUsed represents a "DeployKeyNameAlreadyUsed" kind of error.
type ErrDeployKeyNameAlreadyUsed struct {
RepoID int64
@@ -247,6 +291,10 @@ func (err ErrDeployKeyNameAlreadyUsed) Error() string {
return fmt.Sprintf("public key with name already exists [repo_id: %d, name: %s]", err.RepoID, err.Name)
}
+func (err ErrDeployKeyNameAlreadyUsed) Unwrap() error {
+ return util.ErrNotExist
+}
+
// ErrSSHInvalidTokenSignature represents a "ErrSSHInvalidTokenSignature" kind of error.
type ErrSSHInvalidTokenSignature struct {
Wrapped error
@@ -262,3 +310,7 @@ func IsErrSSHInvalidTokenSignature(err error) bool {
func (err ErrSSHInvalidTokenSignature) Error() string {
return "the provided signature does not sign the token with the provided key"
}
+
+func (err ErrSSHInvalidTokenSignature) Unwrap() error {
+ return util.ErrInvalidArgument
+}
diff --git a/models/asymkey/gpg_key.go b/models/asymkey/gpg_key.go
index 2b99972379..78dc453e0d 100644
--- a/models/asymkey/gpg_key.go
+++ b/models/asymkey/gpg_key.go
@@ -33,7 +33,7 @@ type GPGKey struct {
OwnerID int64 `xorm:"INDEX NOT NULL"`
KeyID string `xorm:"INDEX CHAR(16) NOT NULL"`
PrimaryKeyID string `xorm:"CHAR(16)"`
- Content string `xorm:"TEXT NOT NULL"`
+ Content string `xorm:"MEDIUMTEXT NOT NULL"`
CreatedUnix timeutil.TimeStamp `xorm:"created"`
ExpiredUnix timeutil.TimeStamp
AddedUnix timeutil.TimeStamp
@@ -63,6 +63,15 @@ func (key *GPGKey) AfterLoad(session *xorm.Session) {
}
}
+// PaddedKeyID show KeyID padded to 16 characters
+func (key *GPGKey) PaddedKeyID() string {
+ if len(key.KeyID) > 15 {
+ return key.KeyID
+ }
+ zeros := "0000000000000000"
+ return zeros[0:16-len(key.KeyID)] + key.KeyID
+}
+
// ListGPGKeys returns a list of public keys belongs to given user.
func ListGPGKeys(ctx context.Context, uid int64, listOptions db.ListOptions) ([]*GPGKey, error) {
sess := db.GetEngine(ctx).Table(&GPGKey{}).Where("owner_id=? AND primary_key_id=''", uid)
diff --git a/models/asymkey/gpg_key_test.go b/models/asymkey/gpg_key_test.go
index 07bb77bdf4..2cee45d98f 100644
--- a/models/asymkey/gpg_key_test.go
+++ b/models/asymkey/gpg_key_test.go
@@ -196,7 +196,7 @@ Unknown GPG key with good email
func TestCheckGPGUserEmail(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- _ = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User)
+ _ = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
testEmailWithUpperCaseLetters := `-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v1
diff --git a/models/asymkey/ssh_key.go b/models/asymkey/ssh_key.go
index 107a29e985..9f95bb5baf 100644
--- a/models/asymkey/ssh_key.go
+++ b/models/asymkey/ssh_key.go
@@ -41,7 +41,7 @@ type PublicKey struct {
OwnerID int64 `xorm:"INDEX NOT NULL"`
Name string `xorm:"NOT NULL"`
Fingerprint string `xorm:"INDEX NOT NULL"`
- Content string `xorm:"TEXT NOT NULL"`
+ Content string `xorm:"MEDIUMTEXT NOT NULL"`
Mode perm.AccessMode `xorm:"NOT NULL DEFAULT 2"`
Type KeyType `xorm:"NOT NULL DEFAULT 1"`
LoginSourceID int64 `xorm:"NOT NULL DEFAULT 0"`
diff --git a/models/auth/main_test.go b/models/auth/main_test.go
index ccbdd4e81c..5d52e963b8 100644
--- a/models/auth/main_test.go
+++ b/models/auth/main_test.go
@@ -2,24 +2,22 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
-package auth
+package auth_test
import (
"path/filepath"
"testing"
"code.gitea.io/gitea/models/unittest"
+
+ _ "code.gitea.io/gitea/models"
+ _ "code.gitea.io/gitea/models/activities"
+ _ "code.gitea.io/gitea/models/auth"
+ _ "code.gitea.io/gitea/models/perm/access"
)
func TestMain(m *testing.M) {
unittest.MainTest(m, &unittest.TestOptions{
GiteaRootPath: filepath.Join("..", ".."),
- FixtureFiles: []string{
- "login_source.yml",
- "oauth2_application.yml",
- "oauth2_authorization_code.yml",
- "oauth2_grant.yml",
- "webauthn_credential.yml",
- },
})
}
diff --git a/models/auth/oauth2.go b/models/auth/oauth2.go
index 5a58ec62b7..9fdce24253 100644
--- a/models/auth/oauth2.go
+++ b/models/auth/oauth2.go
@@ -10,6 +10,7 @@ import (
"encoding/base32"
"encoding/base64"
"fmt"
+ "net"
"net/url"
"strings"
@@ -56,6 +57,18 @@ func (app *OAuth2Application) PrimaryRedirectURI() string {
// ContainsRedirectURI checks if redirectURI is allowed for app
func (app *OAuth2Application) ContainsRedirectURI(redirectURI string) bool {
+ uri, err := url.Parse(redirectURI)
+ // ignore port for http loopback uris following https://datatracker.ietf.org/doc/html/rfc8252#section-7.3
+ if err == nil && uri.Scheme == "http" && uri.Port() != "" {
+ ip := net.ParseIP(uri.Hostname())
+ if ip != nil && ip.IsLoopback() {
+ // strip port
+ uri.Host = uri.Hostname()
+ if util.IsStringInSlice(uri.String(), app.RedirectURIs, true) {
+ return true
+ }
+ }
+ }
return util.IsStringInSlice(redirectURI, app.RedirectURIs, true)
}
@@ -212,7 +225,8 @@ func updateOAuth2Application(ctx context.Context, app *OAuth2Application) error
func deleteOAuth2Application(ctx context.Context, id, userid int64) error {
sess := db.GetEngine(ctx)
- if deleted, err := sess.Delete(&OAuth2Application{ID: id, UID: userid}); err != nil {
+ // the userid could be 0 if the app is instance-wide
+ if deleted, err := sess.Where(builder.Eq{"id": id, "uid": userid}).Delete(&OAuth2Application{}); err != nil {
return err
} else if deleted == 0 {
return ErrOAuthApplicationNotFound{ID: id}
@@ -463,7 +477,7 @@ func GetOAuth2GrantsByUserID(ctx context.Context, uid int64) ([]*OAuth2Grant, er
// RevokeOAuth2Grant deletes the grant with grantID and userID
func RevokeOAuth2Grant(ctx context.Context, grantID, userID int64) error {
- _, err := db.DeleteByBean(ctx, &OAuth2Grant{ID: grantID, UserID: userID})
+ _, err := db.GetEngine(ctx).Where(builder.Eq{"id": grantID, "user_id": userID}).Delete(&OAuth2Grant{})
return err
}
@@ -472,7 +486,7 @@ type ErrOAuthClientIDInvalid struct {
ClientID string
}
-// IsErrOauthClientIDInvalid checks if an error is a ErrReviewNotExist.
+// IsErrOauthClientIDInvalid checks if an error is a ErrOAuthClientIDInvalid.
func IsErrOauthClientIDInvalid(err error) bool {
_, ok := err.(ErrOAuthClientIDInvalid)
return ok
@@ -483,6 +497,11 @@ func (err ErrOAuthClientIDInvalid) Error() string {
return fmt.Sprintf("Client ID invalid [Client ID: %s]", err.ClientID)
}
+// Unwrap unwraps this as a ErrNotExist err
+func (err ErrOAuthClientIDInvalid) Unwrap() error {
+ return util.ErrNotExist
+}
+
// ErrOAuthApplicationNotFound will be thrown if id cannot be found
type ErrOAuthApplicationNotFound struct {
ID int64
@@ -499,6 +518,11 @@ func (err ErrOAuthApplicationNotFound) Error() string {
return fmt.Sprintf("OAuth application not found [ID: %d]", err.ID)
}
+// Unwrap unwraps this as a ErrNotExist err
+func (err ErrOAuthApplicationNotFound) Unwrap() error {
+ return util.ErrNotExist
+}
+
// GetActiveOAuth2ProviderSources returns all actived LoginOAuth2 sources
func GetActiveOAuth2ProviderSources() ([]*Source, error) {
sources := make([]*Source, 0, 1)
@@ -512,10 +536,14 @@ func GetActiveOAuth2ProviderSources() ([]*Source, error) {
func GetActiveOAuth2SourceByName(name string) (*Source, error) {
authSource := new(Source)
has, err := db.GetEngine(db.DefaultContext).Where("name = ? and type = ? and is_active = ?", name, OAuth2, true).Get(authSource)
- if !has || err != nil {
+ if err != nil {
return nil, err
}
+ if !has {
+ return nil, fmt.Errorf("oauth2 source not found, name: %q", name)
+ }
+
return authSource, nil
}
diff --git a/models/auth/oauth2_test.go b/models/auth/oauth2_test.go
index cb8c4aeb6a..3815cb3b2c 100644
--- a/models/auth/oauth2_test.go
+++ b/models/auth/oauth2_test.go
@@ -2,11 +2,12 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
-package auth
+package auth_test
import (
"testing"
+ auth_model "code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/unittest"
@@ -17,23 +18,23 @@ import (
func TestOAuth2Application_GenerateClientSecret(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- app := unittest.AssertExistsAndLoadBean(t, &OAuth2Application{ID: 1}).(*OAuth2Application)
+ app := unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Application{ID: 1})
secret, err := app.GenerateClientSecret()
assert.NoError(t, err)
assert.True(t, len(secret) > 0)
- unittest.AssertExistsAndLoadBean(t, &OAuth2Application{ID: 1, ClientSecret: app.ClientSecret})
+ unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Application{ID: 1, ClientSecret: app.ClientSecret})
}
func BenchmarkOAuth2Application_GenerateClientSecret(b *testing.B) {
assert.NoError(b, unittest.PrepareTestDatabase())
- app := unittest.AssertExistsAndLoadBean(b, &OAuth2Application{ID: 1}).(*OAuth2Application)
+ app := unittest.AssertExistsAndLoadBean(b, &auth_model.OAuth2Application{ID: 1})
for i := 0; i < b.N; i++ {
_, _ = app.GenerateClientSecret()
}
}
func TestOAuth2Application_ContainsRedirectURI(t *testing.T) {
- app := &OAuth2Application{
+ app := &auth_model.OAuth2Application{
RedirectURIs: []string{"a", "b", "c"},
}
assert.True(t, app.ContainsRedirectURI("a"))
@@ -42,9 +43,29 @@ func TestOAuth2Application_ContainsRedirectURI(t *testing.T) {
assert.False(t, app.ContainsRedirectURI("d"))
}
+func TestOAuth2Application_ContainsRedirectURI_WithPort(t *testing.T) {
+ app := &auth_model.OAuth2Application{
+ RedirectURIs: []string{"http://127.0.0.1/", "http://::1/", "http://192.168.0.1/", "http://intranet/", "https://127.0.0.1/"},
+ }
+
+ // http loopback uris should ignore port
+ // https://datatracker.ietf.org/doc/html/rfc8252#section-7.3
+ assert.True(t, app.ContainsRedirectURI("http://127.0.0.1:3456/"))
+ assert.True(t, app.ContainsRedirectURI("http://127.0.0.1/"))
+ assert.True(t, app.ContainsRedirectURI("http://[::1]:3456/"))
+
+ // not http
+ assert.False(t, app.ContainsRedirectURI("https://127.0.0.1:3456/"))
+ // not loopback
+ assert.False(t, app.ContainsRedirectURI("http://192.168.0.1:9954/"))
+ assert.False(t, app.ContainsRedirectURI("http://intranet:3456/"))
+ // unparseable
+ assert.False(t, app.ContainsRedirectURI(":"))
+}
+
func TestOAuth2Application_ValidateClientSecret(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- app := unittest.AssertExistsAndLoadBean(t, &OAuth2Application{ID: 1}).(*OAuth2Application)
+ app := unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Application{ID: 1})
secret, err := app.GenerateClientSecret()
assert.NoError(t, err)
assert.True(t, app.ValidateClientSecret([]byte(secret)))
@@ -53,31 +74,31 @@ func TestOAuth2Application_ValidateClientSecret(t *testing.T) {
func TestGetOAuth2ApplicationByClientID(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- app, err := GetOAuth2ApplicationByClientID(db.DefaultContext, "da7da3ba-9a13-4167-856f-3899de0b0138")
+ app, err := auth_model.GetOAuth2ApplicationByClientID(db.DefaultContext, "da7da3ba-9a13-4167-856f-3899de0b0138")
assert.NoError(t, err)
assert.Equal(t, "da7da3ba-9a13-4167-856f-3899de0b0138", app.ClientID)
- app, err = GetOAuth2ApplicationByClientID(db.DefaultContext, "invalid client id")
+ app, err = auth_model.GetOAuth2ApplicationByClientID(db.DefaultContext, "invalid client id")
assert.Error(t, err)
assert.Nil(t, app)
}
func TestCreateOAuth2Application(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- app, err := CreateOAuth2Application(db.DefaultContext, CreateOAuth2ApplicationOptions{Name: "newapp", UserID: 1})
+ app, err := auth_model.CreateOAuth2Application(db.DefaultContext, auth_model.CreateOAuth2ApplicationOptions{Name: "newapp", UserID: 1})
assert.NoError(t, err)
assert.Equal(t, "newapp", app.Name)
assert.Len(t, app.ClientID, 36)
- unittest.AssertExistsAndLoadBean(t, &OAuth2Application{Name: "newapp"})
+ unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Application{Name: "newapp"})
}
func TestOAuth2Application_TableName(t *testing.T) {
- assert.Equal(t, "oauth2_application", new(OAuth2Application).TableName())
+ assert.Equal(t, "oauth2_application", new(auth_model.OAuth2Application).TableName())
}
func TestOAuth2Application_GetGrantByUserID(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- app := unittest.AssertExistsAndLoadBean(t, &OAuth2Application{ID: 1}).(*OAuth2Application)
+ app := unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Application{ID: 1})
grant, err := app.GetGrantByUserID(db.DefaultContext, 1)
assert.NoError(t, err)
assert.Equal(t, int64(1), grant.UserID)
@@ -89,7 +110,7 @@ func TestOAuth2Application_GetGrantByUserID(t *testing.T) {
func TestOAuth2Application_CreateGrant(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- app := unittest.AssertExistsAndLoadBean(t, &OAuth2Application{ID: 1}).(*OAuth2Application)
+ app := unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Application{ID: 1})
grant, err := app.CreateGrant(db.DefaultContext, 2, "")
assert.NoError(t, err)
assert.NotNil(t, grant)
@@ -102,26 +123,26 @@ func TestOAuth2Application_CreateGrant(t *testing.T) {
func TestGetOAuth2GrantByID(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- grant, err := GetOAuth2GrantByID(db.DefaultContext, 1)
+ grant, err := auth_model.GetOAuth2GrantByID(db.DefaultContext, 1)
assert.NoError(t, err)
assert.Equal(t, int64(1), grant.ID)
- grant, err = GetOAuth2GrantByID(db.DefaultContext, 34923458)
+ grant, err = auth_model.GetOAuth2GrantByID(db.DefaultContext, 34923458)
assert.NoError(t, err)
assert.Nil(t, grant)
}
func TestOAuth2Grant_IncreaseCounter(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- grant := unittest.AssertExistsAndLoadBean(t, &OAuth2Grant{ID: 1, Counter: 1}).(*OAuth2Grant)
+ grant := unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Grant{ID: 1, Counter: 1})
assert.NoError(t, grant.IncreaseCounter(db.DefaultContext))
assert.Equal(t, int64(2), grant.Counter)
- unittest.AssertExistsAndLoadBean(t, &OAuth2Grant{ID: 1, Counter: 2})
+ unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Grant{ID: 1, Counter: 2})
}
func TestOAuth2Grant_ScopeContains(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- grant := unittest.AssertExistsAndLoadBean(t, &OAuth2Grant{ID: 1, Scope: "openid profile"}).(*OAuth2Grant)
+ grant := unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Grant{ID: 1, Scope: "openid profile"})
assert.True(t, grant.ScopeContains("openid"))
assert.True(t, grant.ScopeContains("profile"))
assert.False(t, grant.ScopeContains("profil"))
@@ -130,7 +151,7 @@ func TestOAuth2Grant_ScopeContains(t *testing.T) {
func TestOAuth2Grant_GenerateNewAuthorizationCode(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- grant := unittest.AssertExistsAndLoadBean(t, &OAuth2Grant{ID: 1}).(*OAuth2Grant)
+ grant := unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2Grant{ID: 1})
code, err := grant.GenerateNewAuthorizationCode(db.DefaultContext, "https://example2.com/callback", "CjvyTLSdR47G5zYenDA-eDWW4lRrO8yvjcWwbD_deOg", "S256")
assert.NoError(t, err)
assert.NotNil(t, code)
@@ -138,46 +159,46 @@ func TestOAuth2Grant_GenerateNewAuthorizationCode(t *testing.T) {
}
func TestOAuth2Grant_TableName(t *testing.T) {
- assert.Equal(t, "oauth2_grant", new(OAuth2Grant).TableName())
+ assert.Equal(t, "oauth2_grant", new(auth_model.OAuth2Grant).TableName())
}
func TestGetOAuth2GrantsByUserID(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- result, err := GetOAuth2GrantsByUserID(db.DefaultContext, 1)
+ result, err := auth_model.GetOAuth2GrantsByUserID(db.DefaultContext, 1)
assert.NoError(t, err)
assert.Len(t, result, 1)
assert.Equal(t, int64(1), result[0].ID)
assert.Equal(t, result[0].ApplicationID, result[0].Application.ID)
- result, err = GetOAuth2GrantsByUserID(db.DefaultContext, 34134)
+ result, err = auth_model.GetOAuth2GrantsByUserID(db.DefaultContext, 34134)
assert.NoError(t, err)
assert.Empty(t, result)
}
func TestRevokeOAuth2Grant(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- assert.NoError(t, RevokeOAuth2Grant(db.DefaultContext, 1, 1))
- unittest.AssertNotExistsBean(t, &OAuth2Grant{ID: 1, UserID: 1})
+ assert.NoError(t, auth_model.RevokeOAuth2Grant(db.DefaultContext, 1, 1))
+ unittest.AssertNotExistsBean(t, &auth_model.OAuth2Grant{ID: 1, UserID: 1})
}
//////////////////// Authorization Code
func TestGetOAuth2AuthorizationByCode(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- code, err := GetOAuth2AuthorizationByCode(db.DefaultContext, "authcode")
+ code, err := auth_model.GetOAuth2AuthorizationByCode(db.DefaultContext, "authcode")
assert.NoError(t, err)
assert.NotNil(t, code)
assert.Equal(t, "authcode", code.Code)
assert.Equal(t, int64(1), code.ID)
- code, err = GetOAuth2AuthorizationByCode(db.DefaultContext, "does not exist")
+ code, err = auth_model.GetOAuth2AuthorizationByCode(db.DefaultContext, "does not exist")
assert.NoError(t, err)
assert.Nil(t, code)
}
func TestOAuth2AuthorizationCode_ValidateCodeChallenge(t *testing.T) {
// test plain
- code := &OAuth2AuthorizationCode{
+ code := &auth_model.OAuth2AuthorizationCode{
CodeChallengeMethod: "plain",
CodeChallenge: "test123",
}
@@ -185,7 +206,7 @@ func TestOAuth2AuthorizationCode_ValidateCodeChallenge(t *testing.T) {
assert.False(t, code.ValidateCodeChallenge("ierwgjoergjio"))
// test S256
- code = &OAuth2AuthorizationCode{
+ code = &auth_model.OAuth2AuthorizationCode{
CodeChallengeMethod: "S256",
CodeChallenge: "CjvyTLSdR47G5zYenDA-eDWW4lRrO8yvjcWwbD_deOg",
}
@@ -193,14 +214,14 @@ func TestOAuth2AuthorizationCode_ValidateCodeChallenge(t *testing.T) {
assert.False(t, code.ValidateCodeChallenge("wiogjerogorewngoenrgoiuenorg"))
// test unknown
- code = &OAuth2AuthorizationCode{
+ code = &auth_model.OAuth2AuthorizationCode{
CodeChallengeMethod: "monkey",
CodeChallenge: "foiwgjioriogeiogjerger",
}
assert.False(t, code.ValidateCodeChallenge("foiwgjioriogeiogjerger"))
// test no code challenge
- code = &OAuth2AuthorizationCode{
+ code = &auth_model.OAuth2AuthorizationCode{
CodeChallengeMethod: "",
CodeChallenge: "foierjiogerogerg",
}
@@ -208,7 +229,7 @@ func TestOAuth2AuthorizationCode_ValidateCodeChallenge(t *testing.T) {
}
func TestOAuth2AuthorizationCode_GenerateRedirectURI(t *testing.T) {
- code := &OAuth2AuthorizationCode{
+ code := &auth_model.OAuth2AuthorizationCode{
RedirectURI: "https://example.com/callback",
Code: "thecode",
}
@@ -224,11 +245,11 @@ func TestOAuth2AuthorizationCode_GenerateRedirectURI(t *testing.T) {
func TestOAuth2AuthorizationCode_Invalidate(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- code := unittest.AssertExistsAndLoadBean(t, &OAuth2AuthorizationCode{Code: "authcode"}).(*OAuth2AuthorizationCode)
+ code := unittest.AssertExistsAndLoadBean(t, &auth_model.OAuth2AuthorizationCode{Code: "authcode"})
assert.NoError(t, code.Invalidate(db.DefaultContext))
- unittest.AssertNotExistsBean(t, &OAuth2AuthorizationCode{Code: "authcode"})
+ unittest.AssertNotExistsBean(t, &auth_model.OAuth2AuthorizationCode{Code: "authcode"})
}
func TestOAuth2AuthorizationCode_TableName(t *testing.T) {
- assert.Equal(t, "oauth2_authorization_code", new(OAuth2AuthorizationCode).TableName())
+ assert.Equal(t, "oauth2_authorization_code", new(auth_model.OAuth2AuthorizationCode).TableName())
}
diff --git a/models/auth/source.go b/models/auth/source.go
index 6f4f5addcb..f8be5398ae 100644
--- a/models/auth/source.go
+++ b/models/auth/source.go
@@ -12,6 +12,7 @@ import (
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/timeutil"
+ "code.gitea.io/gitea/modules/util"
"xorm.io/xorm"
"xorm.io/xorm/convert"
@@ -366,6 +367,11 @@ func (err ErrSourceNotExist) Error() string {
return fmt.Sprintf("login source does not exist [id: %d]", err.ID)
}
+// Unwrap unwraps this as a ErrNotExist err
+func (err ErrSourceNotExist) Unwrap() error {
+ return util.ErrNotExist
+}
+
// ErrSourceAlreadyExist represents a "SourceAlreadyExist" kind of error.
type ErrSourceAlreadyExist struct {
Name string
@@ -381,6 +387,11 @@ func (err ErrSourceAlreadyExist) Error() string {
return fmt.Sprintf("login source already exists [name: %s]", err.Name)
}
+// Unwrap unwraps this as a ErrExist err
+func (err ErrSourceAlreadyExist) Unwrap() error {
+ return util.ErrAlreadyExist
+}
+
// ErrSourceInUse represents a "SourceInUse" kind of error.
type ErrSourceInUse struct {
ID int64
diff --git a/models/auth/source_test.go b/models/auth/source_test.go
index 6a8e286910..67e96ee19e 100644
--- a/models/auth/source_test.go
+++ b/models/auth/source_test.go
@@ -2,12 +2,13 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
-package auth
+package auth_test
import (
"strings"
"testing"
+ auth_model "code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/unittest"
"code.gitea.io/gitea/modules/json"
@@ -37,13 +38,13 @@ func (source *TestSource) ToDB() ([]byte, error) {
func TestDumpAuthSource(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- authSourceSchema, err := db.TableInfo(new(Source))
+ authSourceSchema, err := db.TableInfo(new(auth_model.Source))
assert.NoError(t, err)
- RegisterTypeConfig(OAuth2, new(TestSource))
+ auth_model.RegisterTypeConfig(auth_model.OAuth2, new(TestSource))
- CreateSource(&Source{
- Type: OAuth2,
+ auth_model.CreateSource(&auth_model.Source{
+ Type: auth_model.OAuth2,
Name: "TestSource",
IsActive: false,
Cfg: &TestSource{
diff --git a/models/token.go b/models/auth/token.go
similarity index 84%
rename from models/token.go
rename to models/auth/token.go
index b89514309c..3afef832da 100644
--- a/models/token.go
+++ b/models/auth/token.go
@@ -3,14 +3,13 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
-package models
+package auth
import (
"crypto/subtle"
"fmt"
"time"
- "code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/setting"
@@ -21,6 +20,42 @@ import (
lru "github.com/hashicorp/golang-lru"
)
+// ErrAccessTokenNotExist represents a "AccessTokenNotExist" kind of error.
+type ErrAccessTokenNotExist struct {
+ Token string
+}
+
+// IsErrAccessTokenNotExist checks if an error is a ErrAccessTokenNotExist.
+func IsErrAccessTokenNotExist(err error) bool {
+ _, ok := err.(ErrAccessTokenNotExist)
+ return ok
+}
+
+func (err ErrAccessTokenNotExist) Error() string {
+ return fmt.Sprintf("access token does not exist [sha: %s]", err.Token)
+}
+
+func (err ErrAccessTokenNotExist) Unwrap() error {
+ return util.ErrNotExist
+}
+
+// ErrAccessTokenEmpty represents a "AccessTokenEmpty" kind of error.
+type ErrAccessTokenEmpty struct{}
+
+// IsErrAccessTokenEmpty checks if an error is a ErrAccessTokenEmpty.
+func IsErrAccessTokenEmpty(err error) bool {
+ _, ok := err.(ErrAccessTokenEmpty)
+ return ok
+}
+
+func (err ErrAccessTokenEmpty) Error() string {
+ return "access token is empty"
+}
+
+func (err ErrAccessTokenEmpty) Unwrap() error {
+ return util.ErrInvalidArgument
+}
+
var successfulAccessTokenCache *lru.Cache
// AccessToken represents a personal access token.
@@ -68,7 +103,7 @@ func NewAccessToken(t *AccessToken) error {
}
t.TokenSalt = salt
t.Token = base.EncodeSha1(gouuid.New().String())
- t.TokenHash = auth.HashToken(t.Token, t.TokenSalt)
+ t.TokenHash = HashToken(t.Token, t.TokenSalt)
t.TokenLastEight = t.Token[len(t.Token)-8:]
_, err = db.GetEngine(db.DefaultContext).Insert(t)
return err
@@ -130,7 +165,7 @@ func GetAccessTokenBySHA(token string) (*AccessToken, error) {
}
for _, t := range tokens {
- tempHash := auth.HashToken(token, t.TokenSalt)
+ tempHash := HashToken(token, t.TokenSalt)
if subtle.ConstantTimeCompare([]byte(t.TokenHash), []byte(tempHash)) == 1 {
if successfulAccessTokenCache != nil {
successfulAccessTokenCache.Add(token, t.ID)
diff --git a/models/token_test.go b/models/auth/token_test.go
similarity index 62%
rename from models/token_test.go
rename to models/auth/token_test.go
index 007148870a..b27ff13406 100644
--- a/models/token_test.go
+++ b/models/auth/token_test.go
@@ -2,11 +2,12 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
-package models
+package auth_test
import (
"testing"
+ auth_model "code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/models/unittest"
"github.com/stretchr/testify/assert"
@@ -14,77 +15,77 @@ import (
func TestNewAccessToken(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- token := &AccessToken{
+ token := &auth_model.AccessToken{
UID: 3,
Name: "Token C",
}
- assert.NoError(t, NewAccessToken(token))
+ assert.NoError(t, auth_model.NewAccessToken(token))
unittest.AssertExistsAndLoadBean(t, token)
- invalidToken := &AccessToken{
+ invalidToken := &auth_model.AccessToken{
ID: token.ID, // duplicate
UID: 2,
Name: "Token F",
}
- assert.Error(t, NewAccessToken(invalidToken))
+ assert.Error(t, auth_model.NewAccessToken(invalidToken))
}
func TestAccessTokenByNameExists(t *testing.T) {
name := "Token Gitea"
assert.NoError(t, unittest.PrepareTestDatabase())
- token := &AccessToken{
+ token := &auth_model.AccessToken{
UID: 3,
Name: name,
}
// Check to make sure it doesn't exists already
- exist, err := AccessTokenByNameExists(token)
+ exist, err := auth_model.AccessTokenByNameExists(token)
assert.NoError(t, err)
assert.False(t, exist)
// Save it to the database
- assert.NoError(t, NewAccessToken(token))
+ assert.NoError(t, auth_model.NewAccessToken(token))
unittest.AssertExistsAndLoadBean(t, token)
// This token must be found by name in the DB now
- exist, err = AccessTokenByNameExists(token)
+ exist, err = auth_model.AccessTokenByNameExists(token)
assert.NoError(t, err)
assert.True(t, exist)
- user4Token := &AccessToken{
+ user4Token := &auth_model.AccessToken{
UID: 4,
Name: name,
}
// Name matches but different user ID, this shouldn't exists in the
// database
- exist, err = AccessTokenByNameExists(user4Token)
+ exist, err = auth_model.AccessTokenByNameExists(user4Token)
assert.NoError(t, err)
assert.False(t, exist)
}
func TestGetAccessTokenBySHA(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- token, err := GetAccessTokenBySHA("d2c6c1ba3890b309189a8e618c72a162e4efbf36")
+ token, err := auth_model.GetAccessTokenBySHA("d2c6c1ba3890b309189a8e618c72a162e4efbf36")
assert.NoError(t, err)
assert.Equal(t, int64(1), token.UID)
assert.Equal(t, "Token A", token.Name)
assert.Equal(t, "2b3668e11cb82d3af8c6e4524fc7841297668f5008d1626f0ad3417e9fa39af84c268248b78c481daa7e5dc437784003494f", token.TokenHash)
assert.Equal(t, "e4efbf36", token.TokenLastEight)
- _, err = GetAccessTokenBySHA("notahash")
+ _, err = auth_model.GetAccessTokenBySHA("notahash")
assert.Error(t, err)
- assert.True(t, IsErrAccessTokenNotExist(err))
+ assert.True(t, auth_model.IsErrAccessTokenNotExist(err))
- _, err = GetAccessTokenBySHA("")
+ _, err = auth_model.GetAccessTokenBySHA("")
assert.Error(t, err)
- assert.True(t, IsErrAccessTokenEmpty(err))
+ assert.True(t, auth_model.IsErrAccessTokenEmpty(err))
}
func TestListAccessTokens(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- tokens, err := ListAccessTokens(ListAccessTokensOptions{UserID: 1})
+ tokens, err := auth_model.ListAccessTokens(auth_model.ListAccessTokensOptions{UserID: 1})
assert.NoError(t, err)
if assert.Len(t, tokens, 2) {
assert.Equal(t, int64(1), tokens[0].UID)
@@ -93,39 +94,39 @@ func TestListAccessTokens(t *testing.T) {
assert.Contains(t, []string{tokens[0].Name, tokens[1].Name}, "Token B")
}
- tokens, err = ListAccessTokens(ListAccessTokensOptions{UserID: 2})
+ tokens, err = auth_model.ListAccessTokens(auth_model.ListAccessTokensOptions{UserID: 2})
assert.NoError(t, err)
if assert.Len(t, tokens, 1) {
assert.Equal(t, int64(2), tokens[0].UID)
assert.Equal(t, "Token A", tokens[0].Name)
}
- tokens, err = ListAccessTokens(ListAccessTokensOptions{UserID: 100})
+ tokens, err = auth_model.ListAccessTokens(auth_model.ListAccessTokensOptions{UserID: 100})
assert.NoError(t, err)
assert.Empty(t, tokens)
}
func TestUpdateAccessToken(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- token, err := GetAccessTokenBySHA("4c6f36e6cf498e2a448662f915d932c09c5a146c")
+ token, err := auth_model.GetAccessTokenBySHA("4c6f36e6cf498e2a448662f915d932c09c5a146c")
assert.NoError(t, err)
token.Name = "Token Z"
- assert.NoError(t, UpdateAccessToken(token))
+ assert.NoError(t, auth_model.UpdateAccessToken(token))
unittest.AssertExistsAndLoadBean(t, token)
}
func TestDeleteAccessTokenByID(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- token, err := GetAccessTokenBySHA("4c6f36e6cf498e2a448662f915d932c09c5a146c")
+ token, err := auth_model.GetAccessTokenBySHA("4c6f36e6cf498e2a448662f915d932c09c5a146c")
assert.NoError(t, err)
assert.Equal(t, int64(1), token.UID)
- assert.NoError(t, DeleteAccessTokenByID(token.ID, 1))
+ assert.NoError(t, auth_model.DeleteAccessTokenByID(token.ID, 1))
unittest.AssertNotExistsBean(t, token)
- err = DeleteAccessTokenByID(100, 100)
+ err = auth_model.DeleteAccessTokenByID(100, 100)
assert.Error(t, err)
- assert.True(t, IsErrAccessTokenNotExist(err))
+ assert.True(t, auth_model.IsErrAccessTokenNotExist(err))
}
diff --git a/models/auth/twofactor.go b/models/auth/twofactor.go
index c5bd972f91..736d4c340c 100644
--- a/models/auth/twofactor.go
+++ b/models/auth/twofactor.go
@@ -41,6 +41,11 @@ func (err ErrTwoFactorNotEnrolled) Error() string {
return fmt.Sprintf("user not enrolled in 2FA [uid: %d]", err.UID)
}
+// Unwrap unwraps this as a ErrNotExist err
+func (err ErrTwoFactorNotEnrolled) Unwrap() error {
+ return util.ErrNotExist
+}
+
// TwoFactor represents a two-factor authentication token.
type TwoFactor struct {
ID int64 `xorm:"pk autoincr"`
diff --git a/models/auth/webauthn.go b/models/auth/webauthn.go
index 2dc3043780..1575b6cbab 100644
--- a/models/auth/webauthn.go
+++ b/models/auth/webauthn.go
@@ -6,12 +6,12 @@ package auth
import (
"context"
- "encoding/base32"
"fmt"
"strings"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/timeutil"
+ "code.gitea.io/gitea/modules/util"
"github.com/duo-labs/webauthn/webauthn"
"xorm.io/xorm"
@@ -20,14 +20,19 @@ import (
// ErrWebAuthnCredentialNotExist represents a "ErrWebAuthnCRedentialNotExist" kind of error.
type ErrWebAuthnCredentialNotExist struct {
ID int64
- CredentialID string
+ CredentialID []byte
}
func (err ErrWebAuthnCredentialNotExist) Error() string {
- if err.CredentialID == "" {
+ if len(err.CredentialID) == 0 {
return fmt.Sprintf("WebAuthn credential does not exist [id: %d]", err.ID)
}
- return fmt.Sprintf("WebAuthn credential does not exist [credential_id: %s]", err.CredentialID)
+ return fmt.Sprintf("WebAuthn credential does not exist [credential_id: %x]", err.CredentialID)
+}
+
+// Unwrap unwraps this as a ErrNotExist err
+func (err ErrWebAuthnCredentialNotExist) Unwrap() error {
+ return util.ErrNotExist
}
// IsErrWebAuthnCredentialNotExist checks if an error is a ErrWebAuthnCredentialNotExist.
@@ -43,7 +48,7 @@ type WebAuthnCredential struct {
Name string
LowerName string `xorm:"unique(s)"`
UserID int64 `xorm:"INDEX unique(s)"`
- CredentialID string `xorm:"INDEX VARCHAR(410)"`
+ CredentialID []byte `xorm:"INDEX VARBINARY(1024)"`
PublicKey []byte
AttestationType string
AAGUID []byte
@@ -94,9 +99,8 @@ type WebAuthnCredentialList []*WebAuthnCredential
func (list WebAuthnCredentialList) ToCredentials() []webauthn.Credential {
creds := make([]webauthn.Credential, 0, len(list))
for _, cred := range list {
- credID, _ := base32.HexEncoding.DecodeString(cred.CredentialID)
creds = append(creds, webauthn.Credential{
- ID: credID,
+ ID: cred.CredentialID,
PublicKey: cred.PublicKey,
AttestationType: cred.AttestationType,
Authenticator: webauthn.Authenticator{
@@ -164,11 +168,11 @@ func HasWebAuthnRegistrationsByUID(uid int64) (bool, error) {
}
// GetWebAuthnCredentialByCredID returns WebAuthn credential by credential ID
-func GetWebAuthnCredentialByCredID(userID int64, credID string) (*WebAuthnCredential, error) {
+func GetWebAuthnCredentialByCredID(userID int64, credID []byte) (*WebAuthnCredential, error) {
return getWebAuthnCredentialByCredID(db.DefaultContext, userID, credID)
}
-func getWebAuthnCredentialByCredID(ctx context.Context, userID int64, credID string) (*WebAuthnCredential, error) {
+func getWebAuthnCredentialByCredID(ctx context.Context, userID int64, credID []byte) (*WebAuthnCredential, error) {
cred := new(WebAuthnCredential)
if found, err := db.GetEngine(ctx).Where("user_id = ? AND credential_id = ?", userID, credID).Get(cred); err != nil {
return nil, err
@@ -187,7 +191,7 @@ func createCredential(ctx context.Context, userID int64, name string, cred *weba
c := &WebAuthnCredential{
UserID: userID,
Name: name,
- CredentialID: base32.HexEncoding.EncodeToString(cred.ID),
+ CredentialID: cred.ID,
PublicKey: cred.PublicKey,
AttestationType: cred.AttestationType,
AAGUID: cred.Authenticator.AAGUID,
diff --git a/models/auth/webauthn_test.go b/models/auth/webauthn_test.go
index 216bf11080..29344376cc 100644
--- a/models/auth/webauthn_test.go
+++ b/models/auth/webauthn_test.go
@@ -2,12 +2,12 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
-package auth
+package auth_test
import (
- "encoding/base32"
"testing"
+ auth_model "code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/models/unittest"
"github.com/duo-labs/webauthn/webauthn"
@@ -17,53 +17,51 @@ import (
func TestGetWebAuthnCredentialByID(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- res, err := GetWebAuthnCredentialByID(1)
+ res, err := auth_model.GetWebAuthnCredentialByID(1)
assert.NoError(t, err)
assert.Equal(t, "WebAuthn credential", res.Name)
- _, err = GetWebAuthnCredentialByID(342432)
+ _, err = auth_model.GetWebAuthnCredentialByID(342432)
assert.Error(t, err)
- assert.True(t, IsErrWebAuthnCredentialNotExist(err))
+ assert.True(t, auth_model.IsErrWebAuthnCredentialNotExist(err))
}
func TestGetWebAuthnCredentialsByUID(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- res, err := GetWebAuthnCredentialsByUID(32)
+ res, err := auth_model.GetWebAuthnCredentialsByUID(32)
assert.NoError(t, err)
assert.Len(t, res, 1)
assert.Equal(t, "WebAuthn credential", res[0].Name)
}
func TestWebAuthnCredential_TableName(t *testing.T) {
- assert.Equal(t, "webauthn_credential", WebAuthnCredential{}.TableName())
+ assert.Equal(t, "webauthn_credential", auth_model.WebAuthnCredential{}.TableName())
}
func TestWebAuthnCredential_UpdateSignCount(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- cred := unittest.AssertExistsAndLoadBean(t, &WebAuthnCredential{ID: 1}).(*WebAuthnCredential)
+ cred := unittest.AssertExistsAndLoadBean(t, &auth_model.WebAuthnCredential{ID: 1})
cred.SignCount = 1
assert.NoError(t, cred.UpdateSignCount())
- unittest.AssertExistsIf(t, true, &WebAuthnCredential{ID: 1, SignCount: 1})
+ unittest.AssertExistsIf(t, true, &auth_model.WebAuthnCredential{ID: 1, SignCount: 1})
}
func TestWebAuthnCredential_UpdateLargeCounter(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- cred := unittest.AssertExistsAndLoadBean(t, &WebAuthnCredential{ID: 1}).(*WebAuthnCredential)
+ cred := unittest.AssertExistsAndLoadBean(t, &auth_model.WebAuthnCredential{ID: 1})
cred.SignCount = 0xffffffff
assert.NoError(t, cred.UpdateSignCount())
- unittest.AssertExistsIf(t, true, &WebAuthnCredential{ID: 1, SignCount: 0xffffffff})
+ unittest.AssertExistsIf(t, true, &auth_model.WebAuthnCredential{ID: 1, SignCount: 0xffffffff})
}
func TestCreateCredential(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- res, err := CreateCredential(1, "WebAuthn Created Credential", &webauthn.Credential{ID: []byte("Test")})
+ res, err := auth_model.CreateCredential(1, "WebAuthn Created Credential", &webauthn.Credential{ID: []byte("Test")})
assert.NoError(t, err)
assert.Equal(t, "WebAuthn Created Credential", res.Name)
- bs, err := base32.HexEncoding.DecodeString(res.CredentialID)
- assert.NoError(t, err)
- assert.Equal(t, []byte("Test"), bs)
+ assert.Equal(t, []byte("Test"), res.CredentialID)
- unittest.AssertExistsIf(t, true, &WebAuthnCredential{Name: "WebAuthn Created Credential", UserID: 1})
+ unittest.AssertExistsIf(t, true, &auth_model.WebAuthnCredential{Name: "WebAuthn Created Credential", UserID: 1})
}
diff --git a/models/avatars/avatar.go b/models/avatars/avatar.go
index 9f7b0c474f..418e9b9ccc 100644
--- a/models/avatars/avatar.go
+++ b/models/avatars/avatar.go
@@ -13,6 +13,7 @@ import (
"sync"
"code.gitea.io/gitea/models/db"
+ system_model "code.gitea.io/gitea/models/system"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/cache"
"code.gitea.io/gitea/modules/log"
@@ -72,7 +73,7 @@ func GetEmailForHash(md5Sum string) (string, error) {
// LibravatarURL returns the URL for the given email. Slow due to the DNS lookup.
// This function should only be called if a federated avatar service is enabled.
func LibravatarURL(email string) (*url.URL, error) {
- urlStr, err := setting.LibravatarService.FromEmail(email)
+ urlStr, err := system_model.LibravatarService.FromEmail(email)
if err != nil {
log.Error("LibravatarService.FromEmail(email=%s): error %v", email, err)
return nil, err
@@ -149,8 +150,10 @@ func generateEmailAvatarLink(email string, size int, final bool) string {
return DefaultAvatarLink()
}
+ enableFederatedAvatar, _ := system_model.GetSetting(system_model.KeyPictureEnableFederatedAvatar)
+
var err error
- if setting.EnableFederatedAvatar && setting.LibravatarService != nil {
+ if enableFederatedAvatar != nil && enableFederatedAvatar.GetValueBool() && system_model.LibravatarService != nil {
emailHash := saveEmailHash(email)
if final {
// for final link, we can spend more time on slow external query
@@ -166,12 +169,16 @@ func generateEmailAvatarLink(email string, size int, final bool) string {
urlStr += "?size=" + strconv.Itoa(size)
}
return urlStr
- } else if !setting.DisableGravatar {
+ }
+
+ disableGravatar, _ := system_model.GetSetting(system_model.KeyPictureDisableGravatar)
+ if disableGravatar != nil && !disableGravatar.GetValueBool() {
// copy GravatarSourceURL, because we will modify its Path.
- avatarURLCopy := *setting.GravatarSourceURL
+ avatarURLCopy := *system_model.GravatarSourceURL
avatarURLCopy.Path = path.Join(avatarURLCopy.Path, HashEmail(email))
return generateRecognizedAvatarURL(avatarURLCopy, size)
}
+
return DefaultAvatarLink()
}
diff --git a/models/avatars/avatar_test.go b/models/avatars/avatar_test.go
index 4d6255ca5f..ace5445fc0 100644
--- a/models/avatars/avatar_test.go
+++ b/models/avatars/avatar_test.go
@@ -2,12 +2,13 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
-package avatars
+package avatars_test
import (
- "net/url"
"testing"
+ avatars_model "code.gitea.io/gitea/models/avatars"
+ system_model "code.gitea.io/gitea/models/system"
"code.gitea.io/gitea/modules/setting"
"github.com/stretchr/testify/assert"
@@ -15,40 +16,43 @@ import (
const gravatarSource = "https://secure.gravatar.com/avatar/"
-func disableGravatar() {
- setting.EnableFederatedAvatar = false
- setting.LibravatarService = nil
- setting.DisableGravatar = true
+func disableGravatar(t *testing.T) {
+ err := system_model.SetSettingNoVersion(system_model.KeyPictureEnableFederatedAvatar, "false")
+ assert.NoError(t, err)
+ err = system_model.SetSettingNoVersion(system_model.KeyPictureDisableGravatar, "true")
+ assert.NoError(t, err)
+ system_model.LibravatarService = nil
}
func enableGravatar(t *testing.T) {
- setting.DisableGravatar = false
- var err error
- setting.GravatarSourceURL, err = url.Parse(gravatarSource)
+ err := system_model.SetSettingNoVersion(system_model.KeyPictureDisableGravatar, "false")
+ assert.NoError(t, err)
+ setting.GravatarSource = gravatarSource
+ err = system_model.Init()
assert.NoError(t, err)
}
func TestHashEmail(t *testing.T) {
assert.Equal(t,
"d41d8cd98f00b204e9800998ecf8427e",
- HashEmail(""),
+ avatars_model.HashEmail(""),
)
assert.Equal(t,
"353cbad9b58e69c96154ad99f92bedc7",
- HashEmail("gitea@example.com"),
+ avatars_model.HashEmail("gitea@example.com"),
)
}
func TestSizedAvatarLink(t *testing.T) {
setting.AppSubURL = "/testsuburl"
- disableGravatar()
+ disableGravatar(t)
assert.Equal(t, "/testsuburl/assets/img/avatar_default.png",
- GenerateEmailAvatarFastLink("gitea@example.com", 100))
+ avatars_model.GenerateEmailAvatarFastLink("gitea@example.com", 100))
enableGravatar(t)
assert.Equal(t,
"https://secure.gravatar.com/avatar/353cbad9b58e69c96154ad99f92bedc7?d=identicon&s=100",
- GenerateEmailAvatarFastLink("gitea@example.com", 100),
+ avatars_model.GenerateEmailAvatarFastLink("gitea@example.com", 100),
)
}
diff --git a/models/avatars/main_test.go b/models/avatars/main_test.go
new file mode 100644
index 0000000000..0e98d8f64d
--- /dev/null
+++ b/models/avatars/main_test.go
@@ -0,0 +1,22 @@
+// Copyright 2020 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package avatars_test
+
+import (
+ "path/filepath"
+ "testing"
+
+ "code.gitea.io/gitea/models/unittest"
+
+ _ "code.gitea.io/gitea/models"
+ _ "code.gitea.io/gitea/models/activities"
+ _ "code.gitea.io/gitea/models/perm/access"
+)
+
+func TestMain(m *testing.M) {
+ unittest.MainTest(m, &unittest.TestOptions{
+ GiteaRootPath: filepath.Join("..", ".."),
+ })
+}
diff --git a/models/consistency.go b/models/consistency.go
deleted file mode 100644
index 18ed9195fc..0000000000
--- a/models/consistency.go
+++ /dev/null
@@ -1,56 +0,0 @@
-// Copyright 2017 The Gitea Authors. All rights reserved.
-// Use of this source code is governed by a MIT-style
-// license that can be found in the LICENSE file.
-
-package models
-
-import (
- "code.gitea.io/gitea/models/db"
- repo_model "code.gitea.io/gitea/models/repo"
- user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/setting"
-
- "xorm.io/builder"
-)
-
-// CountNullArchivedRepository counts the number of repositories with is_archived is null
-func CountNullArchivedRepository() (int64, error) {
- return db.GetEngine(db.DefaultContext).Where(builder.IsNull{"is_archived"}).Count(new(repo_model.Repository))
-}
-
-// FixNullArchivedRepository sets is_archived to false where it is null
-func FixNullArchivedRepository() (int64, error) {
- return db.GetEngine(db.DefaultContext).Where(builder.IsNull{"is_archived"}).Cols("is_archived").NoAutoTime().Update(&repo_model.Repository{
- IsArchived: false,
- })
-}
-
-// CountWrongUserType count OrgUser who have wrong type
-func CountWrongUserType() (int64, error) {
- return db.GetEngine(db.DefaultContext).Where(builder.Eq{"type": 0}.And(builder.Neq{"num_teams": 0})).Count(new(user_model.User))
-}
-
-// FixWrongUserType fix OrgUser who have wrong type
-func FixWrongUserType() (int64, error) {
- return db.GetEngine(db.DefaultContext).Where(builder.Eq{"type": 0}.And(builder.Neq{"num_teams": 0})).Cols("type").NoAutoTime().Update(&user_model.User{Type: 1})
-}
-
-// CountActionCreatedUnixString count actions where created_unix is an empty string
-func CountActionCreatedUnixString() (int64, error) {
- if setting.Database.UseSQLite3 {
- return db.GetEngine(db.DefaultContext).Where(`created_unix = ""`).Count(new(Action))
- }
- return 0, nil
-}
-
-// FixActionCreatedUnixString set created_unix to zero if it is an empty string
-func FixActionCreatedUnixString() (int64, error) {
- if setting.Database.UseSQLite3 {
- res, err := db.GetEngine(db.DefaultContext).Exec(`UPDATE action SET created_unix = 0 WHERE created_unix = ""`)
- if err != nil {
- return 0, err
- }
- return res.RowsAffected()
- }
- return 0, nil
-}
diff --git a/models/db/common.go b/models/db/common.go
new file mode 100644
index 0000000000..1a59a8b5c6
--- /dev/null
+++ b/models/db/common.go
@@ -0,0 +1,23 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package db
+
+import (
+ "strings"
+
+ "code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/util"
+
+ "xorm.io/builder"
+)
+
+// BuildCaseInsensitiveLike returns a condition to check if the given value is like the given key case-insensitively.
+// Handles especially SQLite correctly as UPPER there only transforms ASCII letters.
+func BuildCaseInsensitiveLike(key, value string) builder.Cond {
+ if setting.Database.UseSQLite3 {
+ return builder.Like{"UPPER(" + key + ")", util.ToUpperASCII(value)}
+ }
+ return builder.Like{"UPPER(" + key + ")", strings.ToUpper(value)}
+}
diff --git a/models/db/engine.go b/models/db/engine.go
index 2c329300e3..f0c9ec46e9 100755
--- a/models/db/engine.go
+++ b/models/db/engine.go
@@ -225,7 +225,7 @@ func NamesToBean(names ...string) ([]interface{}, error) {
for _, name := range names {
bean, ok := beanMap[strings.ToLower(strings.TrimSpace(name))]
if !ok {
- return nil, fmt.Errorf("No table found that matches: %s", name)
+ return nil, fmt.Errorf("no table found that matches: %s", name)
}
if !gotBean[bean] {
beans = append(beans, bean)
diff --git a/models/db/engine_test.go b/models/db/engine_test.go
index 41279c5005..c26d94c340 100644
--- a/models/db/engine_test.go
+++ b/models/db/engine_test.go
@@ -5,7 +5,6 @@
package db_test
import (
- "os"
"path/filepath"
"testing"
@@ -20,8 +19,7 @@ import (
func TestDumpDatabase(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- dir, err := os.MkdirTemp(os.TempDir(), "dump")
- assert.NoError(t, err)
+ dir := t.TempDir()
type Version struct {
ID int64 `xorm:"pk autoincr"`
diff --git a/models/db/error.go b/models/db/error.go
index 6557229943..9577fa55db 100644
--- a/models/db/error.go
+++ b/models/db/error.go
@@ -6,6 +6,8 @@ package db
import (
"fmt"
+
+ "code.gitea.io/gitea/modules/util"
)
// ErrCancelled represents an error due to context cancellation
@@ -45,7 +47,8 @@ func (err ErrSSHDisabled) Error() string {
// ErrNotExist represents a non-exist error.
type ErrNotExist struct {
- ID int64
+ Resource string
+ ID int64
}
// IsErrNotExist checks if an error is an ErrNotExist
@@ -55,5 +58,18 @@ func IsErrNotExist(err error) bool {
}
func (err ErrNotExist) Error() string {
- return fmt.Sprintf("record does not exist [id: %d]", err.ID)
+ name := "record"
+ if err.Resource != "" {
+ name = err.Resource
+ }
+
+ if err.ID != 0 {
+ return fmt.Sprintf("%s does not exist [id: %d]", name, err.ID)
+ }
+ return fmt.Sprintf("%s does not exist", name)
+}
+
+// Unwrap unwraps this as a ErrNotExist err
+func (err ErrNotExist) Unwrap() error {
+ return util.ErrNotExist
}
diff --git a/models/db/index.go b/models/db/index.go
index 673c382b27..58a976ad52 100644
--- a/models/db/index.go
+++ b/models/db/index.go
@@ -8,45 +8,15 @@ import (
"context"
"errors"
"fmt"
-
- "code.gitea.io/gitea/modules/setting"
)
// ResourceIndex represents a resource index which could be used as issue/release and others
-// We can create different tables i.e. issue_index, release_index and etc.
+// We can create different tables i.e. issue_index, release_index, etc.
type ResourceIndex struct {
GroupID int64 `xorm:"pk"`
MaxIndex int64 `xorm:"index"`
}
-// UpsertResourceIndex the function will not return until it acquires the lock or receives an error.
-func UpsertResourceIndex(ctx context.Context, tableName string, groupID int64) (err error) {
- // An atomic UPSERT operation (INSERT/UPDATE) is the only operation
- // that ensures that the key is actually locked.
- switch {
- case setting.Database.UseSQLite3 || setting.Database.UsePostgreSQL:
- _, err = Exec(ctx, fmt.Sprintf("INSERT INTO %s (group_id, max_index) "+
- "VALUES (?,1) ON CONFLICT (group_id) DO UPDATE SET max_index = %s.max_index+1",
- tableName, tableName), groupID)
- case setting.Database.UseMySQL:
- _, err = Exec(ctx, fmt.Sprintf("INSERT INTO %s (group_id, max_index) "+
- "VALUES (?,1) ON DUPLICATE KEY UPDATE max_index = max_index+1", tableName),
- groupID)
- case setting.Database.UseMSSQL:
- // https://weblogs.sqlteam.com/dang/2009/01/31/upsert-race-condition-with-merge/
- _, err = Exec(ctx, fmt.Sprintf("MERGE %s WITH (HOLDLOCK) as target "+
- "USING (SELECT ? AS group_id) AS src "+
- "ON src.group_id = target.group_id "+
- "WHEN MATCHED THEN UPDATE SET target.max_index = target.max_index+1 "+
- "WHEN NOT MATCHED THEN INSERT (group_id, max_index) "+
- "VALUES (src.group_id, 1);", tableName),
- groupID)
- default:
- return fmt.Errorf("database type not supported")
- }
- return err
-}
-
var (
// ErrResouceOutdated represents an error when request resource outdated
ErrResouceOutdated = errors.New("resource outdated")
@@ -59,53 +29,85 @@ const (
MaxDupIndexAttempts = 3
)
-// GetNextResourceIndex retried 3 times to generate a resource index
-func GetNextResourceIndex(tableName string, groupID int64) (int64, error) {
- for i := 0; i < MaxDupIndexAttempts; i++ {
- idx, err := getNextResourceIndex(tableName, groupID)
- if err == ErrResouceOutdated {
- continue
- }
- if err != nil {
- return 0, err
- }
- return idx, nil
+// SyncMaxResourceIndex sync the max index with the resource
+func SyncMaxResourceIndex(ctx context.Context, tableName string, groupID, maxIndex int64) (err error) {
+ e := GetEngine(ctx)
+
+ // try to update the max_index and acquire the write-lock for the record
+ res, err := e.Exec(fmt.Sprintf("UPDATE %s SET max_index=? WHERE group_id=? AND max_index", tableName), maxIndex, groupID, maxIndex)
+ if err != nil {
+ return err
}
- return 0, ErrGetResourceIndexFailed
+ affected, err := res.RowsAffected()
+ if err != nil {
+ return err
+ }
+ if affected == 0 {
+ // if nothing is updated, the record might not exist or might be larger, it's safe to try to insert it again and then check whether the record exists
+ _, errIns := e.Exec(fmt.Sprintf("INSERT INTO %s (group_id, max_index) VALUES (?, ?)", tableName), groupID, maxIndex)
+ var savedIdx int64
+ has, err := e.SQL(fmt.Sprintf("SELECT max_index FROM %s WHERE group_id=?", tableName), groupID).Get(&savedIdx)
+ if err != nil {
+ return err
+ }
+ // if the record still doesn't exist, there must be some errors (insert error)
+ if !has {
+ if errIns == nil {
+ return errors.New("impossible error when SyncMaxResourceIndex, insert succeeded but no record is saved")
+ }
+ return errIns
+ }
+ }
+ return nil
}
-// DeleteResouceIndex delete resource index
-func DeleteResouceIndex(ctx context.Context, tableName string, groupID int64) error {
- _, err := Exec(ctx, fmt.Sprintf("DELETE FROM %s WHERE group_id=?", tableName), groupID)
- return err
-}
+// GetNextResourceIndex generates a resource index, it must run in the same transaction where the resource is created
+func GetNextResourceIndex(ctx context.Context, tableName string, groupID int64) (int64, error) {
+ e := GetEngine(ctx)
-// getNextResourceIndex return the next index
-func getNextResourceIndex(tableName string, groupID int64) (int64, error) {
- ctx, commiter, err := TxContext()
+ // try to update the max_index to next value, and acquire the write-lock for the record
+ res, err := e.Exec(fmt.Sprintf("UPDATE %s SET max_index=max_index+1 WHERE group_id=?", tableName), groupID)
if err != nil {
return 0, err
}
- defer commiter.Close()
- var preIdx int64
- if _, err := GetEngine(ctx).SQL(fmt.Sprintf("SELECT max_index FROM %s WHERE group_id = ?", tableName), groupID).Get(&preIdx); err != nil {
+ affected, err := res.RowsAffected()
+ if err != nil {
return 0, err
}
-
- if err := UpsertResourceIndex(ctx, tableName, groupID); err != nil {
- return 0, err
+ if affected == 0 {
+ // this slow path is only for the first time of creating a resource index
+ _, errIns := e.Exec(fmt.Sprintf("INSERT INTO %s (group_id, max_index) VALUES (?, 0)", tableName), groupID)
+ res, err = e.Exec(fmt.Sprintf("UPDATE %s SET max_index=max_index+1 WHERE group_id=?", tableName), groupID)
+ if err != nil {
+ return 0, err
+ }
+ affected, err = res.RowsAffected()
+ if err != nil {
+ return 0, err
+ }
+ // if the update still can not update any records, the record must not exist and there must be some errors (insert error)
+ if affected == 0 {
+ if errIns == nil {
+ return 0, errors.New("impossible error when GetNextResourceIndex, insert and update both succeeded but no record is updated")
+ }
+ return 0, errIns
+ }
}
- var curIdx int64
- has, err := GetEngine(ctx).SQL(fmt.Sprintf("SELECT max_index FROM %s WHERE group_id = ? AND max_index=?", tableName), groupID, preIdx+1).Get(&curIdx)
+ // now, the new index is in database (protected by the transaction and write-lock)
+ var newIdx int64
+ has, err := e.SQL(fmt.Sprintf("SELECT max_index FROM %s WHERE group_id=?", tableName), groupID).Get(&newIdx)
if err != nil {
return 0, err
}
if !has {
- return 0, ErrResouceOutdated
+ return 0, errors.New("impossible error when GetNextResourceIndex, upsert succeeded but no record can be selected")
}
- if err := commiter.Commit(); err != nil {
- return 0, err
- }
- return curIdx, nil
+ return newIdx, nil
+}
+
+// DeleteResourceIndex delete resource index
+func DeleteResourceIndex(ctx context.Context, tableName string, groupID int64) error {
+ _, err := Exec(ctx, fmt.Sprintf("DELETE FROM %s WHERE group_id=?", tableName), groupID)
+ return err
}
diff --git a/models/db/index_test.go b/models/db/index_test.go
new file mode 100644
index 0000000000..1ea30e2b60
--- /dev/null
+++ b/models/db/index_test.go
@@ -0,0 +1,127 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package db_test
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "testing"
+
+ "code.gitea.io/gitea/models/db"
+ "code.gitea.io/gitea/models/unittest"
+
+ "github.com/stretchr/testify/assert"
+)
+
+type TestIndex db.ResourceIndex
+
+func getCurrentResourceIndex(ctx context.Context, tableName string, groupID int64) (int64, error) {
+ e := db.GetEngine(ctx)
+ var idx int64
+ has, err := e.SQL(fmt.Sprintf("SELECT max_index FROM %s WHERE group_id=?", tableName), groupID).Get(&idx)
+ if err != nil {
+ return 0, err
+ }
+ if !has {
+ return 0, errors.New("no record")
+ }
+ return idx, nil
+}
+
+func TestSyncMaxResourceIndex(t *testing.T) {
+ assert.NoError(t, unittest.PrepareTestDatabase())
+ xe := unittest.GetXORMEngine()
+ assert.NoError(t, xe.Sync(&TestIndex{}))
+
+ err := db.SyncMaxResourceIndex(db.DefaultContext, "test_index", 10, 51)
+ assert.NoError(t, err)
+
+ // sync new max index
+ maxIndex, err := getCurrentResourceIndex(db.DefaultContext, "test_index", 10)
+ assert.NoError(t, err)
+ assert.EqualValues(t, 51, maxIndex)
+
+ // smaller index doesn't change
+ err = db.SyncMaxResourceIndex(db.DefaultContext, "test_index", 10, 30)
+ assert.NoError(t, err)
+ maxIndex, err = getCurrentResourceIndex(db.DefaultContext, "test_index", 10)
+ assert.NoError(t, err)
+ assert.EqualValues(t, 51, maxIndex)
+
+ // larger index changes
+ err = db.SyncMaxResourceIndex(db.DefaultContext, "test_index", 10, 62)
+ assert.NoError(t, err)
+ maxIndex, err = getCurrentResourceIndex(db.DefaultContext, "test_index", 10)
+ assert.NoError(t, err)
+ assert.EqualValues(t, 62, maxIndex)
+
+ // commit transaction
+ err = db.WithTx(func(ctx context.Context) error {
+ err = db.SyncMaxResourceIndex(ctx, "test_index", 10, 73)
+ assert.NoError(t, err)
+ maxIndex, err = getCurrentResourceIndex(ctx, "test_index", 10)
+ assert.NoError(t, err)
+ assert.EqualValues(t, 73, maxIndex)
+ return nil
+ })
+ assert.NoError(t, err)
+ maxIndex, err = getCurrentResourceIndex(db.DefaultContext, "test_index", 10)
+ assert.NoError(t, err)
+ assert.EqualValues(t, 73, maxIndex)
+
+ // rollback transaction
+ err = db.WithTx(func(ctx context.Context) error {
+ err = db.SyncMaxResourceIndex(ctx, "test_index", 10, 84)
+ maxIndex, err = getCurrentResourceIndex(ctx, "test_index", 10)
+ assert.NoError(t, err)
+ assert.EqualValues(t, 84, maxIndex)
+ return errors.New("test rollback")
+ })
+ assert.Error(t, err)
+ maxIndex, err = getCurrentResourceIndex(db.DefaultContext, "test_index", 10)
+ assert.NoError(t, err)
+ assert.EqualValues(t, 73, maxIndex) // the max index doesn't change because the transaction was rolled back
+}
+
+func TestGetNextResourceIndex(t *testing.T) {
+ assert.NoError(t, unittest.PrepareTestDatabase())
+ xe := unittest.GetXORMEngine()
+ assert.NoError(t, xe.Sync(&TestIndex{}))
+
+ // create a new record
+ maxIndex, err := db.GetNextResourceIndex(db.DefaultContext, "test_index", 20)
+ assert.NoError(t, err)
+ assert.EqualValues(t, 1, maxIndex)
+
+ // increase the existing record
+ maxIndex, err = db.GetNextResourceIndex(db.DefaultContext, "test_index", 20)
+ assert.NoError(t, err)
+ assert.EqualValues(t, 2, maxIndex)
+
+ // commit transaction
+ err = db.WithTx(func(ctx context.Context) error {
+ maxIndex, err = db.GetNextResourceIndex(ctx, "test_index", 20)
+ assert.NoError(t, err)
+ assert.EqualValues(t, 3, maxIndex)
+ return nil
+ })
+ assert.NoError(t, err)
+ maxIndex, err = getCurrentResourceIndex(db.DefaultContext, "test_index", 20)
+ assert.NoError(t, err)
+ assert.EqualValues(t, 3, maxIndex)
+
+ // rollback transaction
+ err = db.WithTx(func(ctx context.Context) error {
+ maxIndex, err = db.GetNextResourceIndex(ctx, "test_index", 20)
+ assert.NoError(t, err)
+ assert.EqualValues(t, 4, maxIndex)
+ return errors.New("test rollback")
+ })
+ assert.Error(t, err)
+ maxIndex, err = getCurrentResourceIndex(db.DefaultContext, "test_index", 20)
+ assert.NoError(t, err)
+ assert.EqualValues(t, 3, maxIndex) // the max index doesn't change because the transaction was rolled back
+}
diff --git a/models/db/iterate.go b/models/db/iterate.go
new file mode 100644
index 0000000000..3d4fa06eeb
--- /dev/null
+++ b/models/db/iterate.go
@@ -0,0 +1,34 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package db
+
+import (
+ "context"
+
+ "code.gitea.io/gitea/modules/setting"
+)
+
+// IterateObjects iterate all the Bean object
+func IterateObjects[Object any](ctx context.Context, f func(repo *Object) error) error {
+ var start int
+ batchSize := setting.Database.IterateBufferSize
+ sess := GetEngine(ctx)
+ for {
+ repos := make([]*Object, 0, batchSize)
+ if err := sess.Limit(batchSize, start).Find(&repos); err != nil {
+ return err
+ }
+ if len(repos) == 0 {
+ return nil
+ }
+ start += len(repos)
+
+ for _, repo := range repos {
+ if err := f(repo); err != nil {
+ return err
+ }
+ }
+ }
+}
diff --git a/models/db/name.go b/models/db/name.go
index 9c9d18f184..a05d1a789b 100644
--- a/models/db/name.go
+++ b/models/db/name.go
@@ -5,16 +5,17 @@
package db
import (
- "errors"
"fmt"
"regexp"
"strings"
"unicode/utf8"
+
+ "code.gitea.io/gitea/modules/util"
)
var (
// ErrNameEmpty name is empty error
- ErrNameEmpty = errors.New("Name is empty")
+ ErrNameEmpty = util.SilentWrap{Message: "name is empty", Err: util.ErrInvalidArgument}
// AlphaDashDotPattern characters prohibited in a user name (anything except A-Za-z0-9_.-)
AlphaDashDotPattern = regexp.MustCompile(`[^\w-\.]`)
@@ -35,6 +36,11 @@ func (err ErrNameReserved) Error() string {
return fmt.Sprintf("name is reserved [name: %s]", err.Name)
}
+// Unwrap unwraps this as a ErrInvalid err
+func (err ErrNameReserved) Unwrap() error {
+ return util.ErrInvalidArgument
+}
+
// ErrNamePatternNotAllowed represents a "pattern not allowed" error.
type ErrNamePatternNotAllowed struct {
Pattern string
@@ -50,6 +56,11 @@ func (err ErrNamePatternNotAllowed) Error() string {
return fmt.Sprintf("name pattern is not allowed [pattern: %s]", err.Pattern)
}
+// Unwrap unwraps this as a ErrInvalid err
+func (err ErrNamePatternNotAllowed) Unwrap() error {
+ return util.ErrInvalidArgument
+}
+
// ErrNameCharsNotAllowed represents a "character not allowed in name" error.
type ErrNameCharsNotAllowed struct {
Name string
@@ -62,7 +73,12 @@ func IsErrNameCharsNotAllowed(err error) bool {
}
func (err ErrNameCharsNotAllowed) Error() string {
- return fmt.Sprintf("User name is invalid [%s]: must be valid alpha or numeric or dash(-_) or dot characters", err.Name)
+ return fmt.Sprintf("name is invalid [%s]: must be valid alpha or numeric or dash(-_) or dot characters", err.Name)
+}
+
+// Unwrap unwraps this as a ErrInvalid err
+func (err ErrNameCharsNotAllowed) Unwrap() error {
+ return util.ErrInvalidArgument
}
// IsUsableName checks if name is reserved or pattern of name is not allowed
diff --git a/models/error.go b/models/error.go
index 3c617904f8..f4c4bc8f67 100644
--- a/models/error.go
+++ b/models/error.go
@@ -10,6 +10,7 @@ import (
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/git"
+ "code.gitea.io/gitea/modules/util"
)
// ErrUserOwnRepos represents a "UserOwnRepos" kind of error.
@@ -57,101 +58,14 @@ func (err ErrUserOwnPackages) Error() string {
return fmt.Sprintf("user still has ownership of packages [uid: %d]", err.UID)
}
-// __ __.__ __ .__
-// / \ / \__| | _|__|
-// \ \/\/ / | |/ / |
-// \ /| | <| |
-// \__/\ / |__|__|_ \__|
-// \/ \/
-
-// ErrWikiAlreadyExist represents a "WikiAlreadyExist" kind of error.
-type ErrWikiAlreadyExist struct {
- Title string
-}
-
-// IsErrWikiAlreadyExist checks if an error is an ErrWikiAlreadyExist.
-func IsErrWikiAlreadyExist(err error) bool {
- _, ok := err.(ErrWikiAlreadyExist)
- return ok
-}
-
-func (err ErrWikiAlreadyExist) Error() string {
- return fmt.Sprintf("wiki page already exists [title: %s]", err.Title)
-}
-
-// ErrWikiReservedName represents a reserved name error.
-type ErrWikiReservedName struct {
- Title string
-}
-
-// IsErrWikiReservedName checks if an error is an ErrWikiReservedName.
-func IsErrWikiReservedName(err error) bool {
- _, ok := err.(ErrWikiReservedName)
- return ok
-}
-
-func (err ErrWikiReservedName) Error() string {
- return fmt.Sprintf("wiki title is reserved: %s", err.Title)
-}
-
-// ErrWikiInvalidFileName represents an invalid wiki file name.
-type ErrWikiInvalidFileName struct {
- FileName string
-}
-
-// IsErrWikiInvalidFileName checks if an error is an ErrWikiInvalidFileName.
-func IsErrWikiInvalidFileName(err error) bool {
- _, ok := err.(ErrWikiInvalidFileName)
- return ok
-}
-
-func (err ErrWikiInvalidFileName) Error() string {
- return fmt.Sprintf("Invalid wiki filename: %s", err.FileName)
-}
-
-// _____ ___________ __
-// / _ \ ____ ____ ____ ______ _____\__ ___/___ | | __ ____ ____
-// / /_\ \_/ ___\/ ___\/ __ \ / ___// ___/ | | / _ \| |/ // __ \ / \
-// / | \ \__\ \__\ ___/ \___ \ \___ \ | |( <_> ) <\ ___/| | \
-// \____|__ /\___ >___ >___ >____ >____ > |____| \____/|__|_ \\___ >___| /
-// \/ \/ \/ \/ \/ \/ \/ \/ \/
-
-// ErrAccessTokenNotExist represents a "AccessTokenNotExist" kind of error.
-type ErrAccessTokenNotExist struct {
- Token string
-}
-
-// IsErrAccessTokenNotExist checks if an error is a ErrAccessTokenNotExist.
-func IsErrAccessTokenNotExist(err error) bool {
- _, ok := err.(ErrAccessTokenNotExist)
- return ok
-}
-
-func (err ErrAccessTokenNotExist) Error() string {
- return fmt.Sprintf("access token does not exist [sha: %s]", err.Token)
-}
-
-// ErrAccessTokenEmpty represents a "AccessTokenEmpty" kind of error.
-type ErrAccessTokenEmpty struct{}
-
-// IsErrAccessTokenEmpty checks if an error is a ErrAccessTokenEmpty.
-func IsErrAccessTokenEmpty(err error) bool {
- _, ok := err.(ErrAccessTokenEmpty)
- return ok
-}
-
-func (err ErrAccessTokenEmpty) Error() string {
- return "access token is empty"
-}
-
// ErrNoPendingRepoTransfer is an error type for repositories without a pending
// transfer request
type ErrNoPendingRepoTransfer struct {
RepoID int64
}
-func (e ErrNoPendingRepoTransfer) Error() string {
- return fmt.Sprintf("repository doesn't have a pending transfer [repo_id: %d]", e.RepoID)
+func (err ErrNoPendingRepoTransfer) Error() string {
+ return fmt.Sprintf("repository doesn't have a pending transfer [repo_id: %d]", err.RepoID)
}
// IsErrNoPendingTransfer is an error type when a repository has no pending
@@ -161,6 +75,10 @@ func IsErrNoPendingTransfer(err error) bool {
return ok
}
+func (err ErrNoPendingRepoTransfer) Unwrap() error {
+ return util.ErrNotExist
+}
+
// ErrRepoTransferInProgress represents the state of a repository that has an
// ongoing transfer
type ErrRepoTransferInProgress struct {
@@ -178,21 +96,8 @@ func (err ErrRepoTransferInProgress) Error() string {
return fmt.Sprintf("repository is already being transferred [uname: %s, name: %s]", err.Uname, err.Name)
}
-// ErrForkAlreadyExist represents a "ForkAlreadyExist" kind of error.
-type ErrForkAlreadyExist struct {
- Uname string
- RepoName string
- ForkName string
-}
-
-// IsErrForkAlreadyExist checks if an error is an ErrForkAlreadyExist.
-func IsErrForkAlreadyExist(err error) bool {
- _, ok := err.(ErrForkAlreadyExist)
- return ok
-}
-
-func (err ErrForkAlreadyExist) Error() string {
- return fmt.Sprintf("repository is already forked by user [uname: %s, repo path: %s, fork path: %s]", err.Uname, err.RepoName, err.ForkName)
+func (err ErrRepoTransferInProgress) Unwrap() error {
+ return util.ErrAlreadyExist
}
// ErrInvalidCloneAddr represents a "InvalidCloneAddr" kind of error.
@@ -228,6 +133,10 @@ func (err *ErrInvalidCloneAddr) Error() string {
return fmt.Sprintf("migration/cloning from '%s' is not allowed", err.Host)
}
+func (err *ErrInvalidCloneAddr) Unwrap() error {
+ return util.ErrInvalidArgument
+}
+
// ErrUpdateTaskNotExist represents a "UpdateTaskNotExist" kind of error.
type ErrUpdateTaskNotExist struct {
UUID string
@@ -243,35 +152,8 @@ func (err ErrUpdateTaskNotExist) Error() string {
return fmt.Sprintf("update task does not exist [uuid: %s]", err.UUID)
}
-// ErrReleaseAlreadyExist represents a "ReleaseAlreadyExist" kind of error.
-type ErrReleaseAlreadyExist struct {
- TagName string
-}
-
-// IsErrReleaseAlreadyExist checks if an error is a ErrReleaseAlreadyExist.
-func IsErrReleaseAlreadyExist(err error) bool {
- _, ok := err.(ErrReleaseAlreadyExist)
- return ok
-}
-
-func (err ErrReleaseAlreadyExist) Error() string {
- return fmt.Sprintf("release tag already exist [tag_name: %s]", err.TagName)
-}
-
-// ErrReleaseNotExist represents a "ReleaseNotExist" kind of error.
-type ErrReleaseNotExist struct {
- ID int64
- TagName string
-}
-
-// IsErrReleaseNotExist checks if an error is a ErrReleaseNotExist.
-func IsErrReleaseNotExist(err error) bool {
- _, ok := err.(ErrReleaseNotExist)
- return ok
-}
-
-func (err ErrReleaseNotExist) Error() string {
- return fmt.Sprintf("release tag does not exist [id: %d, tag_name: %s]", err.ID, err.TagName)
+func (err ErrUpdateTaskNotExist) Unwrap() error {
+ return util.ErrNotExist
}
// ErrInvalidTagName represents a "InvalidTagName" kind of error.
@@ -289,6 +171,10 @@ func (err ErrInvalidTagName) Error() string {
return fmt.Sprintf("release tag name is not valid [tag_name: %s]", err.TagName)
}
+func (err ErrInvalidTagName) Unwrap() error {
+ return util.ErrInvalidArgument
+}
+
// ErrProtectedTagName represents a "ProtectedTagName" kind of error.
type ErrProtectedTagName struct {
TagName string
@@ -304,6 +190,10 @@ func (err ErrProtectedTagName) Error() string {
return fmt.Sprintf("release tag name is protected [tag_name: %s]", err.TagName)
}
+func (err ErrProtectedTagName) Unwrap() error {
+ return util.ErrPermissionDenied
+}
+
// ErrRepoFileAlreadyExists represents a "RepoFileAlreadyExist" kind of error.
type ErrRepoFileAlreadyExists struct {
Path string
@@ -319,6 +209,10 @@ func (err ErrRepoFileAlreadyExists) Error() string {
return fmt.Sprintf("repository file already exists [path: %s]", err.Path)
}
+func (err ErrRepoFileAlreadyExists) Unwrap() error {
+ return util.ErrAlreadyExist
+}
+
// ErrRepoFileDoesNotExist represents a "RepoFileDoesNotExist" kind of error.
type ErrRepoFileDoesNotExist struct {
Path string
@@ -335,6 +229,10 @@ func (err ErrRepoFileDoesNotExist) Error() string {
return fmt.Sprintf("repository file does not exist [path: %s]", err.Path)
}
+func (err ErrRepoFileDoesNotExist) Unwrap() error {
+ return util.ErrNotExist
+}
+
// ErrFilenameInvalid represents a "FilenameInvalid" kind of error.
type ErrFilenameInvalid struct {
Path string
@@ -350,6 +248,10 @@ func (err ErrFilenameInvalid) Error() string {
return fmt.Sprintf("path contains a malformed path component [path: %s]", err.Path)
}
+func (err ErrFilenameInvalid) Unwrap() error {
+ return util.ErrInvalidArgument
+}
+
// ErrUserCannotCommit represents "UserCannotCommit" kind of error.
type ErrUserCannotCommit struct {
UserName string
@@ -365,6 +267,10 @@ func (err ErrUserCannotCommit) Error() string {
return fmt.Sprintf("user cannot commit to repo [user: %s]", err.UserName)
}
+func (err ErrUserCannotCommit) Unwrap() error {
+ return util.ErrPermissionDenied
+}
+
// ErrFilePathInvalid represents a "FilePathInvalid" kind of error.
type ErrFilePathInvalid struct {
Message string
@@ -386,6 +292,10 @@ func (err ErrFilePathInvalid) Error() string {
return fmt.Sprintf("path is invalid [path: %s]", err.Path)
}
+func (err ErrFilePathInvalid) Unwrap() error {
+ return util.ErrInvalidArgument
+}
+
// ErrFilePathProtected represents a "FilePathProtected" kind of error.
type ErrFilePathProtected struct {
Message string
@@ -405,6 +315,10 @@ func (err ErrFilePathProtected) Error() string {
return fmt.Sprintf("path is protected and can not be changed [path: %s]", err.Path)
}
+func (err ErrFilePathProtected) Unwrap() error {
+ return util.ErrPermissionDenied
+}
+
// __________ .__
// \______ \____________ ____ ____ | |__
// | | _/\_ __ \__ \ / \_/ ___\| | \
@@ -427,6 +341,10 @@ func (err ErrBranchDoesNotExist) Error() string {
return fmt.Sprintf("branch does not exist [name: %s]", err.BranchName)
}
+func (err ErrBranchDoesNotExist) Unwrap() error {
+ return util.ErrNotExist
+}
+
// ErrBranchAlreadyExists represents an error that branch with such name already exists.
type ErrBranchAlreadyExists struct {
BranchName string
@@ -442,6 +360,10 @@ func (err ErrBranchAlreadyExists) Error() string {
return fmt.Sprintf("branch already exists [name: %s]", err.BranchName)
}
+func (err ErrBranchAlreadyExists) Unwrap() error {
+ return util.ErrAlreadyExist
+}
+
// ErrBranchNameConflict represents an error that branch name conflicts with other branch.
type ErrBranchNameConflict struct {
BranchName string
@@ -457,6 +379,10 @@ func (err ErrBranchNameConflict) Error() string {
return fmt.Sprintf("branch conflicts with existing branch [name: %s]", err.BranchName)
}
+func (err ErrBranchNameConflict) Unwrap() error {
+ return util.ErrAlreadyExist
+}
+
// ErrBranchesEqual represents an error that branch name conflicts with other branch.
type ErrBranchesEqual struct {
BaseBranchName string
@@ -473,6 +399,10 @@ func (err ErrBranchesEqual) Error() string {
return fmt.Sprintf("branches are equal [head: %sm base: %s]", err.HeadBranchName, err.BaseBranchName)
}
+func (err ErrBranchesEqual) Unwrap() error {
+ return util.ErrInvalidArgument
+}
+
// ErrDisallowedToMerge represents an error that a branch is protected and the current user is not allowed to modify it.
type ErrDisallowedToMerge struct {
Reason string
@@ -488,6 +418,10 @@ func (err ErrDisallowedToMerge) Error() string {
return fmt.Sprintf("not allowed to merge [reason: %s]", err.Reason)
}
+func (err ErrDisallowedToMerge) Unwrap() error {
+ return util.ErrPermissionDenied
+}
+
// ErrTagAlreadyExists represents an error that tag with such name already exists.
type ErrTagAlreadyExists struct {
TagName string
@@ -503,6 +437,10 @@ func (err ErrTagAlreadyExists) Error() string {
return fmt.Sprintf("tag already exists [name: %s]", err.TagName)
}
+func (err ErrTagAlreadyExists) Unwrap() error {
+ return util.ErrAlreadyExist
+}
+
// ErrSHADoesNotMatch represents a "SHADoesNotMatch" kind of error.
type ErrSHADoesNotMatch struct {
Path string
@@ -535,6 +473,10 @@ func (err ErrSHANotFound) Error() string {
return fmt.Sprintf("sha not found [%s]", err.SHA)
}
+func (err ErrSHANotFound) Unwrap() error {
+ return util.ErrNotExist
+}
+
// ErrCommitIDDoesNotMatch represents a "CommitIDDoesNotMatch" kind of error.
type ErrCommitIDDoesNotMatch struct {
GivenCommitID string
@@ -581,6 +523,10 @@ func (err ErrInvalidMergeStyle) Error() string {
err.ID, err.Style)
}
+func (err ErrInvalidMergeStyle) Unwrap() error {
+ return util.ErrInvalidArgument
+}
+
// ErrMergeConflicts represents an error if merging fails with a conflict
type ErrMergeConflicts struct {
Style repo_model.MergeStyle
@@ -657,71 +603,3 @@ func (err ErrPullRequestHasMerged) Error() string {
return fmt.Sprintf("pull request has merged [id: %d, issue_id: %d, head_repo_id: %d, base_repo_id: %d, head_branch: %s, base_branch: %s]",
err.ID, err.IssueID, err.HeadRepoID, err.BaseRepoID, err.HeadBranch, err.BaseBranch)
}
-
-// _________ __ __ .__
-// / _____// |_ ____ ________ _ _______ _/ |_ ____ | |__
-// \_____ \\ __\/ _ \\____ \ \/ \/ /\__ \\ __\/ ___\| | \
-// / \| | ( <_> ) |_> > / / __ \| | \ \___| Y \
-// /_______ /|__| \____/| __/ \/\_/ (____ /__| \___ >___| /
-// \/ |__| \/ \/ \/
-
-// ErrStopwatchNotExist represents a "Stopwatch Not Exist" kind of error.
-type ErrStopwatchNotExist struct {
- ID int64
-}
-
-// IsErrStopwatchNotExist checks if an error is a ErrStopwatchNotExist.
-func IsErrStopwatchNotExist(err error) bool {
- _, ok := err.(ErrStopwatchNotExist)
- return ok
-}
-
-func (err ErrStopwatchNotExist) Error() string {
- return fmt.Sprintf("stopwatch does not exist [id: %d]", err.ID)
-}
-
-// ___________ __ .______________.__
-// \__ ___/___________ ____ | | __ ____ __| _/\__ ___/|__| _____ ____
-// | | \_ __ \__ \ _/ ___\| |/ // __ \ / __ | | | | |/ \_/ __ \
-// | | | | \// __ \\ \___| <\ ___// /_/ | | | | | Y Y \ ___/
-// |____| |__| (____ /\___ >__|_ \\___ >____ | |____| |__|__|_| /\___ >
-// \/ \/ \/ \/ \/ \/ \/
-
-// ErrTrackedTimeNotExist represents a "TrackedTime Not Exist" kind of error.
-type ErrTrackedTimeNotExist struct {
- ID int64
-}
-
-// IsErrTrackedTimeNotExist checks if an error is a ErrTrackedTimeNotExist.
-func IsErrTrackedTimeNotExist(err error) bool {
- _, ok := err.(ErrTrackedTimeNotExist)
- return ok
-}
-
-func (err ErrTrackedTimeNotExist) Error() string {
- return fmt.Sprintf("tracked time does not exist [id: %d]", err.ID)
-}
-
-// ____ ___ .__ .___
-// | | \______ | | _________ __| _/
-// | | /\____ \| | / _ \__ \ / __ |
-// | | / | |_> > |_( <_> ) __ \_/ /_/ |
-// |______/ | __/|____/\____(____ /\____ |
-// |__| \/ \/
-//
-
-// ErrUploadNotExist represents a "UploadNotExist" kind of error.
-type ErrUploadNotExist struct {
- ID int64
- UUID string
-}
-
-// IsErrUploadNotExist checks if an error is a ErrUploadNotExist.
-func IsErrUploadNotExist(err error) bool {
- _, ok := err.(ErrUploadNotExist)
- return ok
-}
-
-func (err ErrUploadNotExist) Error() string {
- return fmt.Sprintf("attachment does not exist [id: %d, uuid: %s]", err.ID, err.UUID)
-}
diff --git a/models/fixtures/access.yml b/models/fixtures/access.yml
index 4027e5fe92..446502843e 100644
--- a/models/fixtures/access.yml
+++ b/models/fixtures/access.yml
@@ -124,3 +124,15 @@
repo_id: 24
mode: 1
+-
+ id: 22
+ user_id: 31
+ repo_id: 27
+ mode: 4
+
+-
+ id: 23
+ user_id: 31
+ repo_id: 28
+ mode: 4
+
diff --git a/models/fixtures/attachment.yml b/models/fixtures/attachment.yml
index 8612f6ece7..9ad43fa2b7 100644
--- a/models/fixtures/attachment.yml
+++ b/models/fixtures/attachment.yml
@@ -3,9 +3,12 @@
uuid: a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11
repo_id: 1
issue_id: 1
+ release_id: 0
+ uploader_id: 0
comment_id: 0
name: attach1
download_count: 0
+ size: 0
created_unix: 946684800
-
@@ -13,9 +16,12 @@
uuid: a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a12
repo_id: 2
issue_id: 4
+ release_id: 0
+ uploader_id: 0
comment_id: 0
name: attach2
download_count: 1
+ size: 0
created_unix: 946684800
-
@@ -23,9 +29,12 @@
uuid: a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a13
repo_id: 1
issue_id: 2
+ release_id: 0
+ uploader_id: 0
comment_id: 1
name: attach1
download_count: 0
+ size: 0
created_unix: 946684800
-
@@ -33,9 +42,12 @@
uuid: a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a14
repo_id: 1
issue_id: 3
+ release_id: 0
+ uploader_id: 0
comment_id: 1
name: attach2
download_count: 1
+ size: 0
created_unix: 946684800
-
@@ -43,9 +55,12 @@
uuid: a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a15
repo_id: 2
issue_id: 4
+ release_id: 0
+ uploader_id: 0
comment_id: 0
name: attach1
download_count: 0
+ size: 0
created_unix: 946684800
-
@@ -53,9 +68,12 @@
uuid: a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a16
repo_id: 1
issue_id: 5
+ release_id: 0
+ uploader_id: 0
comment_id: 2
name: attach1
download_count: 0
+ size: 0
created_unix: 946684800
-
@@ -63,9 +81,12 @@
uuid: a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a17
repo_id: 1
issue_id: 5
+ release_id: 0
+ uploader_id: 0
comment_id: 2
name: attach1
download_count: 0
+ size: 0
created_unix: 946684800
-
@@ -73,34 +94,49 @@
uuid: a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a18
repo_id: 3
issue_id: 6
+ release_id: 0
+ uploader_id: 0
comment_id: 0
name: attach1
download_count: 0
+ size: 0
created_unix: 946684800
-
id: 9
uuid: a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a19
repo_id: 1
+ issue_id: 0
release_id: 1
+ uploader_id: 0
+ comment_id: 0
name: attach1
download_count: 0
+ size: 0
created_unix: 946684800
-
id: 10
uuid: a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a20
repo_id: 0 # TestGetAttachment/NotLinked
+ issue_id: 0
+ release_id: 0
uploader_id: 8
+ comment_id: 0
name: attach1
download_count: 0
+ size: 0
created_unix: 946684800
-
id: 11
uuid: a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a21
repo_id: 40
+ issue_id: 0
release_id: 2
+ uploader_id: 0
+ comment_id: 0
name: attach1
download_count: 0
+ size: 0
created_unix: 946684800
diff --git a/models/fixtures/follow.yml b/models/fixtures/follow.yml
index 480fa065c7..b8d35828bf 100644
--- a/models/fixtures/follow.yml
+++ b/models/fixtures/follow.yml
@@ -12,3 +12,8 @@
id: 3
user_id: 2
follow_id: 8
+
+-
+ id: 4
+ user_id: 31
+ follow_id: 33
diff --git a/models/fixtures/issue.yml b/models/fixtures/issue.yml
index 39dacc92ff..4dea8add13 100644
--- a/models/fixtures/issue.yml
+++ b/models/fixtures/issue.yml
@@ -3,208 +3,287 @@
repo_id: 1
index: 1
poster_id: 1
+ original_author_id: 0
name: issue1
content: content for the first issue
+ milestone_id: 0
+ priority: 0
is_closed: false
is_pull: false
num_comments: 2
created_unix: 946684800
updated_unix: 978307200
+ is_locked: false
-
id: 2
repo_id: 1
index: 2
poster_id: 1
+ original_author_id: 0
name: issue2
content: content for the second issue
milestone_id: 1
+ priority: 0
is_closed: false
is_pull: true
+ num_comments: 0
created_unix: 946684810
updated_unix: 978307190
-
+ is_locked: false
-
id: 3
repo_id: 1
index: 3
poster_id: 1
+ original_author_id: 0
name: issue3
content: content for the third issue
milestone_id: 3
+ priority: 0
is_closed: false
is_pull: true
+ num_comments: 0
created_unix: 946684820
updated_unix: 978307180
+ is_locked: false
-
id: 4
repo_id: 2
index: 1
poster_id: 2
+ original_author_id: 0
name: issue4
content: content for the fourth issue
+ milestone_id: 0
+ priority: 0
is_closed: true
is_pull: false
+ num_comments: 0
created_unix: 946684830
updated_unix: 978307200
+ is_locked: false
-
id: 5
repo_id: 1
index: 4
poster_id: 2
+ original_author_id: 0
name: issue5
content: content for the fifth issue
+ milestone_id: 0
+ priority: 0
is_closed: true
is_pull: false
+ num_comments: 0
created_unix: 946684840
updated_unix: 978307200
+ is_locked: false
-
id: 6
repo_id: 3
index: 1
poster_id: 1
+ original_author_id: 0
name: issue6
content: content6
+ milestone_id: 0
+ priority: 0
is_closed: false
is_pull: false
num_comments: 0
created_unix: 946684850
updated_unix: 978307200
+ is_locked: false
-
id: 7
repo_id: 2
index: 2
poster_id: 2
+ original_author_id: 0
name: issue7
content: content for the seventh issue
+ milestone_id: 0
+ priority: 0
is_closed: false
is_pull: false
+ num_comments: 0
created_unix: 946684830
updated_unix: 978307200
+ is_locked: false
-
id: 8
repo_id: 10
index: 1
poster_id: 11
+ original_author_id: 0
name: pr2
content: a pull request
+ milestone_id: 0
+ priority: 0
is_closed: false
is_pull: true
+ num_comments: 0
created_unix: 946684820
updated_unix: 978307180
+ is_locked: false
-
id: 9
repo_id: 48
index: 1
poster_id: 11
+ original_author_id: 0
name: pr1
content: a pull request
+ milestone_id: 0
+ priority: 0
is_closed: false
is_pull: true
+ num_comments: 0
created_unix: 946684820
updated_unix: 978307180
+ is_locked: false
-
id: 10
repo_id: 42
index: 1
poster_id: 500
+ original_author_id: 0
name: issue from deleted account
content: content from deleted account
+ milestone_id: 0
+ priority: 0
is_closed: false
is_pull: false
+ num_comments: 0
+ deadline_unix: 1019307200
created_unix: 946684830
updated_unix: 999307200
- deadline_unix: 1019307200
+ is_locked: false
-
id: 11
repo_id: 1
index: 5
poster_id: 1
+ original_author_id: 0
name: pull5
content: content for the a pull request
+ milestone_id: 0
+ priority: 0
is_closed: false
is_pull: true
+ num_comments: 0
created_unix: 1579194806
updated_unix: 1579194806
+ is_locked: false
-
id: 12
repo_id: 3
index: 2
poster_id: 2
+ original_author_id: 0
name: pull6
content: content for the a pull request
+ milestone_id: 0
+ priority: 0
is_closed: false
is_pull: true
+ num_comments: 0
created_unix: 1602935696
updated_unix: 1602935696
-
+ is_locked: false
-
id: 13
repo_id: 50
index: 1
poster_id: 2
+ original_author_id: 0
name: issue in active repo
content: we'll be testing github issue 13171 with this.
+ milestone_id: 0
+ priority: 0
is_closed: false
is_pull: false
+ num_comments: 0
created_unix: 1602935696
updated_unix: 1602935696
+ is_locked: false
-
id: 14
repo_id: 51
index: 1
poster_id: 2
+ original_author_id: 0
name: issue in archived repo
content: we'll be testing github issue 13171 with this.
+ milestone_id: 0
+ priority: 0
is_closed: false
is_pull: false
+ num_comments: 0
created_unix: 1602935696
updated_unix: 1602935696
+ is_locked: false
-
id: 15
repo_id: 5
index: 1
poster_id: 2
+ original_author_id: 0
name: issue in repo not linked to team1
content: content
+ milestone_id: 0
+ priority: 0
is_closed: false
is_pull: false
+ num_comments: 0
created_unix: 1602935696
updated_unix: 1602935696
+ is_locked: false
-
id: 16
repo_id: 32
index: 1
poster_id: 2
+ original_author_id: 0
name: just a normal issue
content: content
+ milestone_id: 0
+ priority: 0
is_closed: false
is_pull: false
+ num_comments: 0
created_unix: 1602935696
updated_unix: 1602935696
+ is_locked: false
-
id: 17
repo_id: 32
index: 2
poster_id: 15
+ original_author_id: 0
name: a issue with a assignment
content: content
+ milestone_id: 0
+ priority: 0
is_closed: false
is_pull: false
+ num_comments: 0
created_unix: 1602935696
updated_unix: 1602935696
+ is_locked: false
diff --git a/models/fixtures/label.yml b/models/fixtures/label.yml
index 1b7ce74681..57bf804457 100644
--- a/models/fixtures/label.yml
+++ b/models/fixtures/label.yml
@@ -15,10 +15,11 @@
color: '#000000'
num_issues: 1
num_closed_issues: 1
+
-
id: 3
repo_id: 0
- org_id: 3
+ org_id: 3
name: orglabel3
color: '#abcdef'
num_issues: 0
@@ -32,7 +33,7 @@
color: '#000000'
num_issues: 1
num_closed_issues: 0
-
+
-
id: 5
repo_id: 10
diff --git a/models/fixtures/milestone.yml b/models/fixtures/milestone.yml
index 4dd3445940..87c30cc96c 100644
--- a/models/fixtures/milestone.yml
+++ b/models/fixtures/milestone.yml
@@ -6,6 +6,7 @@
is_closed: false
num_issues: 1
num_closed_issues: 0
+ completeness: 0
deadline_unix: 253370764800
-
@@ -16,6 +17,7 @@
is_closed: false
num_issues: 0
num_closed_issues: 0
+ completeness: 0
deadline_unix: 253370764800
-
@@ -26,6 +28,7 @@
is_closed: true
num_issues: 1
num_closed_issues: 0
+ completeness: 0
deadline_unix: 253370764800
-
@@ -36,14 +39,16 @@
is_closed: false
num_issues: 0
num_closed_issues: 0
+ completeness: 0
deadline_unix: 253370764800
--
+-
id: 5
repo_id: 10
- name: milestone of repo 10
+ name: milestone of repo 10
content: for testing with PRs
is_closed: false
num_issues: 0
num_closed_issues: 0
+ completeness: 0
deadline_unix: 253370764800
diff --git a/models/fixtures/oauth2_application.yml b/models/fixtures/oauth2_application.yml
index a13e20b10e..34d5a88777 100644
--- a/models/fixtures/oauth2_application.yml
+++ b/models/fixtures/oauth2_application.yml
@@ -4,6 +4,6 @@
name: "Test"
client_id: "da7da3ba-9a13-4167-856f-3899de0b0138"
client_secret: "$2a$10$UYRgUSgekzBp6hYe8pAdc.cgB4Gn06QRKsORUnIYTYQADs.YR/uvi" # bcrypt of "4MK8Na6R55smdCY0WuCCumZ6hjRPnGY5saWVRHHjJiA=
- redirect_uris: '["a"]'
+ redirect_uris: '["a", "https://example.com/xyzzy"]'
created_unix: 1546869730
updated_unix: 1546869730
diff --git a/models/fixtures/org_user.yml b/models/fixtures/org_user.yml
index a0bc4b9b43..d6bbdaa9da 100644
--- a/models/fixtures/org_user.yml
+++ b/models/fixtures/org_user.yml
@@ -63,3 +63,15 @@
uid: 29
org_id: 17
is_public: true
+
+-
+ id: 12
+ uid: 2
+ org_id: 17
+ is_public: true
+
+-
+ id: 13
+ uid: 31
+ org_id: 19
+ is_public: true
diff --git a/models/fixtures/pull_request.yml b/models/fixtures/pull_request.yml
index d45baa711c..165437f032 100644
--- a/models/fixtures/pull_request.yml
+++ b/models/fixtures/pull_request.yml
@@ -60,8 +60,8 @@
head_repo_id: 1
base_repo_id: 1
head_branch: pr-to-update
- base_branch: branch1
- merge_base: 1234567890abcdef
+ base_branch: branch2
+ merge_base: 985f0301dba5e7b34be866819cd15ad3d8f508ee
has_merged: false
-
diff --git a/models/fixtures/repository.yml b/models/fixtures/repository.yml
index 82b3ed16dc..f09953be7e 100644
--- a/models/fixtures/repository.yml
+++ b/models/fixtures/repository.yml
@@ -4,19 +4,29 @@
owner_name: user2
lower_name: repo1
name: repo1
- is_archived: false
- is_empty: false
- is_private: false
+ num_watches: 4
+ num_stars: 0
+ num_forks: 0
num_issues: 2
num_closed_issues: 1
num_pulls: 3
num_closed_pulls: 0
num_milestones: 3
num_closed_milestones: 1
- num_watches: 4
num_projects: 1
num_closed_projects: 0
+ is_private: false
+ is_empty: false
+ is_archived: false
+ is_mirror: false
status: 0
+ is_fork: false
+ fork_id: 0
+ is_template: false
+ template_id: 0
+ size: 0
+ is_fsck_enabled: true
+ close_issues_via_commit_in_any_branch: false
-
id: 2
@@ -24,16 +34,29 @@
owner_name: user2
lower_name: repo2
name: repo2
- is_empty: false
- is_archived: false
- is_private: true
+ num_watches: 0
+ num_stars: 1
+ num_forks: 0
num_issues: 2
num_closed_issues: 1
num_pulls: 0
num_closed_pulls: 0
- num_stars: 1
- close_issues_via_commit_in_any_branch: true
+ num_milestones: 0
+ num_closed_milestones: 0
+ num_projects: 0
+ num_closed_projects: 0
+ is_private: true
+ is_empty: false
+ is_archived: false
+ is_mirror: false
status: 0
+ is_fork: false
+ fork_id: 0
+ is_template: false
+ template_id: 0
+ size: 0
+ is_fsck_enabled: true
+ close_issues_via_commit_in_any_branch: true
-
id: 3
@@ -41,16 +64,29 @@
owner_name: user3
lower_name: repo3
name: repo3
- is_empty: false
- is_private: true
+ num_watches: 0
+ num_stars: 0
+ num_forks: 0
num_issues: 1
num_closed_issues: 0
num_pulls: 1
num_closed_pulls: 0
- num_watches: 0
+ num_milestones: 0
+ num_closed_milestones: 0
num_projects: 1
num_closed_projects: 0
+ is_private: true
+ is_empty: false
+ is_archived: false
+ is_mirror: false
status: 0
+ is_fork: false
+ fork_id: 0
+ is_template: false
+ template_id: 0
+ size: 0
+ is_fsck_enabled: true
+ close_issues_via_commit_in_any_branch: false
-
id: 4
@@ -58,16 +94,29 @@
owner_name: user5
lower_name: repo4
name: repo4
- is_empty: false
- is_private: false
+ num_watches: 0
+ num_stars: 1
+ num_forks: 0
num_issues: 0
num_closed_issues: 0
num_pulls: 0
num_closed_pulls: 0
- num_stars: 1
+ num_milestones: 0
+ num_closed_milestones: 0
num_projects: 0
num_closed_projects: 1
+ is_private: false
+ is_empty: false
+ is_archived: false
+ is_mirror: false
status: 0
+ is_fork: false
+ fork_id: 0
+ is_template: false
+ template_id: 0
+ size: 0
+ is_fsck_enabled: true
+ close_issues_via_commit_in_any_branch: false
-
id: 5
@@ -75,14 +124,29 @@
owner_name: user3
lower_name: repo5
name: repo5
- is_private: true
+ num_watches: 0
+ num_stars: 0
+ num_forks: 0
num_issues: 1
num_closed_issues: 0
num_pulls: 0
num_closed_pulls: 0
- num_watches: 0
+ num_milestones: 0
+ num_closed_milestones: 0
+ num_projects: 0
+ num_closed_projects: 0
+ is_private: true
+ is_empty: true
+ is_archived: false
is_mirror: true
status: 0
+ is_fork: false
+ fork_id: 0
+ is_template: false
+ template_id: 0
+ size: 0
+ is_fsck_enabled: true
+ close_issues_via_commit_in_any_branch: false
-
id: 6
@@ -90,13 +154,29 @@
owner_name: user10
lower_name: repo6
name: repo6
- is_private: true
+ num_watches: 0
+ num_stars: 0
+ num_forks: 0
num_issues: 0
num_closed_issues: 0
num_pulls: 0
num_closed_pulls: 0
+ num_milestones: 0
+ num_closed_milestones: 0
+ num_projects: 0
+ num_closed_projects: 0
+ is_private: true
+ is_empty: true
+ is_archived: false
is_mirror: false
status: 0
+ is_fork: false
+ fork_id: 0
+ is_template: false
+ template_id: 0
+ size: 0
+ is_fsck_enabled: true
+ close_issues_via_commit_in_any_branch: false
-
id: 7
@@ -104,13 +184,29 @@
owner_name: user10
lower_name: repo7
name: repo7
- is_private: true
+ num_watches: 0
+ num_stars: 0
+ num_forks: 0
num_issues: 0
num_closed_issues: 0
num_pulls: 0
num_closed_pulls: 0
+ num_milestones: 0
+ num_closed_milestones: 0
+ num_projects: 0
+ num_closed_projects: 0
+ is_private: true
+ is_empty: true
+ is_archived: false
is_mirror: false
status: 0
+ is_fork: false
+ fork_id: 0
+ is_template: false
+ template_id: 0
+ size: 0
+ is_fsck_enabled: true
+ close_issues_via_commit_in_any_branch: false
-
id: 8
@@ -118,13 +214,29 @@
owner_name: user10
lower_name: repo8
name: repo8
- is_private: false
+ num_watches: 0
+ num_stars: 0
+ num_forks: 0
num_issues: 0
num_closed_issues: 0
num_pulls: 0
num_closed_pulls: 0
+ num_milestones: 0
+ num_closed_milestones: 0
+ num_projects: 0
+ num_closed_projects: 0
+ is_private: false
+ is_empty: true
+ is_archived: false
is_mirror: false
status: 0
+ is_fork: false
+ fork_id: 0
+ is_template: false
+ template_id: 0
+ size: 0
+ is_fsck_enabled: true
+ close_issues_via_commit_in_any_branch: false
-
id: 9
@@ -132,13 +244,29 @@
owner_name: user11
lower_name: repo9
name: repo9
- is_private: false
+ num_watches: 0
+ num_stars: 0
+ num_forks: 0
num_issues: 0
num_closed_issues: 0
num_pulls: 0
num_closed_pulls: 0
+ num_milestones: 0
+ num_closed_milestones: 0
+ num_projects: 0
+ num_closed_projects: 0
+ is_private: false
+ is_empty: true
+ is_archived: false
is_mirror: false
status: 0
+ is_fork: false
+ fork_id: 0
+ is_template: false
+ template_id: 0
+ size: 0
+ is_fsck_enabled: true
+ close_issues_via_commit_in_any_branch: false
-
id: 10
@@ -146,32 +274,59 @@
owner_name: user12
lower_name: repo10
name: repo10
- is_empty: false
- is_private: false
+ num_watches: 0
+ num_stars: 0
+ num_forks: 1
num_issues: 0
num_closed_issues: 0
num_pulls: 1
num_closed_pulls: 0
num_milestones: 1
+ num_closed_milestones: 0
+ num_projects: 0
+ num_closed_projects: 0
+ is_private: false
+ is_empty: false
+ is_archived: false
is_mirror: false
- num_forks: 1
status: 0
+ is_fork: false
+ fork_id: 0
+ is_template: false
+ template_id: 0
+ size: 0
+ is_fsck_enabled: true
+ close_issues_via_commit_in_any_branch: false
-
id: 11
- fork_id: 10
owner_id: 13
owner_name: user13
lower_name: repo11
name: repo11
- is_empty: false
- is_private: false
+ num_watches: 0
+ num_stars: 0
+ num_forks: 0
num_issues: 0
num_closed_issues: 0
num_pulls: 0
num_closed_pulls: 0
+ num_milestones: 0
+ num_closed_milestones: 0
+ num_projects: 0
+ num_closed_projects: 0
+ is_private: false
+ is_empty: false
+ is_archived: false
is_mirror: false
status: 0
+ is_fork: false
+ fork_id: 10
+ is_template: false
+ template_id: 0
+ size: 0
+ is_fsck_enabled: true
+ close_issues_via_commit_in_any_branch: false
-
id: 12
@@ -179,13 +334,29 @@
owner_name: user14
lower_name: test_repo_12
name: test_repo_12
- is_private: false
+ num_watches: 0
+ num_stars: 0
+ num_forks: 0
num_issues: 0
num_closed_issues: 0
num_pulls: 0
num_closed_pulls: 0
+ num_milestones: 0
+ num_closed_milestones: 0
+ num_projects: 0
+ num_closed_projects: 0
+ is_private: false
+ is_empty: true
+ is_archived: false
is_mirror: false
status: 0
+ is_fork: false
+ fork_id: 0
+ is_template: false
+ template_id: 0
+ size: 0
+ is_fsck_enabled: true
+ close_issues_via_commit_in_any_branch: false
-
id: 13
@@ -193,13 +364,29 @@
owner_name: user14
lower_name: test_repo_13
name: test_repo_13
- is_private: true
+ num_watches: 0
+ num_stars: 0
+ num_forks: 0
num_issues: 0
num_closed_issues: 0
num_pulls: 0
num_closed_pulls: 0
+ num_milestones: 0
+ num_closed_milestones: 0
+ num_projects: 0
+ num_closed_projects: 0
+ is_private: true
+ is_empty: true
+ is_archived: false
is_mirror: false
status: 0
+ is_fork: false
+ fork_id: 0
+ is_template: false
+ template_id: 0
+ size: 0
+ is_fsck_enabled: true
+ close_issues_via_commit_in_any_branch: false
-
id: 14
@@ -208,13 +395,29 @@
lower_name: test_repo_14
name: test_repo_14
description: test_description_14
- is_private: false
+ num_watches: 0
+ num_stars: 0
+ num_forks: 0
num_issues: 0
num_closed_issues: 0
num_pulls: 0
num_closed_pulls: 0
+ num_milestones: 0
+ num_closed_milestones: 0
+ num_projects: 0
+ num_closed_projects: 0
+ is_private: false
+ is_empty: true
+ is_archived: false
is_mirror: false
status: 0
+ is_fork: false
+ fork_id: 0
+ is_template: false
+ template_id: 0
+ size: 0
+ is_fsck_enabled: true
+ close_issues_via_commit_in_any_branch: false
-
id: 15
@@ -222,9 +425,29 @@
owner_name: user2
lower_name: repo15
name: repo15
- is_empty: false
+ num_watches: 0
+ num_stars: 0
+ num_forks: 0
+ num_issues: 0
+ num_closed_issues: 0
+ num_pulls: 0
+ num_closed_pulls: 0
+ num_milestones: 0
+ num_closed_milestones: 0
+ num_projects: 0
+ num_closed_projects: 0
is_private: true
+ is_empty: false
+ is_archived: false
+ is_mirror: false
status: 0
+ is_fork: false
+ fork_id: 0
+ is_template: false
+ template_id: 0
+ size: 0
+ is_fsck_enabled: true
+ close_issues_via_commit_in_any_branch: false
-
id: 16
@@ -232,14 +455,29 @@
owner_name: user2
lower_name: repo16
name: repo16
- is_empty: false
- is_private: true
+ num_watches: 0
+ num_stars: 0
+ num_forks: 0
num_issues: 0
num_closed_issues: 0
num_pulls: 0
num_closed_pulls: 0
- num_watches: 0
+ num_milestones: 0
+ num_closed_milestones: 0
+ num_projects: 0
+ num_closed_projects: 0
+ is_private: true
+ is_empty: false
+ is_archived: false
+ is_mirror: false
status: 0
+ is_fork: false
+ fork_id: 0
+ is_template: false
+ template_id: 0
+ size: 0
+ is_fsck_enabled: true
+ close_issues_via_commit_in_any_branch: false
-
id: 17
@@ -247,15 +485,29 @@
owner_name: user15
lower_name: big_test_public_1
name: big_test_public_1
- is_private: false
+ num_watches: 0
+ num_stars: 0
+ num_forks: 0
num_issues: 0
num_closed_issues: 0
num_pulls: 0
num_closed_pulls: 0
- num_watches: 0
+ num_milestones: 0
+ num_closed_milestones: 0
+ num_projects: 0
+ num_closed_projects: 0
+ is_private: false
+ is_empty: true
+ is_archived: false
is_mirror: false
- is_fork: false
status: 0
+ is_fork: false
+ fork_id: 0
+ is_template: false
+ template_id: 0
+ size: 0
+ is_fsck_enabled: true
+ close_issues_via_commit_in_any_branch: false
-
id: 18
@@ -263,14 +515,29 @@
owner_name: user15
lower_name: big_test_public_2
name: big_test_public_2
- is_private: false
+ num_watches: 0
+ num_stars: 0
+ num_forks: 0
num_issues: 0
num_closed_issues: 0
num_pulls: 0
num_closed_pulls: 0
+ num_milestones: 0
+ num_closed_milestones: 0
+ num_projects: 0
+ num_closed_projects: 0
+ is_private: false
+ is_empty: true
+ is_archived: false
is_mirror: false
- is_fork: false
status: 0
+ is_fork: false
+ fork_id: 0
+ is_template: false
+ template_id: 0
+ size: 0
+ is_fsck_enabled: true
+ close_issues_via_commit_in_any_branch: false
-
id: 19
@@ -278,14 +545,29 @@
owner_name: user15
lower_name: big_test_private_1
name: big_test_private_1
- is_private: true
+ num_watches: 0
+ num_stars: 0
+ num_forks: 0
num_issues: 0
num_closed_issues: 0
num_pulls: 0
num_closed_pulls: 0
+ num_milestones: 0
+ num_closed_milestones: 0
+ num_projects: 0
+ num_closed_projects: 0
+ is_private: true
+ is_empty: true
+ is_archived: false
is_mirror: false
- is_fork: false
status: 0
+ is_fork: false
+ fork_id: 0
+ is_template: false
+ template_id: 0
+ size: 0
+ is_fsck_enabled: true
+ close_issues_via_commit_in_any_branch: false
-
id: 20
@@ -293,14 +575,29 @@
owner_name: user15
lower_name: big_test_private_2
name: big_test_private_2
- is_private: true
+ num_watches: 0
+ num_stars: 0
+ num_forks: 0
num_issues: 0
num_closed_issues: 0
num_pulls: 0
num_closed_pulls: 0
+ num_milestones: 0
+ num_closed_milestones: 0
+ num_projects: 0
+ num_closed_projects: 0
+ is_private: true
+ is_empty: true
+ is_archived: false
is_mirror: false
- is_fork: false
status: 0
+ is_fork: false
+ fork_id: 0
+ is_template: false
+ template_id: 0
+ size: 0
+ is_fsck_enabled: true
+ close_issues_via_commit_in_any_branch: false
-
id: 21
@@ -308,14 +605,29 @@
owner_name: user16
lower_name: big_test_public_3
name: big_test_public_3
- is_private: false
+ num_watches: 0
+ num_stars: 0
+ num_forks: 0
num_issues: 0
num_closed_issues: 0
num_pulls: 0
num_closed_pulls: 0
+ num_milestones: 0
+ num_closed_milestones: 0
+ num_projects: 0
+ num_closed_projects: 0
+ is_private: false
+ is_empty: true
+ is_archived: false
is_mirror: false
- is_fork: false
status: 0
+ is_fork: false
+ fork_id: 0
+ is_template: false
+ template_id: 0
+ size: 0
+ is_fsck_enabled: true
+ close_issues_via_commit_in_any_branch: false
-
id: 22
@@ -323,14 +635,29 @@
owner_name: user16
lower_name: big_test_private_3
name: big_test_private_3
- is_private: true
+ num_watches: 0
+ num_stars: 0
+ num_forks: 0
num_issues: 0
num_closed_issues: 0
num_pulls: 0
num_closed_pulls: 0
+ num_milestones: 0
+ num_closed_milestones: 0
+ num_projects: 0
+ num_closed_projects: 0
+ is_private: true
+ is_empty: true
+ is_archived: false
is_mirror: false
- is_fork: false
status: 0
+ is_fork: false
+ fork_id: 0
+ is_template: false
+ template_id: 0
+ size: 0
+ is_fsck_enabled: true
+ close_issues_via_commit_in_any_branch: false
-
id: 23
@@ -338,14 +665,29 @@
owner_name: user17
lower_name: big_test_public_4
name: big_test_public_4
- is_private: false
+ num_watches: 0
+ num_stars: 0
+ num_forks: 0
num_issues: 0
num_closed_issues: 0
num_pulls: 0
num_closed_pulls: 0
+ num_milestones: 0
+ num_closed_milestones: 0
+ num_projects: 0
+ num_closed_projects: 0
+ is_private: false
+ is_empty: true
+ is_archived: false
is_mirror: false
- is_fork: false
status: 0
+ is_fork: false
+ fork_id: 0
+ is_template: false
+ template_id: 0
+ size: 0
+ is_fsck_enabled: true
+ close_issues_via_commit_in_any_branch: false
-
id: 24
@@ -353,14 +695,29 @@
owner_name: user17
lower_name: big_test_private_4
name: big_test_private_4
- is_private: true
+ num_watches: 0
+ num_stars: 0
+ num_forks: 0
num_issues: 0
num_closed_issues: 0
num_pulls: 0
num_closed_pulls: 0
+ num_milestones: 0
+ num_closed_milestones: 0
+ num_projects: 0
+ num_closed_projects: 0
+ is_private: true
+ is_empty: true
+ is_archived: false
is_mirror: false
- is_fork: false
status: 0
+ is_fork: false
+ fork_id: 0
+ is_template: false
+ template_id: 0
+ size: 0
+ is_fsck_enabled: true
+ close_issues_via_commit_in_any_branch: false
-
id: 25
@@ -368,15 +725,29 @@
owner_name: user20
lower_name: big_test_public_mirror_5
name: big_test_public_mirror_5
- is_private: false
+ num_watches: 0
+ num_stars: 0
+ num_forks: 0
num_issues: 0
num_closed_issues: 0
num_pulls: 0
num_closed_pulls: 0
- num_watches: 0
+ num_milestones: 0
+ num_closed_milestones: 0
+ num_projects: 0
+ num_closed_projects: 0
+ is_private: false
+ is_empty: true
+ is_archived: false
is_mirror: true
- is_fork: false
status: 0
+ is_fork: false
+ fork_id: 0
+ is_template: false
+ template_id: 0
+ size: 0
+ is_fsck_enabled: true
+ close_issues_via_commit_in_any_branch: false
-
id: 26
@@ -384,15 +755,29 @@
owner_name: user20
lower_name: big_test_private_mirror_5
name: big_test_private_mirror_5
- is_private: true
+ num_watches: 0
+ num_stars: 0
+ num_forks: 0
num_issues: 0
num_closed_issues: 0
num_pulls: 0
num_closed_pulls: 0
- num_watches: 0
+ num_milestones: 0
+ num_closed_milestones: 0
+ num_projects: 0
+ num_closed_projects: 0
+ is_private: true
+ is_empty: true
+ is_archived: false
is_mirror: true
- is_fork: false
status: 0
+ is_fork: false
+ fork_id: 0
+ is_template: false
+ template_id: 0
+ size: 0
+ is_fsck_enabled: true
+ close_issues_via_commit_in_any_branch: false
-
id: 27
@@ -400,16 +785,29 @@
owner_name: user19
lower_name: big_test_public_mirror_6
name: big_test_public_mirror_6
- is_private: false
+ num_watches: 0
+ num_stars: 0
+ num_forks: 1
num_issues: 0
num_closed_issues: 0
num_pulls: 0
num_closed_pulls: 0
- num_watches: 0
+ num_milestones: 0
+ num_closed_milestones: 0
+ num_projects: 0
+ num_closed_projects: 0
+ is_private: false
+ is_empty: true
+ is_archived: false
is_mirror: true
- num_forks: 1
- is_fork: false
status: 0
+ is_fork: false
+ fork_id: 0
+ is_template: false
+ template_id: 0
+ size: 0
+ is_fsck_enabled: true
+ close_issues_via_commit_in_any_branch: false
-
id: 28
@@ -417,48 +815,89 @@
owner_name: user19
lower_name: big_test_private_mirror_6
name: big_test_private_mirror_6
- is_private: true
+ num_watches: 0
+ num_stars: 0
+ num_forks: 1
num_issues: 0
num_closed_issues: 0
num_pulls: 0
num_closed_pulls: 0
- num_watches: 0
+ num_milestones: 0
+ num_closed_milestones: 0
+ num_projects: 0
+ num_closed_projects: 0
+ is_private: true
+ is_empty: true
+ is_archived: false
is_mirror: true
- num_forks: 1
- is_fork: false
status: 0
+ is_fork: false
+ fork_id: 0
+ is_template: false
+ template_id: 0
+ size: 0
+ is_fsck_enabled: true
+ close_issues_via_commit_in_any_branch: false
-
id: 29
- fork_id: 27
owner_id: 20
owner_name: user20
lower_name: big_test_public_fork_7
name: big_test_public_fork_7
- is_private: false
+ num_watches: 0
+ num_stars: 0
+ num_forks: 0
num_issues: 0
num_closed_issues: 0
num_pulls: 0
num_closed_pulls: 0
+ num_milestones: 0
+ num_closed_milestones: 0
+ num_projects: 0
+ num_closed_projects: 0
+ is_private: false
+ is_empty: true
+ is_archived: false
is_mirror: false
- is_fork: true
status: 0
+ is_fork: true
+ fork_id: 27
+ is_template: false
+ template_id: 0
+ size: 0
+ is_fsck_enabled: true
+ close_issues_via_commit_in_any_branch: false
-
id: 30
- fork_id: 28
owner_id: 20
owner_name: user20
lower_name: big_test_private_fork_7
name: big_test_private_fork_7
- is_private: true
+ num_watches: 0
+ num_stars: 0
+ num_forks: 0
num_issues: 0
num_closed_issues: 0
num_pulls: 0
num_closed_pulls: 0
+ num_milestones: 0
+ num_closed_milestones: 0
+ num_projects: 0
+ num_closed_projects: 0
+ is_private: true
+ is_empty: true
+ is_archived: false
is_mirror: false
- is_fork: true
status: 0
+ is_fork: true
+ fork_id: 28
+ is_template: false
+ template_id: 0
+ size: 0
+ is_fsck_enabled: true
+ close_issues_via_commit_in_any_branch: false
-
id: 31
@@ -466,13 +905,29 @@
owner_name: user2
lower_name: repo20
name: repo20
- is_empty: false
- is_private: true
+ num_watches: 0
num_stars: 0
num_forks: 0
num_issues: 0
+ num_closed_issues: 0
+ num_pulls: 0
+ num_closed_pulls: 0
+ num_milestones: 0
+ num_closed_milestones: 0
+ num_projects: 0
+ num_closed_projects: 0
+ is_private: true
+ is_empty: false
+ is_archived: false
is_mirror: false
status: 0
+ is_fork: false
+ fork_id: 0
+ is_template: false
+ template_id: 0
+ size: 0
+ is_fsck_enabled: true
+ close_issues_via_commit_in_any_branch: false
-
id: 32 # org public repo
@@ -480,12 +935,29 @@
owner_name: user3
lower_name: repo21
name: repo21
- is_private: false
+ num_watches: 0
num_stars: 0
num_forks: 0
num_issues: 2
+ num_closed_issues: 0
+ num_pulls: 0
+ num_closed_pulls: 0
+ num_milestones: 0
+ num_closed_milestones: 0
+ num_projects: 0
+ num_closed_projects: 0
+ is_private: false
+ is_empty: true
+ is_archived: false
is_mirror: false
status: 0
+ is_fork: false
+ fork_id: 0
+ is_template: false
+ template_id: 0
+ size: 0
+ is_fsck_enabled: true
+ close_issues_via_commit_in_any_branch: false
-
id: 33
@@ -493,9 +965,29 @@
owner_name: user2
lower_name: utf8
name: utf8
- is_empty: false
+ num_watches: 0
+ num_stars: 0
+ num_forks: 0
+ num_issues: 0
+ num_closed_issues: 0
+ num_pulls: 0
+ num_closed_pulls: 0
+ num_milestones: 0
+ num_closed_milestones: 0
+ num_projects: 0
+ num_closed_projects: 0
is_private: false
+ is_empty: false
+ is_archived: false
+ is_mirror: false
status: 0
+ is_fork: false
+ fork_id: 0
+ is_template: false
+ template_id: 0
+ size: 0
+ is_fsck_enabled: true
+ close_issues_via_commit_in_any_branch: false
-
id: 34
@@ -503,12 +995,29 @@
owner_name: user21
lower_name: golang
name: golang
- is_private: false
+ num_watches: 0
num_stars: 0
num_forks: 0
num_issues: 0
+ num_closed_issues: 0
+ num_pulls: 0
+ num_closed_pulls: 0
+ num_milestones: 0
+ num_closed_milestones: 0
+ num_projects: 0
+ num_closed_projects: 0
+ is_private: false
+ is_empty: true
+ is_archived: false
is_mirror: false
status: 0
+ is_fork: false
+ fork_id: 0
+ is_template: false
+ template_id: 0
+ size: 0
+ is_fsck_enabled: true
+ close_issues_via_commit_in_any_branch: false
-
id: 35
@@ -516,12 +1025,29 @@
owner_name: user21
lower_name: graphql
name: graphql
- is_private: false
+ num_watches: 0
num_stars: 0
num_forks: 0
num_issues: 0
+ num_closed_issues: 0
+ num_pulls: 0
+ num_closed_pulls: 0
+ num_milestones: 0
+ num_closed_milestones: 0
+ num_projects: 0
+ num_closed_projects: 0
+ is_private: false
+ is_empty: true
+ is_archived: false
is_mirror: false
status: 0
+ is_fork: false
+ fork_id: 0
+ is_template: false
+ template_id: 0
+ size: 0
+ is_fsck_enabled: true
+ close_issues_via_commit_in_any_branch: false
-
id: 36
@@ -529,13 +1055,29 @@
owner_name: user2
lower_name: commits_search_test
name: commits_search_test
- is_empty: false
- is_private: false
+ num_watches: 0
num_stars: 0
num_forks: 0
num_issues: 0
+ num_closed_issues: 0
+ num_pulls: 0
+ num_closed_pulls: 0
+ num_milestones: 0
+ num_closed_milestones: 0
+ num_projects: 0
+ num_closed_projects: 0
+ is_private: false
+ is_empty: false
+ is_archived: false
is_mirror: false
status: 0
+ is_fork: false
+ fork_id: 0
+ is_template: false
+ template_id: 0
+ size: 0
+ is_fsck_enabled: true
+ close_issues_via_commit_in_any_branch: false
-
id: 37
@@ -543,13 +1085,29 @@
owner_name: user2
lower_name: git_hooks_test
name: git_hooks_test
- is_empty: false
- is_private: false
+ num_watches: 0
num_stars: 0
num_forks: 0
num_issues: 0
+ num_closed_issues: 0
+ num_pulls: 0
+ num_closed_pulls: 0
+ num_milestones: 0
+ num_closed_milestones: 0
+ num_projects: 0
+ num_closed_projects: 0
+ is_private: false
+ is_empty: false
+ is_archived: false
is_mirror: false
status: 0
+ is_fork: false
+ fork_id: 0
+ is_template: false
+ template_id: 0
+ size: 0
+ is_fsck_enabled: true
+ close_issues_via_commit_in_any_branch: false
-
id: 38
@@ -557,13 +1115,29 @@
owner_name: limited_org
lower_name: public_repo_on_limited_org
name: public_repo_on_limited_org
- is_empty: false
- is_private: false
+ num_watches: 0
num_stars: 0
num_forks: 0
num_issues: 0
+ num_closed_issues: 0
+ num_pulls: 0
+ num_closed_pulls: 0
+ num_milestones: 0
+ num_closed_milestones: 0
+ num_projects: 0
+ num_closed_projects: 0
+ is_private: false
+ is_empty: false
+ is_archived: false
is_mirror: false
status: 0
+ is_fork: false
+ fork_id: 0
+ is_template: false
+ template_id: 0
+ size: 0
+ is_fsck_enabled: true
+ close_issues_via_commit_in_any_branch: false
-
id: 39
@@ -571,13 +1145,29 @@
owner_name: limited_org
lower_name: private_repo_on_limited_org
name: private_repo_on_limited_org
- is_empty: false
- is_private: true
+ num_watches: 0
num_stars: 0
num_forks: 0
num_issues: 0
+ num_closed_issues: 0
+ num_pulls: 0
+ num_closed_pulls: 0
+ num_milestones: 0
+ num_closed_milestones: 0
+ num_projects: 0
+ num_closed_projects: 0
+ is_private: true
+ is_empty: false
+ is_archived: false
is_mirror: false
status: 0
+ is_fork: false
+ fork_id: 0
+ is_template: false
+ template_id: 0
+ size: 0
+ is_fsck_enabled: true
+ close_issues_via_commit_in_any_branch: false
-
id: 40
@@ -585,13 +1175,29 @@
owner_name: privated_org
lower_name: public_repo_on_private_org
name: public_repo_on_private_org
- is_empty: false
- is_private: false
+ num_watches: 0
num_stars: 0
num_forks: 0
num_issues: 0
+ num_closed_issues: 0
+ num_pulls: 0
+ num_closed_pulls: 0
+ num_milestones: 0
+ num_closed_milestones: 0
+ num_projects: 0
+ num_closed_projects: 0
+ is_private: false
+ is_empty: false
+ is_archived: false
is_mirror: false
status: 0
+ is_fork: false
+ fork_id: 0
+ is_template: false
+ template_id: 0
+ size: 0
+ is_fsck_enabled: true
+ close_issues_via_commit_in_any_branch: false
-
id: 41
@@ -599,12 +1205,29 @@
owner_name: privated_org
lower_name: private_repo_on_private_org
name: private_repo_on_private_org
- is_empty: false
- is_private: true
+ num_watches: 0
num_stars: 0
num_forks: 0
num_issues: 0
+ num_closed_issues: 0
+ num_pulls: 0
+ num_closed_pulls: 0
+ num_milestones: 0
+ num_closed_milestones: 0
+ num_projects: 0
+ num_closed_projects: 0
+ is_private: true
+ is_empty: false
+ is_archived: false
is_mirror: false
+ status: 0
+ is_fork: false
+ fork_id: 0
+ is_template: false
+ template_id: 0
+ size: 0
+ is_fsck_enabled: true
+ close_issues_via_commit_in_any_branch: false
-
id: 42
@@ -612,14 +1235,29 @@
owner_name: user2
lower_name: glob
name: glob
- is_empty: false
- is_private: false
+ num_watches: 0
num_stars: 0
num_forks: 0
num_issues: 1
+ num_closed_issues: 0
+ num_pulls: 0
+ num_closed_pulls: 0
num_milestones: 1
- is_mirror:
+ num_closed_milestones: 0
+ num_projects: 0
+ num_closed_projects: 0
+ is_private: false
+ is_empty: false
is_archived: false
+ is_mirror: false
+ status: 0
+ is_fork: false
+ fork_id: 0
+ is_template: false
+ template_id: 0
+ size: 0
+ is_fsck_enabled: true
+ close_issues_via_commit_in_any_branch: false
-
id: 43
@@ -627,12 +1265,29 @@
owner_name: org26
lower_name: repo26
name: repo26
- is_private: true
+ num_watches: 0
num_stars: 0
num_forks: 0
num_issues: 0
+ num_closed_issues: 0
+ num_pulls: 0
+ num_closed_pulls: 0
+ num_milestones: 0
+ num_closed_milestones: 0
+ num_projects: 0
+ num_closed_projects: 0
+ is_private: true
+ is_empty: true
+ is_archived: false
is_mirror: false
status: 0
+ is_fork: false
+ fork_id: 0
+ is_template: false
+ template_id: 0
+ size: 0
+ is_fsck_enabled: true
+ close_issues_via_commit_in_any_branch: false
-
id: 44
@@ -640,14 +1295,29 @@
owner_name: user27
lower_name: template1
name: template1
- is_empty: false
- is_private: false
- is_template: true
+ num_watches: 0
num_stars: 0
num_forks: 0
num_issues: 0
+ num_closed_issues: 0
+ num_pulls: 0
+ num_closed_pulls: 0
+ num_milestones: 0
+ num_closed_milestones: 0
+ num_projects: 0
+ num_closed_projects: 0
+ is_private: false
+ is_empty: false
+ is_archived: false
is_mirror: false
status: 0
+ is_fork: false
+ fork_id: 0
+ is_template: true
+ template_id: 0
+ size: 0
+ is_fsck_enabled: true
+ close_issues_via_commit_in_any_branch: false
-
id: 45
@@ -655,13 +1325,29 @@
owner_name: user27
lower_name: template2
name: template2
- is_private: false
- is_template: true
+ num_watches: 0
num_stars: 0
num_forks: 0
num_issues: 0
+ num_closed_issues: 0
+ num_pulls: 0
+ num_closed_pulls: 0
+ num_milestones: 0
+ num_closed_milestones: 0
+ num_projects: 0
+ num_closed_projects: 0
+ is_private: false
+ is_empty: true
+ is_archived: false
is_mirror: false
status: 0
+ is_fork: false
+ fork_id: 0
+ is_template: true
+ template_id: 0
+ size: 0
+ is_fsck_enabled: true
+ close_issues_via_commit_in_any_branch: false
-
id: 46
@@ -669,13 +1355,29 @@
owner_name: org26
lower_name: repo_external_tracker
name: repo_external_tracker
- is_empty: false
- is_private: false
+ num_watches: 0
num_stars: 0
num_forks: 0
num_issues: 0
+ num_closed_issues: 0
+ num_pulls: 0
+ num_closed_pulls: 0
+ num_milestones: 0
+ num_closed_milestones: 0
+ num_projects: 0
+ num_closed_projects: 0
+ is_private: false
+ is_empty: false
+ is_archived: false
is_mirror: false
status: 0
+ is_fork: false
+ fork_id: 0
+ is_template: false
+ template_id: 0
+ size: 0
+ is_fsck_enabled: true
+ close_issues_via_commit_in_any_branch: false
-
id: 47
@@ -683,13 +1385,29 @@
owner_name: org26
lower_name: repo_external_tracker_numeric
name: repo_external_tracker_numeric
- is_empty: false
- is_private: false
+ num_watches: 0
num_stars: 0
num_forks: 0
num_issues: 0
+ num_closed_issues: 0
+ num_pulls: 0
+ num_closed_pulls: 0
+ num_milestones: 0
+ num_closed_milestones: 0
+ num_projects: 0
+ num_closed_projects: 0
+ is_private: false
+ is_empty: false
+ is_archived: false
is_mirror: false
status: 0
+ is_fork: false
+ fork_id: 0
+ is_template: false
+ template_id: 0
+ size: 0
+ is_fsck_enabled: true
+ close_issues_via_commit_in_any_branch: false
-
id: 48
@@ -697,14 +1415,29 @@
owner_name: org26
lower_name: repo_external_tracker_alpha
name: repo_external_tracker_alpha
- is_empty: false
- is_private: false
+ num_watches: 0
num_stars: 0
num_forks: 0
num_issues: 0
+ num_closed_issues: 0
num_pulls: 1
+ num_closed_pulls: 0
+ num_milestones: 0
+ num_closed_milestones: 0
+ num_projects: 0
+ num_closed_projects: 0
+ is_private: false
+ is_empty: false
+ is_archived: false
is_mirror: false
status: 0
+ is_fork: false
+ fork_id: 0
+ is_template: false
+ template_id: 0
+ size: 0
+ is_fsck_enabled: true
+ close_issues_via_commit_in_any_branch: false
-
id: 49
@@ -712,13 +1445,29 @@
owner_name: user27
lower_name: repo49
name: repo49
- is_empty: false
- is_private: false
+ num_watches: 0
num_stars: 0
num_forks: 0
num_issues: 0
+ num_closed_issues: 0
+ num_pulls: 0
+ num_closed_pulls: 0
+ num_milestones: 0
+ num_closed_milestones: 0
+ num_projects: 0
+ num_closed_projects: 0
+ is_private: false
+ is_empty: false
+ is_archived: false
is_mirror: false
status: 0
+ is_fork: false
+ fork_id: 0
+ is_template: false
+ template_id: 0
+ size: 0
+ is_fsck_enabled: true
+ close_issues_via_commit_in_any_branch: false
-
id: 50
@@ -726,19 +1475,29 @@
owner_name: user30
lower_name: repo50
name: repo50
- is_archived: false
- is_empty: false
- is_private: false
+ num_watches: 0
+ num_stars: 0
+ num_forks: 0
num_issues: 1
num_closed_issues: 0
num_pulls: 0
num_closed_pulls: 0
num_milestones: 0
num_closed_milestones: 0
- num_watches: 0
num_projects: 0
num_closed_projects: 0
+ is_private: false
+ is_empty: false
+ is_archived: false
+ is_mirror: false
status: 0
+ is_fork: false
+ fork_id: 0
+ is_template: false
+ template_id: 0
+ size: 0
+ is_fsck_enabled: true
+ close_issues_via_commit_in_any_branch: false
-
id: 51
@@ -746,19 +1505,29 @@
owner_name: user30
lower_name: repo51
name: repo51
- is_archived: true
- is_empty: false
- is_private: false
+ num_watches: 0
+ num_stars: 0
+ num_forks: 0
num_issues: 1
num_closed_issues: 0
num_pulls: 0
num_closed_pulls: 0
num_milestones: 0
num_closed_milestones: 0
- num_watches: 0
num_projects: 0
num_closed_projects: 0
+ is_private: false
+ is_empty: false
+ is_archived: true
+ is_mirror: false
status: 0
+ is_fork: false
+ fork_id: 0
+ is_template: false
+ template_id: 0
+ size: 0
+ is_fsck_enabled: true
+ close_issues_via_commit_in_any_branch: false
-
id: 52
@@ -766,6 +1535,26 @@
owner_name: user30
lower_name: empty
name: empty
- is_empty: true
+ num_watches: 0
+ num_stars: 0
+ num_forks: 0
+ num_issues: 0
+ num_closed_issues: 0
+ num_pulls: 0
+ num_closed_pulls: 0
+ num_milestones: 0
+ num_closed_milestones: 0
+ num_projects: 0
+ num_closed_projects: 0
is_private: true
+ is_empty: true
+ is_archived: false
+ is_mirror: false
status: 0
+ is_fork: false
+ fork_id: 0
+ is_template: false
+ template_id: 0
+ size: 0
+ is_fsck_enabled: true
+ close_issues_via_commit_in_any_branch: false
diff --git a/models/fixtures/system_setting.yml b/models/fixtures/system_setting.yml
new file mode 100644
index 0000000000..6c960168fc
--- /dev/null
+++ b/models/fixtures/system_setting.yml
@@ -0,0 +1,15 @@
+-
+ id: 1
+ setting_key: 'disable_gravatar'
+ setting_value: 'false'
+ version: 1
+ created: 1653533198
+ updated: 1653533198
+
+-
+ id: 2
+ setting_key: 'enable_federated_avatar'
+ setting_value: 'false'
+ version: 1
+ created: 1653533198
+ updated: 1653533198
diff --git a/models/fixtures/team.yml b/models/fixtures/team.yml
index f6dfd1e9d0..ea47a33f1c 100644
--- a/models/fixtures/team.yml
+++ b/models/fixtures/team.yml
@@ -6,6 +6,7 @@
authorize: 4 # owner
num_repos: 3
num_members: 1
+ includes_all_repositories: false
can_create_org_repo: true
-
@@ -16,6 +17,7 @@
authorize: 2 # write
num_repos: 1
num_members: 2
+ includes_all_repositories: false
can_create_org_repo: false
-
@@ -26,6 +28,7 @@
authorize: 4 # owner
num_repos: 0
num_members: 1
+ includes_all_repositories: false
can_create_org_repo: true
-
@@ -36,6 +39,7 @@
authorize: 4 # owner
num_repos: 0
num_members: 1
+ includes_all_repositories: false
can_create_org_repo: true
-
@@ -46,6 +50,7 @@
authorize: 4 # owner
num_repos: 2
num_members: 2
+ includes_all_repositories: false
can_create_org_repo: true
-
@@ -55,7 +60,8 @@
name: Owners
authorize: 4 # owner
num_repos: 2
- num_members: 1
+ num_members: 2
+ includes_all_repositories: false
can_create_org_repo: true
-
@@ -66,6 +72,7 @@
authorize: 2 # write
num_repos: 1
num_members: 1
+ includes_all_repositories: false
can_create_org_repo: false
-
@@ -76,6 +83,7 @@
authorize: 2 # write
num_repos: 1
num_members: 1
+ includes_all_repositories: false
can_create_org_repo: false
-
@@ -86,6 +94,7 @@
authorize: 1 # read
num_repos: 1
num_members: 2
+ includes_all_repositories: false
can_create_org_repo: false
-
@@ -93,9 +102,10 @@
org_id: 25
lower_name: notowners
name: NotOwners
- authorize: 1 # owner
+ authorize: 1 # read
num_repos: 0
num_members: 1
+ includes_all_repositories: false
can_create_org_repo: false
-
@@ -106,6 +116,7 @@
authorize: 1 # read
num_repos: 0
num_members: 0
+ includes_all_repositories: false
can_create_org_repo: false
-
@@ -116,6 +127,7 @@
authorize: 3 # admin
num_repos: 0
num_members: 1
+ includes_all_repositories: false
can_create_org_repo: true
-
@@ -126,4 +138,5 @@
authorize: 3 # admin
num_repos: 0
num_members: 1
+ includes_all_repositories: false
can_create_org_repo: false
diff --git a/models/fixtures/team_user.yml b/models/fixtures/team_user.yml
index 8f21164df4..845741effd 100644
--- a/models/fixtures/team_user.yml
+++ b/models/fixtures/team_user.yml
@@ -87,3 +87,9 @@
org_id: 17
team_id: 9
uid: 29
+
+-
+ id: 16
+ org_id: 19
+ team_id: 6
+ uid: 31
diff --git a/models/fixtures/topic.yml b/models/fixtures/topic.yml
index 6cd0b37fa1..055addf510 100644
--- a/models/fixtures/topic.yml
+++ b/models/fixtures/topic.yml
@@ -8,18 +8,22 @@
name: database
repo_count: 1
-- id: 3
+-
+ id: 3
name: SQL
repo_count: 1
-- id: 4
+-
+ id: 4
name: graphql
repo_count: 1
-- id: 5
+-
+ id: 5
name: topicname1
repo_count: 1
-- id: 6
+-
+ id: 6
name: topicname2
repo_count: 2
diff --git a/models/fixtures/user.yml b/models/fixtures/user.yml
index 67ba869c76..0e3348e146 100644
--- a/models/fixtures/user.yml
+++ b/models/fixtures/user.yml
@@ -4,589 +4,1219 @@
id: 1
lower_name: user1
name: user1
- login_name: user1
full_name: User One
email: user1@example.com
+ keep_email_private: false
email_notifications_preference: enabled
+ passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b
passwd_hash_algo: argon2
- passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b # password
- type: 0 # individual
+ must_change_password: false
+ login_source: 0
+ login_name: user1
+ type: 0
salt: ZogKvWdyEx
+ max_repo_creation: -1
+ is_active: true
is_admin: true
+ is_restricted: false
+ allow_git_hook: false
+ allow_import_local: false
+ allow_create_organization: true
+ prohibit_login: false
avatar: avatar1
avatar_email: user1@example.com
+ use_custom_avatar: false
+ num_followers: 0
+ num_following: 0
+ num_stars: 0
num_repos: 0
- is_active: true
+ num_teams: 0
+ num_members: 0
+ visibility: 0
+ repo_admin_change_team_access: false
+ theme: ""
+ keep_activity_private: false
-
id: 2
lower_name: user2
name: user2
- login_name: user2
- full_name: " < Ur Tw >< "
+ full_name: ' < Ur Tw >< '
email: user2@example.com
keep_email_private: true
email_notifications_preference: enabled
+ passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b
passwd_hash_algo: argon2
- passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b # password
- type: 0 # individual
+ must_change_password: false
+ login_source: 0
+ login_name: user2
+ type: 0
salt: ZogKvWdyEx
+ max_repo_creation: -1
+ is_active: true
is_admin: false
+ is_restricted: false
+ allow_git_hook: false
+ allow_import_local: false
+ allow_create_organization: true
+ prohibit_login: false
avatar: avatar2
avatar_email: user2@example.com
- num_repos: 9
- num_stars: 2
+ use_custom_avatar: false
num_followers: 2
num_following: 1
- is_active: true
+ num_stars: 2
+ num_repos: 9
+ num_teams: 0
+ num_members: 0
+ visibility: 0
+ repo_admin_change_team_access: false
+ theme: ""
+ keep_activity_private: false
-
id: 3
lower_name: user3
name: user3
- login_name: user3
- full_name: " <<<< >> >> > >> > >>> >> "
+ full_name: ' <<<< >> >> > >> > >>> >> '
email: user3@example.com
+ keep_email_private: false
email_notifications_preference: onmention
+ passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b
passwd_hash_algo: argon2
- passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b # password
- type: 1 # organization
+ must_change_password: false
+ login_source: 0
+ login_name: user3
+ type: 1
salt: ZogKvWdyEx
+ max_repo_creation: -1
+ is_active: false
is_admin: false
+ is_restricted: false
+ allow_git_hook: false
+ allow_import_local: false
+ allow_create_organization: true
+ prohibit_login: false
avatar: avatar3
avatar_email: user3@example.com
+ use_custom_avatar: false
+ num_followers: 0
+ num_following: 0
+ num_stars: 0
num_repos: 3
- num_members: 3
num_teams: 4
+ num_members: 3
+ visibility: 0
+ repo_admin_change_team_access: false
+ theme: ""
+ keep_activity_private: false
-
id: 4
lower_name: user4
name: user4
- login_name: user4
- full_name: " "
+ full_name: ' '
email: user4@example.com
+ keep_email_private: false
email_notifications_preference: onmention
+ passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b
passwd_hash_algo: argon2
- passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b # password
- type: 0 # individual
+ must_change_password: false
+ login_source: 0
+ login_name: user4
+ type: 0
salt: ZogKvWdyEx
+ max_repo_creation: -1
+ is_active: true
is_admin: false
+ is_restricted: false
+ allow_git_hook: false
+ allow_import_local: false
+ allow_create_organization: true
+ prohibit_login: false
avatar: avatar4
avatar_email: user4@example.com
- num_repos: 0
+ use_custom_avatar: false
+ num_followers: 0
num_following: 1
- is_active: true
+ num_stars: 0
+ num_repos: 0
+ num_teams: 0
+ num_members: 0
+ visibility: 0
+ repo_admin_change_team_access: false
+ theme: ""
+ keep_activity_private: false
-
id: 5
lower_name: user5
name: user5
- login_name: user5
full_name: User Five
email: user5@example.com
+ keep_email_private: false
email_notifications_preference: enabled
+ passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b
passwd_hash_algo: argon2
- passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b # password
- type: 0 # individual
+ must_change_password: false
+ login_source: 0
+ login_name: user5
+ type: 0
salt: ZogKvWdyEx
+ max_repo_creation: -1
+ is_active: true
is_admin: false
+ is_restricted: false
+ allow_git_hook: false
+ allow_import_local: false
+ allow_create_organization: false
+ prohibit_login: false
avatar: avatar5
avatar_email: user5@example.com
- num_repos: 1
- allow_create_organization: false
- is_active: true
+ use_custom_avatar: false
+ num_followers: 0
num_following: 0
+ num_stars: 0
+ num_repos: 1
+ num_teams: 0
+ num_members: 0
+ visibility: 0
+ repo_admin_change_team_access: false
+ theme: ""
+ keep_activity_private: false
-
id: 6
lower_name: user6
name: user6
- login_name: user6
full_name: User Six
email: user6@example.com
+ keep_email_private: false
email_notifications_preference: enabled
+ passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b
passwd_hash_algo: argon2
- passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b # password
- type: 1 # organization
+ must_change_password: false
+ login_source: 0
+ login_name: user6
+ type: 1
salt: ZogKvWdyEx
+ max_repo_creation: -1
+ is_active: false
is_admin: false
+ is_restricted: false
+ allow_git_hook: false
+ allow_import_local: false
+ allow_create_organization: true
+ prohibit_login: false
avatar: avatar6
avatar_email: user6@example.com
+ use_custom_avatar: false
+ num_followers: 0
+ num_following: 0
+ num_stars: 0
num_repos: 0
- num_members: 2
num_teams: 2
+ num_members: 2
+ visibility: 0
+ repo_admin_change_team_access: false
+ theme: ""
+ keep_activity_private: false
-
id: 7
lower_name: user7
name: user7
- login_name: user7
full_name: User Seven
email: user7@example.com
+ keep_email_private: false
email_notifications_preference: disabled
+ passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b
passwd_hash_algo: argon2
- passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b # password
- type: 1 # organization
+ must_change_password: false
+ login_source: 0
+ login_name: user7
+ type: 1
salt: ZogKvWdyEx
+ max_repo_creation: -1
+ is_active: false
is_admin: false
+ is_restricted: false
+ allow_git_hook: false
+ allow_import_local: false
+ allow_create_organization: true
+ prohibit_login: false
avatar: avatar7
avatar_email: user7@example.com
+ use_custom_avatar: false
+ num_followers: 0
+ num_following: 0
+ num_stars: 0
num_repos: 0
- num_members: 1
num_teams: 1
+ num_members: 1
+ visibility: 0
+ repo_admin_change_team_access: false
+ theme: ""
+ keep_activity_private: false
-
id: 8
lower_name: user8
name: user8
- login_name: user8
full_name: User Eight
email: user8@example.com
+ keep_email_private: false
email_notifications_preference: enabled
+ passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b
passwd_hash_algo: argon2
- passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b # password
- type: 0 # individual
+ must_change_password: false
+ login_source: 0
+ login_name: user8
+ type: 0
salt: ZogKvWdyEx
+ max_repo_creation: -1
+ is_active: true
is_admin: false
+ is_restricted: false
+ allow_git_hook: false
+ allow_import_local: false
+ allow_create_organization: true
+ prohibit_login: false
avatar: avatar8
avatar_email: user8@example.com
- num_repos: 0
- is_active: true
+ use_custom_avatar: false
num_followers: 1
num_following: 1
+ num_stars: 0
+ num_repos: 0
+ num_teams: 0
+ num_members: 0
+ visibility: 0
+ repo_admin_change_team_access: false
+ theme: ""
+ keep_activity_private: false
-
id: 9
lower_name: user9
name: user9
- login_name: user9
full_name: User Nine
email: user9@example.com
+ keep_email_private: false
email_notifications_preference: onmention
+ passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b
passwd_hash_algo: argon2
- passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b # password
- type: 0 # individual
+ must_change_password: false
+ login_source: 0
+ login_name: user9
+ type: 0
salt: ZogKvWdyEx
+ max_repo_creation: -1
+ is_active: false
is_admin: false
+ is_restricted: false
+ allow_git_hook: false
+ allow_import_local: false
+ allow_create_organization: true
+ prohibit_login: false
avatar: avatar9
avatar_email: user9@example.com
+ use_custom_avatar: false
+ num_followers: 0
+ num_following: 0
+ num_stars: 0
num_repos: 0
- is_active: false
+ num_teams: 0
+ num_members: 0
+ visibility: 0
+ repo_admin_change_team_access: false
+ theme: ""
+ keep_activity_private: false
-
id: 10
lower_name: user10
name: user10
- login_name: user10
full_name: User Ten
email: user10@example.com
+ keep_email_private: false
+ email_notifications_preference: enabled
+ passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b
passwd_hash_algo: argon2
- passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b # password
- type: 0 # individual
+ must_change_password: false
+ login_source: 0
+ login_name: user10
+ type: 0
salt: ZogKvWdyEx
+ max_repo_creation: -1
+ is_active: true
is_admin: false
+ is_restricted: false
+ allow_git_hook: false
+ allow_import_local: false
+ allow_create_organization: true
+ prohibit_login: false
avatar: avatar10
avatar_email: user10@example.com
+ use_custom_avatar: false
+ num_followers: 0
+ num_following: 0
+ num_stars: 0
num_repos: 3
- is_active: true
+ num_teams: 0
+ num_members: 0
+ visibility: 0
+ repo_admin_change_team_access: false
+ theme: ""
+ keep_activity_private: false
-
id: 11
lower_name: user11
name: user11
- login_name: user11
full_name: User Eleven
email: user11@example.com
+ keep_email_private: false
+ email_notifications_preference: enabled
+ passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b
passwd_hash_algo: argon2
- passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b # password
- type: 0 # individual
+ must_change_password: false
+ login_source: 0
+ login_name: user11
+ type: 0
salt: ZogKvWdyEx
+ max_repo_creation: -1
+ is_active: true
is_admin: false
+ is_restricted: false
+ allow_git_hook: false
+ allow_import_local: false
+ allow_create_organization: true
+ prohibit_login: false
avatar: avatar11
avatar_email: user11@example.com
+ use_custom_avatar: false
+ num_followers: 0
+ num_following: 0
+ num_stars: 0
num_repos: 1
- is_active: true
+ num_teams: 0
+ num_members: 0
+ visibility: 0
+ repo_admin_change_team_access: false
+ theme: ""
+ keep_activity_private: false
-
id: 12
lower_name: user12
name: user12
- login_name: user12
full_name: User 12
email: user12@example.com
+ keep_email_private: false
+ email_notifications_preference: enabled
+ passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b
passwd_hash_algo: argon2
- passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b # password
- type: 0 # individual
+ must_change_password: false
+ login_source: 0
+ login_name: user12
+ type: 0
salt: ZogKvWdyEx
+ max_repo_creation: -1
+ is_active: true
is_admin: false
+ is_restricted: false
+ allow_git_hook: false
+ allow_import_local: false
+ allow_create_organization: true
+ prohibit_login: false
avatar: avatar12
avatar_email: user12@example.com
+ use_custom_avatar: false
+ num_followers: 0
+ num_following: 0
+ num_stars: 0
num_repos: 1
- is_active: true
+ num_teams: 0
+ num_members: 0
+ visibility: 0
+ repo_admin_change_team_access: false
+ theme: ""
+ keep_activity_private: false
-
id: 13
lower_name: user13
name: user13
- login_name: user13
full_name: User 13
email: user13@example.com
+ keep_email_private: false
+ email_notifications_preference: enabled
+ passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b
passwd_hash_algo: argon2
- passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b # password
- type: 0 # individual
+ must_change_password: false
+ login_source: 0
+ login_name: user13
+ type: 0
salt: ZogKvWdyEx
+ max_repo_creation: -1
+ is_active: true
is_admin: false
+ is_restricted: false
+ allow_git_hook: false
+ allow_import_local: false
+ allow_create_organization: true
+ prohibit_login: false
avatar: avatar13
avatar_email: user13@example.com
+ use_custom_avatar: false
+ num_followers: 0
+ num_following: 0
+ num_stars: 0
num_repos: 1
- is_active: true
+ num_teams: 0
+ num_members: 0
+ visibility: 0
+ repo_admin_change_team_access: false
+ theme: ""
+ keep_activity_private: false
-
id: 14
lower_name: user14
name: user14
- login_name: user14
full_name: User 14
email: user14@example.com
+ keep_email_private: false
+ email_notifications_preference: enabled
+ passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b
passwd_hash_algo: argon2
- passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b # password
- type: 0 # individual
+ must_change_password: false
+ login_source: 0
+ login_name: user14
+ type: 0
salt: ZogKvWdyEx
+ max_repo_creation: -1
+ is_active: true
is_admin: false
+ is_restricted: false
+ allow_git_hook: false
+ allow_import_local: false
+ allow_create_organization: true
+ prohibit_login: false
avatar: avatar14
avatar_email: user13@example.com
+ use_custom_avatar: false
+ num_followers: 0
+ num_following: 0
+ num_stars: 0
num_repos: 3
- is_active: true
+ num_teams: 0
+ num_members: 0
+ visibility: 0
+ repo_admin_change_team_access: false
+ theme: ""
+ keep_activity_private: false
-
id: 15
lower_name: user15
name: user15
- login_name: user15
full_name: User 15
email: user15@example.com
+ keep_email_private: false
+ email_notifications_preference: enabled
+ passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b
passwd_hash_algo: argon2
- passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b # password
- type: 0 # individual
+ must_change_password: false
+ login_source: 0
+ login_name: user15
+ type: 0
salt: ZogKvWdyEx
+ max_repo_creation: -1
+ is_active: true
is_admin: false
+ is_restricted: false
+ allow_git_hook: false
+ allow_import_local: false
+ allow_create_organization: true
+ prohibit_login: false
avatar: avatar15
avatar_email: user15@example.com
+ use_custom_avatar: false
+ num_followers: 0
+ num_following: 0
+ num_stars: 0
num_repos: 4
- is_active: true
+ num_teams: 0
+ num_members: 0
+ visibility: 0
+ repo_admin_change_team_access: false
+ theme: ""
+ keep_activity_private: false
-
id: 16
lower_name: user16
name: user16
- login_name: user16
full_name: User 16
email: user16@example.com
+ keep_email_private: false
+ email_notifications_preference: enabled
+ passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b
passwd_hash_algo: argon2
- passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b # password
- type: 0 # individual
+ must_change_password: false
+ login_source: 0
+ login_name: user16
+ type: 0
salt: ZogKvWdyEx
+ max_repo_creation: -1
+ is_active: true
is_admin: false
+ is_restricted: false
+ allow_git_hook: false
+ allow_import_local: false
+ allow_create_organization: true
+ prohibit_login: false
avatar: avatar16
avatar_email: user16@example.com
+ use_custom_avatar: false
+ num_followers: 0
+ num_following: 0
+ num_stars: 0
num_repos: 2
- is_active: true
+ num_teams: 0
+ num_members: 0
+ visibility: 0
+ repo_admin_change_team_access: false
+ theme: ""
+ keep_activity_private: false
-
id: 17
lower_name: user17
name: user17
- login_name: user17
full_name: User 17
email: user17@example.com
+ keep_email_private: false
+ email_notifications_preference: enabled
+ passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b
passwd_hash_algo: argon2
- passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b # password
- type: 1 # organization
+ must_change_password: false
+ login_source: 0
+ login_name: user17
+ type: 1
salt: ZogKvWdyEx
+ max_repo_creation: -1
+ is_active: true
is_admin: false
+ is_restricted: false
+ allow_git_hook: false
+ allow_import_local: false
+ allow_create_organization: true
+ prohibit_login: false
avatar: avatar17
avatar_email: user17@example.com
+ use_custom_avatar: false
+ num_followers: 0
+ num_following: 0
+ num_stars: 0
num_repos: 2
- is_active: true
- num_members: 3
num_teams: 3
+ num_members: 4
+ visibility: 0
+ repo_admin_change_team_access: false
+ theme: ""
+ keep_activity_private: false
-
id: 18
lower_name: user18
name: user18
- login_name: user18
full_name: User 18
email: user18@example.com
+ keep_email_private: false
+ email_notifications_preference: enabled
+ passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b
passwd_hash_algo: argon2
- passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b # password
- type: 0 # individual
+ must_change_password: false
+ login_source: 0
+ login_name: user18
+ type: 0
salt: ZogKvWdyEx
+ max_repo_creation: -1
+ is_active: true
is_admin: false
+ is_restricted: false
+ allow_git_hook: false
+ allow_import_local: false
+ allow_create_organization: true
+ prohibit_login: false
avatar: avatar18
avatar_email: user18@example.com
+ use_custom_avatar: false
+ num_followers: 0
+ num_following: 0
+ num_stars: 0
num_repos: 0
- is_active: true
+ num_teams: 0
+ num_members: 0
+ visibility: 0
+ repo_admin_change_team_access: false
+ theme: ""
+ keep_activity_private: false
-
id: 19
lower_name: user19
name: user19
- login_name: user19
full_name: User 19
email: user19@example.com
+ keep_email_private: false
+ email_notifications_preference: enabled
+ passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b
passwd_hash_algo: argon2
- passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b # password
- type: 1 # organization
+ must_change_password: false
+ login_source: 0
+ login_name: user19
+ type: 1
salt: ZogKvWdyEx
+ max_repo_creation: -1
+ is_active: true
is_admin: false
+ is_restricted: false
+ allow_git_hook: false
+ allow_import_local: false
+ allow_create_organization: true
+ prohibit_login: false
avatar: avatar19
avatar_email: user19@example.com
+ use_custom_avatar: false
+ num_followers: 0
+ num_following: 0
+ num_stars: 0
num_repos: 2
- is_active: true
- num_members: 1
num_teams: 1
+ num_members: 2
+ visibility: 0
+ repo_admin_change_team_access: false
+ theme: ""
+ keep_activity_private: false
-
id: 20
lower_name: user20
name: user20
- login_name: user20
full_name: User 20
email: user20@example.com
+ keep_email_private: false
+ email_notifications_preference: enabled
+ passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b
passwd_hash_algo: argon2
- passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b # password
- type: 0 # individual
+ must_change_password: false
+ login_source: 0
+ login_name: user20
+ type: 0
salt: ZogKvWdyEx
+ max_repo_creation: -1
+ is_active: true
is_admin: false
+ is_restricted: false
+ allow_git_hook: false
+ allow_import_local: false
+ allow_create_organization: true
+ prohibit_login: false
avatar: avatar20
avatar_email: user20@example.com
+ use_custom_avatar: false
+ num_followers: 0
+ num_following: 0
+ num_stars: 0
num_repos: 4
- is_active: true
+ num_teams: 0
+ num_members: 0
+ visibility: 0
+ repo_admin_change_team_access: false
+ theme: ""
+ keep_activity_private: false
-
id: 21
lower_name: user21
name: user21
- login_name: user21
full_name: User 21
email: user21@example.com
+ keep_email_private: false
+ email_notifications_preference: enabled
+ passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b
passwd_hash_algo: argon2
- passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b # password
- type: 0 # individual
+ must_change_password: false
+ login_source: 0
+ login_name: user21
+ type: 0
salt: ZogKvWdyEx
+ max_repo_creation: -1
+ is_active: true
is_admin: false
+ is_restricted: false
+ allow_git_hook: false
+ allow_import_local: false
+ allow_create_organization: true
+ prohibit_login: false
avatar: avatar21
avatar_email: user21@example.com
+ use_custom_avatar: false
+ num_followers: 0
+ num_following: 0
+ num_stars: 0
num_repos: 2
- is_active: true
+ num_teams: 0
+ num_members: 0
+ visibility: 0
+ repo_admin_change_team_access: false
+ theme: ""
+ keep_activity_private: false
-
id: 22
lower_name: limited_org
name: limited_org
- login_name: limited_org
full_name: Limited Org
email: limited_org@example.com
+ keep_email_private: false
+ email_notifications_preference: enabled
+ passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b
passwd_hash_algo: argon2
- passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b # password
- type: 1 # organization
+ must_change_password: false
+ login_source: 0
+ login_name: limited_org
+ type: 1
salt: ZogKvWdyEx
+ max_repo_creation: -1
+ is_active: true
is_admin: false
+ is_restricted: false
+ allow_git_hook: false
+ allow_import_local: false
+ allow_create_organization: true
+ prohibit_login: false
avatar: avatar22
avatar_email: limited_org@example.com
+ use_custom_avatar: false
+ num_followers: 0
+ num_following: 0
+ num_stars: 0
num_repos: 2
- is_active: true
- num_members: 0
num_teams: 0
+ num_members: 0
visibility: 1
+ repo_admin_change_team_access: false
+ theme: ""
+ keep_activity_private: false
-
id: 23
lower_name: privated_org
name: privated_org
- login_name: privated_org
full_name: Privated Org
email: privated_org@example.com
+ keep_email_private: false
+ email_notifications_preference: enabled
+ passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b
passwd_hash_algo: argon2
- passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b # password
- type: 1 # organization
+ must_change_password: false
+ login_source: 0
+ login_name: privated_org
+ type: 1
salt: ZogKvWdyEx
+ max_repo_creation: -1
+ is_active: true
is_admin: false
+ is_restricted: false
+ allow_git_hook: false
+ allow_import_local: false
+ allow_create_organization: true
+ prohibit_login: false
avatar: avatar23
avatar_email: privated_org@example.com
+ use_custom_avatar: false
+ num_followers: 0
+ num_following: 0
+ num_stars: 0
num_repos: 2
- is_active: true
- num_members: 0
num_teams: 0
+ num_members: 0
visibility: 2
+ repo_admin_change_team_access: false
+ theme: ""
+ keep_activity_private: false
-
id: 24
lower_name: user24
name: user24
- login_name: user24
- full_name: "user24"
+ full_name: user24
email: user24@example.com
keep_email_private: true
+ email_notifications_preference: enabled
+ passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b
passwd_hash_algo: argon2
- passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b # password
- type: 0 # individual
+ must_change_password: false
+ login_source: 0
+ login_name: user24
+ type: 0
salt: ZogKvWdyEx
+ max_repo_creation: -1
+ is_active: true
is_admin: false
+ is_restricted: false
+ allow_git_hook: false
+ allow_import_local: false
+ allow_create_organization: true
+ prohibit_login: false
avatar: avatar24
avatar_email: user24@example.com
- num_repos: 0
- num_stars: 0
+ use_custom_avatar: false
num_followers: 0
num_following: 0
- is_active: true
+ num_stars: 0
+ num_repos: 0
+ num_teams: 0
+ num_members: 0
+ visibility: 0
+ repo_admin_change_team_access: false
+ theme: ""
+ keep_activity_private: false
-
id: 25
lower_name: org25
name: org25
- login_name: org25
- full_name: "org25"
+ full_name: org25
email: org25@example.com
+ keep_email_private: false
+ email_notifications_preference: enabled
+ passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b
passwd_hash_algo: argon2
- passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b # password
- type: 1 # organization
+ must_change_password: false
+ login_source: 0
+ login_name: org25
+ type: 1
salt: ZogKvWdyEx
+ max_repo_creation: -1
+ is_active: false
is_admin: false
+ is_restricted: false
+ allow_git_hook: false
+ allow_import_local: false
+ allow_create_organization: true
+ prohibit_login: false
avatar: avatar25
avatar_email: org25@example.com
+ use_custom_avatar: false
+ num_followers: 0
+ num_following: 0
+ num_stars: 0
num_repos: 0
- num_members: 1
num_teams: 1
+ num_members: 1
+ visibility: 0
+ repo_admin_change_team_access: false
+ theme: ""
+ keep_activity_private: false
-
id: 26
lower_name: org26
name: org26
- login_name: org26
- full_name: "Org26"
+ full_name: Org26
email: org26@example.com
+ keep_email_private: false
email_notifications_preference: onmention
+ passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b
passwd_hash_algo: argon2
- passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b # password
- type: 1 # organization
+ must_change_password: false
+ login_source: 0
+ login_name: org26
+ type: 1
salt: ZogKvWdyEx
+ max_repo_creation: -1
+ is_active: false
is_admin: false
+ is_restricted: false
+ allow_git_hook: false
+ allow_import_local: false
+ allow_create_organization: true
+ prohibit_login: false
avatar: avatar26
avatar_email: org26@example.com
+ use_custom_avatar: false
+ num_followers: 0
+ num_following: 0
+ num_stars: 0
num_repos: 4
- num_members: 0
num_teams: 1
+ num_members: 0
+ visibility: 0
repo_admin_change_team_access: true
+ theme: ""
+ keep_activity_private: false
-
id: 27
lower_name: user27
name: user27
- login_name: user27
full_name: User Twenty-Seven
email: user27@example.com
+ keep_email_private: false
email_notifications_preference: enabled
+ passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b
passwd_hash_algo: argon2
- passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b # password
- type: 0 # individual
+ must_change_password: false
+ login_source: 0
+ login_name: user27
+ type: 0
salt: ZogKvWdyEx
+ max_repo_creation: -1
+ is_active: true
is_admin: false
+ is_restricted: false
+ allow_git_hook: false
+ allow_import_local: false
+ allow_create_organization: true
+ prohibit_login: false
avatar: avatar27
avatar_email: user27@example.com
+ use_custom_avatar: false
+ num_followers: 0
+ num_following: 0
+ num_stars: 0
num_repos: 3
+ num_teams: 0
+ num_members: 0
+ visibility: 0
+ repo_admin_change_team_access: false
+ theme: ""
+ keep_activity_private: false
-
id: 28
lower_name: user28
name: user28
- login_name: user28
- full_name: "user27"
+ full_name: user27
email: user28@example.com
keep_email_private: true
+ email_notifications_preference: enabled
+ passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b
passwd_hash_algo: argon2
- passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b # password
- type: 0 # individual
+ must_change_password: false
+ login_source: 0
+ login_name: user28
+ type: 0
salt: ZogKvWdyEx
+ max_repo_creation: -1
+ is_active: true
is_admin: false
+ is_restricted: false
+ allow_git_hook: false
+ allow_import_local: false
+ allow_create_organization: true
+ prohibit_login: false
avatar: avatar28
avatar_email: user28@example.com
- num_repos: 0
- num_stars: 0
+ use_custom_avatar: false
num_followers: 0
num_following: 0
- is_active: true
+ num_stars: 0
+ num_repos: 0
+ num_teams: 0
+ num_members: 0
+ visibility: 0
+ repo_admin_change_team_access: false
+ theme: ""
+ keep_activity_private: false
-
id: 29
lower_name: user29
name: user29
- login_name: user29
full_name: User 29
email: user29@example.com
+ keep_email_private: false
+ email_notifications_preference: enabled
+ passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b
passwd_hash_algo: argon2
- passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b # password
- type: 0 # individual
+ must_change_password: false
+ login_source: 0
+ login_name: user29
+ type: 0
salt: ZogKvWdyEx
+ max_repo_creation: -1
+ is_active: true
is_admin: false
is_restricted: true
+ allow_git_hook: false
+ allow_import_local: false
+ allow_create_organization: true
+ prohibit_login: false
avatar: avatar29
avatar_email: user29@example.com
+ use_custom_avatar: false
+ num_followers: 0
+ num_following: 0
+ num_stars: 0
num_repos: 0
- is_active: true
+ num_teams: 0
+ num_members: 0
+ visibility: 0
+ repo_admin_change_team_access: false
+ theme: ""
+ keep_activity_private: false
-
id: 30
lower_name: user30
name: user30
- login_name: user30
full_name: User Thirty
email: user30@example.com
+ keep_email_private: false
+ email_notifications_preference: enabled
+ passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b
passwd_hash_algo: argon2
- passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b # password
- type: 0 # individual
+ must_change_password: false
+ login_source: 0
+ login_name: user30
+ type: 0
salt: ZogKvWdyEx
+ max_repo_creation: -1
+ is_active: true
is_admin: false
is_restricted: true
+ allow_git_hook: false
+ allow_import_local: false
+ allow_create_organization: true
+ prohibit_login: true
avatar: avatar29
avatar_email: user30@example.com
+ use_custom_avatar: false
+ num_followers: 0
+ num_following: 0
+ num_stars: 0
num_repos: 3
- is_active: true
- prohibit_login: true
+ num_teams: 0
+ num_members: 0
+ visibility: 0
+ repo_admin_change_team_access: false
+ theme: ""
+ keep_activity_private: false
-
id: 31
lower_name: user31
name: user31
- login_name: user31
- full_name: "user31"
+ full_name: user31
email: user31@example.com
+ keep_email_private: false
+ email_notifications_preference: enabled
+ passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b
passwd_hash_algo: argon2
- passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b # password
- type: 0 # individual
+ must_change_password: false
+ login_source: 0
+ login_name: user31
+ type: 0
salt: ZogKvWdyEx
+ max_repo_creation: -1
+ is_active: true
is_admin: false
- visibility: 2
+ is_restricted: false
+ allow_git_hook: false
+ allow_import_local: false
+ allow_create_organization: true
+ prohibit_login: false
avatar: avatar31
avatar_email: user31@example.com
+ use_custom_avatar: false
+ num_followers: 0
+ num_following: 1
+ num_stars: 0
num_repos: 0
- is_active: true
+ num_teams: 0
+ num_members: 0
+ visibility: 2
+ repo_admin_change_team_access: false
+ theme: ""
+ keep_activity_private: false
-
id: 32
lower_name: user32
name: user32
- login_name: user32
full_name: User 32 (U2F test)
email: user32@example.com
- passwd: 7d93daa0d1e6f2305cc8fa496847d61dc7320bb16262f9c55dd753480207234cdd96a93194e408341971742f4701772a025a # password
- type: 0 # individual
+ keep_email_private: false
+ email_notifications_preference: enabled
+ passwd: 7d93daa0d1e6f2305cc8fa496847d61dc7320bb16262f9c55dd753480207234cdd96a93194e408341971742f4701772a025a
+ passwd_hash_algo: argon2
+ must_change_password: false
+ login_source: 0
+ login_name: user32
+ type: 0
salt: ZogKvWdyEx
+ max_repo_creation: -1
+ is_active: true
is_admin: false
is_restricted: false
+ allow_git_hook: false
+ allow_import_local: false
+ allow_create_organization: true
+ prohibit_login: false
avatar: avatar32
avatar_email: user30@example.com
+ use_custom_avatar: false
+ num_followers: 0
+ num_following: 0
+ num_stars: 0
num_repos: 0
+ num_teams: 0
+ num_members: 0
+ visibility: 0
+ repo_admin_change_team_access: false
+ theme: ""
+ keep_activity_private: false
+
+-
+ id: 33
+ lower_name: user33
+ name: user33
+ full_name: User 33 (Limited Visibility)
+ email: user33@example.com
+ keep_email_private: false
+ email_notifications_preference: enabled
+ passwd: a3d5fcd92bae586c2e3dbe72daea7a0d27833a8d0227aa1704f4bbd775c1f3b03535b76dd93b0d4d8d22a519dca47df1547b
+ passwd_hash_algo: argon2
+ must_change_password: false
+ login_source: 0
+ login_name: user33
+ type: 0
+ salt: ZogKvWdyEx
+ max_repo_creation: -1
is_active: true
+ is_admin: false
+ is_restricted: false
+ allow_git_hook: false
+ allow_import_local: false
+ allow_create_organization: true
+ prohibit_login: false
+ avatar: avatar33
+ avatar_email: user33@example.com
+ use_custom_avatar: false
+ num_followers: 1
+ num_following: 0
+ num_stars: 0
+ num_repos: 0
+ num_teams: 0
+ num_members: 0
+ visibility: 1
+ repo_admin_change_team_access: false
+ theme: ""
+ keep_activity_private: false
diff --git a/models/fixtures/webauthn_credential.yml b/models/fixtures/webauthn_credential.yml
index b4109a03f2..bc43127fcd 100644
--- a/models/fixtures/webauthn_credential.yml
+++ b/models/fixtures/webauthn_credential.yml
@@ -1,5 +1,6 @@
-- id: 1
- name: "WebAuthn credential"
+-
+ id: 1
+ name: WebAuthn credential
user_id: 32
attestation_type: none
sign_count: 0
diff --git a/models/foreignreference/error.go b/models/foreignreference/error.go
index d783a08730..a1db773cd2 100644
--- a/models/foreignreference/error.go
+++ b/models/foreignreference/error.go
@@ -6,6 +6,8 @@ package foreignreference
import (
"fmt"
+
+ "code.gitea.io/gitea/modules/util"
)
// ErrLocalIndexNotExist represents a "LocalIndexNotExist" kind of error.
@@ -25,6 +27,10 @@ func (err ErrLocalIndexNotExist) Error() string {
return fmt.Sprintf("repository %d has no LocalIndex for ForeignIndex %d of type %s", err.RepoID, err.ForeignIndex, err.Type)
}
+func (err ErrLocalIndexNotExist) Unwrap() error {
+ return util.ErrNotExist
+}
+
// ErrForeignIndexNotExist represents a "ForeignIndexNotExist" kind of error.
type ErrForeignIndexNotExist struct {
RepoID int64
@@ -41,3 +47,7 @@ func IsErrForeignIndexNotExist(err error) bool {
func (err ErrForeignIndexNotExist) Error() string {
return fmt.Sprintf("repository %d has no ForeignIndex for LocalIndex %d of type %s", err.RepoID, err.LocalIndex, err.Type)
}
+
+func (err ErrForeignIndexNotExist) Unwrap() error {
+ return util.ErrNotExist
+}
diff --git a/models/git/branches_test.go b/models/git/branches_test.go
index 8102d28d48..58c4ad027b 100644
--- a/models/git/branches_test.go
+++ b/models/git/branches_test.go
@@ -18,8 +18,8 @@ import (
func TestAddDeletedBranch(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository)
- firstBranch := unittest.AssertExistsAndLoadBean(t, &git_model.DeletedBranch{ID: 1}).(*git_model.DeletedBranch)
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
+ firstBranch := unittest.AssertExistsAndLoadBean(t, &git_model.DeletedBranch{ID: 1})
assert.Error(t, git_model.AddDeletedBranch(repo.ID, firstBranch.Name, firstBranch.Commit, firstBranch.DeletedByID))
assert.NoError(t, git_model.AddDeletedBranch(repo.ID, "test", "5655464564554545466464656", int64(1)))
@@ -27,7 +27,7 @@ func TestAddDeletedBranch(t *testing.T) {
func TestGetDeletedBranches(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository)
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
branches, err := git_model.GetDeletedBranches(repo.ID)
assert.NoError(t, err)
@@ -36,7 +36,7 @@ func TestGetDeletedBranches(t *testing.T) {
func TestGetDeletedBranch(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- firstBranch := unittest.AssertExistsAndLoadBean(t, &git_model.DeletedBranch{ID: 1}).(*git_model.DeletedBranch)
+ firstBranch := unittest.AssertExistsAndLoadBean(t, &git_model.DeletedBranch{ID: 1})
assert.NotNil(t, getDeletedBranch(t, firstBranch))
}
@@ -44,8 +44,8 @@ func TestGetDeletedBranch(t *testing.T) {
func TestDeletedBranchLoadUser(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- firstBranch := unittest.AssertExistsAndLoadBean(t, &git_model.DeletedBranch{ID: 1}).(*git_model.DeletedBranch)
- secondBranch := unittest.AssertExistsAndLoadBean(t, &git_model.DeletedBranch{ID: 2}).(*git_model.DeletedBranch)
+ firstBranch := unittest.AssertExistsAndLoadBean(t, &git_model.DeletedBranch{ID: 1})
+ secondBranch := unittest.AssertExistsAndLoadBean(t, &git_model.DeletedBranch{ID: 2})
branch := getDeletedBranch(t, firstBranch)
assert.Nil(t, branch.DeletedBy)
@@ -62,9 +62,9 @@ func TestDeletedBranchLoadUser(t *testing.T) {
func TestRemoveDeletedBranch(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository)
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
- firstBranch := unittest.AssertExistsAndLoadBean(t, &git_model.DeletedBranch{ID: 1}).(*git_model.DeletedBranch)
+ firstBranch := unittest.AssertExistsAndLoadBean(t, &git_model.DeletedBranch{ID: 1})
err := git_model.RemoveDeletedBranchByID(repo.ID, 1)
assert.NoError(t, err)
@@ -73,7 +73,7 @@ func TestRemoveDeletedBranch(t *testing.T) {
}
func getDeletedBranch(t *testing.T, branch *git_model.DeletedBranch) *git_model.DeletedBranch {
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository)
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
deletedBranch, err := git_model.GetDeletedBranchByID(repo.ID, branch.ID)
assert.NoError(t, err)
@@ -99,7 +99,7 @@ func TestFindRenamedBranch(t *testing.T) {
func TestRenameBranch(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository)
+ repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
_isDefault := false
ctx, committer, err := db.TxContext()
@@ -117,16 +117,16 @@ func TestRenameBranch(t *testing.T) {
}))
assert.Equal(t, true, _isDefault)
- repo1 = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository)
+ repo1 = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
assert.Equal(t, "main", repo1.DefaultBranch)
- pull := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1}).(*issues_model.PullRequest) // merged
+ pull := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1}) // merged
assert.Equal(t, "master", pull.BaseBranch)
- pull = unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 2}).(*issues_model.PullRequest) // open
+ pull = unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 2}) // open
assert.Equal(t, "main", pull.BaseBranch)
- renamedBranch := unittest.AssertExistsAndLoadBean(t, &git_model.RenamedBranch{ID: 2}).(*git_model.RenamedBranch)
+ renamedBranch := unittest.AssertExistsAndLoadBean(t, &git_model.RenamedBranch{ID: 2})
assert.Equal(t, "master", renamedBranch.From)
assert.Equal(t, "main", renamedBranch.To)
assert.Equal(t, int64(1), renamedBranch.RepoID)
@@ -143,7 +143,7 @@ func TestOnlyGetDeletedBranchOnCorrectRepo(t *testing.T) {
// Get deletedBranch with ID of 1 on repo with ID 2.
// This should return a nil branch as this deleted branch
// is actually on repo with ID 1.
- repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}).(*repo_model.Repository)
+ repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2})
deletedBranch, err := git_model.GetDeletedBranchByID(repo2.ID, 1)
@@ -153,7 +153,7 @@ func TestOnlyGetDeletedBranchOnCorrectRepo(t *testing.T) {
// Now get the deletedBranch with ID of 1 on repo with ID 1.
// This should return the deletedBranch.
- repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository)
+ repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
deletedBranch, err = git_model.GetDeletedBranchByID(repo1.ID, 1)
diff --git a/models/git/commit_status_test.go b/models/git/commit_status_test.go
index 9919297430..7b81b1549c 100644
--- a/models/git/commit_status_test.go
+++ b/models/git/commit_status_test.go
@@ -19,7 +19,7 @@ import (
func TestGetCommitStatuses(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository)
+ repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
sha1 := "1234123412341234123412341234123412341234"
diff --git a/models/git/lfs.go b/models/git/lfs.go
index ec963cf593..1dab31d5f9 100644
--- a/models/git/lfs.go
+++ b/models/git/lfs.go
@@ -6,7 +6,6 @@ package git
import (
"context"
- "errors"
"fmt"
"code.gitea.io/gitea/models/db"
@@ -17,6 +16,7 @@ import (
"code.gitea.io/gitea/modules/lfs"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/timeutil"
+ "code.gitea.io/gitea/modules/util"
"xorm.io/builder"
)
@@ -38,6 +38,10 @@ func (err ErrLFSLockNotExist) Error() string {
return fmt.Sprintf("lfs lock does not exist [id: %d, rid: %d, path: %s]", err.ID, err.RepoID, err.Path)
}
+func (err ErrLFSLockNotExist) Unwrap() error {
+ return util.ErrNotExist
+}
+
// ErrLFSUnauthorizedAction represents a "LFSUnauthorizedAction" kind of error.
type ErrLFSUnauthorizedAction struct {
RepoID int64
@@ -58,6 +62,10 @@ func (err ErrLFSUnauthorizedAction) Error() string {
return fmt.Sprintf("User %s doesn't have read access for lfs lock [rid: %d]", err.UserName, err.RepoID)
}
+func (err ErrLFSUnauthorizedAction) Unwrap() error {
+ return util.ErrPermissionDenied
+}
+
// ErrLFSLockAlreadyExist represents a "LFSLockAlreadyExist" kind of error.
type ErrLFSLockAlreadyExist struct {
RepoID int64
@@ -74,6 +82,10 @@ func (err ErrLFSLockAlreadyExist) Error() string {
return fmt.Sprintf("lfs lock already exists [rid: %d, path: %s]", err.RepoID, err.Path)
}
+func (err ErrLFSLockAlreadyExist) Unwrap() error {
+ return util.ErrAlreadyExist
+}
+
// ErrLFSFileLocked represents a "LFSFileLocked" kind of error.
type ErrLFSFileLocked struct {
RepoID int64
@@ -91,6 +103,10 @@ func (err ErrLFSFileLocked) Error() string {
return fmt.Sprintf("File is lfs locked [repo: %d, locked by: %s, path: %s]", err.RepoID, err.UserName, err.Path)
}
+func (err ErrLFSFileLocked) Unwrap() error {
+ return util.ErrPermissionDenied
+}
+
// LFSMetaObject stores metadata for LFS tracked files.
type LFSMetaObject struct {
ID int64 `xorm:"pk autoincr"`
@@ -114,7 +130,7 @@ type LFSTokenResponse struct {
// ErrLFSObjectNotExist is returned from lfs models functions in order
// to differentiate between database and missing object errors.
-var ErrLFSObjectNotExist = errors.New("LFS Meta object does not exist")
+var ErrLFSObjectNotExist = db.ErrNotExist{Resource: "LFS Meta object"}
// NewLFSMetaObject stores a given populated LFSMetaObject structure in the database
// if it is not already present.
@@ -278,29 +294,6 @@ func LFSAutoAssociate(metas []*LFSMetaObject, user *user_model.User, repoID int6
return committer.Commit()
}
-// IterateLFS iterates lfs object
-func IterateLFS(f func(mo *LFSMetaObject) error) error {
- var start int
- const batchSize = 100
- e := db.GetEngine(db.DefaultContext)
- for {
- mos := make([]*LFSMetaObject, 0, batchSize)
- if err := e.Limit(batchSize, start).Find(&mos); err != nil {
- return err
- }
- if len(mos) == 0 {
- return nil
- }
- start += len(mos)
-
- for _, mo := range mos {
- if err := f(mo); err != nil {
- return err
- }
- }
- }
-}
-
// CopyLFS copies LFS data from one repo to another
func CopyLFS(ctx context.Context, newRepo, oldRepo *repo_model.Repository) error {
var lfsObjects []*LFSMetaObject
diff --git a/models/issues/assignees_test.go b/models/issues/assignees_test.go
index 37d966f140..291bb673da 100644
--- a/models/issues/assignees_test.go
+++ b/models/issues/assignees_test.go
@@ -68,8 +68,8 @@ func TestUpdateAssignee(t *testing.T) {
func TestMakeIDsFromAPIAssigneesToAdd(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- _ = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User)
- _ = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
+ _ = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
+ _ = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
IDs, err := issues_model.MakeIDsFromAPIAssigneesToAdd("", []string{""})
assert.NoError(t, err)
diff --git a/models/issues/comment.go b/models/issues/comment.go
index a71afda9e0..9ab6cab7d0 100644
--- a/models/issues/comment.go
+++ b/models/issues/comment.go
@@ -28,6 +28,7 @@ import (
"code.gitea.io/gitea/modules/references"
"code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/timeutil"
+ "code.gitea.io/gitea/modules/util"
"xorm.io/builder"
"xorm.io/xorm"
@@ -49,6 +50,10 @@ func (err ErrCommentNotExist) Error() string {
return fmt.Sprintf("comment does not exist [id: %d, issue_id: %d]", err.ID, err.IssueID)
}
+func (err ErrCommentNotExist) Unwrap() error {
+ return util.ErrNotExist
+}
+
// CommentType defines whether a comment is just a simple comment, an action (like close) or a reference.
type CommentType int
diff --git a/models/issues/comment_list.go b/models/issues/comment_list.go
index e3406a5cbe..70105d7ff0 100644
--- a/models/issues/comment_list.go
+++ b/models/issues/comment_list.go
@@ -17,13 +17,11 @@ import (
type CommentList []*Comment
func (comments CommentList) getPosterIDs() []int64 {
- posterIDs := make(map[int64]struct{}, len(comments))
+ posterIDs := make(container.Set[int64], len(comments))
for _, comment := range comments {
- if _, ok := posterIDs[comment.PosterID]; !ok {
- posterIDs[comment.PosterID] = struct{}{}
- }
+ posterIDs.Add(comment.PosterID)
}
- return container.KeysInt64(posterIDs)
+ return posterIDs.Values()
}
func (comments CommentList) loadPosters(ctx context.Context) error {
@@ -70,13 +68,11 @@ func (comments CommentList) getCommentIDs() []int64 {
}
func (comments CommentList) getLabelIDs() []int64 {
- ids := make(map[int64]struct{}, len(comments))
+ ids := make(container.Set[int64], len(comments))
for _, comment := range comments {
- if _, ok := ids[comment.LabelID]; !ok {
- ids[comment.LabelID] = struct{}{}
- }
+ ids.Add(comment.LabelID)
}
- return container.KeysInt64(ids)
+ return ids.Values()
}
func (comments CommentList) loadLabels(ctx context.Context) error { //nolint
@@ -120,13 +116,11 @@ func (comments CommentList) loadLabels(ctx context.Context) error { //nolint
}
func (comments CommentList) getMilestoneIDs() []int64 {
- ids := make(map[int64]struct{}, len(comments))
+ ids := make(container.Set[int64], len(comments))
for _, comment := range comments {
- if _, ok := ids[comment.MilestoneID]; !ok {
- ids[comment.MilestoneID] = struct{}{}
- }
+ ids.Add(comment.MilestoneID)
}
- return container.KeysInt64(ids)
+ return ids.Values()
}
func (comments CommentList) loadMilestones(ctx context.Context) error {
@@ -163,13 +157,11 @@ func (comments CommentList) loadMilestones(ctx context.Context) error {
}
func (comments CommentList) getOldMilestoneIDs() []int64 {
- ids := make(map[int64]struct{}, len(comments))
+ ids := make(container.Set[int64], len(comments))
for _, comment := range comments {
- if _, ok := ids[comment.OldMilestoneID]; !ok {
- ids[comment.OldMilestoneID] = struct{}{}
- }
+ ids.Add(comment.OldMilestoneID)
}
- return container.KeysInt64(ids)
+ return ids.Values()
}
func (comments CommentList) loadOldMilestones(ctx context.Context) error {
@@ -206,13 +198,11 @@ func (comments CommentList) loadOldMilestones(ctx context.Context) error {
}
func (comments CommentList) getAssigneeIDs() []int64 {
- ids := make(map[int64]struct{}, len(comments))
+ ids := make(container.Set[int64], len(comments))
for _, comment := range comments {
- if _, ok := ids[comment.AssigneeID]; !ok {
- ids[comment.AssigneeID] = struct{}{}
- }
+ ids.Add(comment.AssigneeID)
}
- return container.KeysInt64(ids)
+ return ids.Values()
}
func (comments CommentList) loadAssignees(ctx context.Context) error {
@@ -259,16 +249,14 @@ func (comments CommentList) loadAssignees(ctx context.Context) error {
// getIssueIDs returns all the issue ids on this comment list which issue hasn't been loaded
func (comments CommentList) getIssueIDs() []int64 {
- ids := make(map[int64]struct{}, len(comments))
+ ids := make(container.Set[int64], len(comments))
for _, comment := range comments {
if comment.Issue != nil {
continue
}
- if _, ok := ids[comment.IssueID]; !ok {
- ids[comment.IssueID] = struct{}{}
- }
+ ids.Add(comment.IssueID)
}
- return container.KeysInt64(ids)
+ return ids.Values()
}
// Issues returns all the issues of comments
@@ -334,16 +322,14 @@ func (comments CommentList) loadIssues(ctx context.Context) error {
}
func (comments CommentList) getDependentIssueIDs() []int64 {
- ids := make(map[int64]struct{}, len(comments))
+ ids := make(container.Set[int64], len(comments))
for _, comment := range comments {
if comment.DependentIssue != nil {
continue
}
- if _, ok := ids[comment.DependentIssueID]; !ok {
- ids[comment.DependentIssueID] = struct{}{}
- }
+ ids.Add(comment.DependentIssueID)
}
- return container.KeysInt64(ids)
+ return ids.Values()
}
func (comments CommentList) loadDependentIssues(ctx context.Context) error {
@@ -439,13 +425,11 @@ func (comments CommentList) loadAttachments(ctx context.Context) (err error) {
}
func (comments CommentList) getReviewIDs() []int64 {
- ids := make(map[int64]struct{}, len(comments))
+ ids := make(container.Set[int64], len(comments))
for _, comment := range comments {
- if _, ok := ids[comment.ReviewID]; !ok {
- ids[comment.ReviewID] = struct{}{}
- }
+ ids.Add(comment.ReviewID)
}
- return container.KeysInt64(ids)
+ return ids.Values()
}
func (comments CommentList) loadReviews(ctx context.Context) error { //nolint
diff --git a/models/issues/comment_test.go b/models/issues/comment_test.go
index 06b0b85e3c..f12da0177f 100644
--- a/models/issues/comment_test.go
+++ b/models/issues/comment_test.go
@@ -20,9 +20,9 @@ import (
func TestCreateComment(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{}).(*issues_model.Issue)
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID}).(*repo_model.Repository)
- doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User)
+ issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{})
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID})
+ doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
now := time.Now().Unix()
comment, err := issues_model.CreateComment(&issues_model.CreateCommentOptions{
@@ -42,15 +42,15 @@ func TestCreateComment(t *testing.T) {
unittest.AssertInt64InRange(t, now, then, int64(comment.CreatedUnix))
unittest.AssertExistsAndLoadBean(t, comment) // assert actually added to DB
- updatedIssue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: issue.ID}).(*issues_model.Issue)
+ updatedIssue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: issue.ID})
unittest.AssertInt64InRange(t, now, then, int64(updatedIssue.UpdatedUnix))
}
func TestFetchCodeComments(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 2}).(*issues_model.Issue)
- user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User)
+ issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 2})
+ user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
res, err := issues_model.FetchCodeComments(db.DefaultContext, issue, user)
assert.NoError(t, err)
assert.Contains(t, res, "README.md")
@@ -58,7 +58,7 @@ func TestFetchCodeComments(t *testing.T) {
assert.Len(t, res["README.md"][4], 1)
assert.Equal(t, int64(4), res["README.md"][4][0].ID)
- user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
+ user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
res, err = issues_model.FetchCodeComments(db.DefaultContext, issue, user2)
assert.NoError(t, err)
assert.Len(t, res, 1)
diff --git a/models/issues/content_history.go b/models/issues/content_history.go
index 3e321784bd..f5cfa65b8f 100644
--- a/models/issues/content_history.go
+++ b/models/issues/content_history.go
@@ -12,6 +12,7 @@ import (
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/timeutil"
+ "code.gitea.io/gitea/modules/util"
"xorm.io/builder"
)
@@ -201,6 +202,10 @@ func (err ErrIssueContentHistoryNotExist) Error() string {
return fmt.Sprintf("issue content history does not exist [id: %d]", err.ID)
}
+func (err ErrIssueContentHistoryNotExist) Unwrap() error {
+ return util.ErrNotExist
+}
+
// GetIssueContentHistoryByID get issue content history
func GetIssueContentHistoryByID(dbCtx context.Context, id int64) (*ContentHistory, error) {
h := &ContentHistory{}
diff --git a/models/issues/dependency.go b/models/issues/dependency.go
index d664c0758e..4754ed0f5f 100644
--- a/models/issues/dependency.go
+++ b/models/issues/dependency.go
@@ -11,6 +11,7 @@ import (
"code.gitea.io/gitea/models/db"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/timeutil"
+ "code.gitea.io/gitea/modules/util"
)
// ErrDependencyExists represents a "DependencyAlreadyExists" kind of error.
@@ -29,6 +30,10 @@ func (err ErrDependencyExists) Error() string {
return fmt.Sprintf("issue dependency does already exist [issue id: %d, dependency id: %d]", err.IssueID, err.DependencyID)
}
+func (err ErrDependencyExists) Unwrap() error {
+ return util.ErrAlreadyExist
+}
+
// ErrDependencyNotExists represents a "DependencyAlreadyExists" kind of error.
type ErrDependencyNotExists struct {
IssueID int64
@@ -45,6 +50,10 @@ func (err ErrDependencyNotExists) Error() string {
return fmt.Sprintf("issue dependency does not exist [issue id: %d, dependency id: %d]", err.IssueID, err.DependencyID)
}
+func (err ErrDependencyNotExists) Unwrap() error {
+ return util.ErrNotExist
+}
+
// ErrCircularDependency represents a "DependencyCircular" kind of error.
type ErrCircularDependency struct {
IssueID int64
@@ -91,6 +100,10 @@ func (err ErrUnknownDependencyType) Error() string {
return fmt.Sprintf("unknown dependency type [type: %d]", err.Type)
}
+func (err ErrUnknownDependencyType) Unwrap() error {
+ return util.ErrInvalidArgument
+}
+
// IssueDependency represents an issue dependency
type IssueDependency struct {
ID int64 `xorm:"pk autoincr"`
diff --git a/models/issues/issue.go b/models/issues/issue.go
index 064f0d22ab..f77166db11 100644
--- a/models/issues/issue.go
+++ b/models/issues/issue.go
@@ -13,7 +13,6 @@ import (
"strconv"
"strings"
- admin_model "code.gitea.io/gitea/models/admin"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/foreignreference"
"code.gitea.io/gitea/models/organization"
@@ -21,13 +20,13 @@ import (
access_model "code.gitea.io/gitea/models/perm/access"
project_model "code.gitea.io/gitea/models/project"
repo_model "code.gitea.io/gitea/models/repo"
+ system_model "code.gitea.io/gitea/models/system"
"code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/references"
- "code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util"
@@ -53,6 +52,10 @@ func (err ErrIssueNotExist) Error() string {
return fmt.Sprintf("issue does not exist [id: %d, repo_id: %d, index: %d]", err.ID, err.RepoID, err.Index)
}
+func (err ErrIssueNotExist) Unwrap() error {
+ return util.ErrNotExist
+}
+
// ErrIssueIsClosed represents a "IssueIsClosed" kind of error.
type ErrIssueIsClosed struct {
ID int64
@@ -1065,19 +1068,19 @@ func NewIssueWithIndex(ctx context.Context, doer *user_model.User, opts NewIssue
// NewIssue creates new issue with labels for repository.
func NewIssue(repo *repo_model.Repository, issue *Issue, labelIDs []int64, uuids []string) (err error) {
- idx, err := db.GetNextResourceIndex("issue_index", repo.ID)
- if err != nil {
- return fmt.Errorf("generate issue index failed: %v", err)
- }
-
- issue.Index = idx
-
ctx, committer, err := db.TxContext()
if err != nil {
return err
}
defer committer.Close()
+ idx, err := db.GetNextResourceIndex(ctx, "issue_index", repo.ID)
+ if err != nil {
+ return fmt.Errorf("generate issue index failed: %w", err)
+ }
+
+ issue.Index = idx
+
if err = NewIssueWithIndex(ctx, issue.Poster, NewIssueOptions{
Repo: repo,
Issue: issue,
@@ -1187,6 +1190,7 @@ type IssuesOptions struct { //nolint
PosterID int64
MentionedID int64
ReviewRequestedID int64
+ SubscriberID int64
MilestoneIDs []int64
ProjectID int64
ProjectBoardID int64
@@ -1300,6 +1304,10 @@ func (opts *IssuesOptions) setupSessionNoLimit(sess *xorm.Session) {
applyReviewRequestedCondition(sess, opts.ReviewRequestedID)
}
+ if opts.SubscriberID > 0 {
+ applySubscribedCondition(sess, opts.SubscriberID)
+ }
+
if len(opts.MilestoneIDs) > 0 {
sess.In("issue.milestone_id", opts.MilestoneIDs)
}
@@ -1464,6 +1472,37 @@ func applyReviewRequestedCondition(sess *xorm.Session, reviewRequestedID int64)
reviewRequestedID, ReviewTypeApprove, ReviewTypeReject, ReviewTypeRequest, reviewRequestedID)
}
+func applySubscribedCondition(sess *xorm.Session, subscriberID int64) *xorm.Session {
+ return sess.And(
+ builder.
+ NotIn("issue.id",
+ builder.Select("issue_id").
+ From("issue_watch").
+ Where(builder.Eq{"is_watching": false, "user_id": subscriberID}),
+ ),
+ ).And(
+ builder.Or(
+ builder.In("issue.id", builder.
+ Select("issue_id").
+ From("issue_watch").
+ Where(builder.Eq{"is_watching": true, "user_id": subscriberID}),
+ ),
+ builder.In("issue.id", builder.
+ Select("issue_id").
+ From("comment").
+ Where(builder.Eq{"poster_id": subscriberID}),
+ ),
+ builder.Eq{"issue.poster_id": subscriberID},
+ builder.In("issue.repo_id", builder.
+ Select("id").
+ From("watch").
+ Where(builder.And(builder.Eq{"user_id": subscriberID},
+ builder.In("mode", repo_model.WatchModeNormal, repo_model.WatchModeAuto))),
+ ),
+ ),
+ )
+}
+
// CountIssuesByRepo map from repoID to number of issues matching the options
func CountIssuesByRepo(opts *IssuesOptions) (map[int64]int64, error) {
e := db.GetEngine(db.DefaultContext)
@@ -1903,23 +1942,17 @@ func GetRepoIssueStats(repoID, uid int64, filterMode int, isPull bool) (numOpen,
func SearchIssueIDsByKeyword(ctx context.Context, kw string, repoIDs []int64, limit, start int) (int64, []int64, error) {
repoCond := builder.In("repo_id", repoIDs)
subQuery := builder.Select("id").From("issue").Where(repoCond)
- // SQLite's UPPER function only transforms ASCII letters.
- if setting.Database.UseSQLite3 {
- kw = util.ToUpperASCII(kw)
- } else {
- kw = strings.ToUpper(kw)
- }
cond := builder.And(
repoCond,
builder.Or(
- builder.Like{"UPPER(name)", kw},
- builder.Like{"UPPER(content)", kw},
+ db.BuildCaseInsensitiveLike("name", kw),
+ db.BuildCaseInsensitiveLike("content", kw),
builder.In("id", builder.Select("issue_id").
From("comment").
Where(builder.And(
builder.Eq{"type": CommentTypeComment},
builder.In("issue_id", subQuery),
- builder.Like{"UPPER(content)", kw},
+ db.BuildCaseInsensitiveLike("content", kw),
)),
),
),
@@ -2442,7 +2475,7 @@ func DeleteOrphanedIssues() error {
// Remove issue attachment files.
for i := range attachmentPaths {
- admin_model.RemoveAllWithNotice(db.DefaultContext, "Delete issue attachment", attachmentPaths[i])
+ system_model.RemoveAllWithNotice(db.DefaultContext, "Delete issue attachment", attachmentPaths[i])
}
return nil
}
diff --git a/models/issues/issue_index.go b/models/issues/issue_index.go
index 100e814317..f4acc5aa1b 100644
--- a/models/issues/issue_index.go
+++ b/models/issues/issue_index.go
@@ -15,16 +15,12 @@ func RecalculateIssueIndexForRepo(repoID int64) error {
}
defer committer.Close()
- if err := db.UpsertResourceIndex(ctx, "issue_index", repoID); err != nil {
- return err
- }
-
var max int64
- if _, err := db.GetEngine(ctx).Select(" MAX(`index`)").Table("issue").Where("repo_id=?", repoID).Get(&max); err != nil {
+ if _, err = db.GetEngine(ctx).Select(" MAX(`index`)").Table("issue").Where("repo_id=?", repoID).Get(&max); err != nil {
return err
}
- if _, err := db.GetEngine(ctx).Exec("UPDATE `issue_index` SET max_index=? WHERE group_id=?", max, repoID); err != nil {
+ if err = db.SyncMaxResourceIndex(ctx, "issue_index", repoID, max); err != nil {
return err
}
diff --git a/models/issues/issue_list.go b/models/issues/issue_list.go
index e311e80b1d..deadb6a564 100644
--- a/models/issues/issue_list.go
+++ b/models/issues/issue_list.go
@@ -9,6 +9,7 @@ import (
"fmt"
"code.gitea.io/gitea/models/db"
+ project_model "code.gitea.io/gitea/models/project"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/container"
@@ -21,16 +22,16 @@ type IssueList []*Issue
// get the repo IDs to be loaded later, these IDs are for issue.Repo and issue.PullRequest.HeadRepo
func (issues IssueList) getRepoIDs() []int64 {
- repoIDs := make(map[int64]struct{}, len(issues))
+ repoIDs := make(container.Set[int64], len(issues))
for _, issue := range issues {
if issue.Repo == nil {
- repoIDs[issue.RepoID] = struct{}{}
+ repoIDs.Add(issue.RepoID)
}
if issue.PullRequest != nil && issue.PullRequest.HeadRepo == nil {
- repoIDs[issue.PullRequest.HeadRepoID] = struct{}{}
+ repoIDs.Add(issue.PullRequest.HeadRepoID)
}
}
- return container.KeysInt64(repoIDs)
+ return repoIDs.Values()
}
func (issues IssueList) loadRepositories(ctx context.Context) ([]*repo_model.Repository, error) {
@@ -78,13 +79,11 @@ func (issues IssueList) LoadRepositories() ([]*repo_model.Repository, error) {
}
func (issues IssueList) getPosterIDs() []int64 {
- posterIDs := make(map[int64]struct{}, len(issues))
+ posterIDs := make(container.Set[int64], len(issues))
for _, issue := range issues {
- if _, ok := posterIDs[issue.PosterID]; !ok {
- posterIDs[issue.PosterID] = struct{}{}
- }
+ posterIDs.Add(issue.PosterID)
}
- return container.KeysInt64(posterIDs)
+ return posterIDs.Values()
}
func (issues IssueList) loadPosters(ctx context.Context) error {
@@ -184,13 +183,11 @@ func (issues IssueList) loadLabels(ctx context.Context) error {
}
func (issues IssueList) getMilestoneIDs() []int64 {
- ids := make(map[int64]struct{}, len(issues))
+ ids := make(container.Set[int64], len(issues))
for _, issue := range issues {
- if _, ok := ids[issue.MilestoneID]; !ok {
- ids[issue.MilestoneID] = struct{}{}
- }
+ ids.Add(issue.MilestoneID)
}
- return container.KeysInt64(ids)
+ return ids.Values()
}
func (issues IssueList) loadMilestones(ctx context.Context) error {
@@ -222,6 +219,43 @@ func (issues IssueList) loadMilestones(ctx context.Context) error {
return nil
}
+func (issues IssueList) getProjectIDs() []int64 {
+ ids := make(container.Set[int64], len(issues))
+ for _, issue := range issues {
+ ids.Add(issue.ProjectID())
+ }
+ return ids.Values()
+}
+
+func (issues IssueList) loadProjects(ctx context.Context) error {
+ projectIDs := issues.getProjectIDs()
+ if len(projectIDs) == 0 {
+ return nil
+ }
+
+ projectMaps := make(map[int64]*project_model.Project, len(projectIDs))
+ left := len(projectIDs)
+ for left > 0 {
+ limit := db.DefaultMaxInSize
+ if left < limit {
+ limit = left
+ }
+ err := db.GetEngine(ctx).
+ In("id", projectIDs[:limit]).
+ Find(&projectMaps)
+ if err != nil {
+ return err
+ }
+ left -= limit
+ projectIDs = projectIDs[limit:]
+ }
+
+ for _, issue := range issues {
+ issue.Project = projectMaps[issue.ProjectID()]
+ }
+ return nil
+}
+
func (issues IssueList) loadAssignees(ctx context.Context) error {
if len(issues) == 0 {
return nil
@@ -495,6 +529,10 @@ func (issues IssueList) loadAttributes(ctx context.Context) error {
return fmt.Errorf("issue.loadAttributes: loadMilestones: %v", err)
}
+ if err := issues.loadProjects(ctx); err != nil {
+ return fmt.Errorf("issue.loadAttributes: loadProjects: %v", err)
+ }
+
if err := issues.loadAssignees(ctx); err != nil {
return fmt.Errorf("issue.loadAttributes: loadAssignees: %v", err)
}
diff --git a/models/issues/issue_list_test.go b/models/issues/issue_list_test.go
index 6b978f9ae6..f2cfca9bc0 100644
--- a/models/issues/issue_list_test.go
+++ b/models/issues/issue_list_test.go
@@ -18,9 +18,9 @@ func TestIssueList_LoadRepositories(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
issueList := issues_model.IssueList{
- unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1}).(*issues_model.Issue),
- unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 2}).(*issues_model.Issue),
- unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 4}).(*issues_model.Issue),
+ unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1}),
+ unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 2}),
+ unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 4}),
}
repos, err := issueList.LoadRepositories()
@@ -35,8 +35,8 @@ func TestIssueList_LoadAttributes(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
setting.Service.EnableTimetracking = true
issueList := issues_model.IssueList{
- unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1}).(*issues_model.Issue),
- unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 4}).(*issues_model.Issue),
+ unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1}),
+ unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 4}),
}
assert.NoError(t, issueList.LoadAttributes())
diff --git a/models/issues/issue_project.go b/models/issues/issue_project.go
index aed78611ea..8299087c5b 100644
--- a/models/issues/issue_project.go
+++ b/models/issues/issue_project.go
@@ -68,6 +68,7 @@ func LoadIssuesFromBoard(b *project_model.Board) (IssueList, error) {
issues, err := Issues(&IssuesOptions{
ProjectBoardID: b.ID,
ProjectID: b.ProjectID,
+ SortType: "project-column-sorting",
})
if err != nil {
return nil, err
@@ -79,6 +80,7 @@ func LoadIssuesFromBoard(b *project_model.Board) (IssueList, error) {
issues, err := Issues(&IssuesOptions{
ProjectBoardID: -1, // Issues without ProjectBoardID
ProjectID: b.ProjectID,
+ SortType: "project-column-sorting",
})
if err != nil {
return nil, err
diff --git a/models/issues/issue_test.go b/models/issues/issue_test.go
index 019e578da8..bef5d03e8a 100644
--- a/models/issues/issue_test.go
+++ b/models/issues/issue_test.go
@@ -29,13 +29,13 @@ func TestIssue_ReplaceLabels(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
testSuccess := func(issueID int64, labelIDs []int64) {
- issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: issueID}).(*issues_model.Issue)
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID}).(*repo_model.Repository)
- doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User)
+ issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: issueID})
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issue.RepoID})
+ doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
labels := make([]*issues_model.Label, len(labelIDs))
for i, labelID := range labelIDs {
- labels[i] = unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: labelID, RepoID: repo.ID}).(*issues_model.Label)
+ labels[i] = unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: labelID, RepoID: repo.ID})
}
assert.NoError(t, issues_model.ReplaceIssueLabels(issue, labels, doer))
unittest.AssertCount(t, &issues_model.IssueLabel{IssueID: issueID}, len(labelIDs))
@@ -59,7 +59,7 @@ func Test_GetIssueIDsByRepoID(t *testing.T) {
func TestIssueAPIURL(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1}).(*issues_model.Issue)
+ issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1})
err := issue.LoadAttributes(db.DefaultContext)
assert.NoError(t, err)
@@ -117,8 +117,8 @@ func TestIssue_ClearLabels(t *testing.T) {
}
for _, test := range tests {
assert.NoError(t, unittest.PrepareTestDatabase())
- issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: test.issueID}).(*issues_model.Issue)
- doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: test.doerID}).(*user_model.User)
+ issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: test.issueID})
+ doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: test.doerID})
assert.NoError(t, issues_model.ClearIssueLabels(issue, doer))
unittest.AssertNotExistsBean(t, &issues_model.IssueLabel{IssueID: test.issueID})
}
@@ -126,7 +126,7 @@ func TestIssue_ClearLabels(t *testing.T) {
func TestUpdateIssueCols(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{}).(*issues_model.Issue)
+ issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{})
const newTitle = "New Title for unit test"
issue.Title = newTitle
@@ -138,7 +138,7 @@ func TestUpdateIssueCols(t *testing.T) {
assert.NoError(t, issues_model.UpdateIssueCols(db.DefaultContext, issue, "name"))
then := time.Now().Unix()
- updatedIssue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: issue.ID}).(*issues_model.Issue)
+ updatedIssue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: issue.ID})
assert.EqualValues(t, newTitle, updatedIssue.Title)
assert.EqualValues(t, prevContent, updatedIssue.Content)
unittest.AssertInt64InRange(t, now, then, int64(updatedIssue.UpdatedUnix))
@@ -291,8 +291,8 @@ func TestGetUserIssueStats(t *testing.T) {
{
issues_model.UserIssueStatsOptions{
UserID: 2,
- Org: unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 3}).(*organization.Organization),
- Team: unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 7}).(*organization.Team),
+ Org: unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 3}),
+ Team: unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 7}),
FilterMode: issues_model.FilterModeAll,
},
issues_model.IssueStats{
@@ -347,7 +347,7 @@ func TestIssue_SearchIssueIDsByKeyword(t *testing.T) {
func TestGetRepoIDsForIssuesOptions(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
+ user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
for _, test := range []struct {
Opts issues_model.IssuesOptions
ExpectedRepoIDs []int64
@@ -378,8 +378,8 @@ func TestGetRepoIDsForIssuesOptions(t *testing.T) {
func testInsertIssue(t *testing.T, title, content string, expectIndex int64) *issues_model.Issue {
var newIssue issues_model.Issue
t.Run(title, func(t *testing.T) {
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository)
- user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
+ user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
issue := issues_model.Issue{
RepoID: repo.ID,
@@ -420,10 +420,10 @@ func TestIssue_ResolveMentions(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
testSuccess := func(owner, repo, doer string, mentions []string, expected []int64) {
- o := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: owner}).(*user_model.User)
- r := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerID: o.ID, LowerName: repo}).(*repo_model.Repository)
+ o := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: owner})
+ r := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerID: o.ID, LowerName: repo})
issue := &issues_model.Issue{RepoID: r.ID}
- d := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: doer}).(*user_model.User)
+ d := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: doer})
resolved, err := issues_model.ResolveIssueMentionsByVisibility(db.DefaultContext, issue, d, mentions)
assert.NoError(t, err)
ids := make([]int64, len(resolved))
@@ -504,7 +504,7 @@ func TestCorrectIssueStats(t *testing.T) {
func TestIssueForeignReference(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 4}).(*issues_model.Issue)
+ issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 4})
assert.NotEqualValues(t, issue.Index, issue.ID) // make sure they are different to avoid false positive
// it is fine for an issue to not have a foreign reference
@@ -537,7 +537,7 @@ func TestIssueForeignReference(t *testing.T) {
func TestMilestoneList_LoadTotalTrackedTimes(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
miles := issues_model.MilestoneList{
- unittest.AssertExistsAndLoadBean(t, &issues_model.Milestone{ID: 1}).(*issues_model.Milestone),
+ unittest.AssertExistsAndLoadBean(t, &issues_model.Milestone{ID: 1}),
}
assert.NoError(t, miles.LoadTotalTrackedTimes())
@@ -547,7 +547,7 @@ func TestMilestoneList_LoadTotalTrackedTimes(t *testing.T) {
func TestLoadTotalTrackedTime(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- milestone := unittest.AssertExistsAndLoadBean(t, &issues_model.Milestone{ID: 1}).(*issues_model.Milestone)
+ milestone := unittest.AssertExistsAndLoadBean(t, &issues_model.Milestone{ID: 1})
assert.NoError(t, milestone.LoadTotalTrackedTime())
diff --git a/models/issues/issue_user_test.go b/models/issues/issue_user_test.go
index 33e9f98ecc..7dd84ed68c 100644
--- a/models/issues/issue_user_test.go
+++ b/models/issues/issue_user_test.go
@@ -18,7 +18,7 @@ import (
func Test_NewIssueUsers(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository)
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
newIssue := &issues_model.Issue{
RepoID: repo.ID,
PosterID: 4,
@@ -39,7 +39,7 @@ func Test_NewIssueUsers(t *testing.T) {
func TestUpdateIssueUserByRead(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1}).(*issues_model.Issue)
+ issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1})
assert.NoError(t, issues_model.UpdateIssueUserByRead(4, issue.ID))
unittest.AssertExistsAndLoadBean(t, &issues_model.IssueUser{IssueID: issue.ID, UID: 4}, "is_read=1")
@@ -52,7 +52,7 @@ func TestUpdateIssueUserByRead(t *testing.T) {
func TestUpdateIssueUsersByMentions(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1}).(*issues_model.Issue)
+ issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1})
uids := []int64{2, 5}
assert.NoError(t, issues_model.UpdateIssueUsersByMentions(db.DefaultContext, issue.ID, uids))
diff --git a/models/issues/issue_watch_test.go b/models/issues/issue_watch_test.go
index c6b6416d9b..7aaf9f7f5d 100644
--- a/models/issues/issue_watch_test.go
+++ b/models/issues/issue_watch_test.go
@@ -18,11 +18,11 @@ func TestCreateOrUpdateIssueWatch(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
assert.NoError(t, issues_model.CreateOrUpdateIssueWatch(3, 1, true))
- iw := unittest.AssertExistsAndLoadBean(t, &issues_model.IssueWatch{UserID: 3, IssueID: 1}).(*issues_model.IssueWatch)
+ iw := unittest.AssertExistsAndLoadBean(t, &issues_model.IssueWatch{UserID: 3, IssueID: 1})
assert.True(t, iw.IsWatching)
assert.NoError(t, issues_model.CreateOrUpdateIssueWatch(1, 1, false))
- iw = unittest.AssertExistsAndLoadBean(t, &issues_model.IssueWatch{UserID: 1, IssueID: 1}).(*issues_model.IssueWatch)
+ iw = unittest.AssertExistsAndLoadBean(t, &issues_model.IssueWatch{UserID: 1, IssueID: 1})
assert.False(t, iw.IsWatching)
}
diff --git a/models/issues/issue_xref_test.go b/models/issues/issue_xref_test.go
index 6bb19d5328..0f72fc7ca6 100644
--- a/models/issues/issue_xref_test.go
+++ b/models/issues/issue_xref_test.go
@@ -27,7 +27,7 @@ func TestXRef_AddCrossReferences(t *testing.T) {
// PR to close issue #1
content := fmt.Sprintf("content2, closes #%d", itarget.Index)
pr := testCreateIssue(t, 1, 2, "title2", content, true)
- ref := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{IssueID: itarget.ID, RefIssueID: pr.ID, RefCommentID: 0}).(*issues_model.Comment)
+ ref := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{IssueID: itarget.ID, RefIssueID: pr.ID, RefCommentID: 0})
assert.Equal(t, issues_model.CommentTypePullRef, ref.Type)
assert.Equal(t, pr.RepoID, ref.RefRepoID)
assert.True(t, ref.RefIsPull)
@@ -36,7 +36,7 @@ func TestXRef_AddCrossReferences(t *testing.T) {
// Comment on PR to reopen issue #1
content = fmt.Sprintf("content2, reopens #%d", itarget.Index)
c := testCreateComment(t, 1, 2, pr.ID, content)
- ref = unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{IssueID: itarget.ID, RefIssueID: pr.ID, RefCommentID: c.ID}).(*issues_model.Comment)
+ ref = unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{IssueID: itarget.ID, RefIssueID: pr.ID, RefCommentID: c.ID})
assert.Equal(t, issues_model.CommentTypeCommentRef, ref.Type)
assert.Equal(t, pr.RepoID, ref.RefRepoID)
assert.True(t, ref.RefIsPull)
@@ -45,7 +45,7 @@ func TestXRef_AddCrossReferences(t *testing.T) {
// Issue mentioning issue #1
content = fmt.Sprintf("content3, mentions #%d", itarget.Index)
i := testCreateIssue(t, 1, 2, "title3", content, false)
- ref = unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{IssueID: itarget.ID, RefIssueID: i.ID, RefCommentID: 0}).(*issues_model.Comment)
+ ref = unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{IssueID: itarget.ID, RefIssueID: i.ID, RefCommentID: 0})
assert.Equal(t, issues_model.CommentTypeIssueRef, ref.Type)
assert.Equal(t, pr.RepoID, ref.RefRepoID)
assert.False(t, ref.RefIsPull)
@@ -57,7 +57,7 @@ func TestXRef_AddCrossReferences(t *testing.T) {
// Cross-reference to issue #4 by admin
content = fmt.Sprintf("content5, mentions user3/repo3#%d", itarget.Index)
i = testCreateIssue(t, 2, 1, "title5", content, false)
- ref = unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{IssueID: itarget.ID, RefIssueID: i.ID, RefCommentID: 0}).(*issues_model.Comment)
+ ref = unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{IssueID: itarget.ID, RefIssueID: i.ID, RefCommentID: 0})
assert.Equal(t, issues_model.CommentTypeIssueRef, ref.Type)
assert.Equal(t, i.RepoID, ref.RefRepoID)
assert.False(t, ref.RefIsPull)
@@ -78,15 +78,15 @@ func TestXRef_NeuterCrossReferences(t *testing.T) {
// Issue mentioning issue #1
title := fmt.Sprintf("title2, mentions #%d", itarget.Index)
i := testCreateIssue(t, 1, 2, title, "content2", false)
- ref := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{IssueID: itarget.ID, RefIssueID: i.ID, RefCommentID: 0}).(*issues_model.Comment)
+ ref := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{IssueID: itarget.ID, RefIssueID: i.ID, RefCommentID: 0})
assert.Equal(t, issues_model.CommentTypeIssueRef, ref.Type)
assert.Equal(t, references.XRefActionNone, ref.RefAction)
- d := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
+ d := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
i.Title = "title2, no mentions"
assert.NoError(t, issues_model.ChangeIssueTitle(i, d, title))
- ref = unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{IssueID: itarget.ID, RefIssueID: i.ID, RefCommentID: 0}).(*issues_model.Comment)
+ ref = unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{IssueID: itarget.ID, RefIssueID: i.ID, RefCommentID: 0})
assert.Equal(t, issues_model.CommentTypeIssueRef, ref.Type)
assert.Equal(t, references.XRefActionNeutered, ref.RefAction)
}
@@ -94,7 +94,7 @@ func TestXRef_NeuterCrossReferences(t *testing.T) {
func TestXRef_ResolveCrossReferences(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- d := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
+ d := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
i1 := testCreateIssue(t, 1, 2, "title1", "content1", false)
i2 := testCreateIssue(t, 1, 2, "title2", "content2", false)
@@ -103,10 +103,10 @@ func TestXRef_ResolveCrossReferences(t *testing.T) {
assert.NoError(t, err)
pr := testCreatePR(t, 1, 2, "titlepr", fmt.Sprintf("closes #%d", i1.Index))
- rp := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{IssueID: i1.ID, RefIssueID: pr.Issue.ID, RefCommentID: 0}).(*issues_model.Comment)
+ rp := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{IssueID: i1.ID, RefIssueID: pr.Issue.ID, RefCommentID: 0})
c1 := testCreateComment(t, 1, 2, pr.Issue.ID, fmt.Sprintf("closes #%d", i2.Index))
- r1 := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{IssueID: i2.ID, RefIssueID: pr.Issue.ID, RefCommentID: c1.ID}).(*issues_model.Comment)
+ r1 := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{IssueID: i2.ID, RefIssueID: pr.Issue.ID, RefCommentID: c1.ID})
// Must be ignored
c2 := testCreateComment(t, 1, 2, pr.Issue.ID, fmt.Sprintf("mentions #%d", i2.Index))
@@ -117,7 +117,7 @@ func TestXRef_ResolveCrossReferences(t *testing.T) {
unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{IssueID: i3.ID, RefIssueID: pr.Issue.ID, RefCommentID: c3.ID})
c4 := testCreateComment(t, 1, 2, pr.Issue.ID, fmt.Sprintf("closes #%d", i3.Index))
- r4 := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{IssueID: i3.ID, RefIssueID: pr.Issue.ID, RefCommentID: c4.ID}).(*issues_model.Comment)
+ r4 := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{IssueID: i3.ID, RefIssueID: pr.Issue.ID, RefCommentID: c4.ID})
refs, err := pr.ResolveCrossReferences(db.DefaultContext)
assert.NoError(t, err)
@@ -128,10 +128,14 @@ func TestXRef_ResolveCrossReferences(t *testing.T) {
}
func testCreateIssue(t *testing.T, repo, doer int64, title, content string, ispull bool) *issues_model.Issue {
- r := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repo}).(*repo_model.Repository)
- d := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: doer}).(*user_model.User)
+ r := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repo})
+ d := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: doer})
- idx, err := db.GetNextResourceIndex("issue_index", r.ID)
+ ctx, committer, err := db.TxContext()
+ assert.NoError(t, err)
+ defer committer.Close()
+
+ idx, err := db.GetNextResourceIndex(ctx, "issue_index", r.ID)
assert.NoError(t, err)
i := &issues_model.Issue{
RepoID: r.ID,
@@ -143,9 +147,6 @@ func testCreateIssue(t *testing.T, repo, doer int64, title, content string, ispu
Index: idx,
}
- ctx, committer, err := db.TxContext()
- assert.NoError(t, err)
- defer committer.Close()
err = issues_model.NewIssueWithIndex(ctx, d, issues_model.NewIssueOptions{
Repo: r,
Issue: i,
@@ -159,8 +160,8 @@ func testCreateIssue(t *testing.T, repo, doer int64, title, content string, ispu
}
func testCreatePR(t *testing.T, repo, doer int64, title, content string) *issues_model.PullRequest {
- r := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repo}).(*repo_model.Repository)
- d := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: doer}).(*user_model.User)
+ r := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repo})
+ d := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: doer})
i := &issues_model.Issue{RepoID: r.ID, PosterID: d.ID, Poster: d, Title: title, Content: content, IsPull: true}
pr := &issues_model.PullRequest{HeadRepoID: repo, BaseRepoID: repo, HeadBranch: "head", BaseBranch: "base", Status: issues_model.PullRequestStatusMergeable}
assert.NoError(t, issues_model.NewPullRequest(db.DefaultContext, r, i, nil, nil, pr))
@@ -169,8 +170,8 @@ func testCreatePR(t *testing.T, repo, doer int64, title, content string) *issues
}
func testCreateComment(t *testing.T, repo, doer, issue int64, content string) *issues_model.Comment {
- d := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: doer}).(*user_model.User)
- i := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: issue}).(*issues_model.Issue)
+ d := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: doer})
+ i := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: issue})
c := &issues_model.Comment{Type: issues_model.CommentTypeComment, PosterID: doer, Poster: d, IssueID: issue, Issue: i, Content: content}
ctx, committer, err := db.TxContext()
diff --git a/models/issues/label.go b/models/issues/label.go
index 667a608687..be97454e26 100644
--- a/models/issues/label.go
+++ b/models/issues/label.go
@@ -38,6 +38,10 @@ func (err ErrRepoLabelNotExist) Error() string {
return fmt.Sprintf("label does not exist [label_id: %d, repo_id: %d]", err.LabelID, err.RepoID)
}
+func (err ErrRepoLabelNotExist) Unwrap() error {
+ return util.ErrNotExist
+}
+
// ErrOrgLabelNotExist represents a "OrgLabelNotExist" kind of error.
type ErrOrgLabelNotExist struct {
LabelID int64
@@ -54,6 +58,10 @@ func (err ErrOrgLabelNotExist) Error() string {
return fmt.Sprintf("label does not exist [label_id: %d, org_id: %d]", err.LabelID, err.OrgID)
}
+func (err ErrOrgLabelNotExist) Unwrap() error {
+ return util.ErrNotExist
+}
+
// ErrLabelNotExist represents a "LabelNotExist" kind of error.
type ErrLabelNotExist struct {
LabelID int64
@@ -69,6 +77,10 @@ func (err ErrLabelNotExist) Error() string {
return fmt.Sprintf("label does not exist [label_id: %d]", err.LabelID)
}
+func (err ErrLabelNotExist) Unwrap() error {
+ return util.ErrNotExist
+}
+
// LabelColorPattern is a regexp witch can validate LabelColor
var LabelColorPattern = regexp.MustCompile("^#?(?:[0-9a-fA-F]{6}|[0-9a-fA-F]{3})$")
diff --git a/models/issues/label_test.go b/models/issues/label_test.go
index 33f114b5fe..9ad6fd427b 100644
--- a/models/issues/label_test.go
+++ b/models/issues/label_test.go
@@ -21,17 +21,17 @@ import (
func TestLabel_CalOpenIssues(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- label := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 1}).(*issues_model.Label)
+ label := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 1})
label.CalOpenIssues()
assert.EqualValues(t, 2, label.NumOpenIssues)
}
func TestLabel_ForegroundColor(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- label := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 1}).(*issues_model.Label)
+ label := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 1})
assert.Equal(t, template.CSS("#000"), label.ForegroundColor())
- label = unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 2}).(*issues_model.Label)
+ label = unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 2})
assert.Equal(t, template.CSS("#fff"), label.ForegroundColor())
}
@@ -260,7 +260,7 @@ func TestGetLabelsByIssueID(t *testing.T) {
func TestUpdateLabel(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- label := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 1}).(*issues_model.Label)
+ label := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 1})
// make sure update wont overwrite it
update := &issues_model.Label{
ID: label.ID,
@@ -271,7 +271,7 @@ func TestUpdateLabel(t *testing.T) {
label.Color = update.Color
label.Name = update.Name
assert.NoError(t, issues_model.UpdateLabel(update))
- newLabel := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 1}).(*issues_model.Label)
+ newLabel := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 1})
assert.EqualValues(t, label.ID, newLabel.ID)
assert.EqualValues(t, label.Color, newLabel.Color)
assert.EqualValues(t, label.Name, newLabel.Name)
@@ -281,7 +281,7 @@ func TestUpdateLabel(t *testing.T) {
func TestDeleteLabel(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- label := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 1}).(*issues_model.Label)
+ label := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 1})
assert.NoError(t, issues_model.DeleteLabel(label.RepoID, label.ID))
unittest.AssertNotExistsBean(t, &issues_model.Label{ID: label.ID, RepoID: label.RepoID})
@@ -301,9 +301,9 @@ func TestHasIssueLabel(t *testing.T) {
func TestNewIssueLabel(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- label := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 2}).(*issues_model.Label)
- issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1}).(*issues_model.Issue)
- doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
+ label := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 2})
+ issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1})
+ doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
// add new IssueLabel
prevNumIssues := label.NumIssues
@@ -316,7 +316,7 @@ func TestNewIssueLabel(t *testing.T) {
LabelID: label.ID,
Content: "1",
})
- label = unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 2}).(*issues_model.Label)
+ label = unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 2})
assert.EqualValues(t, prevNumIssues+1, label.NumIssues)
// re-add existing IssueLabel
@@ -326,10 +326,10 @@ func TestNewIssueLabel(t *testing.T) {
func TestNewIssueLabels(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- label1 := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 1}).(*issues_model.Label)
- label2 := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 2}).(*issues_model.Label)
- issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 5}).(*issues_model.Issue)
- doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
+ label1 := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 1})
+ label2 := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 2})
+ issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 5})
+ doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
assert.NoError(t, issues_model.NewIssueLabels(issue, []*issues_model.Label{label1, label2}, doer))
unittest.AssertExistsAndLoadBean(t, &issues_model.IssueLabel{IssueID: issue.ID, LabelID: label1.ID})
@@ -341,10 +341,10 @@ func TestNewIssueLabels(t *testing.T) {
Content: "1",
})
unittest.AssertExistsAndLoadBean(t, &issues_model.IssueLabel{IssueID: issue.ID, LabelID: label1.ID})
- label1 = unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 1}).(*issues_model.Label)
+ label1 = unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 1})
assert.EqualValues(t, 3, label1.NumIssues)
assert.EqualValues(t, 1, label1.NumClosedIssues)
- label2 = unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 2}).(*issues_model.Label)
+ label2 = unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 2})
assert.EqualValues(t, 1, label2.NumIssues)
assert.EqualValues(t, 1, label2.NumClosedIssues)
@@ -357,9 +357,9 @@ func TestNewIssueLabels(t *testing.T) {
func TestDeleteIssueLabel(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
testSuccess := func(labelID, issueID, doerID int64) {
- label := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: labelID}).(*issues_model.Label)
- issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: issueID}).(*issues_model.Issue)
- doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: doerID}).(*user_model.User)
+ label := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: labelID})
+ issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: issueID})
+ doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: doerID})
expectedNumIssues := label.NumIssues
expectedNumClosedIssues := label.NumClosedIssues
@@ -383,7 +383,7 @@ func TestDeleteIssueLabel(t *testing.T) {
IssueID: issueID,
LabelID: labelID,
}, `content=""`)
- label = unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: labelID}).(*issues_model.Label)
+ label = unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: labelID})
assert.EqualValues(t, expectedNumIssues, label.NumIssues)
assert.EqualValues(t, expectedNumClosedIssues, label.NumClosedIssues)
}
diff --git a/models/issues/milestone.go b/models/issues/milestone.go
index c49799f391..3ccade7411 100644
--- a/models/issues/milestone.go
+++ b/models/issues/milestone.go
@@ -15,6 +15,7 @@ import (
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/timeutil"
+ "code.gitea.io/gitea/modules/util"
"xorm.io/builder"
)
@@ -39,6 +40,10 @@ func (err ErrMilestoneNotExist) Error() string {
return fmt.Sprintf("milestone does not exist [id: %d, repo_id: %d]", err.ID, err.RepoID)
}
+func (err ErrMilestoneNotExist) Unwrap() error {
+ return util.ErrNotExist
+}
+
// Milestone represents a milestone of repository.
type Milestone struct {
ID int64 `xorm:"pk autoincr"`
@@ -361,7 +366,7 @@ func (opts GetMilestonesOption) toCond() builder.Cond {
}
if len(opts.Name) != 0 {
- cond = cond.And(builder.Like{"UPPER(name)", strings.ToUpper(opts.Name)})
+ cond = cond.And(db.BuildCaseInsensitiveLike("name", opts.Name))
}
return cond
diff --git a/models/issues/milestone_test.go b/models/issues/milestone_test.go
index a6fbf9c23b..f04a2b2b3b 100644
--- a/models/issues/milestone_test.go
+++ b/models/issues/milestone_test.go
@@ -40,7 +40,7 @@ func TestGetMilestoneByRepoID(t *testing.T) {
func TestGetMilestonesByRepoID(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
test := func(repoID int64, state api.StateType) {
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repoID}).(*repo_model.Repository)
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repoID})
milestones, _, err := issues_model.GetMilestones(issues_model.GetMilestonesOption{
RepoID: repo.ID,
State: state,
@@ -88,7 +88,7 @@ func TestGetMilestonesByRepoID(t *testing.T) {
func TestGetMilestones(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository)
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
test := func(sortType string, sortCond func(*issues_model.Milestone) int) {
for _, page := range []int{0, 1} {
milestones, _, err := issues_model.GetMilestones(issues_model.GetMilestonesOption{
@@ -150,7 +150,7 @@ func TestGetMilestones(t *testing.T) {
func TestCountRepoMilestones(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
test := func(repoID int64) {
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repoID}).(*repo_model.Repository)
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repoID})
count, err := issues_model.CountMilestones(db.DefaultContext, issues_model.GetMilestonesOption{
RepoID: repoID,
State: api.StateAll,
@@ -173,7 +173,7 @@ func TestCountRepoMilestones(t *testing.T) {
func TestCountRepoClosedMilestones(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
test := func(repoID int64) {
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repoID}).(*repo_model.Repository)
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repoID})
count, err := issues_model.CountMilestones(db.DefaultContext, issues_model.GetMilestonesOption{
RepoID: repoID,
State: api.StateClosed,
@@ -196,7 +196,7 @@ func TestCountRepoClosedMilestones(t *testing.T) {
func TestCountMilestonesByRepoIDs(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
milestonesCount := func(repoID int64) (int, int) {
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repoID}).(*repo_model.Repository)
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repoID})
return repo.NumOpenMilestones, repo.NumClosedMilestones
}
repo1OpenCount, repo1ClosedCount := milestonesCount(1)
@@ -215,8 +215,8 @@ func TestCountMilestonesByRepoIDs(t *testing.T) {
func TestGetMilestonesByRepoIDs(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository)
- repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}).(*repo_model.Repository)
+ repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
+ repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2})
test := func(sortType string, sortCond func(*issues_model.Milestone) int) {
for _, page := range []int{0, 1} {
openMilestones, err := issues_model.GetMilestonesByRepoIDs([]int64{repo1.ID, repo2.ID}, page, false, sortType)
@@ -262,7 +262,7 @@ func TestGetMilestonesStats(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
test := func(repoID int64) {
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repoID}).(*repo_model.Repository)
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repoID})
stats, err := issues_model.GetMilestonesStatsByRepoCond(builder.And(builder.Eq{"repo_id": repoID}))
assert.NoError(t, err)
assert.EqualValues(t, repo.NumMilestones-repo.NumClosedMilestones, stats.OpenCount)
@@ -277,8 +277,8 @@ func TestGetMilestonesStats(t *testing.T) {
assert.EqualValues(t, 0, stats.OpenCount)
assert.EqualValues(t, 0, stats.ClosedCount)
- repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository)
- repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}).(*repo_model.Repository)
+ repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
+ repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2})
milestoneStats, err := issues_model.GetMilestonesStatsByRepoCond(builder.In("repo_id", []int64{repo1.ID, repo2.ID}))
assert.NoError(t, err)
@@ -301,7 +301,7 @@ func TestNewMilestone(t *testing.T) {
func TestChangeMilestoneStatus(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- milestone := unittest.AssertExistsAndLoadBean(t, &issues_model.Milestone{ID: 1}).(*issues_model.Milestone)
+ milestone := unittest.AssertExistsAndLoadBean(t, &issues_model.Milestone{ID: 1})
assert.NoError(t, issues_model.ChangeMilestoneStatus(milestone, true))
unittest.AssertExistsAndLoadBean(t, &issues_model.Milestone{ID: 1}, "is_closed=1")
@@ -324,11 +324,11 @@ func TestDeleteMilestoneByRepoID(t *testing.T) {
func TestUpdateMilestone(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- milestone := unittest.AssertExistsAndLoadBean(t, &issues_model.Milestone{ID: 1}).(*issues_model.Milestone)
+ milestone := unittest.AssertExistsAndLoadBean(t, &issues_model.Milestone{ID: 1})
milestone.Name = " newMilestoneName "
milestone.Content = "newMilestoneContent"
assert.NoError(t, issues_model.UpdateMilestone(milestone, milestone.IsClosed))
- milestone = unittest.AssertExistsAndLoadBean(t, &issues_model.Milestone{ID: 1}).(*issues_model.Milestone)
+ milestone = unittest.AssertExistsAndLoadBean(t, &issues_model.Milestone{ID: 1})
assert.EqualValues(t, "newMilestoneName", milestone.Name)
unittest.CheckConsistencyFor(t, &issues_model.Milestone{})
}
@@ -336,7 +336,7 @@ func TestUpdateMilestone(t *testing.T) {
func TestUpdateMilestoneCounters(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{MilestoneID: 1},
- "is_closed=0").(*issues_model.Issue)
+ "is_closed=0")
issue.IsClosed = true
issue.ClosedUnix = timeutil.TimeStampNow()
diff --git a/models/issues/pull.go b/models/issues/pull.go
index f96b03445e..18b67eb305 100644
--- a/models/issues/pull.go
+++ b/models/issues/pull.go
@@ -46,6 +46,10 @@ func (err ErrPullRequestNotExist) Error() string {
err.ID, err.IssueID, err.HeadRepoID, err.BaseRepoID, err.HeadBranch, err.BaseBranch)
}
+func (err ErrPullRequestNotExist) Unwrap() error {
+ return util.ErrNotExist
+}
+
// ErrPullRequestAlreadyExists represents a "PullRequestAlreadyExists"-error
type ErrPullRequestAlreadyExists struct {
ID int64
@@ -68,6 +72,10 @@ func (err ErrPullRequestAlreadyExists) Error() string {
err.ID, err.IssueID, err.HeadRepoID, err.BaseRepoID, err.HeadBranch, err.BaseBranch)
}
+func (err ErrPullRequestAlreadyExists) Unwrap() error {
+ return util.ErrAlreadyExist
+}
+
// ErrPullRequestHeadRepoMissing represents a "ErrPullRequestHeadRepoMissing" error
type ErrPullRequestHeadRepoMissing struct {
ID int64
@@ -490,13 +498,6 @@ func (pr *PullRequest) SetMerged(ctx context.Context) (bool, error) {
// NewPullRequest creates new pull request with labels for repository.
func NewPullRequest(outerCtx context.Context, repo *repo_model.Repository, issue *Issue, labelIDs []int64, uuids []string, pr *PullRequest) (err error) {
- idx, err := db.GetNextResourceIndex("issue_index", repo.ID)
- if err != nil {
- return fmt.Errorf("generate pull request index failed: %v", err)
- }
-
- issue.Index = idx
-
ctx, committer, err := db.TxContext()
if err != nil {
return err
@@ -504,6 +505,13 @@ func NewPullRequest(outerCtx context.Context, repo *repo_model.Repository, issue
defer committer.Close()
ctx.WithContext(outerCtx)
+ idx, err := db.GetNextResourceIndex(ctx, "issue_index", repo.ID)
+ if err != nil {
+ return fmt.Errorf("generate pull request index failed: %v", err)
+ }
+
+ issue.Index = idx
+
if err = NewIssueWithIndex(ctx, issue.Poster, NewIssueOptions{
Repo: repo,
Issue: issue,
diff --git a/models/issues/pull_test.go b/models/issues/pull_test.go
index 0d1991383d..fb46e3071e 100644
--- a/models/issues/pull_test.go
+++ b/models/issues/pull_test.go
@@ -16,7 +16,7 @@ import (
func TestPullRequest_LoadAttributes(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1}).(*issues_model.PullRequest)
+ pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1})
assert.NoError(t, pr.LoadAttributes())
assert.NotNil(t, pr.Merger)
assert.Equal(t, pr.MergerID, pr.Merger.ID)
@@ -24,7 +24,7 @@ func TestPullRequest_LoadAttributes(t *testing.T) {
func TestPullRequest_LoadIssue(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1}).(*issues_model.PullRequest)
+ pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1})
assert.NoError(t, pr.LoadIssue())
assert.NotNil(t, pr.Issue)
assert.Equal(t, int64(2), pr.Issue.ID)
@@ -35,7 +35,7 @@ func TestPullRequest_LoadIssue(t *testing.T) {
func TestPullRequest_LoadBaseRepo(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1}).(*issues_model.PullRequest)
+ pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1})
assert.NoError(t, pr.LoadBaseRepo())
assert.NotNil(t, pr.BaseRepo)
assert.Equal(t, pr.BaseRepoID, pr.BaseRepo.ID)
@@ -46,7 +46,7 @@ func TestPullRequest_LoadBaseRepo(t *testing.T) {
func TestPullRequest_LoadHeadRepo(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1}).(*issues_model.PullRequest)
+ pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1})
assert.NoError(t, pr.LoadHeadRepo())
assert.NotNil(t, pr.HeadRepo)
assert.Equal(t, pr.HeadRepoID, pr.HeadRepo.ID)
@@ -180,12 +180,12 @@ func TestGetPullRequestByIssueID(t *testing.T) {
func TestPullRequest_Update(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1}).(*issues_model.PullRequest)
+ pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1})
pr.BaseBranch = "baseBranch"
pr.HeadBranch = "headBranch"
pr.Update()
- pr = unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: pr.ID}).(*issues_model.PullRequest)
+ pr = unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: pr.ID})
assert.Equal(t, "baseBranch", pr.BaseBranch)
assert.Equal(t, "headBranch", pr.HeadBranch)
unittest.CheckConsistencyFor(t, pr)
@@ -200,7 +200,7 @@ func TestPullRequest_UpdateCols(t *testing.T) {
}
assert.NoError(t, pr.UpdateCols("head_branch"))
- pr = unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1}).(*issues_model.PullRequest)
+ pr = unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1})
assert.Equal(t, "master", pr.BaseBranch)
assert.Equal(t, "headBranch", pr.HeadBranch)
unittest.CheckConsistencyFor(t, pr)
@@ -210,8 +210,8 @@ func TestPullRequestList_LoadAttributes(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
prs := []*issues_model.PullRequest{
- unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1}).(*issues_model.PullRequest),
- unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 2}).(*issues_model.PullRequest),
+ unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1}),
+ unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 2}),
}
assert.NoError(t, issues_model.PullRequestList(prs).LoadAttributes())
for _, pr := range prs {
@@ -227,7 +227,7 @@ func TestPullRequestList_LoadAttributes(t *testing.T) {
func TestPullRequest_IsWorkInProgress(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 2}).(*issues_model.PullRequest)
+ pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 2})
pr.LoadIssue()
assert.False(t, pr.IsWorkInProgress())
@@ -242,7 +242,7 @@ func TestPullRequest_IsWorkInProgress(t *testing.T) {
func TestPullRequest_GetWorkInProgressPrefixWorkInProgress(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 2}).(*issues_model.PullRequest)
+ pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 2})
pr.LoadIssue()
assert.Empty(t, pr.GetWorkInProgressPrefix())
diff --git a/models/issues/reaction.go b/models/issues/reaction.go
index 87d6ff4310..02cffad3ba 100644
--- a/models/issues/reaction.go
+++ b/models/issues/reaction.go
@@ -15,6 +15,7 @@ import (
"code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/timeutil"
+ "code.gitea.io/gitea/modules/util"
"xorm.io/builder"
)
@@ -34,6 +35,10 @@ func (err ErrForbiddenIssueReaction) Error() string {
return fmt.Sprintf("'%s' is not an allowed reaction", err.Reaction)
}
+func (err ErrForbiddenIssueReaction) Unwrap() error {
+ return util.ErrPermissionDenied
+}
+
// ErrReactionAlreadyExist is used when a existing reaction was try to created
type ErrReactionAlreadyExist struct {
Reaction string
@@ -49,6 +54,10 @@ func (err ErrReactionAlreadyExist) Error() string {
return fmt.Sprintf("reaction '%s' already exists", err.Reaction)
}
+func (err ErrReactionAlreadyExist) Unwrap() error {
+ return util.ErrAlreadyExist
+}
+
// Reaction represents a reactions on issues and comments.
type Reaction struct {
ID int64 `xorm:"pk autoincr"`
@@ -181,6 +190,10 @@ func createReaction(ctx context.Context, opts *ReactionOptions) (*Reaction, erro
Reaction: opts.Type,
UserID: opts.DoerID,
}
+ if findOpts.CommentID == 0 {
+ // explicit search of Issue Reactions where CommentID = 0
+ findOpts.CommentID = -1
+ }
existingR, _, err := FindReactions(ctx, findOpts)
if err != nil {
@@ -207,7 +220,7 @@ type ReactionOptions struct {
// CreateReaction creates reaction for issue or comment.
func CreateReaction(opts *ReactionOptions) (*Reaction, error) {
- if !setting.UI.ReactionsMap[opts.Type] {
+ if !setting.UI.ReactionsLookup.Contains(opts.Type) {
return nil, ErrForbiddenIssueReaction{opts.Type}
}
@@ -256,16 +269,23 @@ func DeleteReaction(ctx context.Context, opts *ReactionOptions) error {
CommentID: opts.CommentID,
}
- _, err := db.GetEngine(ctx).Where("original_author_id = 0").Delete(reaction)
+ sess := db.GetEngine(ctx).Where("original_author_id = 0")
+ if opts.CommentID == -1 {
+ reaction.CommentID = 0
+ sess.MustCols("comment_id")
+ }
+
+ _, err := sess.Delete(reaction)
return err
}
// DeleteIssueReaction deletes a reaction on issue.
func DeleteIssueReaction(doerID, issueID int64, content string) error {
return DeleteReaction(db.DefaultContext, &ReactionOptions{
- Type: content,
- DoerID: doerID,
- IssueID: issueID,
+ Type: content,
+ DoerID: doerID,
+ IssueID: issueID,
+ CommentID: -1,
})
}
@@ -305,16 +325,14 @@ func (list ReactionList) GroupByType() map[string]ReactionList {
}
func (list ReactionList) getUserIDs() []int64 {
- userIDs := make(map[int64]struct{}, len(list))
+ userIDs := make(container.Set[int64], len(list))
for _, reaction := range list {
if reaction.OriginalAuthor != "" {
continue
}
- if _, ok := userIDs[reaction.UserID]; !ok {
- userIDs[reaction.UserID] = struct{}{}
- }
+ userIDs.Add(reaction.UserID)
}
- return container.KeysInt64(userIDs)
+ return userIDs.Values()
}
func valuesUser(m map[int64]*user_model.User) []*user_model.User {
diff --git a/models/issues/reaction_test.go b/models/issues/reaction_test.go
index ee1b6687a2..835a667619 100644
--- a/models/issues/reaction_test.go
+++ b/models/issues/reaction_test.go
@@ -32,7 +32,7 @@ func addReaction(t *testing.T, doerID, issueID, commentID int64, content string)
func TestIssueAddReaction(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User)
+ user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
var issue1ID int64 = 1
@@ -44,7 +44,7 @@ func TestIssueAddReaction(t *testing.T) {
func TestIssueAddDuplicateReaction(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User)
+ user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
var issue1ID int64 = 1
@@ -58,14 +58,14 @@ func TestIssueAddDuplicateReaction(t *testing.T) {
assert.Error(t, err)
assert.Equal(t, issues_model.ErrReactionAlreadyExist{Reaction: "heart"}, err)
- existingR := unittest.AssertExistsAndLoadBean(t, &issues_model.Reaction{Type: "heart", UserID: user1.ID, IssueID: issue1ID}).(*issues_model.Reaction)
+ existingR := unittest.AssertExistsAndLoadBean(t, &issues_model.Reaction{Type: "heart", UserID: user1.ID, IssueID: issue1ID})
assert.Equal(t, existingR.ID, reaction.ID)
}
func TestIssueDeleteReaction(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User)
+ user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
var issue1ID int64 = 1
@@ -82,14 +82,14 @@ func TestIssueReactionCount(t *testing.T) {
setting.UI.ReactionMaxUserNum = 2
- user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User)
- user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
- user3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3}).(*user_model.User)
- user4 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}).(*user_model.User)
+ user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
+ user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
+ user3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3})
+ user4 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4})
ghost := user_model.NewGhostUser()
var issueID int64 = 2
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository)
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
addReaction(t, user1.ID, issueID, 0, "heart")
addReaction(t, user2.ID, issueID, 0, "heart")
@@ -122,7 +122,7 @@ func TestIssueReactionCount(t *testing.T) {
func TestIssueCommentAddReaction(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User)
+ user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
var issue1ID int64 = 1
var comment1ID int64 = 1
@@ -135,10 +135,10 @@ func TestIssueCommentAddReaction(t *testing.T) {
func TestIssueCommentDeleteReaction(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User)
- user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
- user3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3}).(*user_model.User)
- user4 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}).(*user_model.User)
+ user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
+ user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
+ user3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3})
+ user4 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4})
var issue1ID int64 = 1
var comment1ID int64 = 1
@@ -163,7 +163,7 @@ func TestIssueCommentDeleteReaction(t *testing.T) {
func TestIssueCommentReactionCount(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User)
+ user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
var issue1ID int64 = 1
var comment1ID int64 = 1
diff --git a/models/issues/review.go b/models/issues/review.go
index 5835900801..26fcea9eef 100644
--- a/models/issues/review.go
+++ b/models/issues/review.go
@@ -39,6 +39,10 @@ func (err ErrReviewNotExist) Error() string {
return fmt.Sprintf("review does not exist [id: %d]", err.ID)
}
+func (err ErrReviewNotExist) Unwrap() error {
+ return util.ErrNotExist
+}
+
// ErrNotValidReviewRequest an not allowed review request modify
type ErrNotValidReviewRequest struct {
Reason string
@@ -59,6 +63,10 @@ func (err ErrNotValidReviewRequest) Error() string {
err.RepoID)
}
+func (err ErrNotValidReviewRequest) Unwrap() error {
+ return util.ErrInvalidArgument
+}
+
// ReviewType defines the sort of feedback a review gives
type ReviewType int
diff --git a/models/issues/review_test.go b/models/issues/review_test.go
index 3506604b46..46d1cc777b 100644
--- a/models/issues/review_test.go
+++ b/models/issues/review_test.go
@@ -29,22 +29,22 @@ func TestGetReviewByID(t *testing.T) {
func TestReview_LoadAttributes(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- review := unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 1}).(*issues_model.Review)
+ review := unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 1})
assert.NoError(t, review.LoadAttributes(db.DefaultContext))
assert.NotNil(t, review.Issue)
assert.NotNil(t, review.Reviewer)
- invalidReview1 := unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 2}).(*issues_model.Review)
+ invalidReview1 := unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 2})
assert.Error(t, invalidReview1.LoadAttributes(db.DefaultContext))
- invalidReview2 := unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 3}).(*issues_model.Review)
+ invalidReview2 := unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 3})
assert.Error(t, invalidReview2.LoadAttributes(db.DefaultContext))
}
func TestReview_LoadCodeComments(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- review := unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 4}).(*issues_model.Review)
+ review := unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 4})
assert.NoError(t, review.LoadAttributes(db.DefaultContext))
assert.NoError(t, review.LoadCodeComments(db.DefaultContext))
assert.Len(t, review.CodeComments, 1)
@@ -74,8 +74,8 @@ func TestFindReviews(t *testing.T) {
func TestGetCurrentReview(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 2}).(*issues_model.Issue)
- user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User)
+ issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 2})
+ user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
review, err := issues_model.GetCurrentReview(db.DefaultContext, user, issue)
assert.NoError(t, err)
@@ -83,7 +83,7 @@ func TestGetCurrentReview(t *testing.T) {
assert.Equal(t, issues_model.ReviewTypePending, review.Type)
assert.Equal(t, "Pending Review", review.Content)
- user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 7}).(*user_model.User)
+ user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 7})
review2, err := issues_model.GetCurrentReview(db.DefaultContext, user2, issue)
assert.Error(t, err)
assert.True(t, issues_model.IsErrReviewNotExist(err))
@@ -93,8 +93,8 @@ func TestGetCurrentReview(t *testing.T) {
func TestCreateReview(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 2}).(*issues_model.Issue)
- user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User)
+ issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 2})
+ user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
review, err := issues_model.CreateReview(db.DefaultContext, issues_model.CreateReviewOptions{
Content: "New Review",
@@ -110,10 +110,10 @@ func TestCreateReview(t *testing.T) {
func TestGetReviewersByIssueID(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 3}).(*issues_model.Issue)
- user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
- user3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3}).(*user_model.User)
- user4 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}).(*user_model.User)
+ issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 3})
+ user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
+ user3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3})
+ user4 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4})
expectedReviews := []*issues_model.Review{}
expectedReviews = append(expectedReviews,
@@ -150,43 +150,43 @@ func TestGetReviewersByIssueID(t *testing.T) {
func TestDismissReview(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- rejectReviewExample := unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 9}).(*issues_model.Review)
- requestReviewExample := unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 11}).(*issues_model.Review)
- approveReviewExample := unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 8}).(*issues_model.Review)
+ rejectReviewExample := unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 9})
+ requestReviewExample := unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 11})
+ approveReviewExample := unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 8})
assert.False(t, rejectReviewExample.Dismissed)
assert.False(t, requestReviewExample.Dismissed)
assert.False(t, approveReviewExample.Dismissed)
assert.NoError(t, issues_model.DismissReview(rejectReviewExample, true))
- rejectReviewExample = unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 9}).(*issues_model.Review)
- requestReviewExample = unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 11}).(*issues_model.Review)
+ rejectReviewExample = unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 9})
+ requestReviewExample = unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 11})
assert.True(t, rejectReviewExample.Dismissed)
assert.False(t, requestReviewExample.Dismissed)
assert.NoError(t, issues_model.DismissReview(requestReviewExample, true))
- rejectReviewExample = unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 9}).(*issues_model.Review)
- requestReviewExample = unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 11}).(*issues_model.Review)
+ rejectReviewExample = unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 9})
+ requestReviewExample = unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 11})
assert.True(t, rejectReviewExample.Dismissed)
assert.False(t, requestReviewExample.Dismissed)
assert.False(t, approveReviewExample.Dismissed)
assert.NoError(t, issues_model.DismissReview(requestReviewExample, true))
- rejectReviewExample = unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 9}).(*issues_model.Review)
- requestReviewExample = unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 11}).(*issues_model.Review)
+ rejectReviewExample = unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 9})
+ requestReviewExample = unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 11})
assert.True(t, rejectReviewExample.Dismissed)
assert.False(t, requestReviewExample.Dismissed)
assert.False(t, approveReviewExample.Dismissed)
assert.NoError(t, issues_model.DismissReview(requestReviewExample, false))
- rejectReviewExample = unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 9}).(*issues_model.Review)
- requestReviewExample = unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 11}).(*issues_model.Review)
+ rejectReviewExample = unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 9})
+ requestReviewExample = unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 11})
assert.True(t, rejectReviewExample.Dismissed)
assert.False(t, requestReviewExample.Dismissed)
assert.False(t, approveReviewExample.Dismissed)
assert.NoError(t, issues_model.DismissReview(requestReviewExample, false))
- rejectReviewExample = unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 9}).(*issues_model.Review)
- requestReviewExample = unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 11}).(*issues_model.Review)
+ rejectReviewExample = unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 9})
+ requestReviewExample = unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 11})
assert.True(t, rejectReviewExample.Dismissed)
assert.False(t, requestReviewExample.Dismissed)
assert.False(t, approveReviewExample.Dismissed)
diff --git a/models/issues/stopwatch.go b/models/issues/stopwatch.go
index 0a7ad41f9c..a87fbfafa2 100644
--- a/models/issues/stopwatch.go
+++ b/models/issues/stopwatch.go
@@ -25,6 +25,10 @@ func (err ErrIssueStopwatchNotExist) Error() string {
return fmt.Sprintf("issue stopwatch doesn't exist[uid: %d, issue_id: %d", err.UserID, err.IssueID)
}
+func (err ErrIssueStopwatchNotExist) Unwrap() error {
+ return util.ErrNotExist
+}
+
// ErrIssueStopwatchAlreadyExist represents an error that stopwatch is already exist
type ErrIssueStopwatchAlreadyExist struct {
UserID int64
@@ -35,6 +39,10 @@ func (err ErrIssueStopwatchAlreadyExist) Error() string {
return fmt.Sprintf("issue stopwatch already exists[uid: %d, issue_id: %d", err.UserID, err.IssueID)
}
+func (err ErrIssueStopwatchAlreadyExist) Unwrap() error {
+ return util.ErrAlreadyExist
+}
+
// Stopwatch represents a stopwatch for time tracking.
type Stopwatch struct {
ID int64 `xorm:"pk autoincr"`
diff --git a/models/issues/stopwatch_test.go b/models/issues/stopwatch_test.go
index c0573964d5..a5e33f1cf6 100644
--- a/models/issues/stopwatch_test.go
+++ b/models/issues/stopwatch_test.go
@@ -70,7 +70,7 @@ func TestCreateOrStopIssueStopwatch(t *testing.T) {
assert.NoError(t, err)
assert.NoError(t, issues_model.CreateOrStopIssueStopwatch(user3, issue1))
- sw := unittest.AssertExistsAndLoadBean(t, &issues_model.Stopwatch{UserID: 3, IssueID: 1}).(*issues_model.Stopwatch)
+ sw := unittest.AssertExistsAndLoadBean(t, &issues_model.Stopwatch{UserID: 3, IssueID: 1})
assert.LessOrEqual(t, sw.CreatedUnix, timeutil.TimeStampNow())
assert.NoError(t, issues_model.CreateOrStopIssueStopwatch(user2, issue2))
diff --git a/models/issues/tracked_time.go b/models/issues/tracked_time.go
index 9f8767362f..ca21eb5149 100644
--- a/models/issues/tracked_time.go
+++ b/models/issues/tracked_time.go
@@ -236,7 +236,7 @@ func DeleteIssueUserTimes(issue *Issue, user *user_model.User) error {
return err
}
if removedTime == 0 {
- return db.ErrNotExist{}
+ return db.ErrNotExist{Resource: "tracked_time"}
}
if err := issue.LoadRepo(ctx); err != nil {
@@ -296,7 +296,7 @@ func deleteTimes(ctx context.Context, opts FindTrackedTimesOptions) (removedTime
func deleteTime(ctx context.Context, t *TrackedTime) error {
if t.Deleted {
- return db.ErrNotExist{ID: t.ID}
+ return db.ErrNotExist{Resource: "tracked_time", ID: t.ID}
}
t.Deleted = true
_, err := db.GetEngine(ctx).ID(t.ID).Cols("deleted").Update(t)
@@ -310,7 +310,7 @@ func GetTrackedTimeByID(id int64) (*TrackedTime, error) {
if err != nil {
return nil, err
} else if !has {
- return nil, db.ErrNotExist{ID: id}
+ return nil, db.ErrNotExist{Resource: "tracked_time", ID: id}
}
return time, nil
}
diff --git a/models/issues/tracked_time_test.go b/models/issues/tracked_time_test.go
index 787ba9b701..ba8b242d99 100644
--- a/models/issues/tracked_time_test.go
+++ b/models/issues/tracked_time_test.go
@@ -32,10 +32,10 @@ func TestAddTime(t *testing.T) {
assert.Equal(t, int64(1), trackedTime.IssueID)
assert.Equal(t, int64(3661), trackedTime.Time)
- tt := unittest.AssertExistsAndLoadBean(t, &issues_model.TrackedTime{UserID: 3, IssueID: 1}).(*issues_model.TrackedTime)
+ tt := unittest.AssertExistsAndLoadBean(t, &issues_model.TrackedTime{UserID: 3, IssueID: 1})
assert.Equal(t, int64(3661), tt.Time)
- comment := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{Type: issues_model.CommentTypeAddTimeManual, PosterID: 3, IssueID: 1}).(*issues_model.Comment)
+ comment := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{Type: issues_model.CommentTypeAddTimeManual, PosterID: 3, IssueID: 1})
assert.Equal(t, comment.Content, "1 hour 1 minute")
}
diff --git a/models/main_test.go b/models/main_test.go
index bb2fedc15a..3584001569 100644
--- a/models/main_test.go
+++ b/models/main_test.go
@@ -7,12 +7,15 @@ package models
import (
"testing"
+ activities_model "code.gitea.io/gitea/models/activities"
"code.gitea.io/gitea/models/organization"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/setting"
+ _ "code.gitea.io/gitea/models/system"
+
"github.com/stretchr/testify/assert"
)
@@ -28,7 +31,7 @@ func TestFixturesAreConsistent(t *testing.T) {
&user_model.User{},
&repo_model.Repository{},
&organization.Team{},
- &Action{})
+ &activities_model.Action{})
}
func TestMain(m *testing.M) {
diff --git a/models/migrate.go b/models/migrate.go
index 0af3891cb8..d842fb967b 100644
--- a/models/migrate.go
+++ b/models/migrate.go
@@ -9,6 +9,8 @@ import (
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
+ repo_model "code.gitea.io/gitea/models/repo"
+ "code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/structs"
)
@@ -98,9 +100,9 @@ func InsertIssueComments(comments []*issues_model.Comment) error {
return nil
}
- issueIDs := make(map[int64]bool)
+ issueIDs := make(container.Set[int64])
for _, comment := range comments {
- issueIDs[comment.IssueID] = true
+ issueIDs.Add(comment.IssueID)
}
ctx, committer, err := db.TxContext()
@@ -154,7 +156,7 @@ func InsertPullRequests(prs ...*issues_model.PullRequest) error {
}
// InsertReleases migrates release
-func InsertReleases(rels ...*Release) error {
+func InsertReleases(rels ...*repo_model.Release) error {
ctx, committer, err := db.TxContext()
if err != nil {
return err
@@ -191,7 +193,7 @@ func UpdateMigrationsByType(tp structs.GitServiceType, externalUserID string, us
return err
}
- if err := UpdateReleasesMigrationsByType(tp, externalUserID, userID); err != nil {
+ if err := repo_model.UpdateReleasesMigrationsByType(tp, externalUserID, userID); err != nil {
return err
}
diff --git a/models/migrate_test.go b/models/migrate_test.go
index b6525278ec..bc7729673a 100644
--- a/models/migrate_test.go
+++ b/models/migrate_test.go
@@ -21,7 +21,7 @@ import (
func TestMigrate_InsertMilestones(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
reponame := "repo1"
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{Name: reponame}).(*repo_model.Repository)
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{Name: reponame})
name := "milestonetest1"
ms := &issues_model.Milestone{
RepoID: repo.ID,
@@ -30,7 +30,7 @@ func TestMigrate_InsertMilestones(t *testing.T) {
err := InsertMilestones(ms)
assert.NoError(t, err)
unittest.AssertExistsAndLoadBean(t, ms)
- repoModified := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repo.ID}).(*repo_model.Repository)
+ repoModified := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repo.ID})
assert.EqualValues(t, repo.NumMilestones+1, repoModified.NumMilestones)
unittest.CheckConsistencyFor(t, &issues_model.Milestone{})
@@ -39,10 +39,10 @@ func TestMigrate_InsertMilestones(t *testing.T) {
func assertCreateIssues(t *testing.T, isPull bool) {
assert.NoError(t, unittest.PrepareTestDatabase())
reponame := "repo1"
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{Name: reponame}).(*repo_model.Repository)
- owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User)
- label := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 1}).(*issues_model.Label)
- milestone := unittest.AssertExistsAndLoadBean(t, &issues_model.Milestone{ID: 1}).(*issues_model.Milestone)
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{Name: reponame})
+ owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
+ label := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 1})
+ milestone := unittest.AssertExistsAndLoadBean(t, &issues_model.Milestone{ID: 1})
assert.EqualValues(t, milestone.ID, 1)
reaction := &issues_model.Reaction{
Type: "heart",
@@ -72,7 +72,7 @@ func assertCreateIssues(t *testing.T, isPull bool) {
err := InsertIssues(is)
assert.NoError(t, err)
- i := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{Title: title}).(*issues_model.Issue)
+ i := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{Title: title})
assert.Nil(t, i.ForeignReference)
err = i.LoadAttributes(db.DefaultContext)
assert.NoError(t, err)
@@ -90,9 +90,9 @@ func TestMigrate_CreateIssuesIsPullTrue(t *testing.T) {
func TestMigrate_InsertIssueComments(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1}).(*issues_model.Issue)
+ issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1})
_ = issue.LoadRepo(db.DefaultContext)
- owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: issue.Repo.OwnerID}).(*user_model.User)
+ owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: issue.Repo.OwnerID})
reaction := &issues_model.Reaction{
Type: "heart",
UserID: owner.ID,
@@ -109,7 +109,7 @@ func TestMigrate_InsertIssueComments(t *testing.T) {
err := InsertIssueComments([]*issues_model.Comment{comment})
assert.NoError(t, err)
- issueModified := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1}).(*issues_model.Issue)
+ issueModified := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1})
assert.EqualValues(t, issue.NumComments+1, issueModified.NumComments)
unittest.CheckConsistencyFor(t, &issues_model.Issue{})
@@ -118,8 +118,8 @@ func TestMigrate_InsertIssueComments(t *testing.T) {
func TestMigrate_InsertPullRequests(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
reponame := "repo1"
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{Name: reponame}).(*repo_model.Repository)
- owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User)
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{Name: reponame})
+ owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
i := &issues_model.Issue{
RepoID: repo.ID,
@@ -138,7 +138,7 @@ func TestMigrate_InsertPullRequests(t *testing.T) {
err := InsertPullRequests(p)
assert.NoError(t, err)
- _ = unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{IssueID: i.ID}).(*issues_model.PullRequest)
+ _ = unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{IssueID: i.ID})
unittest.CheckConsistencyFor(t, &issues_model.Issue{}, &issues_model.PullRequest{})
}
@@ -149,7 +149,7 @@ func TestMigrate_InsertReleases(t *testing.T) {
a := &repo_model.Attachment{
UUID: "a0eebc91-9c0c-4ef7-bb6e-6bb9bd380a12",
}
- r := &Release{
+ r := &repo_model.Release{
Attachments: []*repo_model.Attachment{a},
}
diff --git a/models/migrations/fixtures/Test_storeWebauthnCredentialIDAsBytes/expected_webauthn_credential.yml b/models/migrations/fixtures/Test_storeWebauthnCredentialIDAsBytes/expected_webauthn_credential.yml
new file mode 100644
index 0000000000..55a237a0d6
--- /dev/null
+++ b/models/migrations/fixtures/Test_storeWebauthnCredentialIDAsBytes/expected_webauthn_credential.yml
@@ -0,0 +1,9 @@
+-
+ id: 1
+ credential_id: "TVHE44TOH7DF7V48SEAIT3EMMJ7TGBOQ289E5AQB34S98LFCUFJ7U2NAVI8RJG6K2F4TC8AQ8KBNO7AGEOQOL9NE43GR63HTEHJSLOG="
+-
+ id: 2
+ credential_id: "051CLMMKB62S6M9M2A4H54K7MMCQALFJ36G4TGB2S9A47APLTILU6C6744CEBG4EKCGV357N21BSLH8JD33GQMFAR6DQ70S76P34J6FR="
+-
+ id: 4
+ credential_id: "APU4B1NDTEVTEM60V4T0FRL7SRJMO9KIE2AKFQ8JDGTQ7VHFI41FDEFTDLBVQEAE4ER49QV2GTGVFDNBO31BPOA3OQN6879OT6MTU3G="
diff --git a/models/migrations/fixtures/Test_storeWebauthnCredentialIDAsBytes/webauthn_credential.yml b/models/migrations/fixtures/Test_storeWebauthnCredentialIDAsBytes/webauthn_credential.yml
new file mode 100644
index 0000000000..c02a76e374
--- /dev/null
+++ b/models/migrations/fixtures/Test_storeWebauthnCredentialIDAsBytes/webauthn_credential.yml
@@ -0,0 +1,31 @@
+-
+ id: 1
+ lower_name: "u2fkey-correctly-migrated"
+ name: "u2fkey-correctly-migrated"
+ user_id: 1
+ credential_id: "TVHE44TOH7DF7V48SEAIT3EMMJ7TGBOQ289E5AQB34S98LFCUFJ7U2NAVI8RJG6K2F4TC8AQ8KBNO7AGEOQOL9NE43GR63HTEHJSLOG="
+ public_key: 0x040d0967a2cad045011631187576492a0beb5b377954b4f694c5afc8bdf25270f87f09a9ab6ce9c282f447ba71b2f2bae2105b32b847e0704f310f48644e3eddf2
+ attestation_type: 'fido-u2f'
+ sign_count: 1
+ clone_warning: false
+-
+ id: 2
+ lower_name: "non-u2f-key"
+ name: "non-u2f-key"
+ user_id: 1
+ credential_id: "051CLMMKB62S6M9M2A4H54K7MMCQALFJ36G4TGB2S9A47APLTILU6C6744CEBG4EKCGV357N21BSLH8JD33GQMFAR6DQ70S76P34J6FR"
+ public_key: 0x040d0967a2cad045011631187576492a0beb5b377954b4f694c5afc8bdf25270f87f09a9ab6ce9c282f447ba71b2f2bae2105b32b847e0704f310f48644e3eddf2
+ attestation_type: 'none'
+ sign_count: 1
+ clone_warning: false
+-
+ id: 4
+ lower_name: "packed-key"
+ name: "packed-key"
+ user_id: 1
+ credential_id: "APU4B1NDTEVTEM60V4T0FRL7SRJMO9KIE2AKFQ8JDGTQ7VHFI41FDEFTDLBVQEAE4ER49QV2GTGVFDNBO31BPOA3OQN6879OT6MTU3G="
+ public_key: 0x040d0967a2cad045011631187576492a0beb5b377954b4f694c5afc8bdf25270f87f09a9ab6ce9c282f447ba71b2f2bae2105b32b847e0704f310f48644e3eddf2
+ attestation_type: 'fido-u2f'
+ sign_count: 1
+ clone_warning: false
+
diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go
index 1b2a743b6d..afe1445a23 100644
--- a/models/migrations/migrations.go
+++ b/models/migrations/migrations.go
@@ -398,6 +398,25 @@ var migrations = []Migration{
NewMigration("Improve Action table indices v2", improveActionTableIndices),
// v219 -> v220
NewMigration("Add sync_on_commit column to push_mirror table", addSyncOnCommitColForPushMirror),
+ // v220 -> v221
+ NewMigration("Add container repository property", addContainerRepositoryProperty),
+ // v221 -> v222
+ NewMigration("Store WebAuthentication CredentialID as bytes and increase size to at least 1024", storeWebauthnCredentialIDAsBytes),
+ // v222 -> v223
+ NewMigration("Drop old CredentialID column", dropOldCredentialIDColumn),
+ // v223 -> v224
+ NewMigration("Rename CredentialIDBytes column to CredentialID", renameCredentialIDBytes),
+
+ // Gitea 1.17.0 ends at v224
+
+ // v224 -> v225
+ NewMigration("Add badges to users", createUserBadgesTable),
+ // v225 -> v226
+ NewMigration("Alter gpg_key/public_key content TEXT fields to MEDIUMTEXT", alterPublicGPGKeyContentFieldsToMediumText),
+ // v226 -> v227
+ NewMigration("Conan and generic packages do not need to be semantically versioned", fixPackageSemverField),
+ // v227 -> v228
+ NewMigration("Create key/value table for system settings", createSystemSettingsTable),
}
// GetCurrentDBVersion returns the current db version
diff --git a/models/migrations/migrations_test.go b/models/migrations/migrations_test.go
index 46782f24a1..5cd70626b4 100644
--- a/models/migrations/migrations_test.go
+++ b/models/migrations/migrations_test.go
@@ -46,7 +46,7 @@ func TestMain(m *testing.M) {
giteaConf := os.Getenv("GITEA_CONF")
if giteaConf == "" {
- giteaConf = path.Join(filepath.Dir(setting.AppPath), "integrations/sqlite.ini")
+ giteaConf = path.Join(filepath.Dir(setting.AppPath), "tests/sqlite.ini")
fmt.Printf("Environment variable $GITEA_CONF not set - defaulting to %s\n", giteaConf)
}
@@ -66,11 +66,10 @@ func TestMain(m *testing.M) {
setting.SetCustomPathAndConf("", "", "")
setting.LoadForTest()
- if err = git.InitOnceWithSync(context.Background()); err != nil {
- fmt.Printf("Unable to InitOnceWithSync: %v\n", err)
+ if err = git.InitFull(context.Background()); err != nil {
+ fmt.Printf("Unable to InitFull: %v\n", err)
os.Exit(1)
}
- git.CheckLFSVersion()
setting.InitDBConfig()
setting.NewLogServices(true)
@@ -206,8 +205,7 @@ func prepareTestEnv(t *testing.T, skip int, syncModels ...interface{}) (*xorm.En
ourSkip += skip
deferFn := PrintCurrentTest(t, ourSkip)
assert.NoError(t, os.RemoveAll(setting.RepoRootPath))
- assert.NoError(t, unittest.CopyDir(path.Join(filepath.Dir(setting.AppPath), "integrations/gitea-repositories-meta"), setting.RepoRootPath))
- assert.NoError(t, git.InitOnceWithSync(context.Background())) // the gitconfig has been removed above, so sync the gitconfig again
+ assert.NoError(t, unittest.CopyDir(path.Join(filepath.Dir(setting.AppPath), "tests/gitea-repositories-meta"), setting.RepoRootPath))
ownerDirs, err := os.ReadDir(setting.RepoRootPath)
if err != nil {
assert.NoError(t, err, "unable to read the new repo root: %v\n", err)
diff --git a/models/migrations/testlogger_test.go b/models/migrations/testlogger_test.go
index adbf19c0db..0455d9c9a6 100644
--- a/models/migrations/testlogger_test.go
+++ b/models/migrations/testlogger_test.go
@@ -188,5 +188,5 @@ func (log *TestLogger) GetName() string {
func init() {
log.Register("test", NewTestLogger)
_, filename, _, _ := runtime.Caller(0)
- prefix = strings.TrimSuffix(filename, "integrations/testlogger.go")
+ prefix = strings.TrimSuffix(filename, "tests/testlogger.go")
}
diff --git a/models/migrations/v115.go b/models/migrations/v115.go
index 7708ed5e28..b242ccf238 100644
--- a/models/migrations/v115.go
+++ b/models/migrations/v115.go
@@ -13,6 +13,7 @@ import (
"path/filepath"
"time"
+ "code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
@@ -39,7 +40,7 @@ func renameExistingUserAvatarName(x *xorm.Engine) error {
}
log.Info("%d User Avatar(s) to migrate ...", count)
- deleteList := make(map[string]struct{})
+ deleteList := make(container.Set[string])
start := 0
migrated := 0
for {
@@ -86,7 +87,7 @@ func renameExistingUserAvatarName(x *xorm.Engine) error {
return fmt.Errorf("[user: %s] user table update: %v", user.LowerName, err)
}
- deleteList[filepath.Join(setting.Avatar.Path, oldAvatar)] = struct{}{}
+ deleteList.Add(filepath.Join(setting.Avatar.Path, oldAvatar))
migrated++
select {
case <-ticker.C:
diff --git a/models/migrations/v218.go b/models/migrations/v218.go
index dee8e5517e..e08c6c5b0f 100644
--- a/models/migrations/v218.go
+++ b/models/migrations/v218.go
@@ -5,6 +5,7 @@
package migrations
import (
+ "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/timeutil"
"xorm.io/xorm"
@@ -37,8 +38,14 @@ func (*improveActionTableIndicesAction) TableIndices() []*schemas.Index {
actUserIndex := schemas.NewIndex("au_r_c_u_d", schemas.IndexType)
actUserIndex.AddColumn("act_user_id", "repo_id", "created_unix", "user_id", "is_deleted")
+ indices := []*schemas.Index{actUserIndex, repoIndex}
+ if setting.Database.UsePostgreSQL {
+ cudIndex := schemas.NewIndex("c_u_d", schemas.IndexType)
+ cudIndex.AddColumn("created_unix", "user_id", "is_deleted")
+ indices = append(indices, cudIndex)
+ }
- return []*schemas.Index{actUserIndex, repoIndex}
+ return indices
}
func improveActionTableIndices(x *xorm.Engine) error {
diff --git a/models/migrations/v220.go b/models/migrations/v220.go
new file mode 100644
index 0000000000..8138bc5bb1
--- /dev/null
+++ b/models/migrations/v220.go
@@ -0,0 +1,28 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package migrations
+
+import (
+ packages_model "code.gitea.io/gitea/models/packages"
+ container_module "code.gitea.io/gitea/modules/packages/container"
+
+ "xorm.io/xorm"
+ "xorm.io/xorm/schemas"
+)
+
+func addContainerRepositoryProperty(x *xorm.Engine) (err error) {
+ switch x.Dialect().URI().DBType {
+ case schemas.SQLITE:
+ _, err = x.Exec("INSERT INTO package_property (ref_type, ref_id, name, value) SELECT ?, p.id, ?, u.lower_name || '/' || p.lower_name FROM package p JOIN `user` u ON p.owner_id = u.id WHERE p.type = ?",
+ packages_model.PropertyTypePackage, container_module.PropertyRepository, packages_model.TypeContainer)
+ case schemas.MSSQL:
+ _, err = x.Exec("INSERT INTO package_property (ref_type, ref_id, name, value) SELECT ?, p.id, ?, u.lower_name + '/' + p.lower_name FROM package p JOIN `user` u ON p.owner_id = u.id WHERE p.type = ?",
+ packages_model.PropertyTypePackage, container_module.PropertyRepository, packages_model.TypeContainer)
+ default:
+ _, err = x.Exec("INSERT INTO package_property (ref_type, ref_id, name, value) SELECT ?, p.id, ?, CONCAT(u.lower_name, '/', p.lower_name) FROM package p JOIN `user` u ON p.owner_id = u.id WHERE p.type = ?",
+ packages_model.PropertyTypePackage, container_module.PropertyRepository, packages_model.TypeContainer)
+ }
+ return err
+}
diff --git a/models/migrations/v221.go b/models/migrations/v221.go
new file mode 100644
index 0000000000..f3bcfcdf1d
--- /dev/null
+++ b/models/migrations/v221.go
@@ -0,0 +1,75 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package migrations
+
+import (
+ "encoding/base32"
+ "fmt"
+
+ "code.gitea.io/gitea/modules/timeutil"
+
+ "xorm.io/xorm"
+)
+
+func storeWebauthnCredentialIDAsBytes(x *xorm.Engine) error {
+ // Create webauthnCredential table
+ type webauthnCredential struct {
+ ID int64 `xorm:"pk autoincr"`
+ Name string
+ LowerName string `xorm:"unique(s)"`
+ UserID int64 `xorm:"INDEX unique(s)"`
+ CredentialID string `xorm:"INDEX VARCHAR(410)"`
+ // Note the lack of INDEX here - these will be created once the column is renamed in v223.go
+ CredentialIDBytes []byte `xorm:"VARBINARY(1024)"` // CredentialID is at most 1023 bytes as per spec released 20 July 2022
+ PublicKey []byte
+ AttestationType string
+ AAGUID []byte
+ SignCount uint32 `xorm:"BIGINT"`
+ CloneWarning bool
+ CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
+ UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
+ }
+ if err := x.Sync2(&webauthnCredential{}); err != nil {
+ return err
+ }
+
+ var start int
+ creds := make([]*webauthnCredential, 0, 50)
+ for {
+ err := x.Select("id, credential_id").OrderBy("id").Limit(50, start).Find(&creds)
+ if err != nil {
+ return err
+ }
+
+ err = func() error {
+ sess := x.NewSession()
+ defer sess.Close()
+ if err := sess.Begin(); err != nil {
+ return fmt.Errorf("unable to allow start session. Error: %w", err)
+ }
+ for _, cred := range creds {
+ cred.CredentialIDBytes, err = base32.HexEncoding.DecodeString(cred.CredentialID)
+ if err != nil {
+ return fmt.Errorf("unable to parse credential id %s for credential[%d]: %w", cred.CredentialID, cred.ID, err)
+ }
+ count, err := sess.ID(cred.ID).Cols("credential_id_bytes").Update(cred)
+ if count != 1 || err != nil {
+ return fmt.Errorf("unable to update credential id bytes for credential[%d]: %d,%w", cred.ID, count, err)
+ }
+ }
+ return sess.Commit()
+ }()
+ if err != nil {
+ return err
+ }
+
+ if len(creds) < 50 {
+ break
+ }
+ start += 50
+ creds = creds[:0]
+ }
+ return nil
+}
diff --git a/models/migrations/v221_test.go b/models/migrations/v221_test.go
new file mode 100644
index 0000000000..c50ca5c873
--- /dev/null
+++ b/models/migrations/v221_test.go
@@ -0,0 +1,65 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package migrations
+
+import (
+ "encoding/base32"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func Test_storeWebauthnCredentialIDAsBytes(t *testing.T) {
+ // Create webauthnCredential table
+ type WebauthnCredential struct {
+ ID int64 `xorm:"pk autoincr"`
+ Name string
+ LowerName string `xorm:"unique(s)"`
+ UserID int64 `xorm:"INDEX unique(s)"`
+ CredentialID string `xorm:"INDEX VARCHAR(410)"`
+ PublicKey []byte
+ AttestationType string
+ AAGUID []byte
+ SignCount uint32 `xorm:"BIGINT"`
+ CloneWarning bool
+ }
+
+ type ExpectedWebauthnCredential struct {
+ ID int64 `xorm:"pk autoincr"`
+ CredentialID string // CredentialID is at most 1023 bytes as per spec released 20 July 2022
+ }
+
+ type ConvertedWebauthnCredential struct {
+ ID int64 `xorm:"pk autoincr"`
+ CredentialIDBytes []byte `xorm:"VARBINARY(1024)"` // CredentialID is at most 1023 bytes as per spec released 20 July 2022
+ }
+
+ // Prepare and load the testing database
+ x, deferable := prepareTestEnv(t, 0, new(WebauthnCredential), new(ExpectedWebauthnCredential))
+ defer deferable()
+ if x == nil || t.Failed() {
+ return
+ }
+
+ if err := storeWebauthnCredentialIDAsBytes(x); err != nil {
+ assert.NoError(t, err)
+ return
+ }
+
+ expected := []ExpectedWebauthnCredential{}
+ if err := x.Table("expected_webauthn_credential").Asc("id").Find(&expected); !assert.NoError(t, err) {
+ return
+ }
+
+ got := []ConvertedWebauthnCredential{}
+ if err := x.Table("webauthn_credential").Select("id, credential_id_bytes").Asc("id").Find(&got); !assert.NoError(t, err) {
+ return
+ }
+
+ for i, e := range expected {
+ credIDBytes, _ := base32.HexEncoding.DecodeString(e.CredentialID)
+ assert.Equal(t, credIDBytes, got[i].CredentialIDBytes)
+ }
+}
diff --git a/models/migrations/v222.go b/models/migrations/v222.go
new file mode 100644
index 0000000000..99acdfd206
--- /dev/null
+++ b/models/migrations/v222.go
@@ -0,0 +1,64 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package migrations
+
+import (
+ "context"
+ "fmt"
+
+ "code.gitea.io/gitea/modules/timeutil"
+
+ "xorm.io/xorm"
+)
+
+func dropOldCredentialIDColumn(x *xorm.Engine) error {
+ // This migration maybe rerun so that we should check if it has been run
+ credentialIDExist, err := x.Dialect().IsColumnExist(x.DB(), context.Background(), "webauthn_credential", "credential_id")
+ if err != nil {
+ return err
+ }
+ if !credentialIDExist {
+ // Column is already non-extant
+ return nil
+ }
+ credentialIDBytesExists, err := x.Dialect().IsColumnExist(x.DB(), context.Background(), "webauthn_credential", "credential_id_bytes")
+ if err != nil {
+ return err
+ }
+ if !credentialIDBytesExists {
+ // looks like 221 hasn't properly run
+ return fmt.Errorf("webauthn_credential does not have a credential_id_bytes column... it is not safe to run this migration")
+ }
+
+ // Create webauthnCredential table
+ type webauthnCredential struct {
+ ID int64 `xorm:"pk autoincr"`
+ Name string
+ LowerName string `xorm:"unique(s)"`
+ UserID int64 `xorm:"INDEX unique(s)"`
+ CredentialID string `xorm:"INDEX VARCHAR(410)"`
+ // Note the lack of the INDEX on CredentialIDBytes - we will add this in v223.go
+ CredentialIDBytes []byte `xorm:"VARBINARY(1024)"` // CredentialID is at most 1023 bytes as per spec released 20 July 2022
+ PublicKey []byte
+ AttestationType string
+ AAGUID []byte
+ SignCount uint32 `xorm:"BIGINT"`
+ CloneWarning bool
+ CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
+ UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
+ }
+ if err := x.Sync2(&webauthnCredential{}); err != nil {
+ return err
+ }
+
+ // Drop the old credential ID
+ sess := x.NewSession()
+ defer sess.Close()
+
+ if err := dropTableColumns(sess, "webauthn_credential", "credential_id"); err != nil {
+ return fmt.Errorf("unable to drop old credentialID column: %w", err)
+ }
+ return sess.Commit()
+}
diff --git a/models/migrations/v223.go b/models/migrations/v223.go
new file mode 100644
index 0000000000..d7ee4812b8
--- /dev/null
+++ b/models/migrations/v223.go
@@ -0,0 +1,103 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package migrations
+
+import (
+ "context"
+ "fmt"
+
+ "code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/timeutil"
+
+ "xorm.io/xorm"
+)
+
+func renameCredentialIDBytes(x *xorm.Engine) error {
+ // This migration maybe rerun so that we should check if it has been run
+ credentialIDExist, err := x.Dialect().IsColumnExist(x.DB(), context.Background(), "webauthn_credential", "credential_id")
+ if err != nil {
+ return err
+ }
+ if credentialIDExist {
+ credentialIDBytesExists, err := x.Dialect().IsColumnExist(x.DB(), context.Background(), "webauthn_credential", "credential_id_bytes")
+ if err != nil {
+ return err
+ }
+ if !credentialIDBytesExists {
+ return nil
+ }
+ }
+
+ err = func() error {
+ // webauthnCredential table
+ type webauthnCredential struct {
+ ID int64 `xorm:"pk autoincr"`
+ Name string
+ LowerName string `xorm:"unique(s)"`
+ UserID int64 `xorm:"INDEX unique(s)"`
+ // Note the lack of INDEX here
+ CredentialIDBytes []byte `xorm:"VARBINARY(1024)"` // CredentialID is at most 1023 bytes as per spec released 20 July 2022
+ PublicKey []byte
+ AttestationType string
+ AAGUID []byte
+ SignCount uint32 `xorm:"BIGINT"`
+ CloneWarning bool
+ CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
+ UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
+ }
+ sess := x.NewSession()
+ defer sess.Close()
+ if err := sess.Begin(); err != nil {
+ return err
+ }
+
+ if err := sess.Sync2(new(webauthnCredential)); err != nil {
+ return fmt.Errorf("error on Sync2: %v", err)
+ }
+
+ if credentialIDExist {
+ // if both errors and message exist, drop message at first
+ if err := dropTableColumns(sess, "webauthn_credential", "credential_id"); err != nil {
+ return err
+ }
+ }
+
+ switch {
+ case setting.Database.UseMySQL:
+ if _, err := sess.Exec("ALTER TABLE `webauthn_credential` CHANGE credential_id_bytes credential_id VARBINARY(1024)"); err != nil {
+ return err
+ }
+ case setting.Database.UseMSSQL:
+ if _, err := sess.Exec("sp_rename 'webauthn_credential.credential_id_bytes', 'credential_id', 'COLUMN'"); err != nil {
+ return err
+ }
+ default:
+ if _, err := sess.Exec("ALTER TABLE `webauthn_credential` RENAME COLUMN credential_id_bytes TO credential_id"); err != nil {
+ return err
+ }
+ }
+ return sess.Commit()
+ }()
+ if err != nil {
+ return err
+ }
+
+ // Create webauthnCredential table
+ type webauthnCredential struct {
+ ID int64 `xorm:"pk autoincr"`
+ Name string
+ LowerName string `xorm:"unique(s)"`
+ UserID int64 `xorm:"INDEX unique(s)"`
+ CredentialID []byte `xorm:"INDEX VARBINARY(1024)"` // CredentialID is at most 1023 bytes as per spec released 20 July 2022
+ PublicKey []byte
+ AttestationType string
+ AAGUID []byte
+ SignCount uint32 `xorm:"BIGINT"`
+ CloneWarning bool
+ CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
+ UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
+ }
+ return x.Sync2(&webauthnCredential{})
+}
diff --git a/models/migrations/v224.go b/models/migrations/v224.go
new file mode 100644
index 0000000000..2ed161ef4d
--- /dev/null
+++ b/models/migrations/v224.go
@@ -0,0 +1,28 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package migrations
+
+import (
+ "xorm.io/xorm"
+)
+
+func createUserBadgesTable(x *xorm.Engine) error {
+ type Badge struct {
+ ID int64 `xorm:"pk autoincr"`
+ Description string
+ ImageURL string
+ }
+
+ type userBadge struct {
+ ID int64 `xorm:"pk autoincr"`
+ BadgeID int64
+ UserID int64 `xorm:"INDEX"`
+ }
+
+ if err := x.Sync2(new(Badge)); err != nil {
+ return err
+ }
+ return x.Sync2(new(userBadge))
+}
diff --git a/models/migrations/v225.go b/models/migrations/v225.go
new file mode 100644
index 0000000000..6dd460eb68
--- /dev/null
+++ b/models/migrations/v225.go
@@ -0,0 +1,29 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package migrations
+
+import (
+ "code.gitea.io/gitea/modules/setting"
+
+ "xorm.io/xorm"
+)
+
+func alterPublicGPGKeyContentFieldsToMediumText(x *xorm.Engine) error {
+ sess := x.NewSession()
+ defer sess.Close()
+ if err := sess.Begin(); err != nil {
+ return err
+ }
+
+ if setting.Database.UseMySQL {
+ if _, err := sess.Exec("ALTER TABLE `gpg_key` CHANGE `content` `content` MEDIUMTEXT"); err != nil {
+ return err
+ }
+ if _, err := sess.Exec("ALTER TABLE `public_key` CHANGE `content` `content` MEDIUMTEXT"); err != nil {
+ return err
+ }
+ }
+ return sess.Commit()
+}
diff --git a/models/migrations/v226.go b/models/migrations/v226.go
new file mode 100644
index 0000000000..2f85bca21f
--- /dev/null
+++ b/models/migrations/v226.go
@@ -0,0 +1,15 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package migrations
+
+import (
+ "xorm.io/builder"
+ "xorm.io/xorm"
+)
+
+func fixPackageSemverField(x *xorm.Engine) error {
+ _, err := x.Exec(builder.Update(builder.Eq{"semver_compatible": false}).From("`package`").Where(builder.In("`type`", "conan", "generic")))
+ return err
+}
diff --git a/models/migrations/v227.go b/models/migrations/v227.go
new file mode 100644
index 0000000000..8a3a9c877e
--- /dev/null
+++ b/models/migrations/v227.go
@@ -0,0 +1,64 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package migrations
+
+import (
+ "fmt"
+ "strconv"
+
+ "code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/timeutil"
+
+ "xorm.io/xorm"
+)
+
+type SystemSetting struct {
+ ID int64 `xorm:"pk autoincr"`
+ SettingKey string `xorm:"varchar(255) unique"` // ensure key is always lowercase
+ SettingValue string `xorm:"text"`
+ Version int `xorm:"version"` // prevent to override
+ Created timeutil.TimeStamp `xorm:"created"`
+ Updated timeutil.TimeStamp `xorm:"updated"`
+}
+
+func insertSettingsIfNotExist(x *xorm.Engine, sysSettings []*SystemSetting) error {
+ sess := x.NewSession()
+ defer sess.Close()
+ if err := sess.Begin(); err != nil {
+ return err
+ }
+ for _, setting := range sysSettings {
+ exist, err := sess.Table("system_setting").Where("setting_key=?", setting.SettingKey).Exist()
+ if err != nil {
+ return err
+ }
+ if !exist {
+ if _, err := sess.Insert(setting); err != nil {
+ return err
+ }
+ }
+ }
+ return sess.Commit()
+}
+
+func createSystemSettingsTable(x *xorm.Engine) error {
+ if err := x.Sync2(new(SystemSetting)); err != nil {
+ return fmt.Errorf("sync2: %v", err)
+ }
+
+ // migrate xx to database
+ sysSettings := []*SystemSetting{
+ {
+ SettingKey: "picture.disable_gravatar",
+ SettingValue: strconv.FormatBool(setting.DisableGravatar),
+ },
+ {
+ SettingKey: "picture.enable_federated_avatar",
+ SettingValue: strconv.FormatBool(setting.EnableFederatedAvatar),
+ },
+ }
+
+ return insertSettingsIfNotExist(x, sysSettings)
+}
diff --git a/models/org.go b/models/org.go
index efcb7183e7..53be80c3df 100644
--- a/models/org.go
+++ b/models/org.go
@@ -8,79 +8,13 @@ package models
import (
"context"
"fmt"
- "strings"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/organization"
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"
-
- "xorm.io/builder"
)
-// MinimalOrg represents a simple orgnization with only needed columns
-type MinimalOrg = organization.Organization
-
-// GetUserOrgsList returns one user's all orgs list
-func GetUserOrgsList(user *user_model.User) ([]*MinimalOrg, error) {
- schema, err := db.TableInfo(new(user_model.User))
- if err != nil {
- return nil, err
- }
-
- outputCols := []string{
- "id",
- "name",
- "full_name",
- "visibility",
- "avatar",
- "avatar_email",
- "use_custom_avatar",
- }
-
- groupByCols := &strings.Builder{}
- for _, col := range outputCols {
- fmt.Fprintf(groupByCols, "`%s`.%s,", schema.Name, col)
- }
- groupByStr := groupByCols.String()
- groupByStr = groupByStr[0 : len(groupByStr)-1]
-
- sess := db.GetEngine(db.DefaultContext)
- sess = sess.Select(groupByStr+", count(distinct repo_id) as org_count").
- Table("user").
- Join("INNER", "team", "`team`.org_id = `user`.id").
- Join("INNER", "team_user", "`team`.id = `team_user`.team_id").
- Join("LEFT", builder.
- Select("id as repo_id, owner_id as repo_owner_id").
- From("repository").
- Where(repo_model.AccessibleRepositoryCondition(user, unit.TypeInvalid)), "`repository`.repo_owner_id = `team`.org_id").
- Where("`team_user`.uid = ?", user.ID).
- GroupBy(groupByStr)
-
- type OrgCount struct {
- organization.Organization `xorm:"extends"`
- OrgCount int
- }
-
- orgCounts := make([]*OrgCount, 0, 10)
-
- if err := sess.
- Asc("`user`.name").
- Find(&orgCounts); err != nil {
- return nil, err
- }
-
- orgs := make([]*MinimalOrg, len(orgCounts))
- for i, orgCount := range orgCounts {
- orgCount.Organization.NumRepos = orgCount.OrgCount
- orgs[i] = &orgCount.Organization
- }
-
- return orgs, nil
-}
-
func removeOrgUser(ctx context.Context, orgID, userID int64) error {
ou := new(organization.OrgUser)
diff --git a/models/org_team.go b/models/org_team.go
index 5d29e33337..61ddd2a047 100644
--- a/models/org_team.go
+++ b/models/org_team.go
@@ -25,12 +25,12 @@ import (
"xorm.io/builder"
)
-func addRepository(ctx context.Context, t *organization.Team, repo *repo_model.Repository) (err error) {
+func AddRepository(ctx context.Context, t *organization.Team, repo *repo_model.Repository) (err error) {
if err = organization.AddTeamRepo(ctx, t.OrgID, t.ID, repo.ID); err != nil {
return err
}
- if _, err = db.GetEngine(ctx).Incr("num_repos").ID(t.ID).Update(new(organization.Team)); err != nil {
+ if err = organization.IncrTeamRepoNum(ctx, t.ID); err != nil {
return fmt.Errorf("update team: %v", err)
}
@@ -58,16 +58,15 @@ func addRepository(ctx context.Context, t *organization.Team, repo *repo_model.R
// addAllRepositories adds all repositories to the team.
// If the team already has some repositories they will be left unchanged.
func addAllRepositories(ctx context.Context, t *organization.Team) error {
- var orgRepos []repo_model.Repository
- e := db.GetEngine(ctx)
- if err := e.Where("owner_id = ?", t.OrgID).Find(&orgRepos); err != nil {
+ orgRepos, err := organization.GetOrgRepositories(ctx, t.OrgID)
+ if err != nil {
return fmt.Errorf("get org repos: %v", err)
}
for _, repo := range orgRepos {
if !organization.HasTeamRepo(ctx, t.OrgID, t.ID, repo.ID) {
- if err := addRepository(ctx, t, &repo); err != nil {
- return fmt.Errorf("addRepository: %v", err)
+ if err := AddRepository(ctx, t, repo); err != nil {
+ return fmt.Errorf("AddRepository: %v", err)
}
}
}
@@ -90,27 +89,6 @@ func AddAllRepositories(t *organization.Team) (err error) {
return committer.Commit()
}
-// AddRepository adds new repository to team of organization.
-func AddRepository(t *organization.Team, repo *repo_model.Repository) (err error) {
- if repo.OwnerID != t.OrgID {
- return errors.New("Repository does not belong to organization")
- } else if HasRepository(t, repo.ID) {
- return nil
- }
-
- ctx, committer, err := db.TxContext()
- if err != nil {
- return err
- }
- defer committer.Close()
-
- if err = addRepository(ctx, t, repo); err != nil {
- return err
- }
-
- return committer.Commit()
-}
-
// RemoveAllRepositories removes all repositories from team and recalculates access
func RemoveAllRepositories(t *organization.Team) (err error) {
if t.IncludesAllRepositories {
diff --git a/models/org_team_test.go b/models/org_team_test.go
index 35ee9af85a..a600d07c0c 100644
--- a/models/org_team_test.go
+++ b/models/org_team_test.go
@@ -23,7 +23,7 @@ func TestTeam_AddMember(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
test := func(teamID, userID int64) {
- team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: teamID}).(*organization.Team)
+ team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: teamID})
assert.NoError(t, AddTeamMember(team, userID))
unittest.AssertExistsAndLoadBean(t, &organization.TeamUser{UID: userID, TeamID: teamID})
unittest.CheckConsistencyFor(t, &organization.Team{ID: teamID}, &user_model.User{ID: team.OrgID})
@@ -37,7 +37,7 @@ func TestTeam_RemoveMember(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
testSuccess := func(teamID, userID int64) {
- team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: teamID}).(*organization.Team)
+ team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: teamID})
assert.NoError(t, RemoveTeamMember(team, userID))
unittest.AssertNotExistsBean(t, &organization.TeamUser{UID: userID, TeamID: teamID})
unittest.CheckConsistencyFor(t, &organization.Team{ID: teamID})
@@ -47,7 +47,7 @@ func TestTeam_RemoveMember(t *testing.T) {
testSuccess(3, 2)
testSuccess(3, unittest.NonexistentID)
- team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 1}).(*organization.Team)
+ team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 1})
err := RemoveTeamMember(team, 2)
assert.True(t, organization.IsErrLastOrgOwner(err))
}
@@ -56,7 +56,7 @@ func TestTeam_HasRepository(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
test := func(teamID, repoID int64, expected bool) {
- team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: teamID}).(*organization.Team)
+ team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: teamID})
assert.Equal(t, expected, HasRepository(team, repoID))
}
test(1, 1, false)
@@ -68,30 +68,11 @@ func TestTeam_HasRepository(t *testing.T) {
test(2, 5, false)
}
-func TestTeam_AddRepository(t *testing.T) {
- assert.NoError(t, unittest.PrepareTestDatabase())
-
- testSuccess := func(teamID, repoID int64) {
- team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: teamID}).(*organization.Team)
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repoID}).(*repo_model.Repository)
- assert.NoError(t, AddRepository(team, repo))
- unittest.AssertExistsAndLoadBean(t, &organization.TeamRepo{TeamID: teamID, RepoID: repoID})
- unittest.CheckConsistencyFor(t, &organization.Team{ID: teamID}, &repo_model.Repository{ID: repoID})
- }
- testSuccess(2, 3)
- testSuccess(2, 5)
-
- team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 1}).(*organization.Team)
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository)
- assert.Error(t, AddRepository(team, repo))
- unittest.CheckConsistencyFor(t, &organization.Team{ID: 1}, &repo_model.Repository{ID: 1})
-}
-
func TestTeam_RemoveRepository(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
testSuccess := func(teamID, repoID int64) {
- team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: teamID}).(*organization.Team)
+ team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: teamID})
assert.NoError(t, RemoveRepository(team, repoID))
unittest.AssertNotExistsBean(t, &organization.TeamRepo{TeamID: teamID, RepoID: repoID})
unittest.CheckConsistencyFor(t, &organization.Team{ID: teamID}, &repo_model.Repository{ID: repoID})
@@ -120,17 +101,17 @@ func TestUpdateTeam(t *testing.T) {
// successful update
assert.NoError(t, unittest.PrepareTestDatabase())
- team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 2}).(*organization.Team)
+ team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 2})
team.LowerName = "newname"
team.Name = "newName"
team.Description = strings.Repeat("A long description!", 100)
team.AccessMode = perm.AccessModeAdmin
assert.NoError(t, UpdateTeam(team, true, false))
- team = unittest.AssertExistsAndLoadBean(t, &organization.Team{Name: "newName"}).(*organization.Team)
+ team = unittest.AssertExistsAndLoadBean(t, &organization.Team{Name: "newName"})
assert.True(t, strings.HasPrefix(team.Description, "A long description!"))
- access := unittest.AssertExistsAndLoadBean(t, &access_model.Access{UserID: 4, RepoID: 3}).(*access_model.Access)
+ access := unittest.AssertExistsAndLoadBean(t, &access_model.Access{UserID: 4, RepoID: 3})
assert.EqualValues(t, perm.AccessModeAdmin, access.Mode)
unittest.CheckConsistencyFor(t, &organization.Team{ID: team.ID})
@@ -140,7 +121,7 @@ func TestUpdateTeam2(t *testing.T) {
// update to already-existing team
assert.NoError(t, unittest.PrepareTestDatabase())
- team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 2}).(*organization.Team)
+ team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 2})
team.LowerName = "owners"
team.Name = "Owners"
team.Description = strings.Repeat("A long description!", 100)
@@ -153,15 +134,15 @@ func TestUpdateTeam2(t *testing.T) {
func TestDeleteTeam(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 2}).(*organization.Team)
+ team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 2})
assert.NoError(t, DeleteTeam(team))
unittest.AssertNotExistsBean(t, &organization.Team{ID: team.ID})
unittest.AssertNotExistsBean(t, &organization.TeamRepo{TeamID: team.ID})
unittest.AssertNotExistsBean(t, &organization.TeamUser{TeamID: team.ID})
// check that team members don't have "leftover" access to repos
- user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}).(*user_model.User)
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3}).(*repo_model.Repository)
+ user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4})
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3})
accessMode, err := access_model.AccessLevel(user, repo)
assert.NoError(t, err)
assert.True(t, accessMode < perm.AccessModeWrite)
@@ -171,7 +152,7 @@ func TestAddTeamMember(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
test := func(teamID, userID int64) {
- team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: teamID}).(*organization.Team)
+ team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: teamID})
assert.NoError(t, AddTeamMember(team, userID))
unittest.AssertExistsAndLoadBean(t, &organization.TeamUser{UID: userID, TeamID: teamID})
unittest.CheckConsistencyFor(t, &organization.Team{ID: teamID}, &user_model.User{ID: team.OrgID})
@@ -185,7 +166,7 @@ func TestRemoveTeamMember(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
testSuccess := func(teamID, userID int64) {
- team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: teamID}).(*organization.Team)
+ team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: teamID})
assert.NoError(t, RemoveTeamMember(team, userID))
unittest.AssertNotExistsBean(t, &organization.TeamUser{UID: userID, TeamID: teamID})
unittest.CheckConsistencyFor(t, &organization.Team{ID: teamID})
@@ -195,15 +176,15 @@ func TestRemoveTeamMember(t *testing.T) {
testSuccess(3, 2)
testSuccess(3, unittest.NonexistentID)
- team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 1}).(*organization.Team)
+ team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 1})
err := RemoveTeamMember(team, 2)
assert.True(t, organization.IsErrLastOrgOwner(err))
}
func TestRepository_RecalculateAccesses3(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- team5 := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 5}).(*organization.Team)
- user29 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 29}).(*user_model.User)
+ team5 := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 5})
+ user29 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 29})
has, err := db.GetEngine(db.DefaultContext).Get(&access_model.Access{UserID: 29, RepoID: 23})
assert.NoError(t, err)
diff --git a/models/org_test.go b/models/org_test.go
index af11bed280..23b417119e 100644
--- a/models/org_test.go
+++ b/models/org_test.go
@@ -16,14 +16,14 @@ import (
func TestUser_RemoveMember(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- org := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 3}).(*organization.Organization)
+ org := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 3})
// remove a user that is a member
unittest.AssertExistsAndLoadBean(t, &organization.OrgUser{UID: 4, OrgID: 3})
prevNumMembers := org.NumMembers
assert.NoError(t, RemoveOrgUser(org.ID, 4))
unittest.AssertNotExistsBean(t, &organization.OrgUser{UID: 4, OrgID: 3})
- org = unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 3}).(*organization.Organization)
+ org = unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 3})
assert.Equal(t, prevNumMembers-1, org.NumMembers)
// remove a user that is not a member
@@ -31,7 +31,7 @@ func TestUser_RemoveMember(t *testing.T) {
prevNumMembers = org.NumMembers
assert.NoError(t, RemoveOrgUser(org.ID, 5))
unittest.AssertNotExistsBean(t, &organization.OrgUser{UID: 5, OrgID: 3})
- org = unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 3}).(*organization.Organization)
+ org = unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 3})
assert.Equal(t, prevNumMembers, org.NumMembers)
unittest.CheckConsistencyFor(t, &user_model.User{}, &organization.Team{})
@@ -40,14 +40,14 @@ func TestUser_RemoveMember(t *testing.T) {
func TestRemoveOrgUser(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
testSuccess := func(orgID, userID int64) {
- org := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: orgID}).(*user_model.User)
+ org := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: orgID})
expectedNumMembers := org.NumMembers
if unittest.BeanExists(t, &organization.OrgUser{OrgID: orgID, UID: userID}) {
expectedNumMembers--
}
assert.NoError(t, RemoveOrgUser(orgID, userID))
unittest.AssertNotExistsBean(t, &organization.OrgUser{OrgID: orgID, UID: userID})
- org = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: orgID}).(*user_model.User)
+ org = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: orgID})
assert.EqualValues(t, expectedNumMembers, org.NumMembers)
}
testSuccess(3, 4)
diff --git a/models/organization/mini_org.go b/models/organization/mini_org.go
new file mode 100644
index 0000000000..36cf948e65
--- /dev/null
+++ b/models/organization/mini_org.go
@@ -0,0 +1,78 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package organization
+
+import (
+ "fmt"
+ "strings"
+
+ "code.gitea.io/gitea/models/db"
+ repo_model "code.gitea.io/gitea/models/repo"
+ "code.gitea.io/gitea/models/unit"
+ user_model "code.gitea.io/gitea/models/user"
+
+ "xorm.io/builder"
+)
+
+// MinimalOrg represents a simple organization with only the needed columns
+type MinimalOrg = Organization
+
+// GetUserOrgsList returns all organizations the given user has access to
+func GetUserOrgsList(user *user_model.User) ([]*MinimalOrg, error) {
+ schema, err := db.TableInfo(new(user_model.User))
+ if err != nil {
+ return nil, err
+ }
+
+ outputCols := []string{
+ "id",
+ "name",
+ "full_name",
+ "visibility",
+ "avatar",
+ "avatar_email",
+ "use_custom_avatar",
+ }
+
+ groupByCols := &strings.Builder{}
+ for _, col := range outputCols {
+ fmt.Fprintf(groupByCols, "`%s`.%s,", schema.Name, col)
+ }
+ groupByStr := groupByCols.String()
+ groupByStr = groupByStr[0 : len(groupByStr)-1]
+
+ sess := db.GetEngine(db.DefaultContext)
+ sess = sess.Select(groupByStr+", count(distinct repo_id) as org_count").
+ Table("user").
+ Join("INNER", "team", "`team`.org_id = `user`.id").
+ Join("INNER", "team_user", "`team`.id = `team_user`.team_id").
+ Join("LEFT", builder.
+ Select("id as repo_id, owner_id as repo_owner_id").
+ From("repository").
+ Where(repo_model.AccessibleRepositoryCondition(user, unit.TypeInvalid)), "`repository`.repo_owner_id = `team`.org_id").
+ Where("`team_user`.uid = ?", user.ID).
+ GroupBy(groupByStr)
+
+ type OrgCount struct {
+ Organization `xorm:"extends"`
+ OrgCount int
+ }
+
+ orgCounts := make([]*OrgCount, 0, 10)
+
+ if err := sess.
+ Asc("`user`.name").
+ Find(&orgCounts); err != nil {
+ return nil, err
+ }
+
+ orgs := make([]*MinimalOrg, len(orgCounts))
+ for i, orgCount := range orgCounts {
+ orgCount.Organization.NumRepos = orgCount.OrgCount
+ orgs[i] = &orgCount.Organization
+ }
+
+ return orgs, nil
+}
diff --git a/models/organization/org.go b/models/organization/org.go
index 044ea06563..fbbf6d04fa 100644
--- a/models/organization/org.go
+++ b/models/organization/org.go
@@ -18,6 +18,7 @@ import (
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/structs"
+ "code.gitea.io/gitea/modules/util"
"xorm.io/builder"
)
@@ -45,6 +46,10 @@ func (err ErrOrgNotExist) Error() string {
return fmt.Sprintf("org does not exist [id: %d, name: %s]", err.ID, err.Name)
}
+func (err ErrOrgNotExist) Unwrap() error {
+ return util.ErrNotExist
+}
+
// ErrLastOrgOwner represents a "LastOrgOwner" kind of error.
type ErrLastOrgOwner struct {
UID int64
@@ -73,6 +78,10 @@ func (err ErrUserNotAllowedCreateOrg) Error() string {
return "user is not allowed to create organizations"
}
+func (err ErrUserNotAllowedCreateOrg) Unwrap() error {
+ return util.ErrPermissionDenied
+}
+
// Organization represents an organization
type Organization user_model.User
diff --git a/models/organization/org_repo.go b/models/organization/org_repo.go
new file mode 100644
index 0000000000..364374f71b
--- /dev/null
+++ b/models/organization/org_repo.go
@@ -0,0 +1,18 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package organization
+
+import (
+ "context"
+
+ "code.gitea.io/gitea/models/db"
+ repo_model "code.gitea.io/gitea/models/repo"
+)
+
+// GetOrgRepositories get repos belonging to the given organization
+func GetOrgRepositories(ctx context.Context, orgID int64) ([]*repo_model.Repository, error) {
+ var orgRepos []*repo_model.Repository
+ return orgRepos, db.GetEngine(ctx).Where("owner_id = ?", orgID).Find(&orgRepos)
+}
diff --git a/models/organization/org_test.go b/models/organization/org_test.go
index 3a135498a3..0fba6e2592 100644
--- a/models/organization/org_test.go
+++ b/models/organization/org_test.go
@@ -31,7 +31,7 @@ func TestUser_IsOwnedBy(t *testing.T) {
{2, 2, false}, // user2 is not an organization
{2, 3, false},
} {
- org := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: testCase.OrgID}).(*organization.Organization)
+ org := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: testCase.OrgID})
isOwner, err := org.IsOwnedBy(testCase.UserID)
assert.NoError(t, err)
assert.Equal(t, testCase.ExpectedOwner, isOwner)
@@ -52,7 +52,7 @@ func TestUser_IsOrgMember(t *testing.T) {
{2, 2, false}, // user2 is not an organization
{2, 3, false},
} {
- org := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: testCase.OrgID}).(*organization.Organization)
+ org := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: testCase.OrgID})
isMember, err := org.IsOrgMember(testCase.UserID)
assert.NoError(t, err)
assert.Equal(t, testCase.ExpectedMember, isMember)
@@ -61,7 +61,7 @@ func TestUser_IsOrgMember(t *testing.T) {
func TestUser_GetTeam(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- org := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 3}).(*organization.Organization)
+ org := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 3})
team, err := org.GetTeam("team1")
assert.NoError(t, err)
assert.Equal(t, org.ID, team.OrgID)
@@ -70,26 +70,26 @@ func TestUser_GetTeam(t *testing.T) {
_, err = org.GetTeam("does not exist")
assert.True(t, organization.IsErrTeamNotExist(err))
- nonOrg := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 2}).(*organization.Organization)
+ nonOrg := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 2})
_, err = nonOrg.GetTeam("team")
assert.True(t, organization.IsErrTeamNotExist(err))
}
func TestUser_GetOwnerTeam(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- org := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 3}).(*organization.Organization)
+ org := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 3})
team, err := org.GetOwnerTeam()
assert.NoError(t, err)
assert.Equal(t, org.ID, team.OrgID)
- nonOrg := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 2}).(*organization.Organization)
+ nonOrg := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 2})
_, err = nonOrg.GetOwnerTeam()
assert.True(t, organization.IsErrTeamNotExist(err))
}
func TestUser_GetTeams(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- org := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 3}).(*organization.Organization)
+ org := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 3})
teams, err := org.LoadTeams()
assert.NoError(t, err)
if assert.Len(t, teams, 4) {
@@ -102,7 +102,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}).(*organization.Organization)
+ org := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 3})
members, _, err := org.GetMembers()
assert.NoError(t, err)
if assert.Len(t, members, 3) {
@@ -275,7 +275,7 @@ func TestChangeOrgUserStatus(t *testing.T) {
testSuccess := func(orgID, userID int64, public bool) {
assert.NoError(t, organization.ChangeOrgUserStatus(orgID, userID, public))
- orgUser := unittest.AssertExistsAndLoadBean(t, &organization.OrgUser{OrgID: orgID, UID: userID}).(*organization.OrgUser)
+ orgUser := unittest.AssertExistsAndLoadBean(t, &organization.OrgUser{OrgID: orgID, UID: userID})
assert.Equal(t, public, orgUser.IsPublic)
}
@@ -287,7 +287,7 @@ func TestChangeOrgUserStatus(t *testing.T) {
func TestUser_GetUserTeamIDs(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- org := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 3}).(*organization.Organization)
+ org := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 3})
testSuccess := func(userID int64, expected []int64) {
teamIDs, err := org.GetUserTeamIDs(userID)
assert.NoError(t, err)
@@ -300,7 +300,7 @@ func TestUser_GetUserTeamIDs(t *testing.T) {
func TestAccessibleReposEnv_CountRepos(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- org := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 3}).(*organization.Organization)
+ org := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 3})
testSuccess := func(userID, expectedCount int64) {
env, err := organization.AccessibleReposEnv(db.DefaultContext, org, userID)
assert.NoError(t, err)
@@ -314,7 +314,7 @@ func TestAccessibleReposEnv_CountRepos(t *testing.T) {
func TestAccessibleReposEnv_RepoIDs(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- org := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 3}).(*organization.Organization)
+ org := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 3})
testSuccess := func(userID, _, pageSize int64, expectedRepoIDs []int64) {
env, err := organization.AccessibleReposEnv(db.DefaultContext, org, userID)
assert.NoError(t, err)
@@ -328,7 +328,7 @@ func TestAccessibleReposEnv_RepoIDs(t *testing.T) {
func TestAccessibleReposEnv_Repos(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- org := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 3}).(*organization.Organization)
+ org := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 3})
testSuccess := func(userID int64, expectedRepoIDs []int64) {
env, err := organization.AccessibleReposEnv(db.DefaultContext, org, userID)
assert.NoError(t, err)
@@ -337,7 +337,7 @@ func TestAccessibleReposEnv_Repos(t *testing.T) {
expectedRepos := make([]*repo_model.Repository, len(expectedRepoIDs))
for i, repoID := range expectedRepoIDs {
expectedRepos[i] = unittest.AssertExistsAndLoadBean(t,
- &repo_model.Repository{ID: repoID}).(*repo_model.Repository)
+ &repo_model.Repository{ID: repoID})
}
assert.Equal(t, expectedRepos, repos)
}
@@ -347,7 +347,7 @@ func TestAccessibleReposEnv_Repos(t *testing.T) {
func TestAccessibleReposEnv_MirrorRepos(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- org := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 3}).(*organization.Organization)
+ org := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 3})
testSuccess := func(userID int64, expectedRepoIDs []int64) {
env, err := organization.AccessibleReposEnv(db.DefaultContext, org, userID)
assert.NoError(t, err)
@@ -356,7 +356,7 @@ func TestAccessibleReposEnv_MirrorRepos(t *testing.T) {
expectedRepos := make([]*repo_model.Repository, len(expectedRepoIDs))
for i, repoID := range expectedRepoIDs {
expectedRepos[i] = unittest.AssertExistsAndLoadBean(t,
- &repo_model.Repository{ID: repoID}).(*repo_model.Repository)
+ &repo_model.Repository{ID: repoID})
}
assert.Equal(t, expectedRepos, repos)
}
@@ -366,8 +366,8 @@ func TestAccessibleReposEnv_MirrorRepos(t *testing.T) {
func TestHasOrgVisibleTypePublic(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
- user3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3}).(*user_model.User)
+ owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
+ user3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3})
const newOrgName = "test-org-public"
org := &organization.Organization{
@@ -378,7 +378,7 @@ func TestHasOrgVisibleTypePublic(t *testing.T) {
unittest.AssertNotExistsBean(t, &user_model.User{Name: org.Name, Type: user_model.UserTypeOrganization})
assert.NoError(t, organization.CreateOrganization(org, owner))
org = unittest.AssertExistsAndLoadBean(t,
- &organization.Organization{Name: org.Name, Type: user_model.UserTypeOrganization}).(*organization.Organization)
+ &organization.Organization{Name: org.Name, Type: user_model.UserTypeOrganization})
test1 := organization.HasOrgOrUserVisible(db.DefaultContext, org.AsUser(), owner)
test2 := organization.HasOrgOrUserVisible(db.DefaultContext, org.AsUser(), user3)
test3 := organization.HasOrgOrUserVisible(db.DefaultContext, org.AsUser(), nil)
@@ -389,8 +389,8 @@ func TestHasOrgVisibleTypePublic(t *testing.T) {
func TestHasOrgVisibleTypeLimited(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
- user3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3}).(*user_model.User)
+ owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
+ user3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3})
const newOrgName = "test-org-limited"
org := &organization.Organization{
@@ -401,7 +401,7 @@ func TestHasOrgVisibleTypeLimited(t *testing.T) {
unittest.AssertNotExistsBean(t, &user_model.User{Name: org.Name, Type: user_model.UserTypeOrganization})
assert.NoError(t, organization.CreateOrganization(org, owner))
org = unittest.AssertExistsAndLoadBean(t,
- &organization.Organization{Name: org.Name, Type: user_model.UserTypeOrganization}).(*organization.Organization)
+ &organization.Organization{Name: org.Name, Type: user_model.UserTypeOrganization})
test1 := organization.HasOrgOrUserVisible(db.DefaultContext, org.AsUser(), owner)
test2 := organization.HasOrgOrUserVisible(db.DefaultContext, org.AsUser(), user3)
test3 := organization.HasOrgOrUserVisible(db.DefaultContext, org.AsUser(), nil)
@@ -412,8 +412,8 @@ func TestHasOrgVisibleTypeLimited(t *testing.T) {
func TestHasOrgVisibleTypePrivate(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
- user3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3}).(*user_model.User)
+ owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
+ user3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3})
const newOrgName = "test-org-private"
org := &organization.Organization{
@@ -424,7 +424,7 @@ func TestHasOrgVisibleTypePrivate(t *testing.T) {
unittest.AssertNotExistsBean(t, &user_model.User{Name: org.Name, Type: user_model.UserTypeOrganization})
assert.NoError(t, organization.CreateOrganization(org, owner))
org = unittest.AssertExistsAndLoadBean(t,
- &organization.Organization{Name: org.Name, Type: user_model.UserTypeOrganization}).(*organization.Organization)
+ &organization.Organization{Name: org.Name, Type: user_model.UserTypeOrganization})
test1 := organization.HasOrgOrUserVisible(db.DefaultContext, org.AsUser(), owner)
test2 := organization.HasOrgOrUserVisible(db.DefaultContext, org.AsUser(), user3)
test3 := organization.HasOrgOrUserVisible(db.DefaultContext, org.AsUser(), nil)
@@ -453,8 +453,8 @@ func TestGetUsersWhoCanCreateOrgRepo(t *testing.T) {
func TestUser_RemoveOrgRepo(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- org := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 3}).(*organization.Organization)
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerID: org.ID}).(*repo_model.Repository)
+ org := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 3})
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerID: org.ID})
// remove a repo that does belong to org
unittest.AssertExistsAndLoadBean(t, &organization.TeamRepo{RepoID: repo.ID, OrgID: org.ID})
@@ -478,7 +478,7 @@ func TestCreateOrganization(t *testing.T) {
// successful creation of org
assert.NoError(t, unittest.PrepareTestDatabase())
- owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
+ owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
const newOrgName = "neworg"
org := &organization.Organization{
Name: newOrgName,
@@ -487,9 +487,9 @@ func TestCreateOrganization(t *testing.T) {
unittest.AssertNotExistsBean(t, &user_model.User{Name: newOrgName, Type: user_model.UserTypeOrganization})
assert.NoError(t, organization.CreateOrganization(org, owner))
org = unittest.AssertExistsAndLoadBean(t,
- &organization.Organization{Name: newOrgName, Type: user_model.UserTypeOrganization}).(*organization.Organization)
+ &organization.Organization{Name: newOrgName, Type: user_model.UserTypeOrganization})
ownerTeam := unittest.AssertExistsAndLoadBean(t,
- &organization.Team{Name: organization.OwnerTeamName, OrgID: org.ID}).(*organization.Team)
+ &organization.Team{Name: organization.OwnerTeamName, OrgID: org.ID})
unittest.AssertExistsAndLoadBean(t, &organization.TeamUser{UID: owner.ID, TeamID: ownerTeam.ID})
unittest.CheckConsistencyFor(t, &user_model.User{}, &organization.Team{})
}
@@ -498,7 +498,7 @@ func TestCreateOrganization2(t *testing.T) {
// unauthorized creation of org
assert.NoError(t, unittest.PrepareTestDatabase())
- owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5}).(*user_model.User)
+ owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5})
const newOrgName = "neworg"
org := &organization.Organization{
Name: newOrgName,
@@ -516,7 +516,7 @@ func TestCreateOrganization3(t *testing.T) {
// create org with same name as existent org
assert.NoError(t, unittest.PrepareTestDatabase())
- owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
+ owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
org := &organization.Organization{Name: "user3"} // should already exist
unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: org.Name}) // sanity check
err := organization.CreateOrganization(org, owner)
@@ -529,7 +529,7 @@ func TestCreateOrganization4(t *testing.T) {
// create org with unusable name
assert.NoError(t, unittest.PrepareTestDatabase())
- owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
+ owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
err := organization.CreateOrganization(&organization.Organization{Name: "assets"}, owner)
assert.Error(t, err)
assert.True(t, db.IsErrNameReserved(err))
diff --git a/models/organization/org_user_test.go b/models/organization/org_user_test.go
index 22ee5217f9..aed3ea23cf 100644
--- a/models/organization/org_user_test.go
+++ b/models/organization/org_user_test.go
@@ -130,7 +130,7 @@ func testUserListIsUserOrgOwner(t *testing.T, orgID int64, expected map[int64]bo
func TestAddOrgUser(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
testSuccess := func(orgID, userID int64, isPublic bool) {
- org := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: orgID}).(*user_model.User)
+ org := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: orgID})
expectedNumMembers := org.NumMembers
if !unittest.BeanExists(t, &organization.OrgUser{OrgID: orgID, UID: userID}) {
expectedNumMembers++
@@ -139,7 +139,7 @@ func TestAddOrgUser(t *testing.T) {
ou := &organization.OrgUser{OrgID: orgID, UID: userID}
unittest.AssertExistsAndLoadBean(t, ou)
assert.Equal(t, isPublic, ou.IsPublic)
- org = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: orgID}).(*user_model.User)
+ org = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: orgID})
assert.EqualValues(t, expectedNumMembers, org.NumMembers)
}
diff --git a/models/organization/team.go b/models/organization/team.go
index 0b53c84d67..83e5bd6fe1 100644
--- a/models/organization/team.go
+++ b/models/organization/team.go
@@ -16,6 +16,7 @@ import (
"code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/util"
"xorm.io/builder"
)
@@ -43,6 +44,10 @@ func (err ErrTeamAlreadyExist) Error() string {
return fmt.Sprintf("team already exists [org_id: %d, name: %s]", err.OrgID, err.Name)
}
+func (err ErrTeamAlreadyExist) Unwrap() error {
+ return util.ErrAlreadyExist
+}
+
// ErrTeamNotExist represents a "TeamNotExist" error
type ErrTeamNotExist struct {
OrgID int64
@@ -60,6 +65,10 @@ func (err ErrTeamNotExist) Error() string {
return fmt.Sprintf("team does not exist [org_id %d, team_id %d, name: %s]", err.OrgID, err.TeamID, err.Name)
}
+func (err ErrTeamNotExist) Unwrap() error {
+ return util.ErrNotExist
+}
+
// OwnerTeamName return the owner team name
const OwnerTeamName = "Owners"
@@ -96,16 +105,7 @@ type SearchTeamOptions struct {
IncludeDesc bool
}
-// SearchTeam search for teams. Caller is responsible to check permissions.
-func SearchTeam(opts *SearchTeamOptions) ([]*Team, int64, error) {
- if opts.Page <= 0 {
- opts.Page = 1
- }
- if opts.PageSize == 0 {
- // Default limit
- opts.PageSize = 10
- }
-
+func (opts *SearchTeamOptions) toCond() builder.Cond {
cond := builder.NewCond()
if len(opts.Keyword) > 0 {
@@ -117,28 +117,32 @@ func SearchTeam(opts *SearchTeamOptions) ([]*Team, int64, error) {
cond = cond.And(keywordCond)
}
- cond = cond.And(builder.Eq{"org_id": opts.OrgID})
+ if opts.OrgID > 0 {
+ cond = cond.And(builder.Eq{"`team`.org_id": opts.OrgID})
+ }
+ if opts.UserID > 0 {
+ cond = cond.And(builder.Eq{"team_user.uid": opts.UserID})
+ }
+
+ return cond
+}
+
+// SearchTeam search for teams. Caller is responsible to check permissions.
+func SearchTeam(opts *SearchTeamOptions) ([]*Team, int64, error) {
sess := db.GetEngine(db.DefaultContext)
- count, err := sess.
- Where(cond).
- Count(new(Team))
- if err != nil {
- return nil, 0, err
- }
+ opts.SetDefaultValues()
+ cond := opts.toCond()
- sess = sess.Where(cond)
- if opts.PageSize == -1 {
- opts.PageSize = int(count)
- } else {
- sess = sess.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize)
+ if opts.UserID > 0 {
+ sess = sess.Join("INNER", "team_user", "team_user.team_id = team.id")
}
+ sess = db.SetSessionPagination(sess, opts)
teams := make([]*Team, 0, opts.PageSize)
- if err = sess.
- OrderBy("lower_name").
- Find(&teams); err != nil {
+ count, err := sess.Where(cond).OrderBy("lower_name").FindAndCount(&teams)
+ if err != nil {
return nil, 0, err
}
@@ -346,3 +350,9 @@ func GetRepoTeams(ctx context.Context, repo *repo_model.Repository) (teams []*Te
OrderBy("CASE WHEN name LIKE '" + OwnerTeamName + "' THEN '' ELSE name END").
Find(&teams)
}
+
+// IncrTeamRepoNum increases the number of repos for the given team by 1
+func IncrTeamRepoNum(ctx context.Context, teamID int64) error {
+ _, err := db.GetEngine(ctx).Incr("num_repos").ID(teamID).Update(new(Team))
+ return err
+}
diff --git a/models/organization/team_test.go b/models/organization/team_test.go
index 829c440c29..c8d58a0eb7 100644
--- a/models/organization/team_test.go
+++ b/models/organization/team_test.go
@@ -17,22 +17,22 @@ import (
func TestTeam_IsOwnerTeam(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 1}).(*organization.Team)
+ team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 1})
assert.True(t, team.IsOwnerTeam())
- team = unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 2}).(*organization.Team)
+ team = unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 2})
assert.False(t, team.IsOwnerTeam())
}
func TestTeam_IsMember(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 1}).(*organization.Team)
+ team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 1})
assert.True(t, team.IsMember(2))
assert.False(t, team.IsMember(4))
assert.False(t, team.IsMember(unittest.NonexistentID))
- team = unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 2}).(*organization.Team)
+ team = unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 2})
assert.True(t, team.IsMember(2))
assert.True(t, team.IsMember(4))
assert.False(t, team.IsMember(unittest.NonexistentID))
@@ -42,7 +42,7 @@ func TestTeam_GetRepositories(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
test := func(teamID int64) {
- team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: teamID}).(*organization.Team)
+ team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: teamID})
assert.NoError(t, team.GetRepositoriesCtx(db.DefaultContext))
assert.Len(t, team.Repos, team.NumRepos)
for _, repo := range team.Repos {
@@ -57,7 +57,7 @@ func TestTeam_GetMembers(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
test := func(teamID int64) {
- team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: teamID}).(*organization.Team)
+ team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: teamID})
assert.NoError(t, team.GetMembersCtx(db.DefaultContext))
assert.Len(t, team.Members, team.NumMembers)
for _, member := range team.Members {
@@ -126,7 +126,7 @@ func TestGetTeamMembers(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
test := func(teamID int64) {
- team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: teamID}).(*organization.Team)
+ team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: teamID})
members, err := organization.GetTeamMembers(db.DefaultContext, &organization.SearchMembersOptions{
TeamID: teamID,
})
@@ -173,7 +173,7 @@ func TestHasTeamRepo(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
test := func(teamID, repoID int64, expected bool) {
- team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: teamID}).(*organization.Team)
+ team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: teamID})
assert.Equal(t, expected, organization.HasTeamRepo(db.DefaultContext, team.OrgID, teamID, repoID))
}
test(1, 1, false)
diff --git a/models/packages/conan/search.go b/models/packages/conan/search.go
index 6a2cfa38f5..39a9000459 100644
--- a/models/packages/conan/search.go
+++ b/models/packages/conan/search.go
@@ -12,6 +12,7 @@ import (
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/packages"
+ "code.gitea.io/gitea/modules/container"
conan_module "code.gitea.io/gitea/modules/packages/conan"
"xorm.io/builder"
@@ -88,7 +89,7 @@ func SearchRecipes(ctx context.Context, opts *RecipeSearchOptions) ([]string, er
return nil, err
}
- unique := make(map[string]bool)
+ unique := make(container.Set[string])
for _, info := range results {
recipe := fmt.Sprintf("%s/%s", info.Name, info.Version)
@@ -111,7 +112,7 @@ func SearchRecipes(ctx context.Context, opts *RecipeSearchOptions) ([]string, er
}
}
- unique[recipe] = true
+ unique.Add(recipe)
}
recipes := make([]string, 0, len(unique))
diff --git a/models/packages/container/search.go b/models/packages/container/search.go
index 972cac9528..a3409fe743 100644
--- a/models/packages/container/search.go
+++ b/models/packages/container/search.go
@@ -12,6 +12,7 @@ import (
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/packages"
+ user_model "code.gitea.io/gitea/models/user"
container_module "code.gitea.io/gitea/modules/packages/container"
"xorm.io/builder"
@@ -210,6 +211,7 @@ func SearchImageTags(ctx context.Context, opts *ImageTagsSearchOptions) ([]*pack
return pvs, count, err
}
+// SearchExpiredUploadedBlobs gets all uploaded blobs which are older than specified
func SearchExpiredUploadedBlobs(ctx context.Context, olderThan time.Duration) ([]*packages.PackageFile, error) {
var cond builder.Cond = builder.Eq{
"package_version.is_internal": true,
@@ -225,3 +227,37 @@ func SearchExpiredUploadedBlobs(ctx context.Context, olderThan time.Duration) ([
Where(cond).
Find(&pfs)
}
+
+// GetRepositories gets a sorted list of all repositories
+func GetRepositories(ctx context.Context, actor *user_model.User, n int, last string) ([]string, error) {
+ var cond builder.Cond = builder.Eq{
+ "package.type": packages.TypeContainer,
+ "package_property.ref_type": packages.PropertyTypePackage,
+ "package_property.name": container_module.PropertyRepository,
+ }
+
+ cond = cond.And(builder.Exists(
+ builder.
+ Select("package_version.id").
+ Where(builder.Eq{"package_version.is_internal": false}.And(builder.Expr("package.id = package_version.package_id"))).
+ From("package_version"),
+ ))
+
+ if last != "" {
+ cond = cond.And(builder.Gt{"package_property.value": strings.ToLower(last)})
+ }
+
+ cond = cond.And(user_model.BuildCanSeeUserCondition(actor))
+
+ sess := db.GetEngine(ctx).
+ Table("package").
+ Select("package_property.value").
+ Join("INNER", "user", "`user`.id = package.owner_id").
+ Join("INNER", "package_property", "package_property.ref_id = package.id").
+ Where(cond).
+ Asc("package_property.value").
+ Limit(n)
+
+ repositories := make([]string, 0, n)
+ return repositories, sess.Find(&repositories)
+}
diff --git a/models/packages/descriptor.go b/models/packages/descriptor.go
index fbdc40f37f..357574a706 100644
--- a/models/packages/descriptor.go
+++ b/models/packages/descriptor.go
@@ -19,8 +19,10 @@ import (
"code.gitea.io/gitea/modules/packages/maven"
"code.gitea.io/gitea/modules/packages/npm"
"code.gitea.io/gitea/modules/packages/nuget"
+ "code.gitea.io/gitea/modules/packages/pub"
"code.gitea.io/gitea/modules/packages/pypi"
"code.gitea.io/gitea/modules/packages/rubygems"
+ "code.gitea.io/gitea/modules/packages/vagrant"
"github.com/hashicorp/go-version"
)
@@ -40,15 +42,16 @@ func (l PackagePropertyList) GetByName(name string) string {
// PackageDescriptor describes a package
type PackageDescriptor struct {
- Package *Package
- Owner *user_model.User
- Repository *repo_model.Repository
- Version *PackageVersion
- SemVer *version.Version
- Creator *user_model.User
- Properties PackagePropertyList
- Metadata interface{}
- Files []*PackageFileDescriptor
+ Package *Package
+ Owner *user_model.User
+ Repository *repo_model.Repository
+ Version *PackageVersion
+ SemVer *version.Version
+ Creator *user_model.User
+ PackageProperties PackagePropertyList
+ VersionProperties PackagePropertyList
+ Metadata interface{}
+ Files []*PackageFileDescriptor
}
// PackageFileDescriptor describes a package file
@@ -102,6 +105,10 @@ func GetPackageDescriptor(ctx context.Context, pv *PackageVersion) (*PackageDesc
return nil, err
}
}
+ pps, err := GetProperties(ctx, PropertyTypePackage, p.ID)
+ if err != nil {
+ return nil, err
+ }
pvps, err := GetProperties(ctx, PropertyTypeVersion, pv.ID)
if err != nil {
return nil, err
@@ -138,10 +145,14 @@ func GetPackageDescriptor(ctx context.Context, pv *PackageVersion) (*PackageDesc
metadata = &npm.Metadata{}
case TypeMaven:
metadata = &maven.Metadata{}
+ case TypePub:
+ metadata = &pub.Metadata{}
case TypePyPI:
metadata = &pypi.Metadata{}
case TypeRubyGems:
metadata = &rubygems.Metadata{}
+ case TypeVagrant:
+ metadata = &vagrant.Metadata{}
default:
panic(fmt.Sprintf("unknown package type: %s", string(p.Type)))
}
@@ -152,15 +163,16 @@ func GetPackageDescriptor(ctx context.Context, pv *PackageVersion) (*PackageDesc
}
return &PackageDescriptor{
- Package: p,
- Owner: o,
- Repository: repository,
- Version: pv,
- SemVer: semVer,
- Creator: creator,
- Properties: PackagePropertyList(pvps),
- Metadata: metadata,
- Files: pfds,
+ Package: p,
+ Owner: o,
+ Repository: repository,
+ Version: pv,
+ SemVer: semVer,
+ Creator: creator,
+ PackageProperties: PackagePropertyList(pps),
+ VersionProperties: PackagePropertyList(pvps),
+ Metadata: metadata,
+ Files: pfds,
}, nil
}
diff --git a/models/packages/package.go b/models/packages/package.go
index bdb535492b..e39a7c4e41 100644
--- a/models/packages/package.go
+++ b/models/packages/package.go
@@ -39,8 +39,10 @@ const (
TypeMaven Type = "maven"
TypeNpm Type = "npm"
TypeNuGet Type = "nuget"
+ TypePub Type = "pub"
TypePyPI Type = "pypi"
TypeRubyGems Type = "rubygems"
+ TypeVagrant Type = "vagrant"
)
// Name gets the name of the package type
@@ -62,10 +64,14 @@ func (pt Type) Name() string {
return "npm"
case TypeNuGet:
return "NuGet"
+ case TypePub:
+ return "Pub"
case TypePyPI:
return "PyPI"
case TypeRubyGems:
return "RubyGems"
+ case TypeVagrant:
+ return "Vagrant"
}
panic(fmt.Sprintf("unknown package type: %s", string(pt)))
}
@@ -89,10 +95,14 @@ func (pt Type) SVGName() string {
return "gitea-npm"
case TypeNuGet:
return "gitea-nuget"
+ case TypePub:
+ return "gitea-pub"
case TypePyPI:
return "gitea-python"
case TypeRubyGems:
return "gitea-rubygems"
+ case TypeVagrant:
+ return "gitea-vagrant"
}
panic(fmt.Sprintf("unknown package type: %s", string(pt)))
}
@@ -131,6 +141,12 @@ func TryInsertPackage(ctx context.Context, p *Package) (*Package, error) {
return p, nil
}
+// DeletePackageByID deletes a package by id
+func DeletePackageByID(ctx context.Context, packageID int64) error {
+ _, err := db.GetEngine(ctx).ID(packageID).Delete(&Package{})
+ return err
+}
+
// SetRepositoryLink sets the linked repository
func SetRepositoryLink(ctx context.Context, packageID, repoID int64) error {
_, err := db.GetEngine(ctx).ID(packageID).Cols("repo_id").Update(&Package{RepoID: repoID})
@@ -192,26 +208,32 @@ func GetPackagesByType(ctx context.Context, ownerID int64, packageType Type) ([]
Find(&ps)
}
-// DeletePackagesIfUnreferenced deletes a package if there are no associated versions
-func DeletePackagesIfUnreferenced(ctx context.Context) error {
+// FindUnreferencedPackages gets all packages without associated versions
+func FindUnreferencedPackages(ctx context.Context) ([]*Package, error) {
in := builder.
Select("package.id").
From("package").
LeftJoin("package_version", "package_version.package_id = package.id").
Where(builder.Expr("package_version.id IS NULL"))
- _, err := db.GetEngine(ctx).
+ ps := make([]*Package, 0, 10)
+ return ps, db.GetEngine(ctx).
// double select workaround for MySQL
// https://stackoverflow.com/questions/4471277/mysql-delete-from-with-subquery-as-condition
Where(builder.In("package.id", builder.Select("id").From(in, "temp"))).
- Delete(&Package{})
-
- return err
+ Find(&ps)
}
-// HasOwnerPackages tests if a user/org has packages
+// HasOwnerPackages tests if a user/org has accessible packages
func HasOwnerPackages(ctx context.Context, ownerID int64) (bool, error) {
- return db.GetEngine(ctx).Where("owner_id = ?", ownerID).Exist(&Package{})
+ return db.GetEngine(ctx).
+ Table("package_version").
+ Join("INNER", "package", "package.id = package_version.package_id").
+ Where(builder.Eq{
+ "package_version.is_internal": false,
+ "package.owner_id": ownerID,
+ }).
+ Exist(&PackageVersion{})
}
// HasRepositoryPackages tests if a repository has packages
diff --git a/models/packages/package_property.go b/models/packages/package_property.go
index bf7dc346c6..fc10713801 100644
--- a/models/packages/package_property.go
+++ b/models/packages/package_property.go
@@ -21,9 +21,11 @@ const (
PropertyTypeVersion PropertyType = iota // 0
// PropertyTypeFile means the reference is a package file
PropertyTypeFile // 1
+ // PropertyTypePackage means the reference is a package
+ PropertyTypePackage // 2
)
-// PackageProperty represents a property of a package version or file
+// PackageProperty represents a property of a package, version or file
type PackageProperty struct {
ID int64 `xorm:"pk autoincr"`
RefType PropertyType `xorm:"INDEX NOT NULL"`
@@ -68,3 +70,9 @@ func DeletePropertyByID(ctx context.Context, propertyID int64) error {
_, err := db.GetEngine(ctx).ID(propertyID).Delete(&PackageProperty{})
return err
}
+
+// DeletePropertyByName deletes properties by name
+func DeletePropertyByName(ctx context.Context, refType PropertyType, refID int64, name string) error {
+ _, err := db.GetEngine(ctx).Where("ref_type = ? AND ref_id = ? AND name = ?", refType, refID, name).Delete(&PackageProperty{})
+ return err
+}
diff --git a/models/packages/package_test.go b/models/packages/package_test.go
new file mode 100644
index 0000000000..915ef15f91
--- /dev/null
+++ b/models/packages/package_test.go
@@ -0,0 +1,69 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package packages_test
+
+import (
+ "path/filepath"
+ "testing"
+
+ "code.gitea.io/gitea/models/db"
+ packages_model "code.gitea.io/gitea/models/packages"
+ "code.gitea.io/gitea/models/unittest"
+ user_model "code.gitea.io/gitea/models/user"
+
+ _ "code.gitea.io/gitea/models"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestMain(m *testing.M) {
+ unittest.MainTest(m, &unittest.TestOptions{
+ GiteaRootPath: filepath.Join("..", ".."),
+ })
+}
+
+func TestHasOwnerPackages(t *testing.T) {
+ assert.NoError(t, unittest.PrepareTestDatabase())
+
+ owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
+
+ p, err := packages_model.TryInsertPackage(db.DefaultContext, &packages_model.Package{
+ OwnerID: owner.ID,
+ LowerName: "package",
+ })
+ assert.NotNil(t, p)
+ assert.NoError(t, err)
+
+ // A package without package versions gets automatically cleaned up and should return false
+ has, err := packages_model.HasOwnerPackages(db.DefaultContext, owner.ID)
+ assert.False(t, has)
+ assert.NoError(t, err)
+
+ pv, err := packages_model.GetOrInsertVersion(db.DefaultContext, &packages_model.PackageVersion{
+ PackageID: p.ID,
+ LowerVersion: "internal",
+ IsInternal: true,
+ })
+ assert.NotNil(t, pv)
+ assert.NoError(t, err)
+
+ // A package with an internal package version gets automatically cleaned up and should return false
+ has, err = packages_model.HasOwnerPackages(db.DefaultContext, owner.ID)
+ assert.False(t, has)
+ assert.NoError(t, err)
+
+ pv, err = packages_model.GetOrInsertVersion(db.DefaultContext, &packages_model.PackageVersion{
+ PackageID: p.ID,
+ LowerVersion: "normal",
+ IsInternal: false,
+ })
+ assert.NotNil(t, pv)
+ assert.NoError(t, err)
+
+ // A package with a normal package version should return true
+ has, err = packages_model.HasOwnerPackages(db.DefaultContext, owner.ID)
+ assert.True(t, has)
+ assert.NoError(t, err)
+}
diff --git a/models/packages/package_version.go b/models/packages/package_version.go
index 83c2fdb674..5479bae1c2 100644
--- a/models/packages/package_version.go
+++ b/models/packages/package_version.go
@@ -122,8 +122,9 @@ func getVersionByNameAndVersion(ctx context.Context, ownerID int64, packageType
// GetVersionsByPackageType gets all versions of a specific type
func GetVersionsByPackageType(ctx context.Context, ownerID int64, packageType Type) ([]*PackageVersion, error) {
pvs, _, err := SearchVersions(ctx, &PackageSearchOptions{
- OwnerID: ownerID,
- Type: packageType,
+ OwnerID: ownerID,
+ Type: packageType,
+ IsInternal: util.OptionalBoolFalse,
})
return pvs, err
}
@@ -137,6 +138,7 @@ func GetVersionsByPackageName(ctx context.Context, ownerID int64, packageType Ty
ExactMatch: true,
Value: name,
},
+ IsInternal: util.OptionalBoolFalse,
})
return pvs, err
}
diff --git a/models/perm/access/access_test.go b/models/perm/access/access_test.go
index a5e0448d3d..7f58be4f39 100644
--- a/models/perm/access/access_test.go
+++ b/models/perm/access/access_test.go
@@ -7,13 +7,10 @@ package access_test
import (
"testing"
- "code.gitea.io/gitea/models"
"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"
@@ -23,21 +20,21 @@ import (
func TestAccessLevel(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
- user5 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5}).(*user_model.User)
- user29 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 29}).(*user_model.User)
+ user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
+ user5 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5})
+ user29 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 29})
// A public repository owned by User 2
- repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository)
+ repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
assert.False(t, repo1.IsPrivate)
// A private repository owned by Org 3
- repo3 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3}).(*repo_model.Repository)
+ repo3 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3})
assert.True(t, repo3.IsPrivate)
// Another public repository
- repo4 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4}).(*repo_model.Repository)
+ repo4 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4})
assert.False(t, repo4.IsPrivate)
// org. owned private repo
- repo24 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 24}).(*repo_model.Repository)
+ repo24 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 24})
level, err := access_model.AccessLevel(user2, repo1)
assert.NoError(t, err)
@@ -74,13 +71,13 @@ func TestAccessLevel(t *testing.T) {
func TestHasAccess(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
- user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5}).(*user_model.User)
+ user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
+ user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5})
// A public repository owned by User 2
- repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository)
+ repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
assert.False(t, repo1.IsPrivate)
// A private repository owned by Org 3
- repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3}).(*repo_model.Repository)
+ repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3})
assert.True(t, repo2.IsPrivate)
has, err := access_model.HasAccess(db.DefaultContext, user1.ID, repo1)
@@ -100,7 +97,7 @@ func TestHasAccess(t *testing.T) {
func TestRepository_RecalculateAccesses(t *testing.T) {
// test with organization repo
assert.NoError(t, unittest.PrepareTestDatabase())
- repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3}).(*repo_model.Repository)
+ repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3})
assert.NoError(t, repo1.GetOwner(db.DefaultContext))
_, err := db.GetEngine(db.DefaultContext).Delete(&repo_model.Collaboration{UserID: 2, RepoID: 3})
@@ -117,7 +114,7 @@ func TestRepository_RecalculateAccesses(t *testing.T) {
func TestRepository_RecalculateAccesses2(t *testing.T) {
// test with non-organization repo
assert.NoError(t, unittest.PrepareTestDatabase())
- repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4}).(*repo_model.Repository)
+ repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4})
assert.NoError(t, repo1.GetOwner(db.DefaultContext))
_, err := db.GetEngine(db.DefaultContext).Delete(&repo_model.Collaboration{UserID: 4, RepoID: 4})
@@ -128,249 +125,3 @@ func TestRepository_RecalculateAccesses2(t *testing.T) {
assert.NoError(t, err)
assert.False(t, has)
}
-
-func TestRepoPermissionPublicNonOrgRepo(t *testing.T) {
- assert.NoError(t, unittest.PrepareTestDatabase())
-
- // public non-organization repo
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4}).(*repo_model.Repository)
- assert.NoError(t, repo.LoadUnits(db.DefaultContext))
-
- // plain user
- user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.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.False(t, perm.CanWrite(unit.Type))
- }
-
- // change to collaborator
- assert.NoError(t, models.AddCollaborator(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}).(*user_model.User)
- 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}).(*user_model.User)
- 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}).(*user_model.User)
- 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}).(*repo_model.Repository)
- assert.NoError(t, repo.LoadUnits(db.DefaultContext))
-
- // plain user
- user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}).(*user_model.User)
- 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, models.AddCollaborator(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(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}).(*user_model.User)
- 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}).(*user_model.User)
- 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}).(*repo_model.Repository)
- assert.NoError(t, repo.LoadUnits(db.DefaultContext))
-
- // plain user
- user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5}).(*user_model.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.False(t, perm.CanWrite(unit.Type))
- }
-
- // change to collaborator to default write access
- assert.NoError(t, models.AddCollaborator(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(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}).(*user_model.User)
- 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}).(*user_model.User)
- 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}).(*user_model.User)
- 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}).(*repo_model.Repository)
- assert.NoError(t, repo.LoadUnits(db.DefaultContext))
-
- // plain user
- user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5}).(*user_model.User)
- 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, models.AddCollaborator(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(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}).(*user_model.User)
- 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}).(*organization.Team)
- err = organization.UpdateTeamUnits(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}).(*user_model.User)
- 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}).(*user_model.User)
- 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}).(*user_model.User)
- 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))
- }
-}
diff --git a/models/perm/access/repo_permission.go b/models/perm/access/repo_permission.go
index 99919c70bf..93e3bdd6d8 100644
--- a/models/perm/access/repo_permission.go
+++ b/models/perm/access/repo_permission.go
@@ -430,3 +430,17 @@ func IsRepoReader(ctx context.Context, repo *repo_model.Repository, userID int64
}
return db.GetEngine(ctx).Where("repo_id = ? AND user_id = ? AND mode >= ?", repo.ID, userID, perm_model.AccessModeRead).Get(&Access{})
}
+
+// CheckRepoUnitUser check whether user could visit the unit of this repository
+func CheckRepoUnitUser(ctx context.Context, repo *repo_model.Repository, user *user_model.User, unitType unit.Type) bool {
+ if user != nil && user.IsAdmin {
+ return true
+ }
+ perm, err := GetUserRepoPermission(ctx, repo, user)
+ if err != nil {
+ log.Error("GetUserRepoPermission: %w", err)
+ return false
+ }
+
+ return perm.CanRead(unitType)
+}
diff --git a/models/project/project.go b/models/project/project.go
index 86a77947d8..af2c8ac2af 100644
--- a/models/project/project.go
+++ b/models/project/project.go
@@ -55,6 +55,10 @@ func (err ErrProjectNotExist) Error() string {
return fmt.Sprintf("projects does not exist [id: %d]", err.ID)
}
+func (err ErrProjectNotExist) Unwrap() error {
+ return util.ErrNotExist
+}
+
// ErrProjectBoardNotExist represents a "ProjectBoardNotExist" kind of error.
type ErrProjectBoardNotExist struct {
BoardID int64
@@ -70,6 +74,10 @@ func (err ErrProjectBoardNotExist) Error() string {
return fmt.Sprintf("project board does not exist [id: %d]", err.BoardID)
}
+func (err ErrProjectBoardNotExist) Unwrap() error {
+ return util.ErrNotExist
+}
+
// Project represents a project board
type Project struct {
ID int64 `xorm:"pk autoincr"`
diff --git a/models/pull/automerge.go b/models/pull/automerge.go
index d0aca2e85f..16ab5af093 100644
--- a/models/pull/automerge.go
+++ b/models/pull/automerge.go
@@ -90,7 +90,7 @@ func DeleteScheduledAutoMerge(ctx context.Context, pullID int64) error {
if err != nil {
return err
} else if !exist {
- return db.ErrNotExist{ID: pullID}
+ return db.ErrNotExist{Resource: "auto_merge", ID: pullID}
}
_, err = db.GetEngine(ctx).ID(scheduledPRM.ID).Delete(&AutoMerge{})
diff --git a/models/repo.go b/models/repo.go
index 66ef514739..65159f14af 100644
--- a/models/repo.go
+++ b/models/repo.go
@@ -12,188 +12,34 @@ import (
_ "image/jpeg" // Needed for jpeg support
+ activities_model "code.gitea.io/gitea/models/activities"
admin_model "code.gitea.io/gitea/models/admin"
asymkey_model "code.gitea.io/gitea/models/asymkey"
"code.gitea.io/gitea/models/db"
git_model "code.gitea.io/gitea/models/git"
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/organization"
- "code.gitea.io/gitea/models/perm"
access_model "code.gitea.io/gitea/models/perm/access"
project_model "code.gitea.io/gitea/models/project"
repo_model "code.gitea.io/gitea/models/repo"
+ system_model "code.gitea.io/gitea/models/system"
"code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/models/webhook"
"code.gitea.io/gitea/modules/lfs"
"code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/storage"
- api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/util"
"xorm.io/builder"
)
-// NewRepoContext creates a new repository context
-func NewRepoContext() {
+// ItemsPerPage maximum items per page in forks, watchers and stars of a repo
+var ItemsPerPage = 40
+
+// Init initialize model
+func Init() error {
unit.LoadUnitConfig()
-}
-
-// CheckRepoUnitUser check whether user could visit the unit of this repository
-func CheckRepoUnitUser(ctx context.Context, repo *repo_model.Repository, user *user_model.User, unitType unit.Type) bool {
- if user != nil && user.IsAdmin {
- return true
- }
- perm, err := access_model.GetUserRepoPermission(ctx, repo, user)
- if err != nil {
- log.Error("GetUserRepoPermission(): %v", err)
- return false
- }
-
- return perm.CanRead(unitType)
-}
-
-// CreateRepoOptions contains the create repository options
-type CreateRepoOptions struct {
- Name string
- Description string
- OriginalURL string
- GitServiceType api.GitServiceType
- Gitignores string
- IssueLabels string
- License string
- Readme string
- DefaultBranch string
- IsPrivate bool
- IsMirror bool
- IsTemplate bool
- AutoInit bool
- Status repo_model.RepositoryStatus
- TrustModel repo_model.TrustModelType
- MirrorInterval string
-}
-
-// CreateRepository creates a repository for the user/organization.
-func CreateRepository(ctx context.Context, doer, u *user_model.User, repo *repo_model.Repository, overwriteOrAdopt bool) (err error) {
- if err = repo_model.IsUsableRepoName(repo.Name); err != nil {
- return err
- }
-
- has, err := repo_model.IsRepositoryExist(ctx, u, repo.Name)
- if err != nil {
- return fmt.Errorf("IsRepositoryExist: %v", 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
- units := make([]repo_model.RepoUnit, 0, len(unit.DefaultRepoUnits))
- for _, tp := range unit.DefaultRepoUnits {
- 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, DefaultMergeStyle: repo_model.MergeStyle(setting.Repository.PullRequest.DefaultMergeStyle), AllowRebaseUpdate: true},
- })
- } 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("updateUser: %v", err)
- }
-
- if _, err = db.GetEngine(ctx).Incr("num_repos").ID(u.ID).Update(new(user_model.User)); err != nil {
- return fmt.Errorf("increment user total_repos: %v", 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("loadTeams: %v", err)
- }
- for _, t := range teams {
- if t.IncludesAllRepositories {
- if err := addRepository(ctx, t, repo); err != nil {
- return fmt.Errorf("addRepository: %v", err)
- }
- }
- }
-
- if isAdmin, err := access_model.IsUserRepoAdmin(ctx, repo, doer); err != nil {
- return fmt.Errorf("IsUserRepoAdminCtx: %v", 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: %v", err)
- }
- if err = repo_model.ChangeCollaborationAccessModeCtx(ctx, repo, doer.ID, perm.AccessModeAdmin); err != nil {
- return fmt.Errorf("ChangeCollaborationAccessMode: %v", err)
- }
- }
- } else if err = access_model.RecalculateAccesses(ctx, repo); err != nil {
- // Organization automatically called this in addRepository method.
- return fmt.Errorf("recalculateAccesses: %v", err)
- }
-
- if setting.Service.AutoWatchNewRepos {
- if err = repo_model.WatchRepo(ctx, doer.ID, repo.ID, true); err != nil {
- return fmt.Errorf("watchRepo: %v", err)
- }
- }
-
- if err = webhook.CopyDefaultWebhooksToRepo(ctx, repo.ID); err != nil {
- return fmt.Errorf("copyDefaultWebhooksToRepo: %v", err)
- }
-
- return nil
+ return system_model.Init()
}
// DeleteRepository deletes a repository for a user or organization.
@@ -279,7 +125,7 @@ func DeleteRepository(doer *user_model.User, uid, repoID int64) error {
if err := db.DeleteBeans(ctx,
&access_model.Access{RepoID: repo.ID},
- &Action{RepoID: repo.ID},
+ &activities_model.Action{RepoID: repo.ID},
&repo_model.Collaboration{RepoID: repoID},
&issues_model.Comment{RefRepoID: repoID},
&git_model.CommitStatus{RepoID: repoID},
@@ -289,16 +135,16 @@ func DeleteRepository(doer *user_model.User, uid, repoID int64) error {
&repo_model.LanguageStat{RepoID: repoID},
&issues_model.Milestone{RepoID: repoID},
&repo_model.Mirror{RepoID: repoID},
- &Notification{RepoID: repoID},
+ &activities_model.Notification{RepoID: repoID},
&git_model.ProtectedBranch{RepoID: repoID},
&git_model.ProtectedTag{RepoID: repoID},
&repo_model.PushMirror{RepoID: repoID},
- &Release{RepoID: repoID},
+ &repo_model.Release{RepoID: repoID},
&repo_model.RepoIndexerStatus{RepoID: repoID},
&repo_model.Redirect{RedirectRepoID: repoID},
&repo_model.RepoUnit{RepoID: repoID},
&repo_model.Star{RepoID: repoID},
- &Task{RepoID: repoID},
+ &admin_model.Task{RepoID: repoID},
&repo_model.Watch{RepoID: repoID},
&webhook.Webhook{RepoID: repoID},
); err != nil {
@@ -322,7 +168,7 @@ func DeleteRepository(doer *user_model.User, uid, repoID int64) error {
}
// Delete issue index
- if err := db.DeleteResouceIndex(ctx, "issue_index", repoID); err != nil {
+ if err := db.DeleteResourceIndex(ctx, "issue_index", repoID); err != nil {
return err
}
@@ -377,8 +223,7 @@ func DeleteRepository(doer *user_model.User, uid, repoID int64) error {
archivePaths := make([]string, 0, len(archives))
for _, v := range archives {
- p, _ := v.RelativePath()
- archivePaths = append(archivePaths, p)
+ archivePaths = append(archivePaths, v.RelativePath())
}
if _, err := db.DeleteByBean(ctx, &repo_model.RepoArchiver{RepoID: repoID}); err != nil {
@@ -427,36 +272,36 @@ func DeleteRepository(doer *user_model.User, uid, repoID int64) error {
// Remove repository files.
repoPath := repo.RepoPath()
- admin_model.RemoveAllWithNotice(db.DefaultContext, "Delete repository files", repoPath)
+ system_model.RemoveAllWithNotice(db.DefaultContext, "Delete repository files", repoPath)
// Remove wiki files
if repo.HasWiki() {
- admin_model.RemoveAllWithNotice(db.DefaultContext, "Delete repository wiki", repo.WikiPath())
+ system_model.RemoveAllWithNotice(db.DefaultContext, "Delete repository wiki", repo.WikiPath())
}
// Remove archives
for _, archive := range archivePaths {
- admin_model.RemoveStorageWithNotice(db.DefaultContext, storage.RepoArchives, "Delete repo archive file", archive)
+ system_model.RemoveStorageWithNotice(db.DefaultContext, storage.RepoArchives, "Delete repo archive file", archive)
}
// Remove lfs objects
for _, lfsObj := range lfsPaths {
- admin_model.RemoveStorageWithNotice(db.DefaultContext, storage.LFS, "Delete orphaned LFS file", lfsObj)
+ system_model.RemoveStorageWithNotice(db.DefaultContext, storage.LFS, "Delete orphaned LFS file", lfsObj)
}
// Remove issue attachment files.
for _, attachment := range attachmentPaths {
- admin_model.RemoveStorageWithNotice(db.DefaultContext, storage.Attachments, "Delete issue attachment", attachment)
+ system_model.RemoveStorageWithNotice(db.DefaultContext, storage.Attachments, "Delete issue attachment", attachment)
}
// Remove release attachment files.
for _, releaseAttachment := range releaseAttachments {
- admin_model.RemoveStorageWithNotice(db.DefaultContext, storage.Attachments, "Delete release attachment", releaseAttachment)
+ system_model.RemoveStorageWithNotice(db.DefaultContext, storage.Attachments, "Delete release attachment", releaseAttachment)
}
// Remove attachment with no issue_id and release_id.
for _, newAttachment := range newAttachmentPaths {
- admin_model.RemoveStorageWithNotice(db.DefaultContext, storage.Attachments, "Delete issue attachment", newAttachment)
+ system_model.RemoveStorageWithNotice(db.DefaultContext, storage.Attachments, "Delete issue attachment", newAttachment)
}
if len(repo.Avatar) > 0 {
@@ -598,15 +443,27 @@ func CheckRepoStats(ctx context.Context) error {
repoStatsCorrectNumStars,
"repository count 'num_stars'",
},
+ // Repository.NumIssues
+ {
+ statsQuery("SELECT repo.id FROM `repository` repo WHERE repo.num_issues!=(SELECT COUNT(*) FROM `issue` WHERE repo_id=repo.id AND is_closed=? AND is_pull=?)", false, false),
+ repoStatsCorrectNumIssues,
+ "repository count 'num_issues'",
+ },
// Repository.NumClosedIssues
{
statsQuery("SELECT repo.id FROM `repository` repo WHERE repo.num_closed_issues!=(SELECT COUNT(*) FROM `issue` WHERE repo_id=repo.id AND is_closed=? AND is_pull=?)", true, false),
repoStatsCorrectNumClosedIssues,
"repository count 'num_closed_issues'",
},
+ // Repository.NumPulls
+ {
+ statsQuery("SELECT repo.id FROM `repository` repo WHERE repo.num_pulls!=(SELECT COUNT(*) FROM `issue` WHERE repo_id=repo.id AND is_closed=? AND is_pull=?)", false, true),
+ repoStatsCorrectNumPulls,
+ "repository count 'num_pulls'",
+ },
// Repository.NumClosedPulls
{
- statsQuery("SELECT repo.id FROM `repository` repo WHERE repo.num_closed_issues!=(SELECT COUNT(*) FROM `issue` WHERE repo_id=repo.id AND is_closed=? AND is_pull=?)", true, true),
+ statsQuery("SELECT repo.id FROM `repository` repo WHERE repo.num_closed_pulls!=(SELECT COUNT(*) FROM `issue` WHERE repo_id=repo.id AND is_closed=? AND is_pull=?)", true, true),
repoStatsCorrectNumClosedPulls,
"repository count 'num_closed_pulls'",
},
diff --git a/models/repo/archiver.go b/models/repo/archiver.go
index fd07d8554d..003911943f 100644
--- a/models/repo/archiver.go
+++ b/models/repo/archiver.go
@@ -39,9 +39,9 @@ func init() {
db.RegisterModel(new(RepoArchiver))
}
-// RelativePath returns relative path
-func (archiver *RepoArchiver) RelativePath() (string, error) {
- return fmt.Sprintf("%d/%s/%s.%s", archiver.RepoID, archiver.CommitID[:2], archiver.CommitID, archiver.Type.String()), nil
+// RelativePath returns the archive path relative to the archive storage root.
+func (archiver *RepoArchiver) RelativePath() string {
+ return fmt.Sprintf("%d/%s/%s.%s", archiver.RepoID, archiver.CommitID[:2], archiver.CommitID, archiver.Type.String())
}
var delRepoArchiver = new(RepoArchiver)
diff --git a/models/repo/attachment.go b/models/repo/attachment.go
index ddddac2c3d..5d4e11ae72 100644
--- a/models/repo/attachment.go
+++ b/models/repo/attachment.go
@@ -14,6 +14,7 @@ import (
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/storage"
"code.gitea.io/gitea/modules/timeutil"
+ "code.gitea.io/gitea/modules/util"
)
// Attachment represent a attachment of issue/comment/release.
@@ -83,6 +84,10 @@ func (err ErrAttachmentNotExist) Error() string {
return fmt.Sprintf("attachment does not exist [id: %d, uuid: %s]", err.ID, err.UUID)
}
+func (err ErrAttachmentNotExist) Unwrap() error {
+ return util.ErrNotExist
+}
+
// GetAttachmentByID returns attachment by given id
func GetAttachmentByID(ctx context.Context, id int64) (*Attachment, error) {
attach := &Attachment{}
@@ -226,28 +231,6 @@ func DeleteAttachmentsByRelease(releaseID int64) error {
return err
}
-// IterateAttachment iterates attachments; it should not be used when Gitea is servicing users.
-func IterateAttachment(f func(attach *Attachment) error) error {
- var start int
- const batchSize = 100
- for {
- attachments := make([]*Attachment, 0, batchSize)
- if err := db.GetEngine(db.DefaultContext).Limit(batchSize, start).Find(&attachments); err != nil {
- return err
- }
- if len(attachments) == 0 {
- return nil
- }
- start += len(attachments)
-
- for _, attach := range attachments {
- if err := f(attach); err != nil {
- return err
- }
- }
- }
-}
-
// CountOrphanedAttachments returns the number of bad attachments
func CountOrphanedAttachments() (int64, error) {
return db.GetEngine(db.DefaultContext).Where("(issue_id > 0 and issue_id not in (select id from issue)) or (release_id > 0 and release_id not in (select id from `release`))").
diff --git a/models/repo/collaboration_test.go b/models/repo/collaboration_test.go
index 2e6253d561..cbf46dd286 100644
--- a/models/repo/collaboration_test.go
+++ b/models/repo/collaboration_test.go
@@ -19,7 +19,7 @@ import (
func TestRepository_GetCollaborators(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
test := func(repoID int64) {
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repoID}).(*repo_model.Repository)
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repoID})
collaborators, err := repo_model.GetCollaborators(db.DefaultContext, repo.ID, db.ListOptions{})
assert.NoError(t, err)
expectedLen, err := db.GetEngine(db.DefaultContext).Count(&repo_model.Collaboration{RepoID: repoID})
@@ -40,7 +40,7 @@ func TestRepository_IsCollaborator(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
test := func(repoID, userID int64, expected bool) {
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repoID}).(*repo_model.Repository)
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repoID})
actual, err := repo_model.IsCollaborator(db.DefaultContext, repo.ID, userID)
assert.NoError(t, err)
assert.Equal(t, expected, actual)
@@ -54,13 +54,13 @@ func TestRepository_IsCollaborator(t *testing.T) {
func TestRepository_ChangeCollaborationAccessMode(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4}).(*repo_model.Repository)
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4})
assert.NoError(t, repo_model.ChangeCollaborationAccessMode(repo, 4, perm.AccessModeAdmin))
- collaboration := unittest.AssertExistsAndLoadBean(t, &repo_model.Collaboration{RepoID: repo.ID, UserID: 4}).(*repo_model.Collaboration)
+ collaboration := unittest.AssertExistsAndLoadBean(t, &repo_model.Collaboration{RepoID: repo.ID, UserID: 4})
assert.EqualValues(t, perm.AccessModeAdmin, collaboration.Mode)
- access := unittest.AssertExistsAndLoadBean(t, &access_model.Access{UserID: 4, RepoID: repo.ID}).(*access_model.Access)
+ access := unittest.AssertExistsAndLoadBean(t, &access_model.Access{UserID: 4, RepoID: repo.ID})
assert.EqualValues(t, perm.AccessModeAdmin, access.Mode)
assert.NoError(t, repo_model.ChangeCollaborationAccessMode(repo, 4, perm.AccessModeAdmin))
diff --git a/models/repo/mirror.go b/models/repo/mirror.go
index 8f96e8cee1..297ffd594a 100644
--- a/models/repo/mirror.go
+++ b/models/repo/mirror.go
@@ -8,7 +8,6 @@ package repo
import (
"context"
"errors"
- "fmt"
"time"
"code.gitea.io/gitea/models/db"
@@ -108,12 +107,14 @@ func DeleteMirrorByRepoID(repoID int64) error {
// MirrorsIterate iterates all mirror repositories.
func MirrorsIterate(limit int, f func(idx int, bean interface{}) error) error {
- return db.GetEngine(db.DefaultContext).
+ sess := db.GetEngine(db.DefaultContext).
Where("next_update_unix<=?", time.Now().Unix()).
And("next_update_unix!=0").
- OrderBy("updated_unix ASC").
- Limit(limit).
- Iterate(new(Mirror), f)
+ OrderBy("updated_unix ASC")
+ if limit > 0 {
+ sess = sess.Limit(limit)
+ }
+ return sess.Iterate(new(Mirror), f)
}
// InsertMirror inserts a mirror to database
@@ -121,53 +122,3 @@ func InsertMirror(ctx context.Context, mirror *Mirror) error {
_, err := db.GetEngine(ctx).Insert(mirror)
return err
}
-
-// MirrorRepositoryList contains the mirror repositories
-type MirrorRepositoryList []*Repository
-
-func (repos MirrorRepositoryList) loadAttributes(ctx context.Context) error {
- if len(repos) == 0 {
- return nil
- }
-
- // Load mirrors.
- repoIDs := make([]int64, 0, len(repos))
- for i := range repos {
- if !repos[i].IsMirror {
- continue
- }
-
- repoIDs = append(repoIDs, repos[i].ID)
- }
- mirrors := make([]*Mirror, 0, len(repoIDs))
- if err := db.GetEngine(ctx).
- Where("id > 0").
- In("repo_id", repoIDs).
- Find(&mirrors); err != nil {
- return fmt.Errorf("find mirrors: %v", err)
- }
-
- set := make(map[int64]*Mirror)
- for i := range mirrors {
- set[mirrors[i].RepoID] = mirrors[i]
- }
- for i := range repos {
- repos[i].Mirror = set[repos[i].ID]
- repos[i].Mirror.Repo = repos[i]
- }
- return nil
-}
-
-// LoadAttributes loads the attributes for the given MirrorRepositoryList
-func (repos MirrorRepositoryList) LoadAttributes() error {
- return repos.loadAttributes(db.DefaultContext)
-}
-
-// GetUserMirrorRepositories returns a list of mirror repositories of given user.
-func GetUserMirrorRepositories(userID int64) ([]*Repository, error) {
- repos := make([]*Repository, 0, 10)
- return repos, db.GetEngine(db.DefaultContext).
- Where("owner_id = ?", userID).
- And("is_mirror = ?", true).
- Find(&repos)
-}
diff --git a/models/repo/pushmirror.go b/models/repo/pushmirror.go
index 0a7dea79c9..38d6e72019 100644
--- a/models/repo/pushmirror.go
+++ b/models/repo/pushmirror.go
@@ -5,12 +5,15 @@
package repo
import (
+ "context"
"errors"
"time"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/timeutil"
+
+ "xorm.io/builder"
)
// ErrPushMirrorNotExist mirror does not exist error
@@ -29,6 +32,25 @@ type PushMirror struct {
LastUpdateUnix timeutil.TimeStamp `xorm:"INDEX last_update"`
LastError string `xorm:"text"`
}
+type PushMirrorOptions struct {
+ ID int64
+ RepoID int64
+ RemoteName string
+}
+
+func (opts *PushMirrorOptions) toConds() builder.Cond {
+ cond := builder.NewCond()
+ if opts.RepoID > 0 {
+ cond = cond.And(builder.Eq{"repo_id": opts.RepoID})
+ }
+ if opts.RemoteName != "" {
+ cond = cond.And(builder.Eq{"remote_name": opts.RemoteName})
+ }
+ if opts.ID > 0 {
+ cond = cond.And(builder.Eq{"id": opts.ID})
+ }
+ return cond
+}
func init() {
db.RegisterModel(new(PushMirror))
@@ -53,45 +75,48 @@ func (m *PushMirror) GetRemoteName() string {
}
// InsertPushMirror inserts a push-mirror to database
-func InsertPushMirror(m *PushMirror) error {
- _, err := db.GetEngine(db.DefaultContext).Insert(m)
+func InsertPushMirror(ctx context.Context, m *PushMirror) error {
+ _, err := db.GetEngine(ctx).Insert(m)
return err
}
// UpdatePushMirror updates the push-mirror
-func UpdatePushMirror(m *PushMirror) error {
- _, err := db.GetEngine(db.DefaultContext).ID(m.ID).AllCols().Update(m)
+func UpdatePushMirror(ctx context.Context, m *PushMirror) error {
+ _, err := db.GetEngine(ctx).ID(m.ID).AllCols().Update(m)
return err
}
-// DeletePushMirrorByID deletes a push-mirrors by ID
-func DeletePushMirrorByID(ID int64) error {
- _, err := db.GetEngine(db.DefaultContext).ID(ID).Delete(&PushMirror{})
- return err
+func DeletePushMirrors(ctx context.Context, opts PushMirrorOptions) error {
+ if opts.RepoID > 0 {
+ _, err := db.GetEngine(ctx).Where(opts.toConds()).Delete(&PushMirror{})
+ return err
+ }
+ return errors.New("repoID required and must be set")
}
-// DeletePushMirrorsByRepoID deletes all push-mirrors by repoID
-func DeletePushMirrorsByRepoID(repoID int64) error {
- _, err := db.GetEngine(db.DefaultContext).Delete(&PushMirror{RepoID: repoID})
- return err
-}
-
-// GetPushMirrorByID returns push-mirror information.
-func GetPushMirrorByID(ID int64) (*PushMirror, error) {
- m := &PushMirror{}
- has, err := db.GetEngine(db.DefaultContext).ID(ID).Get(m)
+func GetPushMirror(ctx context.Context, opts PushMirrorOptions) (*PushMirror, error) {
+ mirror := &PushMirror{}
+ exist, err := db.GetEngine(ctx).Where(opts.toConds()).Get(mirror)
if err != nil {
return nil, err
- } else if !has {
+ } else if !exist {
return nil, ErrPushMirrorNotExist
}
- return m, nil
+ return mirror, nil
}
// GetPushMirrorsByRepoID returns push-mirror information of a repository.
-func GetPushMirrorsByRepoID(repoID int64) ([]*PushMirror, error) {
+func GetPushMirrorsByRepoID(ctx context.Context, repoID int64, listOptions db.ListOptions) ([]*PushMirror, int64, error) {
+ sess := db.GetEngine(ctx).Where("repo_id = ?", repoID)
+ if listOptions.Page != 0 {
+ sess = db.SetSessionPagination(sess, &listOptions)
+ mirrors := make([]*PushMirror, 0, listOptions.PageSize)
+ count, err := sess.FindAndCount(&mirrors)
+ return mirrors, count, err
+ }
mirrors := make([]*PushMirror, 0, 10)
- return mirrors, db.GetEngine(db.DefaultContext).Where("repo_id=?", repoID).Find(&mirrors)
+ count, err := sess.FindAndCount(&mirrors)
+ return mirrors, count, err
}
// GetPushMirrorsSyncedOnCommit returns push-mirrors for this repo that should be updated by new commits
@@ -103,11 +128,13 @@ func GetPushMirrorsSyncedOnCommit(repoID int64) ([]*PushMirror, error) {
}
// PushMirrorsIterate iterates all push-mirror repositories.
-func PushMirrorsIterate(limit int, f func(idx int, bean interface{}) error) error {
- return db.GetEngine(db.DefaultContext).
+func PushMirrorsIterate(ctx context.Context, limit int, f func(idx int, bean interface{}) error) error {
+ sess := db.GetEngine(ctx).
Where("last_update + (`interval` / ?) <= ?", time.Second, time.Now().Unix()).
And("`interval` != 0").
- OrderBy("last_update ASC").
- Limit(limit).
- Iterate(new(PushMirror), f)
+ OrderBy("last_update ASC")
+ if limit > 0 {
+ sess = sess.Limit(limit)
+ }
+ return sess.Iterate(new(PushMirror), f)
}
diff --git a/models/repo/pushmirror_test.go b/models/repo/pushmirror_test.go
index d36a48547e..5087e30095 100644
--- a/models/repo/pushmirror_test.go
+++ b/models/repo/pushmirror_test.go
@@ -8,6 +8,7 @@ import (
"testing"
"time"
+ "code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
"code.gitea.io/gitea/modules/timeutil"
@@ -20,20 +21,20 @@ func TestPushMirrorsIterate(t *testing.T) {
now := timeutil.TimeStampNow()
- repo_model.InsertPushMirror(&repo_model.PushMirror{
+ repo_model.InsertPushMirror(db.DefaultContext, &repo_model.PushMirror{
RemoteName: "test-1",
LastUpdateUnix: now,
Interval: 1,
})
long, _ := time.ParseDuration("24h")
- repo_model.InsertPushMirror(&repo_model.PushMirror{
+ repo_model.InsertPushMirror(db.DefaultContext, &repo_model.PushMirror{
RemoteName: "test-2",
LastUpdateUnix: now,
Interval: long,
})
- repo_model.InsertPushMirror(&repo_model.PushMirror{
+ repo_model.InsertPushMirror(db.DefaultContext, &repo_model.PushMirror{
RemoteName: "test-3",
LastUpdateUnix: now,
Interval: 0,
@@ -41,7 +42,7 @@ func TestPushMirrorsIterate(t *testing.T) {
time.Sleep(1 * time.Millisecond)
- repo_model.PushMirrorsIterate(1, func(idx int, bean interface{}) error {
+ repo_model.PushMirrorsIterate(db.DefaultContext, 1, func(idx int, bean interface{}) error {
m, ok := bean.(*repo_model.PushMirror)
assert.True(t, ok)
assert.Equal(t, "test-1", m.RemoteName)
diff --git a/models/repo/redirect.go b/models/repo/redirect.go
index 88fad6f3e3..f28220c2af 100644
--- a/models/repo/redirect.go
+++ b/models/repo/redirect.go
@@ -10,6 +10,7 @@ import (
"strings"
"code.gitea.io/gitea/models/db"
+ "code.gitea.io/gitea/modules/util"
)
// ErrRedirectNotExist represents a "RedirectNotExist" kind of error.
@@ -28,6 +29,10 @@ func (err ErrRedirectNotExist) Error() string {
return fmt.Sprintf("repository redirect does not exist [uid: %d, name: %s]", err.OwnerID, err.RepoName)
}
+func (err ErrRedirectNotExist) Unwrap() error {
+ return util.ErrNotExist
+}
+
// Redirect represents that a repo name should be redirected to another
type Redirect struct {
ID int64 `xorm:"pk autoincr"`
diff --git a/models/repo/redirect_test.go b/models/repo/redirect_test.go
index 05b105cf63..90114667e5 100644
--- a/models/repo/redirect_test.go
+++ b/models/repo/redirect_test.go
@@ -29,7 +29,7 @@ func TestNewRedirect(t *testing.T) {
// redirect to a completely new name
assert.NoError(t, unittest.PrepareTestDatabase())
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository)
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
assert.NoError(t, repo_model.NewRedirect(db.DefaultContext, repo.OwnerID, repo.ID, repo.Name, "newreponame"))
unittest.AssertExistsAndLoadBean(t, &repo_model.Redirect{
@@ -48,7 +48,7 @@ func TestNewRedirect2(t *testing.T) {
// redirect to previously used name
assert.NoError(t, unittest.PrepareTestDatabase())
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository)
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
assert.NoError(t, repo_model.NewRedirect(db.DefaultContext, repo.OwnerID, repo.ID, repo.Name, "oldrepo1"))
unittest.AssertExistsAndLoadBean(t, &repo_model.Redirect{
@@ -67,7 +67,7 @@ func TestNewRedirect3(t *testing.T) {
// redirect for a previously-unredirected repo
assert.NoError(t, unittest.PrepareTestDatabase())
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}).(*repo_model.Repository)
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2})
assert.NoError(t, repo_model.NewRedirect(db.DefaultContext, repo.OwnerID, repo.ID, repo.Name, "newreponame"))
unittest.AssertExistsAndLoadBean(t, &repo_model.Redirect{
diff --git a/models/release.go b/models/repo/release.go
similarity index 82%
rename from models/release.go
rename to models/repo/release.go
index b169920f2f..2e7bc6d322 100644
--- a/models/release.go
+++ b/models/repo/release.go
@@ -3,7 +3,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
-package models
+package repo
import (
"context"
@@ -14,7 +14,6 @@ import (
"strings"
"code.gitea.io/gitea/models/db"
- repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/timeutil"
@@ -23,14 +22,53 @@ import (
"xorm.io/builder"
)
+// ErrReleaseAlreadyExist represents a "ReleaseAlreadyExist" kind of error.
+type ErrReleaseAlreadyExist struct {
+ TagName string
+}
+
+// IsErrReleaseAlreadyExist checks if an error is a ErrReleaseAlreadyExist.
+func IsErrReleaseAlreadyExist(err error) bool {
+ _, ok := err.(ErrReleaseAlreadyExist)
+ return ok
+}
+
+func (err ErrReleaseAlreadyExist) Error() string {
+ return fmt.Sprintf("release tag already exist [tag_name: %s]", err.TagName)
+}
+
+func (err ErrReleaseAlreadyExist) Unwrap() error {
+ return util.ErrAlreadyExist
+}
+
+// ErrReleaseNotExist represents a "ReleaseNotExist" kind of error.
+type ErrReleaseNotExist struct {
+ ID int64
+ TagName string
+}
+
+// IsErrReleaseNotExist checks if an error is a ErrReleaseNotExist.
+func IsErrReleaseNotExist(err error) bool {
+ _, ok := err.(ErrReleaseNotExist)
+ return ok
+}
+
+func (err ErrReleaseNotExist) Error() string {
+ return fmt.Sprintf("release tag does not exist [id: %d, tag_name: %s]", err.ID, err.TagName)
+}
+
+func (err ErrReleaseNotExist) Unwrap() error {
+ return util.ErrNotExist
+}
+
// Release represents a release of repository.
type Release struct {
- ID int64 `xorm:"pk autoincr"`
- RepoID int64 `xorm:"INDEX UNIQUE(n)"`
- Repo *repo_model.Repository `xorm:"-"`
- PublisherID int64 `xorm:"INDEX"`
- Publisher *user_model.User `xorm:"-"`
- TagName string `xorm:"INDEX UNIQUE(n)"`
+ ID int64 `xorm:"pk autoincr"`
+ RepoID int64 `xorm:"INDEX UNIQUE(n)"`
+ Repo *Repository `xorm:"-"`
+ PublisherID int64 `xorm:"INDEX"`
+ Publisher *user_model.User `xorm:"-"`
+ TagName string `xorm:"INDEX UNIQUE(n)"`
OriginalAuthor string
OriginalAuthorID int64 `xorm:"index"`
LowerTagName string
@@ -38,14 +76,14 @@ type Release struct {
Title string
Sha1 string `xorm:"VARCHAR(40)"`
NumCommits int64
- NumCommitsBehind int64 `xorm:"-"`
- Note string `xorm:"TEXT"`
- RenderedNote string `xorm:"-"`
- IsDraft bool `xorm:"NOT NULL DEFAULT false"`
- IsPrerelease bool `xorm:"NOT NULL DEFAULT false"`
- IsTag bool `xorm:"NOT NULL DEFAULT false"`
- Attachments []*repo_model.Attachment `xorm:"-"`
- CreatedUnix timeutil.TimeStamp `xorm:"INDEX"`
+ NumCommitsBehind int64 `xorm:"-"`
+ Note string `xorm:"TEXT"`
+ RenderedNote string `xorm:"-"`
+ IsDraft bool `xorm:"NOT NULL DEFAULT false"`
+ IsPrerelease bool `xorm:"NOT NULL DEFAULT false"`
+ IsTag bool `xorm:"NOT NULL DEFAULT false"`
+ Attachments []*Attachment `xorm:"-"`
+ CreatedUnix timeutil.TimeStamp `xorm:"INDEX"`
}
func init() {
@@ -55,7 +93,7 @@ func init() {
func (r *Release) loadAttributes(ctx context.Context) error {
var err error
if r.Repo == nil {
- r.Repo, err = repo_model.GetRepositoryByIDCtx(ctx, r.RepoID)
+ r.Repo, err = GetRepositoryByIDCtx(ctx, r.RepoID)
if err != nil {
return err
}
@@ -116,7 +154,7 @@ func UpdateRelease(ctx context.Context, rel *Release) error {
// AddReleaseAttachments adds a release attachments
func AddReleaseAttachments(ctx context.Context, releaseID int64, attachmentUUIDs []string) (err error) {
// Check attachments
- attachments, err := repo_model.GetAttachmentsByUUIDs(ctx, attachmentUUIDs)
+ attachments, err := GetAttachmentsByUUIDs(ctx, attachmentUUIDs)
if err != nil {
return fmt.Errorf("GetAttachmentsByUUIDs [uuids: %v]: %v", attachmentUUIDs, err)
}
@@ -170,6 +208,7 @@ type FindReleasesOptions struct {
IsPreRelease util.OptionalBool
IsDraft util.OptionalBool
TagNames []string
+ HasSha1 util.OptionalBool // useful to find draft releases which are created with existing tags
}
func (opts *FindReleasesOptions) toConds(repoID int64) builder.Cond {
@@ -191,6 +230,13 @@ func (opts *FindReleasesOptions) toConds(repoID int64) builder.Cond {
if !opts.IsDraft.IsNone() {
cond = cond.And(builder.Eq{"is_draft": opts.IsDraft.IsTrue()})
}
+ if !opts.HasSha1.IsNone() {
+ if opts.HasSha1.IsTrue() {
+ cond = cond.And(builder.Neq{"sha1": ""})
+ } else {
+ cond = cond.And(builder.Eq{"sha1": ""})
+ }
+ }
return cond
}
@@ -279,9 +325,9 @@ func GetReleaseAttachments(ctx context.Context, rels ...*Release) (err error) {
// Sort
sortedRels := releaseMetaSearch{ID: make([]int64, len(rels)), Rel: make([]*Release, len(rels))}
- var attachments []*repo_model.Attachment
+ var attachments []*Attachment
for index, element := range rels {
- element.Attachments = []*repo_model.Attachment{}
+ element.Attachments = []*Attachment{}
sortedRels.ID[index] = element.ID
sortedRels.Rel[index] = element
}
@@ -291,7 +337,7 @@ func GetReleaseAttachments(ctx context.Context, rels ...*Release) (err error) {
err = db.GetEngine(ctx).
Asc("release_id", "name").
In("release_id", sortedRels.ID).
- Find(&attachments, repo_model.Attachment{})
+ Find(&attachments, Attachment{})
if err != nil {
return err
}
@@ -354,7 +400,7 @@ func UpdateReleasesMigrationsByType(gitServiceType structs.GitServiceType, origi
}
// PushUpdateDeleteTagsContext updates a number of delete tags with context
-func PushUpdateDeleteTagsContext(ctx context.Context, repo *repo_model.Repository, tags []string) error {
+func PushUpdateDeleteTagsContext(ctx context.Context, repo *Repository, tags []string) error {
if len(tags) == 0 {
return nil
}
@@ -384,7 +430,7 @@ func PushUpdateDeleteTagsContext(ctx context.Context, repo *repo_model.Repositor
}
// PushUpdateDeleteTag must be called for any push actions to delete tag
-func PushUpdateDeleteTag(repo *repo_model.Repository, tagName string) error {
+func PushUpdateDeleteTag(repo *Repository, tagName string) error {
rel, err := GetRelease(repo.ID, tagName)
if err != nil {
if IsErrReleaseNotExist(err) {
@@ -409,7 +455,7 @@ func PushUpdateDeleteTag(repo *repo_model.Repository, tagName string) error {
}
// SaveOrUpdateTag must be called for any push actions to add tag
-func SaveOrUpdateTag(repo *repo_model.Repository, newRel *Release) error {
+func SaveOrUpdateTag(repo *Repository, newRel *Release) error {
rel, err := GetRelease(repo.ID, newRel.TagName)
if err != nil && !IsErrReleaseNotExist(err) {
return fmt.Errorf("GetRelease: %v", err)
diff --git a/models/repo/repo.go b/models/repo/repo.go
index bb2a7468ff..ce698baaef 100644
--- a/models/repo/repo.go
+++ b/models/repo/repo.go
@@ -23,9 +23,11 @@ import (
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util"
+
+ "xorm.io/builder"
)
-// ErrUserDoesNotHaveAccessToRepo represets an error where the user doesn't has access to a given repo.
+// ErrUserDoesNotHaveAccessToRepo represents an error where the user doesn't has access to a given repo.
type ErrUserDoesNotHaveAccessToRepo struct {
UserID int64
RepoName string
@@ -41,6 +43,10 @@ func (err ErrUserDoesNotHaveAccessToRepo) Error() string {
return fmt.Sprintf("user doesn't have access to repo [user_id: %d, repo_name: %s]", err.UserID, err.RepoName)
}
+func (err ErrUserDoesNotHaveAccessToRepo) Unwrap() error {
+ return util.ErrPermissionDenied
+}
+
var (
reservedRepoNames = []string{".", "..", "-"}
reservedRepoPatterns = []string{"*.git", "*.wiki", "*.rss", "*.atom"}
@@ -641,6 +647,11 @@ func (err ErrRepoNotExist) Error() string {
err.ID, err.UID, err.OwnerName, err.Name)
}
+// Unwrap unwraps this error as a ErrNotExist error
+func (err ErrRepoNotExist) Unwrap() error {
+ return util.ErrNotExist
+}
+
// GetRepositoryByOwnerAndNameCtx returns the repository by given owner name and repo name
func GetRepositoryByOwnerAndNameCtx(ctx context.Context, ownerName, repoName string) (*Repository, error) {
var repo Repository
@@ -784,3 +795,15 @@ func UpdateRepoIssueNumbers(ctx context.Context, repoID int64, isPull, isClosed
}
return nil
}
+
+// CountNullArchivedRepository counts the number of repositories with is_archived is null
+func CountNullArchivedRepository() (int64, error) {
+ return db.GetEngine(db.DefaultContext).Where(builder.IsNull{"is_archived"}).Count(new(Repository))
+}
+
+// FixNullArchivedRepository sets is_archived to false where it is null
+func FixNullArchivedRepository() (int64, error) {
+ return db.GetEngine(db.DefaultContext).Where(builder.IsNull{"is_archived"}).Cols("is_archived").NoAutoTime().Update(&Repository{
+ IsArchived: false,
+ })
+}
diff --git a/models/repo/repo_list.go b/models/repo/repo_list.go
index 9de76fa5ff..0cd0a3c8e3 100644
--- a/models/repo/repo_list.go
+++ b/models/repo/repo_list.go
@@ -15,36 +15,12 @@ import (
"code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/container"
- "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
"xorm.io/builder"
)
-// IterateRepository iterate repositories
-func IterateRepository(f func(repo *Repository) error) error {
- var start int
- batchSize := setting.Database.IterateBufferSize
- sess := db.GetEngine(db.DefaultContext)
- for {
- repos := make([]*Repository, 0, batchSize)
- if err := sess.Limit(batchSize, start).Find(&repos); err != nil {
- return err
- }
- if len(repos) == 0 {
- return nil
- }
- start += len(repos)
-
- for _, repo := range repos {
- if err := f(repo); err != nil {
- return err
- }
- }
- }
-}
-
// FindReposMapByIDs find repos as map
func FindReposMapByIDs(repoIDs []int64, res map[int64]*Repository) error {
return db.GetEngine(db.DefaultContext).In("id", repoIDs).Find(&res)
@@ -92,10 +68,10 @@ func (repos RepositoryList) loadAttributes(ctx context.Context) error {
return nil
}
- set := make(map[int64]struct{})
+ set := make(container.Set[int64])
repoIDs := make([]int64, len(repos))
for i := range repos {
- set[repos[i].OwnerID] = struct{}{}
+ set.Add(repos[i].OwnerID)
repoIDs[i] = repos[i].ID
}
@@ -103,7 +79,7 @@ func (repos RepositoryList) loadAttributes(ctx context.Context) error {
users := make(map[int64]*user_model.User, len(set))
if err := db.GetEngine(ctx).
Where("id > 0").
- In("id", container.KeysInt64(set)).
+ In("id", set.Values()).
Find(&users); err != nil {
return fmt.Errorf("find users: %v", err)
}
@@ -187,6 +163,10 @@ type SearchRepoOptions struct {
HasMilestones util.OptionalBool
// LowerNames represents valid lower names to restrict to
LowerNames []string
+ // When specified true, apply some filters over the conditions:
+ // - Don't show forks, when opts.Fork is OptionalBoolNone.
+ // - Do not display repositories that don't have a description, an icon and topics.
+ OnlyShowRelevant bool
}
// SearchOrderBy is used to sort the result
@@ -487,8 +467,12 @@ func SearchRepositoryCondition(opts *SearchRepoOptions) builder.Cond {
Where(builder.Eq{"language": opts.Language}).And(builder.Eq{"is_primary": true})))
}
- if opts.Fork != util.OptionalBoolNone {
- cond = cond.And(builder.Eq{"is_fork": opts.Fork == util.OptionalBoolTrue})
+ if opts.Fork != util.OptionalBoolNone || opts.OnlyShowRelevant {
+ if opts.OnlyShowRelevant && opts.Fork == util.OptionalBoolNone {
+ cond = cond.And(builder.Eq{"is_fork": false})
+ } else {
+ cond = cond.And(builder.Eq{"is_fork": opts.Fork == util.OptionalBoolTrue})
+ }
}
if opts.Mirror != util.OptionalBoolNone {
@@ -510,6 +494,25 @@ func SearchRepositoryCondition(opts *SearchRepoOptions) builder.Cond {
cond = cond.And(builder.Eq{"num_milestones": 0}.Or(builder.IsNull{"num_milestones"}))
}
+ if opts.OnlyShowRelevant {
+ // Only show a repo that either has a topic or description.
+ subQueryCond := builder.NewCond()
+
+ // Topic checking. Topics is non-null.
+ subQueryCond = subQueryCond.Or(builder.And(builder.Neq{"topics": "null"}, builder.Neq{"topics": "[]"}))
+
+ // Description checking. Description not empty.
+ subQueryCond = subQueryCond.Or(builder.Neq{"description": ""})
+
+ // Repo has a avatar.
+ subQueryCond = subQueryCond.Or(builder.Neq{"avatar": ""})
+
+ // Always hide repo's that are empty.
+ subQueryCond = subQueryCond.And(builder.Eq{"is_empty": false})
+
+ cond = cond.And(subQueryCond)
+ }
+
return cond
}
@@ -590,6 +593,16 @@ func searchRepositoryByCondition(ctx context.Context, opts *SearchRepoOptions, c
return sess, count, nil
}
+// SearchRepositoryIDsByCondition search repository IDs by given condition.
+func SearchRepositoryIDsByCondition(ctx context.Context, cond builder.Cond) ([]int64, error) {
+ repoIDs := make([]int64, 0, 10)
+ return repoIDs, db.GetEngine(ctx).
+ Table("repository").
+ Cols("id").
+ Where(cond).
+ Find(&repoIDs)
+}
+
// AccessibleRepositoryCondition takes a user a returns a condition for checking if a repository is accessible
func AccessibleRepositoryCondition(user *user_model.User, unitType unit.Type) builder.Cond {
cond := builder.NewCond()
@@ -677,16 +690,16 @@ func AccessibleRepoIDsQuery(user *user_model.User) *builder.Builder {
}
// FindUserCodeAccessibleRepoIDs finds all at Code level accessible repositories' ID by the user's id
-func FindUserCodeAccessibleRepoIDs(user *user_model.User) ([]int64, error) {
- repoIDs := make([]int64, 0, 10)
- if err := db.GetEngine(db.DefaultContext).
- Table("repository").
- Cols("id").
- Where(AccessibleRepositoryCondition(user, unit.TypeCode)).
- Find(&repoIDs); err != nil {
- return nil, fmt.Errorf("FindUserCodeAccesibleRepoIDs: %v", err)
- }
- return repoIDs, nil
+func FindUserCodeAccessibleRepoIDs(ctx context.Context, user *user_model.User) ([]int64, error) {
+ return SearchRepositoryIDsByCondition(ctx, AccessibleRepositoryCondition(user, unit.TypeCode))
+}
+
+// FindUserCodeAccessibleOwnerRepoIDs finds all repository IDs for the given owner whose code the user can see.
+func FindUserCodeAccessibleOwnerRepoIDs(ctx context.Context, ownerID int64, user *user_model.User) ([]int64, error) {
+ return SearchRepositoryIDsByCondition(ctx, builder.NewCond().And(
+ builder.Eq{"owner_id": ownerID},
+ AccessibleRepositoryCondition(user, unit.TypeCode),
+ ))
}
// GetUserRepositories returns a list of repositories of given user.
diff --git a/models/repo/repo_test.go b/models/repo/repo_test.go
index 6f8b282a66..617ec12798 100644
--- a/models/repo/repo_test.go
+++ b/models/repo/repo_test.go
@@ -56,7 +56,7 @@ func TestGetPrivateRepositoryCount(t *testing.T) {
func TestRepoAPIURL(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 10}).(*repo_model.Repository)
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 10})
assert.Equal(t, "https://try.gitea.io/api/v1/repos/user12/repo10", repo.APIURL())
}
diff --git a/models/repo/repo_unit.go b/models/repo/repo_unit.go
index da3e19dece..dd85ca9186 100644
--- a/models/repo/repo_unit.go
+++ b/models/repo/repo_unit.go
@@ -13,6 +13,7 @@ import (
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/timeutil"
+ "code.gitea.io/gitea/modules/util"
"xorm.io/xorm"
"xorm.io/xorm/convert"
@@ -33,6 +34,10 @@ func (err ErrUnitTypeNotExist) Error() string {
return fmt.Sprintf("Unit type does not exist: %s", err.UT.String())
}
+func (err ErrUnitTypeNotExist) Unwrap() error {
+ return util.ErrNotExist
+}
+
// RepoUnit describes all units of a repository
type RepoUnit struct { //revive:disable-line:exported
ID int64
diff --git a/models/repo/star_test.go b/models/repo/star_test.go
index aa72b1dac8..1b53e17d27 100644
--- a/models/repo/star_test.go
+++ b/models/repo/star_test.go
@@ -36,7 +36,7 @@ func TestIsStaring(t *testing.T) {
func TestRepository_GetStargazers(t *testing.T) {
// repo with stargazers
assert.NoError(t, unittest.PrepareTestDatabase())
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4}).(*repo_model.Repository)
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4})
gazers, err := repo_model.GetStargazers(repo, db.ListOptions{Page: 0})
assert.NoError(t, err)
if assert.Len(t, gazers, 1) {
@@ -47,7 +47,7 @@ func TestRepository_GetStargazers(t *testing.T) {
func TestRepository_GetStargazers2(t *testing.T) {
// repo with stargazers
assert.NoError(t, unittest.PrepareTestDatabase())
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3}).(*repo_model.Repository)
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3})
gazers, err := repo_model.GetStargazers(repo, db.ListOptions{Page: 0})
assert.NoError(t, err)
assert.Len(t, gazers, 0)
diff --git a/models/repo/topic.go b/models/repo/topic.go
index 2a16467215..33bbb05af9 100644
--- a/models/repo/topic.go
+++ b/models/repo/topic.go
@@ -11,7 +11,9 @@ import (
"strings"
"code.gitea.io/gitea/models/db"
+ "code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/timeutil"
+ "code.gitea.io/gitea/modules/util"
"xorm.io/builder"
)
@@ -54,6 +56,10 @@ func (err ErrTopicNotExist) Error() string {
return fmt.Sprintf("topic is not exist [name: %s]", err.Name)
}
+func (err ErrTopicNotExist) Unwrap() error {
+ return util.ErrNotExist
+}
+
// ValidateTopic checks a topic by length and match pattern rules
func ValidateTopic(topic string) bool {
return len(topic) <= 35 && topicPattern.MatchString(topic)
@@ -62,7 +68,7 @@ func ValidateTopic(topic string) bool {
// SanitizeAndValidateTopics sanitizes and checks an array or topics
func SanitizeAndValidateTopics(topics []string) (validTopics, invalidTopics []string) {
validTopics = make([]string, 0)
- mValidTopics := make(map[string]struct{})
+ mValidTopics := make(container.Set[string])
invalidTopics = make([]string, 0)
for _, topic := range topics {
@@ -72,12 +78,12 @@ func SanitizeAndValidateTopics(topics []string) (validTopics, invalidTopics []st
continue
}
// ignore same topic twice
- if _, ok := mValidTopics[topic]; ok {
+ if mValidTopics.Contains(topic) {
continue
}
if ValidateTopic(topic) {
validTopics = append(validTopics, topic)
- mValidTopics[topic] = struct{}{}
+ mValidTopics.Add(topic)
} else {
invalidTopics = append(invalidTopics, topic)
}
diff --git a/models/repo/update.go b/models/repo/update.go
index 07776ebc01..64a225d2f5 100644
--- a/models/repo/update.go
+++ b/models/repo/update.go
@@ -63,6 +63,10 @@ func (err ErrReachLimitOfRepo) Error() string {
return fmt.Sprintf("user has reached maximum limit of repositories [limit: %d]", err.Limit)
}
+func (err ErrReachLimitOfRepo) Unwrap() error {
+ return util.ErrPermissionDenied
+}
+
// ErrRepoAlreadyExist represents a "RepoAlreadyExist" kind of error.
type ErrRepoAlreadyExist struct {
Uname string
@@ -79,6 +83,10 @@ func (err ErrRepoAlreadyExist) Error() string {
return fmt.Sprintf("repository already exists [uname: %s, name: %s]", err.Uname, err.Name)
}
+func (err ErrRepoAlreadyExist) Unwrap() error {
+ return util.ErrAlreadyExist
+}
+
// ErrRepoFilesAlreadyExist represents a "RepoFilesAlreadyExist" kind of error.
type ErrRepoFilesAlreadyExist struct {
Uname string
@@ -95,6 +103,10 @@ func (err ErrRepoFilesAlreadyExist) Error() string {
return fmt.Sprintf("repository files already exist [uname: %s, name: %s]", err.Uname, err.Name)
}
+func (err ErrRepoFilesAlreadyExist) Unwrap() error {
+ return util.ErrAlreadyExist
+}
+
// CheckCreateRepository check if could created a repository
func CheckCreateRepository(doer, u *user_model.User, name string, overwriteOrAdopt bool) error {
if !doer.CanCreateRepo() {
diff --git a/models/upload.go b/models/repo/upload.go
similarity index 88%
rename from models/upload.go
rename to models/repo/upload.go
index 4a64ff34e3..e3ce7e458f 100644
--- a/models/upload.go
+++ b/models/repo/upload.go
@@ -3,7 +3,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
-package models
+package repo
import (
"fmt"
@@ -20,13 +20,25 @@ import (
gouuid "github.com/google/uuid"
)
-// ____ ___ .__ .___ ___________.___.__
-// | | \______ | | _________ __| _/ \_ _____/| | | ____ ______
-// | | /\____ \| | / _ \__ \ / __ | | __) | | | _/ __ \ / ___/
-// | | / | |_> > |_( <_> ) __ \_/ /_/ | | \ | | |_\ ___/ \___ \
-// |______/ | __/|____/\____(____ /\____ | \___ / |___|____/\___ >____ >
-// |__| \/ \/ \/ \/ \/
-//
+// ErrUploadNotExist represents a "UploadNotExist" kind of error.
+type ErrUploadNotExist struct {
+ ID int64
+ UUID string
+}
+
+// IsErrUploadNotExist checks if an error is a ErrUploadNotExist.
+func IsErrUploadNotExist(err error) bool {
+ _, ok := err.(ErrUploadNotExist)
+ return ok
+}
+
+func (err ErrUploadNotExist) Error() string {
+ return fmt.Sprintf("attachment does not exist [id: %d, uuid: %s]", err.ID, err.UUID)
+}
+
+func (err ErrUploadNotExist) Unwrap() error {
+ return util.ErrNotExist
+}
// Upload represent a uploaded file to a repo to be deleted when moved
type Upload struct {
diff --git a/models/repo/user_repo.go b/models/repo/user_repo.go
index 71e0c57550..e7125f70f8 100644
--- a/models/repo/user_repo.go
+++ b/models/repo/user_repo.go
@@ -10,6 +10,7 @@ import (
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/perm"
user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/container"
api "code.gitea.io/gitea/modules/structs"
"xorm.io/builder"
@@ -83,37 +84,19 @@ func GetRepoAssignees(ctx context.Context, repo *Repository) (_ []*user_model.Us
return nil, err
}
- uidMap := map[int64]bool{}
- i := 0
- for _, uid := range userIDs {
- if uidMap[uid] {
- continue
- }
- uidMap[uid] = true
- userIDs[i] = uid
- i++
- }
- userIDs = userIDs[:i]
- userIDs = append(userIDs, additionalUserIDs...)
-
- for _, uid := range additionalUserIDs {
- if uidMap[uid] {
- continue
- }
- userIDs[i] = uid
- i++
- }
- userIDs = userIDs[:i]
+ 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(userIDs)+1)
+ users := make([]*user_model.User, 0, len(uniqueUserIDs)+1)
if len(userIDs) > 0 {
- if err = e.In("id", userIDs).OrderBy(user_model.GetOrderByName()).Find(&users); err != nil {
+ if err = e.In("id", uniqueUserIDs.Values()).OrderBy(user_model.GetOrderByName()).Find(&users); err != nil {
return nil, err
}
}
- if !repo.Owner.IsOrganization() && !uidMap[repo.OwnerID] {
+ if !repo.Owner.IsOrganization() && !uniqueUserIDs.Contains(repo.OwnerID) {
users = append(users, repo.Owner)
}
@@ -170,3 +153,15 @@ func GetReviewers(ctx context.Context, repo *Repository, doerID, posterID int64)
users := make([]*user_model.User, 0, 8)
return users, db.GetEngine(ctx).Where(cond).OrderBy(user_model.GetOrderByName()).Find(&users)
}
+
+// GetIssuePosters returns all users that have authored an issue/pull request for the given repository
+func GetIssuePosters(ctx context.Context, repo *Repository, isPull bool) ([]*user_model.User, error) {
+ users := make([]*user_model.User, 0, 8)
+ cond := builder.In("`user`.id",
+ builder.Select("poster_id").From("issue").Where(
+ builder.Eq{"repo_id": repo.ID}.
+ And(builder.Eq{"is_pull": isPull}),
+ ).GroupBy("poster_id"),
+ )
+ return users, db.GetEngine(ctx).Where(cond).OrderBy(user_model.GetOrderByName()).Find(&users)
+}
diff --git a/models/repo/user_repo_test.go b/models/repo/user_repo_test.go
index d024729b9c..6409145920 100644
--- a/models/repo/user_repo_test.go
+++ b/models/repo/user_repo_test.go
@@ -17,13 +17,13 @@ import (
func TestRepoAssignees(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}).(*repo_model.Repository)
+ repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2})
users, err := repo_model.GetRepoAssignees(db.DefaultContext, repo2)
assert.NoError(t, err)
assert.Len(t, users, 1)
assert.Equal(t, users[0].ID, int64(2))
- repo21 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 21}).(*repo_model.Repository)
+ repo21 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 21})
users, err = repo_model.GetRepoAssignees(db.DefaultContext, repo21)
assert.NoError(t, err)
assert.Len(t, users, 3)
@@ -36,7 +36,7 @@ func TestRepoGetReviewers(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
// test public repo
- repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository)
+ repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
ctx := db.DefaultContext
reviewers, err := repo_model.GetReviewers(ctx, repo1, 2, 2)
@@ -54,7 +54,7 @@ func TestRepoGetReviewers(t *testing.T) {
assert.Len(t, reviewers, 3)
// test private user repo
- repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}).(*repo_model.Repository)
+ repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2})
reviewers, err = repo_model.GetReviewers(ctx, repo2, 2, 4)
assert.NoError(t, err)
@@ -62,7 +62,7 @@ func TestRepoGetReviewers(t *testing.T) {
assert.EqualValues(t, reviewers[0].ID, 2)
// test private org repo
- repo3 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3}).(*repo_model.Repository)
+ repo3 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3})
reviewers, err = repo_model.GetReviewers(ctx, repo3, 2, 1)
assert.NoError(t, err)
diff --git a/models/repo/watch_test.go b/models/repo/watch_test.go
index 3875e63fd8..18a2d5d5fd 100644
--- a/models/repo/watch_test.go
+++ b/models/repo/watch_test.go
@@ -30,7 +30,7 @@ func TestIsWatching(t *testing.T) {
func TestGetWatchers(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository)
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
watches, err := repo_model.GetWatchers(db.DefaultContext, repo.ID)
assert.NoError(t, err)
// One watchers are inactive, thus minus 1
@@ -47,7 +47,7 @@ func TestGetWatchers(t *testing.T) {
func TestRepository_GetWatchers(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository)
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
watchers, err := repo_model.GetRepoWatchers(repo.ID, db.ListOptions{Page: 1})
assert.NoError(t, err)
assert.Len(t, watchers, repo.NumWatches)
@@ -55,7 +55,7 @@ func TestRepository_GetWatchers(t *testing.T) {
unittest.AssertExistsAndLoadBean(t, &repo_model.Watch{UserID: watcher.ID, RepoID: repo.ID})
}
- repo = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 9}).(*repo_model.Repository)
+ repo = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 9})
watchers, err = repo_model.GetRepoWatchers(repo.ID, db.ListOptions{Page: 1})
assert.NoError(t, err)
assert.Len(t, watchers, 0)
@@ -64,7 +64,7 @@ func TestRepository_GetWatchers(t *testing.T) {
func TestWatchIfAuto(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository)
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
watchers, err := repo_model.GetRepoWatchers(repo.ID, db.ListOptions{Page: 1})
assert.NoError(t, err)
assert.Len(t, watchers, repo.NumWatches)
diff --git a/models/repo/wiki.go b/models/repo/wiki.go
index abf0155cad..c8886eaa34 100644
--- a/models/repo/wiki.go
+++ b/models/repo/wiki.go
@@ -6,6 +6,7 @@
package repo
import (
+ "fmt"
"path/filepath"
"strings"
@@ -14,6 +15,63 @@ import (
"code.gitea.io/gitea/modules/util"
)
+// ErrWikiAlreadyExist represents a "WikiAlreadyExist" kind of error.
+type ErrWikiAlreadyExist struct {
+ Title string
+}
+
+// IsErrWikiAlreadyExist checks if an error is an ErrWikiAlreadyExist.
+func IsErrWikiAlreadyExist(err error) bool {
+ _, ok := err.(ErrWikiAlreadyExist)
+ return ok
+}
+
+func (err ErrWikiAlreadyExist) Error() string {
+ return fmt.Sprintf("wiki page already exists [title: %s]", err.Title)
+}
+
+func (err ErrWikiAlreadyExist) Unwrap() error {
+ return util.ErrAlreadyExist
+}
+
+// ErrWikiReservedName represents a reserved name error.
+type ErrWikiReservedName struct {
+ Title string
+}
+
+// IsErrWikiReservedName checks if an error is an ErrWikiReservedName.
+func IsErrWikiReservedName(err error) bool {
+ _, ok := err.(ErrWikiReservedName)
+ return ok
+}
+
+func (err ErrWikiReservedName) Error() string {
+ return fmt.Sprintf("wiki title is reserved: %s", err.Title)
+}
+
+func (err ErrWikiReservedName) Unwrap() error {
+ return util.ErrInvalidArgument
+}
+
+// ErrWikiInvalidFileName represents an invalid wiki file name.
+type ErrWikiInvalidFileName struct {
+ FileName string
+}
+
+// IsErrWikiInvalidFileName checks if an error is an ErrWikiInvalidFileName.
+func IsErrWikiInvalidFileName(err error) bool {
+ _, ok := err.(ErrWikiInvalidFileName)
+ return ok
+}
+
+func (err ErrWikiInvalidFileName) Error() string {
+ return fmt.Sprintf("Invalid wiki filename: %s", err.FileName)
+}
+
+func (err ErrWikiInvalidFileName) Unwrap() error {
+ return util.ErrInvalidArgument
+}
+
// WikiCloneLink returns clone URLs of repository wiki.
func (repo *Repository) WikiCloneLink() *CloneLink {
return repo.cloneLink(true)
diff --git a/models/repo/wiki_test.go b/models/repo/wiki_test.go
index 339289e05d..8631736276 100644
--- a/models/repo/wiki_test.go
+++ b/models/repo/wiki_test.go
@@ -18,7 +18,7 @@ import (
func TestRepository_WikiCloneLink(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository)
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
cloneLink := repo.WikiCloneLink()
assert.Equal(t, "ssh://sshuser@try.gitea.io:3000/user2/repo1.wiki.git", cloneLink.SSH)
assert.Equal(t, "https://try.gitea.io/user2/repo1.wiki.git", cloneLink.HTTPS)
@@ -32,15 +32,15 @@ func TestWikiPath(t *testing.T) {
func TestRepository_WikiPath(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository)
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
expected := filepath.Join(setting.RepoRootPath, "user2/repo1.wiki.git")
assert.Equal(t, expected, repo.WikiPath())
}
func TestRepository_HasWiki(t *testing.T) {
unittest.PrepareTestEnv(t)
- repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository)
+ repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
assert.True(t, repo1.HasWiki())
- repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}).(*repo_model.Repository)
+ repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2})
assert.False(t, repo2.HasWiki())
}
diff --git a/models/repo_collaboration.go b/models/repo_collaboration.go
index c8866421bd..05df2f29aa 100644
--- a/models/repo_collaboration.go
+++ b/models/repo_collaboration.go
@@ -11,7 +11,6 @@ import (
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
- "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"
@@ -19,42 +18,6 @@ import (
"xorm.io/builder"
)
-func addCollaborator(ctx context.Context, repo *repo_model.Repository, u *user_model.User) error {
- collaboration := &repo_model.Collaboration{
- RepoID: repo.ID,
- UserID: u.ID,
- }
-
- has, err := db.GetByBean(ctx, collaboration)
- if err != nil {
- return err
- } else if has {
- return nil
- }
- collaboration.Mode = perm.AccessModeWrite
-
- if err = db.Insert(ctx, collaboration); err != nil {
- return err
- }
-
- return access_model.RecalculateUserAccess(ctx, repo, u.ID)
-}
-
-// AddCollaborator adds new collaboration to a repository with default access mode.
-func AddCollaborator(repo *repo_model.Repository, u *user_model.User) error {
- ctx, committer, err := db.TxContext()
- if err != nil {
- return err
- }
- defer committer.Close()
-
- if err := addCollaborator(ctx, repo, u); err != nil {
- return err
- }
-
- return committer.Commit()
-}
-
// DeleteCollaboration removes collaboration relation between the user and repository.
func DeleteCollaboration(repo *repo_model.Repository, uid int64) (err error) {
collaboration := &repo_model.Collaboration{
diff --git a/models/repo_collaboration_test.go b/models/repo_collaboration_test.go
index 4cf4d61218..77034b65d2 100644
--- a/models/repo_collaboration_test.go
+++ b/models/repo_collaboration_test.go
@@ -10,30 +10,14 @@ import (
"code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo"
"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}).(*repo_model.Repository)
- assert.NoError(t, repo.GetOwner(db.DefaultContext))
- user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: userID}).(*user_model.User)
- assert.NoError(t, AddCollaborator(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 TestRepository_DeleteCollaboration(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4}).(*repo_model.Repository)
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4})
assert.NoError(t, repo.GetOwner(db.DefaultContext))
assert.NoError(t, DeleteCollaboration(repo, 4))
unittest.AssertNotExistsBean(t, &repo_model.Collaboration{RepoID: repo.ID, UserID: 4})
diff --git a/models/repo_transfer.go b/models/repo_transfer.go
index 7d07fb252c..636d49b989 100644
--- a/models/repo_transfer.go
+++ b/models/repo_transfer.go
@@ -327,8 +327,8 @@ func TransferOwnership(doer *user_model.User, newOwnerName string, repo *repo_mo
}
for _, t := range teams {
if t.IncludesAllRepositories {
- if err := addRepository(ctx, t, repo); err != nil {
- return fmt.Errorf("addRepository: %v", err)
+ if err := AddRepository(ctx, t, repo); err != nil {
+ return fmt.Errorf("AddRepository: %v", err)
}
}
}
diff --git a/models/repo_transfer_test.go b/models/repo_transfer_test.go
index 9125bb8c8d..7904b04e98 100644
--- a/models/repo_transfer_test.go
+++ b/models/repo_transfer_test.go
@@ -17,8 +17,8 @@ import (
func TestRepositoryTransfer(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3}).(*user_model.User)
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3}).(*repo_model.Repository)
+ doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3})
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3})
transfer, err := GetPendingRepositoryTransfer(repo)
assert.NoError(t, err)
@@ -32,7 +32,7 @@ func TestRepositoryTransfer(t *testing.T) {
assert.Nil(t, transfer)
assert.True(t, IsErrNoPendingTransfer(err))
- user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
+ user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
assert.NoError(t, CreatePendingRepositoryTransfer(doer, user2, repo.ID, nil))
@@ -41,7 +41,7 @@ func TestRepositoryTransfer(t *testing.T) {
assert.NoError(t, transfer.LoadAttributes())
assert.Equal(t, "user2", transfer.Recipient.Name)
- user6 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
+ user6 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
// Only transfer can be started at any given time
err = CreatePendingRepositoryTransfer(doer, user6, repo.ID, nil)
diff --git a/models/appstate/appstate.go b/models/system/appstate.go
similarity index 98%
rename from models/appstate/appstate.go
rename to models/system/appstate.go
index aa5a59e1a3..c11a2512ab 100644
--- a/models/appstate/appstate.go
+++ b/models/system/appstate.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
-package appstate
+package system
import (
"context"
diff --git a/models/system/main_test.go b/models/system/main_test.go
new file mode 100644
index 0000000000..a56c76aedc
--- /dev/null
+++ b/models/system/main_test.go
@@ -0,0 +1,21 @@
+// Copyright 2020 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package system_test
+
+import (
+ "path/filepath"
+ "testing"
+
+ "code.gitea.io/gitea/models/unittest"
+
+ _ "code.gitea.io/gitea/models" // register models
+ _ "code.gitea.io/gitea/models/system" // register models of system
+)
+
+func TestMain(m *testing.M) {
+ unittest.MainTest(m, &unittest.TestOptions{
+ GiteaRootPath: filepath.Join("..", ".."),
+ })
+}
diff --git a/models/admin/notice.go b/models/system/notice.go
similarity index 99%
rename from models/admin/notice.go
rename to models/system/notice.go
index 4d385cf951..3276fa3ffb 100644
--- a/models/admin/notice.go
+++ b/models/system/notice.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
-package admin
+package system
import (
"context"
diff --git a/models/system/notice_test.go b/models/system/notice_test.go
new file mode 100644
index 0000000000..768bcca66c
--- /dev/null
+++ b/models/system/notice_test.go
@@ -0,0 +1,117 @@
+// Copyright 2017 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package system_test
+
+import (
+ "testing"
+
+ "code.gitea.io/gitea/models/db"
+ "code.gitea.io/gitea/models/system"
+ "code.gitea.io/gitea/models/unittest"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestNotice_TrStr(t *testing.T) {
+ notice := &system.Notice{
+ Type: system.NoticeRepository,
+ Description: "test description",
+ }
+ assert.Equal(t, "admin.notices.type_1", notice.TrStr())
+}
+
+func TestCreateNotice(t *testing.T) {
+ assert.NoError(t, unittest.PrepareTestDatabase())
+
+ noticeBean := &system.Notice{
+ Type: system.NoticeRepository,
+ Description: "test description",
+ }
+ unittest.AssertNotExistsBean(t, noticeBean)
+ assert.NoError(t, system.CreateNotice(db.DefaultContext, noticeBean.Type, noticeBean.Description))
+ unittest.AssertExistsAndLoadBean(t, noticeBean)
+}
+
+func TestCreateRepositoryNotice(t *testing.T) {
+ assert.NoError(t, unittest.PrepareTestDatabase())
+
+ noticeBean := &system.Notice{
+ Type: system.NoticeRepository,
+ Description: "test description",
+ }
+ unittest.AssertNotExistsBean(t, noticeBean)
+ assert.NoError(t, system.CreateRepositoryNotice(noticeBean.Description))
+ unittest.AssertExistsAndLoadBean(t, noticeBean)
+}
+
+// TODO TestRemoveAllWithNotice
+
+func TestCountNotices(t *testing.T) {
+ assert.NoError(t, unittest.PrepareTestDatabase())
+ assert.Equal(t, int64(3), system.CountNotices())
+}
+
+func TestNotices(t *testing.T) {
+ assert.NoError(t, unittest.PrepareTestDatabase())
+
+ notices, err := system.Notices(1, 2)
+ assert.NoError(t, err)
+ if assert.Len(t, notices, 2) {
+ assert.Equal(t, int64(3), notices[0].ID)
+ assert.Equal(t, int64(2), notices[1].ID)
+ }
+
+ notices, err = system.Notices(2, 2)
+ assert.NoError(t, err)
+ if assert.Len(t, notices, 1) {
+ assert.Equal(t, int64(1), notices[0].ID)
+ }
+}
+
+func TestDeleteNotice(t *testing.T) {
+ assert.NoError(t, unittest.PrepareTestDatabase())
+
+ unittest.AssertExistsAndLoadBean(t, &system.Notice{ID: 3})
+ assert.NoError(t, system.DeleteNotice(3))
+ unittest.AssertNotExistsBean(t, &system.Notice{ID: 3})
+}
+
+func TestDeleteNotices(t *testing.T) {
+ // delete a non-empty range
+ assert.NoError(t, unittest.PrepareTestDatabase())
+
+ unittest.AssertExistsAndLoadBean(t, &system.Notice{ID: 1})
+ unittest.AssertExistsAndLoadBean(t, &system.Notice{ID: 2})
+ unittest.AssertExistsAndLoadBean(t, &system.Notice{ID: 3})
+ assert.NoError(t, system.DeleteNotices(1, 2))
+ unittest.AssertNotExistsBean(t, &system.Notice{ID: 1})
+ unittest.AssertNotExistsBean(t, &system.Notice{ID: 2})
+ unittest.AssertExistsAndLoadBean(t, &system.Notice{ID: 3})
+}
+
+func TestDeleteNotices2(t *testing.T) {
+ // delete an empty range
+ assert.NoError(t, unittest.PrepareTestDatabase())
+
+ unittest.AssertExistsAndLoadBean(t, &system.Notice{ID: 1})
+ unittest.AssertExistsAndLoadBean(t, &system.Notice{ID: 2})
+ unittest.AssertExistsAndLoadBean(t, &system.Notice{ID: 3})
+ assert.NoError(t, system.DeleteNotices(3, 2))
+ unittest.AssertExistsAndLoadBean(t, &system.Notice{ID: 1})
+ unittest.AssertExistsAndLoadBean(t, &system.Notice{ID: 2})
+ unittest.AssertExistsAndLoadBean(t, &system.Notice{ID: 3})
+}
+
+func TestDeleteNoticesByIDs(t *testing.T) {
+ assert.NoError(t, unittest.PrepareTestDatabase())
+
+ unittest.AssertExistsAndLoadBean(t, &system.Notice{ID: 1})
+ unittest.AssertExistsAndLoadBean(t, &system.Notice{ID: 2})
+ unittest.AssertExistsAndLoadBean(t, &system.Notice{ID: 3})
+ assert.NoError(t, system.DeleteNoticesByIDs([]int64{1, 3}))
+ unittest.AssertNotExistsBean(t, &system.Notice{ID: 1})
+ unittest.AssertExistsAndLoadBean(t, &system.Notice{ID: 2})
+ unittest.AssertNotExistsBean(t, &system.Notice{ID: 3})
+}
diff --git a/models/system/setting.go b/models/system/setting.go
new file mode 100644
index 0000000000..ff8b48e618
--- /dev/null
+++ b/models/system/setting.go
@@ -0,0 +1,261 @@
+// Copyright 2021 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package system
+
+import (
+ "context"
+ "fmt"
+ "net/url"
+ "strconv"
+ "strings"
+
+ "code.gitea.io/gitea/models/db"
+ "code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/timeutil"
+
+ "strk.kbt.io/projects/go/libravatar"
+ "xorm.io/builder"
+)
+
+// Setting is a key value store of user settings
+type Setting struct {
+ ID int64 `xorm:"pk autoincr"`
+ SettingKey string `xorm:"varchar(255) unique"` // ensure key is always lowercase
+ SettingValue string `xorm:"text"`
+ Version int `xorm:"version"` // prevent to override
+ Created timeutil.TimeStamp `xorm:"created"`
+ Updated timeutil.TimeStamp `xorm:"updated"`
+}
+
+// TableName sets the table name for the settings struct
+func (s *Setting) TableName() string {
+ return "system_setting"
+}
+
+func (s *Setting) GetValueBool() bool {
+ b, _ := strconv.ParseBool(s.SettingValue)
+ return b
+}
+
+func init() {
+ db.RegisterModel(new(Setting))
+}
+
+// ErrSettingIsNotExist represents an error that a setting is not exist with special key
+type ErrSettingIsNotExist struct {
+ Key string
+}
+
+// Error implements error
+func (err ErrSettingIsNotExist) Error() string {
+ return fmt.Sprintf("System setting[%s] is not exist", err.Key)
+}
+
+// IsErrSettingIsNotExist return true if err is ErrSettingIsNotExist
+func IsErrSettingIsNotExist(err error) bool {
+ _, ok := err.(ErrSettingIsNotExist)
+ return ok
+}
+
+// ErrDataExpired represents an error that update a record which has been updated by another thread
+type ErrDataExpired struct {
+ Key string
+}
+
+// Error implements error
+func (err ErrDataExpired) Error() string {
+ return fmt.Sprintf("System setting[%s] has been updated by another thread", err.Key)
+}
+
+// IsErrDataExpired return true if err is ErrDataExpired
+func IsErrDataExpired(err error) bool {
+ _, ok := err.(ErrDataExpired)
+ return ok
+}
+
+// GetSetting returns specific setting
+func GetSetting(key string) (*Setting, error) {
+ v, err := GetSettings([]string{key})
+ if err != nil {
+ return nil, err
+ }
+ if len(v) == 0 {
+ return nil, ErrSettingIsNotExist{key}
+ }
+ return v[key], nil
+}
+
+// GetSettings returns specific settings
+func GetSettings(keys []string) (map[string]*Setting, error) {
+ for i := 0; i < len(keys); i++ {
+ keys[i] = strings.ToLower(keys[i])
+ }
+ settings := make([]*Setting, 0, len(keys))
+ if err := db.GetEngine(db.DefaultContext).
+ Where(builder.In("setting_key", keys)).
+ Find(&settings); err != nil {
+ return nil, err
+ }
+ settingsMap := make(map[string]*Setting)
+ for _, s := range settings {
+ settingsMap[s.SettingKey] = s
+ }
+ return settingsMap, nil
+}
+
+type AllSettings map[string]*Setting
+
+func (settings AllSettings) Get(key string) Setting {
+ if v, ok := settings[key]; ok {
+ return *v
+ }
+ return Setting{}
+}
+
+func (settings AllSettings) GetBool(key string) bool {
+ b, _ := strconv.ParseBool(settings.Get(key).SettingValue)
+ return b
+}
+
+func (settings AllSettings) GetVersion(key string) int {
+ return settings.Get(key).Version
+}
+
+// GetAllSettings returns all settings from user
+func GetAllSettings() (AllSettings, error) {
+ settings := make([]*Setting, 0, 5)
+ if err := db.GetEngine(db.DefaultContext).
+ Find(&settings); err != nil {
+ return nil, err
+ }
+ settingsMap := make(map[string]*Setting)
+ for _, s := range settings {
+ settingsMap[s.SettingKey] = s
+ }
+ return settingsMap, nil
+}
+
+// DeleteSetting deletes a specific setting for a user
+func DeleteSetting(setting *Setting) error {
+ _, err := db.GetEngine(db.DefaultContext).Delete(setting)
+ return err
+}
+
+func SetSettingNoVersion(key, value string) error {
+ s, err := GetSetting(key)
+ if IsErrSettingIsNotExist(err) {
+ return SetSetting(&Setting{
+ SettingKey: key,
+ SettingValue: value,
+ })
+ }
+ if err != nil {
+ return err
+ }
+ s.SettingValue = value
+ return SetSetting(s)
+}
+
+// SetSetting updates a users' setting for a specific key
+func SetSetting(setting *Setting) error {
+ if err := upsertSettingValue(strings.ToLower(setting.SettingKey), setting.SettingValue, setting.Version); err != nil {
+ return err
+ }
+ setting.Version++
+ return nil
+}
+
+func upsertSettingValue(key, value string, version int) error {
+ return db.WithTx(func(ctx context.Context) error {
+ e := db.GetEngine(ctx)
+
+ // here we use a general method to do a safe upsert for different databases (and most transaction levels)
+ // 1. try to UPDATE the record and acquire the transaction write lock
+ // if UPDATE returns non-zero rows are changed, OK, the setting is saved correctly
+ // if UPDATE returns "0 rows changed", two possibilities: (a) record doesn't exist (b) value is not changed
+ // 2. do a SELECT to check if the row exists or not (we already have the transaction lock)
+ // 3. if the row doesn't exist, do an INSERT (we are still protected by the transaction lock, so it's safe)
+ //
+ // to optimize the SELECT in step 2, we can use an extra column like `revision=revision+1`
+ // to make sure the UPDATE always returns a non-zero value for existing (unchanged) records.
+
+ res, err := e.Exec("UPDATE system_setting SET setting_value=?, version = version+1 WHERE setting_key=? AND version=?", value, key, version)
+ if err != nil {
+ return err
+ }
+ rows, _ := res.RowsAffected()
+ if rows > 0 {
+ // the existing row is updated, so we can return
+ return nil
+ }
+
+ // in case the value isn't changed, update would return 0 rows changed, so we need this check
+ has, err := e.Exist(&Setting{SettingKey: key})
+ if err != nil {
+ return err
+ }
+ if has {
+ return ErrDataExpired{Key: key}
+ }
+
+ // if no existing row, insert a new row
+ _, err = e.Insert(&Setting{SettingKey: key, SettingValue: value})
+ return err
+ })
+}
+
+var (
+ GravatarSourceURL *url.URL
+ LibravatarService *libravatar.Libravatar
+)
+
+func Init() error {
+ var disableGravatar bool
+ disableGravatarSetting, err := GetSetting(KeyPictureDisableGravatar)
+ if IsErrSettingIsNotExist(err) {
+ disableGravatar = setting.GetDefaultDisableGravatar()
+ disableGravatarSetting = &Setting{SettingValue: strconv.FormatBool(disableGravatar)}
+ } else if err != nil {
+ return err
+ } else {
+ disableGravatar = disableGravatarSetting.GetValueBool()
+ }
+
+ var enableFederatedAvatar bool
+ enableFederatedAvatarSetting, err := GetSetting(KeyPictureEnableFederatedAvatar)
+ if IsErrSettingIsNotExist(err) {
+ enableFederatedAvatar = setting.GetDefaultEnableFederatedAvatar(disableGravatar)
+ enableFederatedAvatarSetting = &Setting{SettingValue: strconv.FormatBool(enableFederatedAvatar)}
+ } else if err != nil {
+ return err
+ } else {
+ enableFederatedAvatar = disableGravatarSetting.GetValueBool()
+ }
+
+ if setting.OfflineMode {
+ disableGravatar = true
+ enableFederatedAvatar = false
+ }
+
+ if disableGravatar || !enableFederatedAvatar {
+ var err error
+ GravatarSourceURL, err = url.Parse(setting.GravatarSource)
+ if err != nil {
+ return fmt.Errorf("Failed to parse Gravatar URL(%s): %v", setting.GravatarSource, err)
+ }
+ }
+
+ if enableFederatedAvatarSetting.GetValueBool() {
+ LibravatarService = libravatar.New()
+ if GravatarSourceURL.Scheme == "https" {
+ LibravatarService.SetUseHTTPS(true)
+ LibravatarService.SetSecureFallbackHost(GravatarSourceURL.Host)
+ } else {
+ LibravatarService.SetUseHTTPS(false)
+ LibravatarService.SetFallbackHost(GravatarSourceURL.Host)
+ }
+ }
+ return nil
+}
diff --git a/models/system/setting_key.go b/models/system/setting_key.go
new file mode 100644
index 0000000000..5a6ea6ed72
--- /dev/null
+++ b/models/system/setting_key.go
@@ -0,0 +1,11 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package system
+
+// enumerate all system setting keys
+const (
+ KeyPictureDisableGravatar = "picture.disable_gravatar"
+ KeyPictureEnableFederatedAvatar = "picture.enable_federated_avatar"
+)
diff --git a/models/system/setting_test.go b/models/system/setting_test.go
new file mode 100644
index 0000000000..d25fc05f31
--- /dev/null
+++ b/models/system/setting_test.go
@@ -0,0 +1,53 @@
+// Copyright 2021 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package system_test
+
+import (
+ "strings"
+ "testing"
+
+ "code.gitea.io/gitea/models/system"
+ "code.gitea.io/gitea/models/unittest"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestSettings(t *testing.T) {
+ keyName := "server.LFS_LOCKS_PAGING_NUM"
+ assert.NoError(t, unittest.PrepareTestDatabase())
+
+ newSetting := &system.Setting{SettingKey: keyName, SettingValue: "50"}
+
+ // create setting
+ err := system.SetSetting(newSetting)
+ assert.NoError(t, err)
+ // test about saving unchanged values
+ err = system.SetSetting(newSetting)
+ assert.NoError(t, err)
+
+ // get specific setting
+ settings, err := system.GetSettings([]string{keyName})
+ assert.NoError(t, err)
+ assert.Len(t, settings, 1)
+ assert.EqualValues(t, newSetting.SettingValue, settings[strings.ToLower(keyName)].SettingValue)
+
+ // updated setting
+ updatedSetting := &system.Setting{SettingKey: keyName, SettingValue: "100", Version: newSetting.Version}
+ err = system.SetSetting(updatedSetting)
+ assert.NoError(t, err)
+
+ // get all settings
+ settings, err = system.GetAllSettings()
+ assert.NoError(t, err)
+ assert.Len(t, settings, 3)
+ assert.EqualValues(t, updatedSetting.SettingValue, settings[strings.ToLower(updatedSetting.SettingKey)].SettingValue)
+
+ // delete setting
+ err = system.DeleteSetting(&system.Setting{SettingKey: strings.ToLower(keyName)})
+ assert.NoError(t, err)
+ settings, err = system.GetAllSettings()
+ assert.NoError(t, err)
+ assert.Len(t, settings, 2)
+}
diff --git a/models/unittest/testdb.go b/models/unittest/testdb.go
index 7f9af55374..2e6c25ae48 100644
--- a/models/unittest/testdb.go
+++ b/models/unittest/testdb.go
@@ -7,12 +7,12 @@ package unittest
import (
"context"
"fmt"
- "net/url"
"os"
"path/filepath"
"testing"
"code.gitea.io/gitea/models/db"
+ system_model "code.gitea.io/gitea/models/system"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/setting"
@@ -91,10 +91,8 @@ func MainTest(m *testing.M, testOpts *TestOptions) {
setting.AppDataPath = appDataPath
setting.AppWorkPath = testOpts.GiteaRootPath
setting.StaticRootPath = testOpts.GiteaRootPath
- setting.GravatarSourceURL, err = url.Parse("https://secure.gravatar.com/avatar/")
- if err != nil {
- fatalTestError("url.Parse: %v\n", err)
- }
+ setting.GravatarSource = "https://secure.gravatar.com/avatar/"
+
setting.Attachment.Storage.Path = filepath.Join(setting.AppDataPath, "attachments")
setting.LFS.Storage.Path = filepath.Join(setting.AppDataPath, "lfs")
@@ -112,19 +110,20 @@ func MainTest(m *testing.M, testOpts *TestOptions) {
if err = storage.Init(); err != nil {
fatalTestError("storage.Init: %v\n", err)
}
+ if err = system_model.Init(); err != nil {
+ fatalTestError("models.Init: %v\n", err)
+ }
if err = util.RemoveAll(repoRootPath); err != nil {
fatalTestError("util.RemoveAll: %v\n", err)
}
- if err = CopyDir(filepath.Join(testOpts.GiteaRootPath, "integrations", "gitea-repositories-meta"), setting.RepoRootPath); err != nil {
+ if err = CopyDir(filepath.Join(testOpts.GiteaRootPath, "tests", "gitea-repositories-meta"), setting.RepoRootPath); err != nil {
fatalTestError("util.CopyDir: %v\n", err)
}
- if err = git.InitOnceWithSync(context.Background()); err != nil {
+ if err = git.InitFull(context.Background()); err != nil {
fatalTestError("git.Init: %v\n", err)
}
- git.CheckLFSVersion()
-
ownerDirs, err := os.ReadDir(setting.RepoRootPath)
if err != nil {
fatalTestError("unable to read the new repo root: %v\n", err)
@@ -204,10 +203,8 @@ func PrepareTestDatabase() error {
func PrepareTestEnv(t testing.TB) {
assert.NoError(t, PrepareTestDatabase())
assert.NoError(t, util.RemoveAll(setting.RepoRootPath))
- metaPath := filepath.Join(giteaRoot, "integrations", "gitea-repositories-meta")
+ metaPath := filepath.Join(giteaRoot, "tests", "gitea-repositories-meta")
assert.NoError(t, CopyDir(metaPath, setting.RepoRootPath))
- assert.NoError(t, git.InitOnceWithSync(context.Background())) // the gitconfig has been removed above, so sync the gitconfig again
-
ownerDirs, err := os.ReadDir(setting.RepoRootPath)
assert.NoError(t, err)
for _, ownerDir := range ownerDirs {
diff --git a/models/unittest/unit_tests.go b/models/unittest/unit_tests.go
index 6c20c2781b..c8673debed 100644
--- a/models/unittest/unit_tests.go
+++ b/models/unittest/unit_tests.go
@@ -55,7 +55,7 @@ func BeanExists(t assert.TestingT, bean interface{}, conditions ...interface{})
}
// AssertExistsAndLoadBean assert that a bean exists and load it from the test database
-func AssertExistsAndLoadBean(t assert.TestingT, bean interface{}, conditions ...interface{}) interface{} {
+func AssertExistsAndLoadBean[T any](t assert.TestingT, bean T, conditions ...interface{}) T {
exists, err := LoadBeanIfExists(bean, conditions...)
assert.NoError(t, err)
assert.True(t, exists,
diff --git a/models/user.go b/models/user.go
index 86a714e746..68be0d8555 100644
--- a/models/user.go
+++ b/models/user.go
@@ -12,6 +12,7 @@ import (
_ "image/jpeg" // Needed for jpeg support
+ activities_model "code.gitea.io/gitea/models/activities"
asymkey_model "code.gitea.io/gitea/models/asymkey"
auth_model "code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/models/db"
@@ -70,14 +71,14 @@ func DeleteUser(ctx context.Context, u *user_model.User, purge bool) (err error)
// ***** END: Follow *****
if err = db.DeleteBeans(ctx,
- &AccessToken{UID: u.ID},
+ &auth_model.AccessToken{UID: u.ID},
&repo_model.Collaboration{UserID: u.ID},
&access_model.Access{UserID: u.ID},
&repo_model.Watch{UserID: u.ID},
&repo_model.Star{UID: u.ID},
&user_model.Follow{UserID: u.ID},
&user_model.Follow{FollowID: u.ID},
- &Action{UserID: u.ID},
+ &activities_model.Action{UserID: u.ID},
&issues_model.IssueUser{UID: u.ID},
&user_model.EmailAddress{UID: u.ID},
&user_model.UserOpenID{UID: u.ID},
@@ -85,6 +86,7 @@ func DeleteUser(ctx context.Context, u *user_model.User, purge bool) (err error)
&organization.TeamUser{UID: u.ID},
&issues_model.Stopwatch{UserID: u.ID},
&user_model.Setting{UserID: u.ID},
+ &user_model.UserBadge{UserID: u.ID},
&pull_model.AutoMerge{DoerID: u.ID},
&pull_model.ReviewState{UserID: u.ID},
); err != nil {
@@ -100,9 +102,9 @@ func DeleteUser(ctx context.Context, u *user_model.User, purge bool) (err error)
// Delete Comments
const batchSize = 50
- for start := 0; ; start += batchSize {
+ for {
comments := make([]*issues_model.Comment, 0, batchSize)
- if err = e.Where("type=? AND poster_id=?", issues_model.CommentTypeComment, u.ID).Limit(batchSize, start).Find(&comments); err != nil {
+ if err = e.Where("type=? AND poster_id=?", issues_model.CommentTypeComment, u.ID).Limit(batchSize, 0).Find(&comments); err != nil {
return err
}
if len(comments) == 0 {
@@ -200,7 +202,7 @@ func DeleteUser(ctx context.Context, u *user_model.User, purge bool) (err error)
// ***** END: ExternalLoginUser *****
if _, err = e.ID(u.ID).Delete(new(user_model.User)); err != nil {
- return fmt.Errorf("Delete: %v", err)
+ return fmt.Errorf("delete: %v", err)
}
return nil
diff --git a/models/user/avatar.go b/models/user/avatar.go
index 6a44a3bcb3..1c75c7406b 100644
--- a/models/user/avatar.go
+++ b/models/user/avatar.go
@@ -14,6 +14,7 @@ import (
"code.gitea.io/gitea/models/avatars"
"code.gitea.io/gitea/models/db"
+ system_model "code.gitea.io/gitea/models/system"
"code.gitea.io/gitea/modules/avatar"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
@@ -67,10 +68,16 @@ func (u *User) AvatarLinkWithSize(size int) string {
useLocalAvatar := false
autoGenerateAvatar := false
+ var disableGravatar bool
+ disableGravatarSetting, _ := system_model.GetSetting(system_model.KeyPictureDisableGravatar)
+ if disableGravatarSetting != nil {
+ disableGravatar = disableGravatarSetting.GetValueBool()
+ }
+
switch {
case u.UseCustomAvatar:
useLocalAvatar = true
- case setting.DisableGravatar, setting.OfflineMode:
+ case disableGravatar, setting.OfflineMode:
useLocalAvatar = true
autoGenerateAvatar = true
}
diff --git a/models/user/badge.go b/models/user/badge.go
new file mode 100644
index 0000000000..5ff840cb8c
--- /dev/null
+++ b/models/user/badge.go
@@ -0,0 +1,42 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package user
+
+import (
+ "context"
+
+ "code.gitea.io/gitea/models/db"
+)
+
+// Badge represents a user badge
+type Badge struct {
+ ID int64 `xorm:"pk autoincr"`
+ Description string
+ ImageURL string
+}
+
+// UserBadge represents a user badge
+type UserBadge struct {
+ ID int64 `xorm:"pk autoincr"`
+ BadgeID int64
+ UserID int64 `xorm:"INDEX"`
+}
+
+func init() {
+ db.RegisterModel(new(Badge))
+ db.RegisterModel(new(UserBadge))
+}
+
+// GetUserBadges returns the user's badges.
+func GetUserBadges(ctx context.Context, u *User) ([]*Badge, int64, error) {
+ sess := db.GetEngine(ctx).
+ Select("`badge`.*").
+ Join("INNER", "user_badge", "`user_badge`.badge_id=badge.id").
+ Where("user_badge.user_id=?", u.ID)
+
+ badges := make([]*Badge, 0, 8)
+ count, err := sess.FindAndCount(&badges)
+ return badges, count, err
+}
diff --git a/models/user/email_address.go b/models/user/email_address.go
index c931db9c16..964e7ae08c 100644
--- a/models/user/email_address.go
+++ b/models/user/email_address.go
@@ -40,7 +40,12 @@ func (err ErrEmailCharIsNotSupported) Error() string {
return fmt.Sprintf("e-mail address contains unsupported character [email: %s]", err.Email)
}
+func (err ErrEmailCharIsNotSupported) Unwrap() error {
+ return util.ErrInvalidArgument
+}
+
// ErrEmailInvalid represents an error where the email address does not comply with RFC 5322
+// or has a leading '-' character
type ErrEmailInvalid struct {
Email string
}
@@ -55,6 +60,10 @@ func (err ErrEmailInvalid) Error() string {
return fmt.Sprintf("e-mail invalid [email: %s]", err.Email)
}
+func (err ErrEmailInvalid) Unwrap() error {
+ return util.ErrInvalidArgument
+}
+
// ErrEmailAlreadyUsed represents a "EmailAlreadyUsed" kind of error.
type ErrEmailAlreadyUsed struct {
Email string
@@ -70,6 +79,10 @@ func (err ErrEmailAlreadyUsed) Error() string {
return fmt.Sprintf("e-mail already in use [email: %s]", err.Email)
}
+func (err ErrEmailAlreadyUsed) Unwrap() error {
+ return util.ErrAlreadyExist
+}
+
// ErrEmailAddressNotExist email address not exist
type ErrEmailAddressNotExist struct {
Email string
@@ -85,6 +98,10 @@ func (err ErrEmailAddressNotExist) Error() string {
return fmt.Sprintf("Email address does not exist [email: %s]", err.Email)
}
+func (err ErrEmailAddressNotExist) Unwrap() error {
+ return util.ErrNotExist
+}
+
// ErrPrimaryEmailCannotDelete primary email address cannot be deleted
type ErrPrimaryEmailCannotDelete struct {
Email string
@@ -100,6 +117,10 @@ func (err ErrPrimaryEmailCannotDelete) Error() string {
return fmt.Sprintf("Primary email address cannot be deleted [email: %s]", err.Email)
}
+func (err ErrPrimaryEmailCannotDelete) Unwrap() error {
+ return util.ErrInvalidArgument
+}
+
// EmailAddress is the list of all email addresses of a user. It also contains the
// primary email address which is saved in user table.
type EmailAddress struct {
@@ -134,9 +155,7 @@ func ValidateEmail(email string) error {
return ErrEmailCharIsNotSupported{email}
}
- if !(email[0] >= 'a' && email[0] <= 'z') &&
- !(email[0] >= 'A' && email[0] <= 'Z') &&
- !(email[0] >= '0' && email[0] <= '9') {
+ if email[0] == '-' {
return ErrEmailInvalid{email}
}
diff --git a/models/user/email_address_test.go b/models/user/email_address_test.go
index 471598c897..b9acaa1113 100644
--- a/models/user/email_address_test.go
+++ b/models/user/email_address_test.go
@@ -281,23 +281,25 @@ func TestEmailAddressValidate(t *testing.T) {
`first~last@iana.org`: nil,
`first;last@iana.org`: user_model.ErrEmailCharIsNotSupported{`first;last@iana.org`},
".233@qq.com": user_model.ErrEmailInvalid{".233@qq.com"},
- "!233@qq.com": user_model.ErrEmailInvalid{"!233@qq.com"},
- "#233@qq.com": user_model.ErrEmailInvalid{"#233@qq.com"},
- "$233@qq.com": user_model.ErrEmailInvalid{"$233@qq.com"},
- "%233@qq.com": user_model.ErrEmailInvalid{"%233@qq.com"},
- "&233@qq.com": user_model.ErrEmailInvalid{"&233@qq.com"},
- "'233@qq.com": user_model.ErrEmailInvalid{"'233@qq.com"},
- "*233@qq.com": user_model.ErrEmailInvalid{"*233@qq.com"},
- "+233@qq.com": user_model.ErrEmailInvalid{"+233@qq.com"},
- "/233@qq.com": user_model.ErrEmailInvalid{"/233@qq.com"},
- "=233@qq.com": user_model.ErrEmailInvalid{"=233@qq.com"},
- "?233@qq.com": user_model.ErrEmailInvalid{"?233@qq.com"},
- "^233@qq.com": user_model.ErrEmailInvalid{"^233@qq.com"},
- "`233@qq.com": user_model.ErrEmailInvalid{"`233@qq.com"},
- "{233@qq.com": user_model.ErrEmailInvalid{"{233@qq.com"},
- "|233@qq.com": user_model.ErrEmailInvalid{"|233@qq.com"},
- "}233@qq.com": user_model.ErrEmailInvalid{"}233@qq.com"},
- "~233@qq.com": user_model.ErrEmailInvalid{"~233@qq.com"},
+ "!233@qq.com": nil,
+ "#233@qq.com": nil,
+ "$233@qq.com": nil,
+ "%233@qq.com": nil,
+ "&233@qq.com": nil,
+ "'233@qq.com": nil,
+ "*233@qq.com": nil,
+ "+233@qq.com": nil,
+ "-233@qq.com": user_model.ErrEmailInvalid{"-233@qq.com"},
+ "/233@qq.com": nil,
+ "=233@qq.com": nil,
+ "?233@qq.com": nil,
+ "^233@qq.com": nil,
+ "_233@qq.com": nil,
+ "`233@qq.com": nil,
+ "{233@qq.com": nil,
+ "|233@qq.com": nil,
+ "}233@qq.com": nil,
+ "~233@qq.com": nil,
";233@qq.com": user_model.ErrEmailCharIsNotSupported{";233@qq.com"},
"Foo ": user_model.ErrEmailCharIsNotSupported{"Foo "},
string([]byte{0xE2, 0x84, 0xAA}): user_model.ErrEmailCharIsNotSupported{string([]byte{0xE2, 0x84, 0xAA})},
diff --git a/models/user/error.go b/models/user/error.go
index 25e0d8ea8a..3fe4ee6657 100644
--- a/models/user/error.go
+++ b/models/user/error.go
@@ -6,6 +6,8 @@ package user
import (
"fmt"
+
+ "code.gitea.io/gitea/modules/util"
)
// ____ ___
@@ -30,6 +32,11 @@ func (err ErrUserAlreadyExist) Error() string {
return fmt.Sprintf("user already exists [name: %s]", err.Name)
}
+// Unwrap unwraps this error as a ErrExist error
+func (err ErrUserAlreadyExist) Unwrap() error {
+ return util.ErrAlreadyExist
+}
+
// ErrUserNotExist represents a "UserNotExist" kind of error.
type ErrUserNotExist struct {
UID int64
@@ -47,6 +54,11 @@ func (err ErrUserNotExist) Error() string {
return fmt.Sprintf("user does not exist [uid: %d, name: %s, keyid: %d]", err.UID, err.Name, err.KeyID)
}
+// Unwrap unwraps this error as a ErrNotExist error
+func (err ErrUserNotExist) Unwrap() error {
+ return util.ErrNotExist
+}
+
// ErrUserProhibitLogin represents a "ErrUserProhibitLogin" kind of error.
type ErrUserProhibitLogin struct {
UID int64
@@ -63,6 +75,11 @@ func (err ErrUserProhibitLogin) Error() string {
return fmt.Sprintf("user is not allowed login [uid: %d, name: %s]", err.UID, err.Name)
}
+// Unwrap unwraps this error as a ErrPermission error
+func (err ErrUserProhibitLogin) Unwrap() error {
+ return util.ErrPermissionDenied
+}
+
// ErrUserInactive represents a "ErrUserInactive" kind of error.
type ErrUserInactive struct {
UID int64
@@ -78,3 +95,8 @@ func IsErrUserInactive(err error) bool {
func (err ErrUserInactive) Error() string {
return fmt.Sprintf("user is inactive [uid: %d, name: %s]", err.UID, err.Name)
}
+
+// Unwrap unwraps this error as a ErrPermission error
+func (err ErrUserInactive) Unwrap() error {
+ return util.ErrPermissionDenied
+}
diff --git a/models/user/external_login_user.go b/models/user/external_login_user.go
index 422823b89c..496717c57b 100644
--- a/models/user/external_login_user.go
+++ b/models/user/external_login_user.go
@@ -10,6 +10,7 @@ import (
"time"
"code.gitea.io/gitea/models/db"
+ "code.gitea.io/gitea/modules/util"
"xorm.io/builder"
)
@@ -31,6 +32,10 @@ func (err ErrExternalLoginUserAlreadyExist) Error() string {
return fmt.Sprintf("external login user already exists [externalID: %s, userID: %d, loginSourceID: %d]", err.ExternalID, err.UserID, err.LoginSourceID)
}
+func (err ErrExternalLoginUserAlreadyExist) Unwrap() error {
+ return util.ErrAlreadyExist
+}
+
// ErrExternalLoginUserNotExist represents a "ExternalLoginUserNotExist" kind of error.
type ErrExternalLoginUserNotExist struct {
UserID int64
@@ -47,6 +52,10 @@ func (err ErrExternalLoginUserNotExist) Error() string {
return fmt.Sprintf("external login user link does not exists [userID: %d, loginSourceID: %d]", err.UserID, err.LoginSourceID)
}
+func (err ErrExternalLoginUserNotExist) Unwrap() error {
+ return util.ErrNotExist
+}
+
// ExternalLoginUser makes the connecting between some existing user and additional external login sources
type ExternalLoginUser struct {
ExternalID string `xorm:"pk NOT NULL"`
diff --git a/models/user/openid.go b/models/user/openid.go
index 8ef0ce5ed7..f8e8a787e6 100644
--- a/models/user/openid.go
+++ b/models/user/openid.go
@@ -10,6 +10,7 @@ import (
"fmt"
"code.gitea.io/gitea/models/db"
+ "code.gitea.io/gitea/modules/util"
)
// ErrOpenIDNotExist openid is not known
@@ -65,6 +66,10 @@ func (err ErrOpenIDAlreadyUsed) Error() string {
return fmt.Sprintf("OpenID already in use [oid: %s]", err.OpenID)
}
+func (err ErrOpenIDAlreadyUsed) Unwrap() error {
+ return util.ErrAlreadyExist
+}
+
// AddUserOpenID adds an pre-verified/normalized OpenID URI to given user.
// NOTE: make sure openid.URI is normalized already
func AddUserOpenID(ctx context.Context, openid *UserOpenID) error {
diff --git a/models/user/redirect.go b/models/user/redirect.go
index 49370218db..af8d6439ad 100644
--- a/models/user/redirect.go
+++ b/models/user/redirect.go
@@ -10,6 +10,7 @@ import (
"strings"
"code.gitea.io/gitea/models/db"
+ "code.gitea.io/gitea/modules/util"
)
// ErrUserRedirectNotExist represents a "UserRedirectNotExist" kind of error.
@@ -27,6 +28,10 @@ func (err ErrUserRedirectNotExist) Error() string {
return fmt.Sprintf("user redirect does not exist [name: %s]", err.Name)
}
+func (err ErrUserRedirectNotExist) Unwrap() error {
+ return util.ErrNotExist
+}
+
// Redirect represents that a user name should be redirected to another
type Redirect struct {
ID int64 `xorm:"pk autoincr"`
diff --git a/models/user/search.go b/models/user/search.go
index 76ff55ea26..0aa9949367 100644
--- a/models/user/search.go
+++ b/models/user/search.go
@@ -9,7 +9,6 @@ import (
"strings"
"code.gitea.io/gitea/models/db"
- "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
@@ -58,24 +57,7 @@ func (opts *SearchUserOptions) toSearchQueryBase() *xorm.Session {
cond = cond.And(builder.In("visibility", opts.Visible))
}
- if opts.Actor != nil {
- // If Admin - they see all users!
- if !opts.Actor.IsAdmin {
- // Users can see an organization they are a member of
- accessCond := builder.In("id", builder.Select("org_id").From("org_user").Where(builder.Eq{"uid": opts.Actor.ID}))
- if !opts.Actor.IsRestricted {
- // Not-Restricted users can see public and limited users/organizations
- accessCond = accessCond.Or(builder.In("visibility", structs.VisibleTypePublic, structs.VisibleTypeLimited))
- }
- // Don't forget about self
- accessCond = accessCond.Or(builder.Eq{"id": opts.Actor.ID})
- cond = cond.And(accessCond)
- }
- } else {
- // Force visibility for privacy
- // Not logged in - only public users
- cond = cond.And(builder.In("visibility", structs.VisibleTypePublic))
- }
+ cond = cond.And(BuildCanSeeUserCondition(opts.Actor))
if opts.UID > 0 {
cond = cond.And(builder.Eq{"id": opts.UID})
@@ -142,24 +124,25 @@ func SearchUsers(opts *SearchUserOptions) (users []*User, _ int64, _ error) {
return users, count, sessQuery.Find(&users)
}
-// IterateUser iterate users
-func IterateUser(f func(user *User) error) error {
- var start int
- batchSize := setting.Database.IterateBufferSize
- for {
- users := make([]*User, 0, batchSize)
- if err := db.GetEngine(db.DefaultContext).Limit(batchSize, start).Find(&users); err != nil {
- return err
- }
- if len(users) == 0 {
- return nil
- }
- start += len(users)
-
- for _, user := range users {
- if err := f(user); err != nil {
- return err
+// BuildCanSeeUserCondition creates a condition which can be used to restrict results to users/orgs the actor can see
+func BuildCanSeeUserCondition(actor *User) builder.Cond {
+ if actor != nil {
+ // If Admin - they see all users!
+ if !actor.IsAdmin {
+ // Users can see an organization they are a member of
+ cond := builder.In("`user`.id", builder.Select("org_id").From("org_user").Where(builder.Eq{"uid": actor.ID}))
+ if !actor.IsRestricted {
+ // Not-Restricted users can see public and limited users/organizations
+ cond = cond.Or(builder.In("`user`.visibility", structs.VisibleTypePublic, structs.VisibleTypeLimited))
}
+ // Don't forget about self
+ return cond.Or(builder.Eq{"`user`.id": actor.ID})
}
+
+ return nil
}
+
+ // Force visibility for privacy
+ // Not logged in - only public users
+ return builder.In("`user`.visibility", structs.VisibleTypePublic)
}
diff --git a/models/user/setting.go b/models/user/setting.go
index fbb6fbab30..5fe7c2ec23 100644
--- a/models/user/setting.go
+++ b/models/user/setting.go
@@ -31,6 +31,34 @@ func init() {
db.RegisterModel(new(Setting))
}
+// ErrUserSettingIsNotExist represents an error that a setting is not exist with special key
+type ErrUserSettingIsNotExist struct {
+ Key string
+}
+
+// Error implements error
+func (err ErrUserSettingIsNotExist) Error() string {
+ return fmt.Sprintf("Setting[%s] is not exist", err.Key)
+}
+
+// IsErrUserSettingIsNotExist return true if err is ErrSettingIsNotExist
+func IsErrUserSettingIsNotExist(err error) bool {
+ _, ok := err.(ErrUserSettingIsNotExist)
+ return ok
+}
+
+// GetSetting returns specific setting
+func GetSetting(uid int64, key string) (*Setting, error) {
+ v, err := GetUserSettings(uid, []string{key})
+ if err != nil {
+ return nil, err
+ }
+ if len(v) == 0 {
+ return nil, ErrUserSettingIsNotExist{key}
+ }
+ return v[key], nil
+}
+
// GetUserSettings returns specific settings from user
func GetUserSettings(uid int64, keys []string) (map[string]*Setting, error) {
settings := make([]*Setting, 0, len(keys))
diff --git a/models/user/user.go b/models/user/user.go
index fbd8df9472..a3c10c2492 100644
--- a/models/user/user.go
+++ b/models/user/user.go
@@ -64,12 +64,14 @@ var AvailableHashAlgorithms = []string{
}
const (
- // EmailNotificationsEnabled indicates that the user would like to receive all email notifications
+ // EmailNotificationsEnabled indicates that the user would like to receive all email notifications except your own
EmailNotificationsEnabled = "enabled"
// EmailNotificationsOnMention indicates that the user would like to be notified via email when mentioned.
EmailNotificationsOnMention = "onmention"
// EmailNotificationsDisabled indicates that the user would not like to be notified via email.
EmailNotificationsDisabled = "disabled"
+ // EmailNotificationsEnabled indicates that the user would like to receive all email notifications and your own
+ EmailNotificationsAndYourOwn = "andyourown"
)
// User represents the object of individual and member of organization.
@@ -891,14 +893,19 @@ func UpdateUser(ctx context.Context, u *User, changePrimaryEmail bool, cols ...s
if err != nil {
return err
}
- if !has {
- // 1. Update old primary email
- if _, err = e.Where("uid=? AND is_primary=?", u.ID, true).Cols("is_primary").Update(&EmailAddress{
- IsPrimary: false,
- }); err != nil {
- return err
+ if has && emailAddress.UID != u.ID {
+ return ErrEmailAlreadyUsed{
+ Email: u.Email,
}
+ }
+ // 1. Update old primary email
+ if _, err = e.Where("uid=? AND is_primary=?", u.ID, true).Cols("is_primary").Update(&EmailAddress{
+ IsPrimary: false,
+ }); err != nil {
+ return err
+ }
+ if !has {
emailAddress.Email = u.Email
emailAddress.UID = u.ID
emailAddress.IsActivated = true
@@ -1045,7 +1052,7 @@ func GetMaileableUsersByIDs(ids []int64, isMention bool) ([]*User, error) {
Where("`type` = ?", UserTypeIndividual).
And("`prohibit_login` = ?", false).
And("`is_active` = ?", true).
- And("`email_notifications_preference` IN ( ?, ?)", EmailNotificationsEnabled, EmailNotificationsOnMention).
+ In("`email_notifications_preference`", EmailNotificationsEnabled, EmailNotificationsOnMention, EmailNotificationsAndYourOwn).
Find(&ous)
}
@@ -1053,7 +1060,7 @@ func GetMaileableUsersByIDs(ids []int64, isMention bool) ([]*User, error) {
Where("`type` = ?", UserTypeIndividual).
And("`prohibit_login` = ?", false).
And("`is_active` = ?", true).
- And("`email_notifications_preference` = ?", EmailNotificationsEnabled).
+ In("`email_notifications_preference`", EmailNotificationsEnabled, EmailNotificationsAndYourOwn).
Find(&ous)
}
@@ -1265,7 +1272,7 @@ func isUserVisibleToViewerCond(viewer *User) builder.Cond {
// IsUserVisibleToViewer check if viewer is able to see user profile
func IsUserVisibleToViewer(ctx context.Context, u, viewer *User) bool {
- if viewer != nil && viewer.IsAdmin {
+ if viewer != nil && (viewer.IsAdmin || viewer.ID == u.ID) {
return true
}
@@ -1304,7 +1311,7 @@ func IsUserVisibleToViewer(ctx context.Context, u, viewer *User) bool {
return false
}
- if count < 0 {
+ if count == 0 {
// No common organization
return false
}
@@ -1315,6 +1322,16 @@ func IsUserVisibleToViewer(ctx context.Context, u, viewer *User) bool {
return false
}
+// CountWrongUserType count OrgUser who have wrong type
+func CountWrongUserType() (int64, error) {
+ return db.GetEngine(db.DefaultContext).Where(builder.Eq{"type": 0}.And(builder.Neq{"num_teams": 0})).Count(new(User))
+}
+
+// FixWrongUserType fix OrgUser who have wrong type
+func FixWrongUserType() (int64, error) {
+ return db.GetEngine(db.DefaultContext).Where(builder.Eq{"type": 0}.And(builder.Neq{"num_teams": 0})).Cols("type").NoAutoTime().Update(&User{Type: 1})
+}
+
func GetOrderByName() string {
if setting.UI.DefaultShowFullName {
return "full_name, name"
diff --git a/models/user/user_test.go b/models/user/user_test.go
index 4994ac53ab..5f2ac0a60c 100644
--- a/models/user/user_test.go
+++ b/models/user/user_test.go
@@ -22,7 +22,7 @@ import (
func TestOAuth2Application_LoadUser(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- app := unittest.AssertExistsAndLoadBean(t, &auth.OAuth2Application{ID: 1}).(*auth.OAuth2Application)
+ app := unittest.AssertExistsAndLoadBean(t, &auth.OAuth2Application{ID: 1})
user, err := user_model.GetUserByID(app.UID)
assert.NoError(t, err)
assert.NotNil(t, user)
@@ -41,10 +41,10 @@ func TestGetUserEmailsByNames(t *testing.T) {
func TestCanCreateOrganization(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- admin := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User)
+ admin := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
assert.True(t, admin.CanCreateOrganization())
- user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
+ user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
assert.True(t, user.CanCreateOrganization())
// Disable user create organization permission.
user.AllowCreateOrganization = false
@@ -102,7 +102,7 @@ func TestSearchUsers(t *testing.T) {
[]int64{9})
testUserSuccess(&user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1}, IsActive: util.OptionalBoolTrue},
- []int64{1, 2, 4, 5, 8, 10, 11, 12, 13, 14, 15, 16, 18, 20, 21, 24, 28, 29, 30, 32})
+ []int64{1, 2, 4, 5, 8, 10, 11, 12, 13, 14, 15, 16, 18, 20, 21, 24, 27, 28, 29, 30, 32})
testUserSuccess(&user_model.SearchUserOptions{Keyword: "user1", OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1}, IsActive: util.OptionalBoolTrue},
[]int64{1, 10, 11, 12, 13, 14, 15, 16, 18})
@@ -141,7 +141,7 @@ func TestEmailNotificationPreferences(t *testing.T) {
{user_model.EmailNotificationsEnabled, 8},
{user_model.EmailNotificationsOnMention, 9},
} {
- user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: test.userID}).(*user_model.User)
+ user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: test.userID})
assert.Equal(t, test.expected, user.EmailNotifications())
// Try all possible settings
@@ -153,6 +153,9 @@ func TestEmailNotificationPreferences(t *testing.T) {
assert.NoError(t, user_model.SetEmailNotifications(user, user_model.EmailNotificationsDisabled))
assert.Equal(t, user_model.EmailNotificationsDisabled, user.EmailNotifications())
+
+ assert.NoError(t, user_model.SetEmailNotifications(user, user_model.EmailNotificationsAndYourOwn))
+ assert.Equal(t, user_model.EmailNotificationsAndYourOwn, user.EmailNotifications())
}
}
@@ -239,7 +242,7 @@ func TestCreateUserInvalidEmail(t *testing.T) {
func TestCreateUserEmailAlreadyUsed(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
+ user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
// add new user with user2's email
user.Name = "testuser"
@@ -285,29 +288,45 @@ func TestGetMaileableUsersByIDs(t *testing.T) {
func TestUpdateUser(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
+ user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
user.KeepActivityPrivate = true
assert.NoError(t, user_model.UpdateUser(db.DefaultContext, user, false))
- user = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
+ user = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
assert.True(t, user.KeepActivityPrivate)
setting.Service.AllowedUserVisibilityModesSlice = []bool{true, false, false}
user.KeepActivityPrivate = false
user.Visibility = structs.VisibleTypePrivate
assert.Error(t, user_model.UpdateUser(db.DefaultContext, user, false))
- user = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
+ user = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
assert.True(t, user.KeepActivityPrivate)
+ newEmail := "new_" + user.Email
+ user.Email = newEmail
+ assert.NoError(t, user_model.UpdateUser(db.DefaultContext, user, true))
+ user = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
+ assert.Equal(t, newEmail, user.Email)
+
user.Email = "no mail@mail.org"
assert.Error(t, user_model.UpdateUser(db.DefaultContext, user, true))
}
+func TestUpdateUserEmailAlreadyUsed(t *testing.T) {
+ assert.NoError(t, unittest.PrepareTestDatabase())
+ user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
+ user3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3})
+
+ user2.Email = user3.Email
+ err := user_model.UpdateUser(db.DefaultContext, user2, true)
+ assert.True(t, user_model.IsErrEmailAlreadyUsed(err))
+}
+
func TestNewUserRedirect(t *testing.T) {
// redirect to a completely new name
assert.NoError(t, unittest.PrepareTestDatabase())
- user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User)
+ user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
assert.NoError(t, user_model.NewUserRedirect(db.DefaultContext, user.ID, user.Name, "newusername"))
unittest.AssertExistsAndLoadBean(t, &user_model.Redirect{
@@ -324,7 +343,7 @@ func TestNewUserRedirect2(t *testing.T) {
// redirect to previously used name
assert.NoError(t, unittest.PrepareTestDatabase())
- user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User)
+ user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
assert.NoError(t, user_model.NewUserRedirect(db.DefaultContext, user.ID, user.Name, "olduser1"))
unittest.AssertExistsAndLoadBean(t, &user_model.Redirect{
@@ -341,7 +360,7 @@ func TestNewUserRedirect3(t *testing.T) {
// redirect for a previously-unredirected user
assert.NoError(t, unittest.PrepareTestDatabase())
- user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
+ user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
assert.NoError(t, user_model.NewUserRedirect(db.DefaultContext, user.ID, user.Name, "newusername"))
unittest.AssertExistsAndLoadBean(t, &user_model.Redirect{
@@ -397,3 +416,56 @@ func TestUnfollowUser(t *testing.T) {
unittest.CheckConsistencyFor(t, &user_model.User{})
}
+
+func TestIsUserVisibleToViewer(t *testing.T) {
+ assert.NoError(t, unittest.PrepareTestDatabase())
+
+ user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) // admin, public
+ user4 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}) // normal, public
+ user20 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 20}) // public, same team as user31
+ user29 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 29}) // public, is restricted
+ user31 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 31}) // private, same team as user20
+ user33 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 33}) // limited, follows 31
+
+ test := func(u, viewer *user_model.User, expected bool) {
+ name := func(u *user_model.User) string {
+ if u == nil {
+ return ""
+ }
+ return u.Name
+ }
+ assert.Equal(t, expected, user_model.IsUserVisibleToViewer(db.DefaultContext, u, viewer), "user %v should be visible to viewer %v: %v", name(u), name(viewer), expected)
+ }
+
+ // admin viewer
+ test(user1, user1, true)
+ test(user20, user1, true)
+ test(user31, user1, true)
+ test(user33, user1, true)
+
+ // non admin viewer
+ test(user4, user4, true)
+ test(user20, user4, true)
+ test(user31, user4, false)
+ test(user33, user4, true)
+ test(user4, nil, true)
+
+ // public user
+ test(user4, user20, true)
+ test(user4, user31, true)
+ test(user4, user33, true)
+
+ // limited user
+ test(user33, user33, true)
+ test(user33, user4, true)
+ test(user33, user29, false)
+ test(user33, nil, false)
+
+ // private user
+ test(user31, user31, true)
+ test(user31, user4, false)
+ test(user31, user20, true)
+ test(user31, user29, false)
+ test(user31, user33, true)
+ test(user31, nil, false)
+}
diff --git a/models/user/user_update.go b/models/user/user_update.go
new file mode 100644
index 0000000000..9c9dc09bb2
--- /dev/null
+++ b/models/user/user_update.go
@@ -0,0 +1,16 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package user
+
+import (
+ "context"
+
+ "code.gitea.io/gitea/models/db"
+)
+
+func IncrUserRepoNum(ctx context.Context, userID int64) error {
+ _, err := db.GetEngine(ctx).Incr("num_repos").ID(userID).Update(new(User))
+ return err
+}
diff --git a/models/webhook/hooktask.go b/models/webhook/hooktask.go
index 4415518cf0..2adfcaa60d 100644
--- a/models/webhook/hooktask.go
+++ b/models/webhook/hooktask.go
@@ -47,6 +47,7 @@ const (
HookEventPullRequestReviewRejected HookEventType = "pull_request_review_rejected"
HookEventPullRequestReviewComment HookEventType = "pull_request_review_comment"
HookEventPullRequestSync HookEventType = "pull_request_sync"
+ HookEventWiki HookEventType = "wiki"
HookEventRepository HookEventType = "repository"
HookEventRelease HookEventType = "release"
HookEventPackage HookEventType = "package"
@@ -76,6 +77,8 @@ func (h HookEventType) Event() string {
return "pull_request_rejected"
case HookEventPullRequestReviewComment:
return "pull_request_comment"
+ case HookEventWiki:
+ return "wiki"
case HookEventRepository:
return "repository"
case HookEventRelease:
diff --git a/models/webhook/webhook.go b/models/webhook/webhook.go
index 1b79a414ad..83200a3d1c 100644
--- a/models/webhook/webhook.go
+++ b/models/webhook/webhook.go
@@ -41,6 +41,10 @@ func (err ErrWebhookNotExist) Error() string {
return fmt.Sprintf("webhook does not exist [id: %d]", err.ID)
}
+func (err ErrWebhookNotExist) Unwrap() error {
+ return util.ErrNotExist
+}
+
// ErrHookTaskNotExist represents a "HookTaskNotExist" kind of error.
type ErrHookTaskNotExist struct {
HookID int64
@@ -57,6 +61,10 @@ func (err ErrHookTaskNotExist) Error() string {
return fmt.Sprintf("hook task does not exist [hook: %d, uuid: %s]", err.HookID, err.UUID)
}
+func (err ErrHookTaskNotExist) Unwrap() error {
+ return util.ErrNotExist
+}
+
// HookContentType is the content type of a web hook
type HookContentType int
@@ -132,6 +140,7 @@ type HookEvents struct {
PullRequestComment bool `json:"pull_request_comment"`
PullRequestReview bool `json:"pull_request_review"`
PullRequestSync bool `json:"pull_request_sync"`
+ Wiki bool `json:"wiki"`
Repository bool `json:"repository"`
Release bool `json:"release"`
Package bool `json:"package"`
@@ -328,6 +337,12 @@ func (w *Webhook) HasPullRequestSyncEvent() bool {
(w.ChooseEvents && w.HookEvents.PullRequestSync)
}
+// HasWikiEvent returns true if hook enabled wiki event.
+func (w *Webhook) HasWikiEvent() bool {
+ return w.SendEverything ||
+ (w.ChooseEvents && w.HookEvent.Wiki)
+}
+
// HasReleaseEvent returns if hook enabled release event.
func (w *Webhook) HasReleaseEvent() bool {
return w.SendEverything ||
@@ -373,6 +388,7 @@ func (w *Webhook) EventCheckers() []struct {
{w.HasPullRequestRejectedEvent, HookEventPullRequestReviewRejected},
{w.HasPullRequestCommentEvent, HookEventPullRequestReviewComment},
{w.HasPullRequestSyncEvent, HookEventPullRequestSync},
+ {w.HasWikiEvent, HookEventWiki},
{w.HasRepositoryEvent, HookEventRepository},
{w.HasReleaseEvent, HookEventRelease},
{w.HasPackageEvent, HookEventPackage},
@@ -399,6 +415,10 @@ func CreateWebhook(ctx context.Context, w *Webhook) error {
// CreateWebhooks creates multiple web hooks
func CreateWebhooks(ctx context.Context, ws []*Webhook) error {
+ // xorm returns err "no element on slice when insert" for empty slices.
+ if len(ws) == 0 {
+ return nil
+ }
for i := 0; i < len(ws); i++ {
ws[i].Type = strings.TrimSpace(ws[i].Type)
}
diff --git a/models/webhook/webhook_test.go b/models/webhook/webhook_test.go
index 4bc811586d..7ec7edc0b7 100644
--- a/models/webhook/webhook_test.go
+++ b/models/webhook/webhook_test.go
@@ -31,14 +31,14 @@ func TestIsValidHookContentType(t *testing.T) {
func TestWebhook_History(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- webhook := unittest.AssertExistsAndLoadBean(t, &Webhook{ID: 1}).(*Webhook)
+ webhook := unittest.AssertExistsAndLoadBean(t, &Webhook{ID: 1})
tasks, err := webhook.History(0)
assert.NoError(t, err)
if assert.Len(t, tasks, 1) {
assert.Equal(t, int64(1), tasks[0].ID)
}
- webhook = unittest.AssertExistsAndLoadBean(t, &Webhook{ID: 2}).(*Webhook)
+ webhook = unittest.AssertExistsAndLoadBean(t, &Webhook{ID: 2})
tasks, err = webhook.History(0)
assert.NoError(t, err)
assert.Len(t, tasks, 0)
@@ -46,7 +46,7 @@ func TestWebhook_History(t *testing.T) {
func TestWebhook_UpdateEvent(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- webhook := unittest.AssertExistsAndLoadBean(t, &Webhook{ID: 1}).(*Webhook)
+ webhook := unittest.AssertExistsAndLoadBean(t, &Webhook{ID: 1})
hookEvent := &HookEvent{
PushOnly: true,
SendEverything: false,
@@ -71,7 +71,7 @@ func TestWebhook_EventsArray(t *testing.T) {
"issues", "issue_assign", "issue_label", "issue_milestone", "issue_comment",
"pull_request", "pull_request_assign", "pull_request_label", "pull_request_milestone",
"pull_request_comment", "pull_request_review_approved", "pull_request_review_rejected",
- "pull_request_review_comment", "pull_request_sync", "repository", "release",
+ "pull_request_review_comment", "pull_request_sync", "wiki", "repository", "release",
"package",
},
(&Webhook{
@@ -162,7 +162,7 @@ func TestGetWebhooksByOrgID(t *testing.T) {
func TestUpdateWebhook(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- hook := unittest.AssertExistsAndLoadBean(t, &Webhook{ID: 2}).(*Webhook)
+ hook := unittest.AssertExistsAndLoadBean(t, &Webhook{ID: 2})
hook.IsActive = true
hook.ContentType = ContentTypeForm
unittest.AssertNotExistsBean(t, hook)
@@ -220,7 +220,7 @@ func TestCreateHookTask(t *testing.T) {
func TestUpdateHookTask(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- hook := unittest.AssertExistsAndLoadBean(t, &HookTask{ID: 1}).(*HookTask)
+ hook := unittest.AssertExistsAndLoadBean(t, &HookTask{ID: 1})
hook.PayloadContent = "new payload content"
hook.DeliveredString = "new delivered string"
hook.IsDelivered = true
diff --git a/modules/activitypub/client_test.go b/modules/activitypub/client_test.go
index b93ef5ac98..62068d53b3 100644
--- a/modules/activitypub/client_test.go
+++ b/modules/activitypub/client_test.go
@@ -23,7 +23,7 @@ import (
func TestActivityPubSignedPost(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User)
+ user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
pubID := "https://example.com/pubID"
c, err := NewClient(user, pubID)
assert.NoError(t, err)
diff --git a/modules/activitypub/user_settings_test.go b/modules/activitypub/user_settings_test.go
index 90c6f680f9..beefde232f 100644
--- a/modules/activitypub/user_settings_test.go
+++ b/modules/activitypub/user_settings_test.go
@@ -17,7 +17,7 @@ import (
func TestUserSettings(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User)
+ user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
pub, priv, err := GetKeyPair(user1)
assert.NoError(t, err)
pub1, err := GetPublicKey(user1)
diff --git a/modules/avatar/identicon/block.go b/modules/avatar/identicon/block.go
index 249a3e0958..270f05e1b0 100644
--- a/modules/avatar/identicon/block.go
+++ b/modules/avatar/identicon/block.go
@@ -36,20 +36,20 @@ func drawBlock(img *image.Paletted, x, y, size, angle int, points []int) {
// blank
//
-// --------
-// | |
-// | |
-// | |
-// --------
+// --------
+// | |
+// | |
+// | |
+// --------
func b0(img *image.Paletted, x, y, size, angle int) {}
// full-filled
//
-// --------
-// |######|
-// |######|
-// |######|
-// --------
+// --------
+// |######|
+// |######|
+// |######|
+// --------
func b1(img *image.Paletted, x, y, size, angle int) {
for i := x; i < x+size; i++ {
for j := y; j < y+size; j++ {
@@ -59,12 +59,13 @@ func b1(img *image.Paletted, x, y, size, angle int) {
}
// a small block
-// ----------
-// | |
-// | #### |
-// | #### |
-// | |
-// ----------
+//
+// ----------
+// | |
+// | #### |
+// | #### |
+// | |
+// ----------
func b2(img *image.Paletted, x, y, size, angle int) {
l := size / 4
x += l
@@ -79,15 +80,15 @@ func b2(img *image.Paletted, x, y, size, angle int) {
// diamond
//
-// ---------
-// | # |
-// | ### |
-// | ##### |
-// |#######|
-// | ##### |
-// | ### |
-// | # |
-// ---------
+// ---------
+// | # |
+// | ### |
+// | ##### |
+// |#######|
+// | ##### |
+// | ### |
+// | # |
+// ---------
func b3(img *image.Paletted, x, y, size, angle int) {
m := size / 2
drawBlock(img, x, y, size, 0, []int{
@@ -101,13 +102,13 @@ func b3(img *image.Paletted, x, y, size, angle int) {
// b4
//
-// -------
-// |#####|
-// |#### |
-// |### |
-// |## |
-// |# |
-// |------
+// -------
+// |#####|
+// |#### |
+// |### |
+// |## |
+// |# |
+// |------
func b4(img *image.Paletted, x, y, size, angle int) {
drawBlock(img, x, y, size, angle, []int{
0, 0,
@@ -119,11 +120,11 @@ func b4(img *image.Paletted, x, y, size, angle int) {
// b5
//
-// ---------
-// | # |
-// | ### |
-// | ##### |
-// |#######|
+// ---------
+// | # |
+// | ### |
+// | ##### |
+// |#######|
func b5(img *image.Paletted, x, y, size, angle int) {
m := size / 2
drawBlock(img, x, y, size, angle, []int{
@@ -136,11 +137,11 @@ func b5(img *image.Paletted, x, y, size, angle int) {
// b6
//
-// --------
-// |### |
-// |### |
-// |### |
-// --------
+// --------
+// |### |
+// |### |
+// |### |
+// --------
func b6(img *image.Paletted, x, y, size, angle int) {
m := size / 2
drawBlock(img, x, y, size, angle, []int{
@@ -154,12 +155,12 @@ func b6(img *image.Paletted, x, y, size, angle int) {
// b7 italic cone
//
-// ---------
-// | # |
-// | ## |
-// | #####|
-// | ####|
-// |--------
+// ---------
+// | # |
+// | ## |
+// | #####|
+// | ####|
+// |--------
func b7(img *image.Paletted, x, y, size, angle int) {
m := size / 2
drawBlock(img, x, y, size, angle, []int{
@@ -173,14 +174,14 @@ func b7(img *image.Paletted, x, y, size, angle int) {
// b8 three small triangles
//
-// -----------
-// | # |
-// | ### |
-// | ##### |
-// | # # |
-// | ### ### |
-// |#########|
-// -----------
+// -----------
+// | # |
+// | ### |
+// | ##### |
+// | # # |
+// | ### ### |
+// |#########|
+// -----------
func b8(img *image.Paletted, x, y, size, angle int) {
m := size / 2
mm := m / 2
@@ -212,13 +213,13 @@ func b8(img *image.Paletted, x, y, size, angle int) {
// b9 italic triangle
//
-// ---------
-// |# |
-// | #### |
-// | #####|
-// | #### |
-// | # |
-// ---------
+// ---------
+// |# |
+// | #### |
+// | #####|
+// | #### |
+// | # |
+// ---------
func b9(img *image.Paletted, x, y, size, angle int) {
m := size / 2
drawBlock(img, x, y, size, angle, []int{
@@ -231,16 +232,16 @@ func b9(img *image.Paletted, x, y, size, angle int) {
// b10
//
-// ----------
-// | ####|
-// | ### |
-// | ## |
-// | # |
-// |#### |
-// |### |
-// |## |
-// |# |
-// ----------
+// ----------
+// | ####|
+// | ### |
+// | ## |
+// | # |
+// |#### |
+// |### |
+// |## |
+// |# |
+// ----------
func b10(img *image.Paletted, x, y, size, angle int) {
m := size / 2
drawBlock(img, x, y, size, angle, []int{
@@ -260,13 +261,13 @@ func b10(img *image.Paletted, x, y, size, angle int) {
// b11
//
-// ----------
-// |#### |
-// |#### |
-// |#### |
-// | |
-// | |
-// ----------
+// ----------
+// |#### |
+// |#### |
+// |#### |
+// | |
+// | |
+// ----------
func b11(img *image.Paletted, x, y, size, angle int) {
m := size / 2
drawBlock(img, x, y, size, angle, []int{
@@ -280,13 +281,13 @@ func b11(img *image.Paletted, x, y, size, angle int) {
// b12
//
-// -----------
-// | |
-// | |
-// |#########|
-// | ##### |
-// | # |
-// -----------
+// -----------
+// | |
+// | |
+// |#########|
+// | ##### |
+// | # |
+// -----------
func b12(img *image.Paletted, x, y, size, angle int) {
m := size / 2
drawBlock(img, x, y, size, angle, []int{
@@ -299,13 +300,13 @@ func b12(img *image.Paletted, x, y, size, angle int) {
// b13
//
-// -----------
-// | |
-// | |
-// | # |
-// | ##### |
-// |#########|
-// -----------
+// -----------
+// | |
+// | |
+// | # |
+// | ##### |
+// |#########|
+// -----------
func b13(img *image.Paletted, x, y, size, angle int) {
m := size / 2
drawBlock(img, x, y, size, angle, []int{
@@ -318,13 +319,13 @@ func b13(img *image.Paletted, x, y, size, angle int) {
// b14
//
-// ---------
-// | # |
-// | ### |
-// |#### |
-// | |
-// | |
-// ---------
+// ---------
+// | # |
+// | ### |
+// |#### |
+// | |
+// | |
+// ---------
func b14(img *image.Paletted, x, y, size, angle int) {
m := size / 2
drawBlock(img, x, y, size, angle, []int{
@@ -337,13 +338,13 @@ func b14(img *image.Paletted, x, y, size, angle int) {
// b15
//
-// ----------
-// |##### |
-// |### |
-// |# |
-// | |
-// | |
-// ----------
+// ----------
+// |##### |
+// |### |
+// |# |
+// | |
+// | |
+// ----------
func b15(img *image.Paletted, x, y, size, angle int) {
m := size / 2
drawBlock(img, x, y, size, angle, []int{
@@ -356,14 +357,14 @@ func b15(img *image.Paletted, x, y, size, angle int) {
// b16
//
-// ---------
-// | # |
-// | ##### |
-// |#######|
-// | # |
-// | ##### |
-// |#######|
-// ---------
+// ---------
+// | # |
+// | ##### |
+// |#######|
+// | # |
+// | ##### |
+// |#######|
+// ---------
func b16(img *image.Paletted, x, y, size, angle int) {
m := size / 2
drawBlock(img, x, y, size, angle, []int{
@@ -383,13 +384,13 @@ func b16(img *image.Paletted, x, y, size, angle int) {
// b17
//
-// ----------
-// |##### |
-// |### |
-// |# |
-// | ##|
-// | ##|
-// ----------
+// ----------
+// |##### |
+// |### |
+// |# |
+// | ##|
+// | ##|
+// ----------
func b17(img *image.Paletted, x, y, size, angle int) {
m := size / 2
@@ -412,13 +413,13 @@ func b17(img *image.Paletted, x, y, size, angle int) {
// b18
//
-// ----------
-// |##### |
-// |#### |
-// |### |
-// |## |
-// |# |
-// ----------
+// ----------
+// |##### |
+// |#### |
+// |### |
+// |## |
+// |# |
+// ----------
func b18(img *image.Paletted, x, y, size, angle int) {
m := size / 2
@@ -432,13 +433,13 @@ func b18(img *image.Paletted, x, y, size, angle int) {
// b19
//
-// ----------
-// |########|
-// |### ###|
-// |# #|
-// |### ###|
-// |########|
-// ----------
+// ----------
+// |########|
+// |### ###|
+// |# #|
+// |### ###|
+// |########|
+// ----------
func b19(img *image.Paletted, x, y, size, angle int) {
m := size / 2
@@ -473,13 +474,13 @@ func b19(img *image.Paletted, x, y, size, angle int) {
// b20
//
-// ----------
-// | ## |
-// |### |
-// |## |
-// |## |
-// |# |
-// ----------
+// ----------
+// | ## |
+// |### |
+// |## |
+// |## |
+// |# |
+// ----------
func b20(img *image.Paletted, x, y, size, angle int) {
m := size / 2
q := size / 4
@@ -494,13 +495,13 @@ func b20(img *image.Paletted, x, y, size, angle int) {
// b21
//
-// ----------
-// | #### |
-// |## #####|
-// |## ##|
-// |## |
-// |# |
-// ----------
+// ----------
+// | #### |
+// |## #####|
+// |## ##|
+// |## |
+// |# |
+// ----------
func b21(img *image.Paletted, x, y, size, angle int) {
m := size / 2
q := size / 4
@@ -522,13 +523,13 @@ func b21(img *image.Paletted, x, y, size, angle int) {
// b22
//
-// ----------
-// | #### |
-// |## ### |
-// |## ##|
-// |## ##|
-// |# #|
-// ----------
+// ----------
+// | #### |
+// |## ### |
+// |## ##|
+// |## ##|
+// |# #|
+// ----------
func b22(img *image.Paletted, x, y, size, angle int) {
m := size / 2
q := size / 4
@@ -550,13 +551,13 @@ func b22(img *image.Paletted, x, y, size, angle int) {
// b23
//
-// ----------
-// | #######|
-// |### #|
-// |## |
-// |## |
-// |# |
-// ----------
+// ----------
+// | #######|
+// |### #|
+// |## |
+// |## |
+// |# |
+// ----------
func b23(img *image.Paletted, x, y, size, angle int) {
m := size / 2
q := size / 4
@@ -578,13 +579,13 @@ func b23(img *image.Paletted, x, y, size, angle int) {
// b24
//
-// ----------
-// | ## ###|
-// |### ###|
-// |## ## |
-// |## ## |
-// |# # |
-// ----------
+// ----------
+// | ## ###|
+// |### ###|
+// |## ## |
+// |## ## |
+// |# # |
+// ----------
func b24(img *image.Paletted, x, y, size, angle int) {
m := size / 2
q := size / 4
@@ -606,13 +607,13 @@ func b24(img *image.Paletted, x, y, size, angle int) {
// b25
//
-// ----------
-// |# #|
-// |## ###|
-// |## ## |
-// |###### |
-// |#### |
-// ----------
+// ----------
+// |# #|
+// |## ###|
+// |## ## |
+// |###### |
+// |#### |
+// ----------
func b25(img *image.Paletted, x, y, size, angle int) {
m := size / 2
q := size / 4
@@ -634,13 +635,13 @@ func b25(img *image.Paletted, x, y, size, angle int) {
// b26
//
-// ----------
-// |# #|
-// |### ###|
-// | #### |
-// |### ###|
-// |# #|
-// ----------
+// ----------
+// |# #|
+// |### ###|
+// | #### |
+// |### ###|
+// |# #|
+// ----------
func b26(img *image.Paletted, x, y, size, angle int) {
m := size / 2
q := size / 4
@@ -676,13 +677,13 @@ func b26(img *image.Paletted, x, y, size, angle int) {
// b27
//
-// ----------
-// |########|
-// |## ###|
-// |# #|
-// |### ##|
-// |########|
-// ----------
+// ----------
+// |########|
+// |## ###|
+// |# #|
+// |### ##|
+// |########|
+// ----------
func b27(img *image.Paletted, x, y, size, angle int) {
m := size / 2
q := size / 4
diff --git a/modules/base/tool.go b/modules/base/tool.go
index a981fd6c57..f1e4a3bf97 100644
--- a/modules/base/tool.go
+++ b/modules/base/tool.go
@@ -241,15 +241,6 @@ func Int64sToStrings(ints []int64) []string {
return strs
}
-// Int64sToMap converts a slice of int64 to a int64 map.
-func Int64sToMap(ints []int64) map[int64]bool {
- m := make(map[int64]bool)
- for _, i := range ints {
- m[i] = true
- }
- return m
-}
-
// Int64sContains returns if a int64 in a slice of int64
func Int64sContains(intsSlice []int64, a int64) bool {
for _, c := range intsSlice {
diff --git a/modules/base/tool_test.go b/modules/base/tool_test.go
index 6685168bac..87de898e0b 100644
--- a/modules/base/tool_test.go
+++ b/modules/base/tool_test.go
@@ -214,16 +214,7 @@ func TestInt64sToStrings(t *testing.T) {
)
}
-func TestInt64sToMap(t *testing.T) {
- assert.Equal(t, map[int64]bool{}, Int64sToMap([]int64{}))
- assert.Equal(t,
- map[int64]bool{1: true, 4: true, 16: true},
- Int64sToMap([]int64{1, 4, 16}),
- )
-}
-
func TestInt64sContains(t *testing.T) {
- assert.Equal(t, map[int64]bool{}, Int64sToMap([]int64{}))
assert.True(t, Int64sContains([]int64{6, 44324, 4324, 32, 1, 2323}, 1))
assert.True(t, Int64sContains([]int64{2323}, 2323))
assert.False(t, Int64sContains([]int64{6, 44324, 4324, 32, 1, 2323}, 232))
diff --git a/modules/charset/ambiguous.go b/modules/charset/ambiguous.go
new file mode 100644
index 0000000000..9dab3b0951
--- /dev/null
+++ b/modules/charset/ambiguous.go
@@ -0,0 +1,54 @@
+// This file is generated by modules/charset/ambiguous/generate.go DO NOT EDIT
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package charset
+
+import (
+ "sort"
+ "strings"
+ "unicode"
+
+ "code.gitea.io/gitea/modules/translation"
+)
+
+// AmbiguousTablesForLocale provides the table of ambiguous characters for this locale.
+func AmbiguousTablesForLocale(locale translation.Locale) []*AmbiguousTable {
+ key := locale.Language()
+ var table *AmbiguousTable
+ var ok bool
+ for len(key) > 0 {
+ if table, ok = AmbiguousCharacters[key]; ok {
+ break
+ }
+ idx := strings.LastIndexAny(key, "-_")
+ if idx < 0 {
+ key = ""
+ } else {
+ key = key[:idx]
+ }
+ }
+ if table == nil {
+ table = AmbiguousCharacters["_default"]
+ }
+
+ return []*AmbiguousTable{
+ table,
+ AmbiguousCharacters["_common"],
+ }
+}
+
+func isAmbiguous(r rune, confusableTo *rune, tables ...*AmbiguousTable) bool {
+ for _, table := range tables {
+ if !unicode.Is(table.RangeTable, r) {
+ continue
+ }
+ i := sort.Search(len(table.Confusable), func(i int) bool {
+ return table.Confusable[i] >= r
+ })
+ (*confusableTo) = table.With[i]
+ return true
+ }
+ return false
+}
diff --git a/modules/charset/ambiguous/ambiguous.json b/modules/charset/ambiguous/ambiguous.json
new file mode 100644
index 0000000000..d0f69f6ae2
--- /dev/null
+++ b/modules/charset/ambiguous/ambiguous.json
@@ -0,0 +1 @@
+"{\"_common\":[8232,32,8233,32,5760,32,8192,32,8193,32,8194,32,8195,32,8196,32,8197,32,8198,32,8200,32,8201,32,8202,32,8287,32,8199,32,8239,32,2042,95,65101,95,65102,95,65103,95,8208,45,8209,45,8210,45,65112,45,1748,45,8259,45,727,45,8722,45,10134,45,11450,45,1549,44,1643,44,8218,44,184,44,42233,44,894,59,2307,58,2691,58,1417,58,1795,58,1796,58,5868,58,65072,58,6147,58,6153,58,8282,58,1475,58,760,58,42889,58,8758,58,720,58,42237,58,451,33,11601,33,660,63,577,63,2429,63,5038,63,42731,63,119149,46,8228,46,1793,46,1794,46,42510,46,68176,46,1632,46,1776,46,42232,46,1373,96,65287,96,8219,96,8242,96,1370,96,1523,96,8175,96,65344,96,900,96,8189,96,8125,96,8127,96,8190,96,697,96,884,96,712,96,714,96,715,96,756,96,699,96,701,96,700,96,702,96,42892,96,1497,96,2036,96,2037,96,5194,96,5836,96,94033,96,94034,96,65339,91,10088,40,10098,40,12308,40,64830,40,65341,93,10089,41,10099,41,12309,41,64831,41,10100,123,119060,123,10101,125,65342,94,8270,42,1645,42,8727,42,66335,42,5941,47,8257,47,8725,47,8260,47,9585,47,10187,47,10744,47,119354,47,12755,47,12339,47,11462,47,20031,47,12035,47,65340,92,65128,92,8726,92,10189,92,10741,92,10745,92,119311,92,119355,92,12756,92,20022,92,12034,92,42872,38,708,94,710,94,5869,43,10133,43,66203,43,8249,60,10094,60,706,60,119350,60,5176,60,5810,60,5120,61,11840,61,12448,61,42239,61,8250,62,10095,62,707,62,119351,62,5171,62,94015,62,8275,126,732,126,8128,126,8764,126,65372,124,65293,45,120784,50,120794,50,120804,50,120814,50,120824,50,130034,50,42842,50,423,50,1000,50,42564,50,5311,50,42735,50,119302,51,120785,51,120795,51,120805,51,120815,51,120825,51,130035,51,42923,51,540,51,439,51,42858,51,11468,51,1248,51,94011,51,71882,51,120786,52,120796,52,120806,52,120816,52,120826,52,130036,52,5070,52,71855,52,120787,53,120797,53,120807,53,120817,53,120827,53,130037,53,444,53,71867,53,120788,54,120798,54,120808,54,120818,54,120828,54,130038,54,11474,54,5102,54,71893,54,119314,55,120789,55,120799,55,120809,55,120819,55,120829,55,130039,55,66770,55,71878,55,2819,56,2538,56,2666,56,125131,56,120790,56,120800,56,120810,56,120820,56,120830,56,130040,56,547,56,546,56,66330,56,2663,57,2920,57,2541,57,3437,57,120791,57,120801,57,120811,57,120821,57,120831,57,130041,57,42862,57,11466,57,71884,57,71852,57,71894,57,9082,97,65345,97,119834,97,119886,97,119938,97,119990,97,120042,97,120094,97,120146,97,120198,97,120250,97,120302,97,120354,97,120406,97,120458,97,593,97,945,97,120514,97,120572,97,120630,97,120688,97,120746,97,65313,65,119808,65,119860,65,119912,65,119964,65,120016,65,120068,65,120120,65,120172,65,120224,65,120276,65,120328,65,120380,65,120432,65,913,65,120488,65,120546,65,120604,65,120662,65,120720,65,5034,65,5573,65,42222,65,94016,65,66208,65,119835,98,119887,98,119939,98,119991,98,120043,98,120095,98,120147,98,120199,98,120251,98,120303,98,120355,98,120407,98,120459,98,388,98,5071,98,5234,98,5551,98,65314,66,8492,66,119809,66,119861,66,119913,66,120017,66,120069,66,120121,66,120173,66,120225,66,120277,66,120329,66,120381,66,120433,66,42932,66,914,66,120489,66,120547,66,120605,66,120663,66,120721,66,5108,66,5623,66,42192,66,66178,66,66209,66,66305,66,65347,99,8573,99,119836,99,119888,99,119940,99,119992,99,120044,99,120096,99,120148,99,120200,99,120252,99,120304,99,120356,99,120408,99,120460,99,7428,99,1010,99,11429,99,43951,99,66621,99,128844,67,71922,67,71913,67,65315,67,8557,67,8450,67,8493,67,119810,67,119862,67,119914,67,119966,67,120018,67,120174,67,120226,67,120278,67,120330,67,120382,67,120434,67,1017,67,11428,67,5087,67,42202,67,66210,67,66306,67,66581,67,66844,67,8574,100,8518,100,119837,100,119889,100,119941,100,119993,100,120045,100,120097,100,120149,100,120201,100,120253,100,120305,100,120357,100,120409,100,120461,100,1281,100,5095,100,5231,100,42194,100,8558,68,8517,68,119811,68,119863,68,119915,68,119967,68,120019,68,120071,68,120123,68,120175,68,120227,68,120279,68,120331,68,120383,68,120435,68,5024,68,5598,68,5610,68,42195,68,8494,101,65349,101,8495,101,8519,101,119838,101,119890,101,119942,101,120046,101,120098,101,120150,101,120202,101,120254,101,120306,101,120358,101,120410,101,120462,101,43826,101,1213,101,8959,69,65317,69,8496,69,119812,69,119864,69,119916,69,120020,69,120072,69,120124,69,120176,69,120228,69,120280,69,120332,69,120384,69,120436,69,917,69,120492,69,120550,69,120608,69,120666,69,120724,69,11577,69,5036,69,42224,69,71846,69,71854,69,66182,69,119839,102,119891,102,119943,102,119995,102,120047,102,120099,102,120151,102,120203,102,120255,102,120307,102,120359,102,120411,102,120463,102,43829,102,42905,102,383,102,7837,102,1412,102,119315,70,8497,70,119813,70,119865,70,119917,70,120021,70,120073,70,120125,70,120177,70,120229,70,120281,70,120333,70,120385,70,120437,70,42904,70,988,70,120778,70,5556,70,42205,70,71874,70,71842,70,66183,70,66213,70,66853,70,65351,103,8458,103,119840,103,119892,103,119944,103,120048,103,120100,103,120152,103,120204,103,120256,103,120308,103,120360,103,120412,103,120464,103,609,103,7555,103,397,103,1409,103,119814,71,119866,71,119918,71,119970,71,120022,71,120074,71,120126,71,120178,71,120230,71,120282,71,120334,71,120386,71,120438,71,1292,71,5056,71,5107,71,42198,71,65352,104,8462,104,119841,104,119945,104,119997,104,120049,104,120101,104,120153,104,120205,104,120257,104,120309,104,120361,104,120413,104,120465,104,1211,104,1392,104,5058,104,65320,72,8459,72,8460,72,8461,72,119815,72,119867,72,119919,72,120023,72,120179,72,120231,72,120283,72,120335,72,120387,72,120439,72,919,72,120494,72,120552,72,120610,72,120668,72,120726,72,11406,72,5051,72,5500,72,42215,72,66255,72,731,105,9075,105,65353,105,8560,105,8505,105,8520,105,119842,105,119894,105,119946,105,119998,105,120050,105,120102,105,120154,105,120206,105,120258,105,120310,105,120362,105,120414,105,120466,105,120484,105,618,105,617,105,953,105,8126,105,890,105,120522,105,120580,105,120638,105,120696,105,120754,105,1110,105,42567,105,1231,105,43893,105,5029,105,71875,105,65354,106,8521,106,119843,106,119895,106,119947,106,119999,106,120051,106,120103,106,120155,106,120207,106,120259,106,120311,106,120363,106,120415,106,120467,106,1011,106,1112,106,65322,74,119817,74,119869,74,119921,74,119973,74,120025,74,120077,74,120129,74,120181,74,120233,74,120285,74,120337,74,120389,74,120441,74,42930,74,895,74,1032,74,5035,74,5261,74,42201,74,119844,107,119896,107,119948,107,120000,107,120052,107,120104,107,120156,107,120208,107,120260,107,120312,107,120364,107,120416,107,120468,107,8490,75,65323,75,119818,75,119870,75,119922,75,119974,75,120026,75,120078,75,120130,75,120182,75,120234,75,120286,75,120338,75,120390,75,120442,75,922,75,120497,75,120555,75,120613,75,120671,75,120729,75,11412,75,5094,75,5845,75,42199,75,66840,75,1472,108,8739,73,9213,73,65512,73,1633,108,1777,73,66336,108,125127,108,120783,73,120793,73,120803,73,120813,73,120823,73,130033,73,65321,73,8544,73,8464,73,8465,73,119816,73,119868,73,119920,73,120024,73,120128,73,120180,73,120232,73,120284,73,120336,73,120388,73,120440,73,65356,108,8572,73,8467,108,119845,108,119897,108,119949,108,120001,108,120053,108,120105,73,120157,73,120209,73,120261,73,120313,73,120365,73,120417,73,120469,73,448,73,120496,73,120554,73,120612,73,120670,73,120728,73,11410,73,1030,73,1216,73,1493,108,1503,108,1575,108,126464,108,126592,108,65166,108,65165,108,1994,108,11599,73,5825,73,42226,73,93992,73,66186,124,66313,124,119338,76,8556,76,8466,76,119819,76,119871,76,119923,76,120027,76,120079,76,120131,76,120183,76,120235,76,120287,76,120339,76,120391,76,120443,76,11472,76,5086,76,5290,76,42209,76,93974,76,71843,76,71858,76,66587,76,66854,76,65325,77,8559,77,8499,77,119820,77,119872,77,119924,77,120028,77,120080,77,120132,77,120184,77,120236,77,120288,77,120340,77,120392,77,120444,77,924,77,120499,77,120557,77,120615,77,120673,77,120731,77,1018,77,11416,77,5047,77,5616,77,5846,77,42207,77,66224,77,66321,77,119847,110,119899,110,119951,110,120003,110,120055,110,120107,110,120159,110,120211,110,120263,110,120315,110,120367,110,120419,110,120471,110,1400,110,1404,110,65326,78,8469,78,119821,78,119873,78,119925,78,119977,78,120029,78,120081,78,120185,78,120237,78,120289,78,120341,78,120393,78,120445,78,925,78,120500,78,120558,78,120616,78,120674,78,120732,78,11418,78,42208,78,66835,78,3074,111,3202,111,3330,111,3458,111,2406,111,2662,111,2790,111,3046,111,3174,111,3302,111,3430,111,3664,111,3792,111,4160,111,1637,111,1781,111,65359,111,8500,111,119848,111,119900,111,119952,111,120056,111,120108,111,120160,111,120212,111,120264,111,120316,111,120368,111,120420,111,120472,111,7439,111,7441,111,43837,111,959,111,120528,111,120586,111,120644,111,120702,111,120760,111,963,111,120532,111,120590,111,120648,111,120706,111,120764,111,11423,111,4351,111,1413,111,1505,111,1607,111,126500,111,126564,111,126596,111,65259,111,65260,111,65258,111,65257,111,1726,111,64428,111,64429,111,64427,111,64426,111,1729,111,64424,111,64425,111,64423,111,64422,111,1749,111,3360,111,4125,111,66794,111,71880,111,71895,111,66604,111,1984,79,2534,79,2918,79,12295,79,70864,79,71904,79,120782,79,120792,79,120802,79,120812,79,120822,79,130032,79,65327,79,119822,79,119874,79,119926,79,119978,79,120030,79,120082,79,120134,79,120186,79,120238,79,120290,79,120342,79,120394,79,120446,79,927,79,120502,79,120560,79,120618,79,120676,79,120734,79,11422,79,1365,79,11604,79,4816,79,2848,79,66754,79,42227,79,71861,79,66194,79,66219,79,66564,79,66838,79,9076,112,65360,112,119849,112,119901,112,119953,112,120005,112,120057,112,120109,112,120161,112,120213,112,120265,112,120317,112,120369,112,120421,112,120473,112,961,112,120530,112,120544,112,120588,112,120602,112,120646,112,120660,112,120704,112,120718,112,120762,112,120776,112,11427,112,65328,80,8473,80,119823,80,119875,80,119927,80,119979,80,120031,80,120083,80,120187,80,120239,80,120291,80,120343,80,120395,80,120447,80,929,80,120504,80,120562,80,120620,80,120678,80,120736,80,11426,80,5090,80,5229,80,42193,80,66197,80,119850,113,119902,113,119954,113,120006,113,120058,113,120110,113,120162,113,120214,113,120266,113,120318,113,120370,113,120422,113,120474,113,1307,113,1379,113,1382,113,8474,81,119824,81,119876,81,119928,81,119980,81,120032,81,120084,81,120188,81,120240,81,120292,81,120344,81,120396,81,120448,81,11605,81,119851,114,119903,114,119955,114,120007,114,120059,114,120111,114,120163,114,120215,114,120267,114,120319,114,120371,114,120423,114,120475,114,43847,114,43848,114,7462,114,11397,114,43905,114,119318,82,8475,82,8476,82,8477,82,119825,82,119877,82,119929,82,120033,82,120189,82,120241,82,120293,82,120345,82,120397,82,120449,82,422,82,5025,82,5074,82,66740,82,5511,82,42211,82,94005,82,65363,115,119852,115,119904,115,119956,115,120008,115,120060,115,120112,115,120164,115,120216,115,120268,115,120320,115,120372,115,120424,115,120476,115,42801,115,445,115,1109,115,43946,115,71873,115,66632,115,65331,83,119826,83,119878,83,119930,83,119982,83,120034,83,120086,83,120138,83,120190,83,120242,83,120294,83,120346,83,120398,83,120450,83,1029,83,1359,83,5077,83,5082,83,42210,83,94010,83,66198,83,66592,83,119853,116,119905,116,119957,116,120009,116,120061,116,120113,116,120165,116,120217,116,120269,116,120321,116,120373,116,120425,116,120477,116,8868,84,10201,84,128872,84,65332,84,119827,84,119879,84,119931,84,119983,84,120035,84,120087,84,120139,84,120191,84,120243,84,120295,84,120347,84,120399,84,120451,84,932,84,120507,84,120565,84,120623,84,120681,84,120739,84,11430,84,5026,84,42196,84,93962,84,71868,84,66199,84,66225,84,66325,84,119854,117,119906,117,119958,117,120010,117,120062,117,120114,117,120166,117,120218,117,120270,117,120322,117,120374,117,120426,117,120478,117,42911,117,7452,117,43854,117,43858,117,651,117,965,117,120534,117,120592,117,120650,117,120708,117,120766,117,1405,117,66806,117,71896,117,8746,85,8899,85,119828,85,119880,85,119932,85,119984,85,120036,85,120088,85,120140,85,120192,85,120244,85,120296,85,120348,85,120400,85,120452,85,1357,85,4608,85,66766,85,5196,85,42228,85,94018,85,71864,85,8744,118,8897,118,65366,118,8564,118,119855,118,119907,118,119959,118,120011,118,120063,118,120115,118,120167,118,120219,118,120271,118,120323,118,120375,118,120427,118,120479,118,7456,118,957,118,120526,118,120584,118,120642,118,120700,118,120758,118,1141,118,1496,118,71430,118,43945,118,71872,118,119309,86,1639,86,1783,86,8548,86,119829,86,119881,86,119933,86,119985,86,120037,86,120089,86,120141,86,120193,86,120245,86,120297,86,120349,86,120401,86,120453,86,1140,86,11576,86,5081,86,5167,86,42719,86,42214,86,93960,86,71840,86,66845,86,623,119,119856,119,119908,119,119960,119,120012,119,120064,119,120116,119,120168,119,120220,119,120272,119,120324,119,120376,119,120428,119,120480,119,7457,119,1121,119,1309,119,1377,119,71434,119,71438,119,71439,119,43907,119,71919,87,71910,87,119830,87,119882,87,119934,87,119986,87,120038,87,120090,87,120142,87,120194,87,120246,87,120298,87,120350,87,120402,87,120454,87,1308,87,5043,87,5076,87,42218,87,5742,120,10539,120,10540,120,10799,120,65368,120,8569,120,119857,120,119909,120,119961,120,120013,120,120065,120,120117,120,120169,120,120221,120,120273,120,120325,120,120377,120,120429,120,120481,120,5441,120,5501,120,5741,88,9587,88,66338,88,71916,88,65336,88,8553,88,119831,88,119883,88,119935,88,119987,88,120039,88,120091,88,120143,88,120195,88,120247,88,120299,88,120351,88,120403,88,120455,88,42931,88,935,88,120510,88,120568,88,120626,88,120684,88,120742,88,11436,88,11613,88,5815,88,42219,88,66192,88,66228,88,66327,88,66855,88,611,121,7564,121,65369,121,119858,121,119910,121,119962,121,120014,121,120066,121,120118,121,120170,121,120222,121,120274,121,120326,121,120378,121,120430,121,120482,121,655,121,7935,121,43866,121,947,121,8509,121,120516,121,120574,121,120632,121,120690,121,120748,121,1199,121,4327,121,71900,121,65337,89,119832,89,119884,89,119936,89,119988,89,120040,89,120092,89,120144,89,120196,89,120248,89,120300,89,120352,89,120404,89,120456,89,933,89,978,89,120508,89,120566,89,120624,89,120682,89,120740,89,11432,89,1198,89,5033,89,5053,89,42220,89,94019,89,71844,89,66226,89,119859,122,119911,122,119963,122,120015,122,120067,122,120119,122,120171,122,120223,122,120275,122,120327,122,120379,122,120431,122,120483,122,7458,122,43923,122,71876,122,66293,90,71909,90,65338,90,8484,90,8488,90,119833,90,119885,90,119937,90,119989,90,120041,90,120197,90,120249,90,120301,90,120353,90,120405,90,120457,90,918,90,120493,90,120551,90,120609,90,120667,90,120725,90,5059,90,42204,90,71849,90,65282,34,65284,36,65285,37,65286,38,65290,42,65291,43,65294,46,65295,47,65296,48,65297,49,65298,50,65299,51,65300,52,65301,53,65302,54,65303,55,65304,56,65305,57,65308,60,65309,61,65310,62,65312,64,65316,68,65318,70,65319,71,65324,76,65329,81,65330,82,65333,85,65334,86,65335,87,65343,95,65346,98,65348,100,65350,102,65355,107,65357,109,65358,110,65361,113,65362,114,65364,116,65365,117,65367,119,65370,122,65371,123,65373,125],\"_default\":[160,32,8211,45,65374,126,65306,58,65281,33,8216,96,8217,96,8245,96,180,96,12494,47,1047,51,1073,54,1072,97,1040,65,1068,98,1042,66,1089,99,1057,67,1077,101,1045,69,1053,72,305,105,1050,75,921,73,1052,77,1086,111,1054,79,1009,112,1088,112,1056,80,1075,114,1058,84,215,120,1093,120,1061,88,1091,121,1059,89,65283,35,65288,40,65289,41,65292,44,65307,59,65311,63],\"cs\":[65374,126,65306,58,65281,33,8216,96,8217,96,8245,96,180,96,12494,47,1047,51,1073,54,1072,97,1040,65,1068,98,1042,66,1089,99,1057,67,1077,101,1045,69,1053,72,305,105,1050,75,921,73,1052,77,1086,111,1054,79,1009,112,1088,112,1056,80,1075,114,1058,84,1093,120,1061,88,1091,121,1059,89,65283,35,65288,40,65289,41,65292,44,65307,59,65311,63],\"de\":[65374,126,65306,58,65281,33,8216,96,8217,96,8245,96,180,96,12494,47,1047,51,1073,54,1072,97,1040,65,1068,98,1042,66,1089,99,1057,67,1077,101,1045,69,1053,72,305,105,1050,75,921,73,1052,77,1086,111,1054,79,1009,112,1088,112,1056,80,1075,114,1058,84,1093,120,1061,88,1091,121,1059,89,65283,35,65288,40,65289,41,65292,44,65307,59,65311,63],\"es\":[8211,45,65374,126,65306,58,65281,33,8245,96,180,96,12494,47,1047,51,1073,54,1072,97,1040,65,1068,98,1042,66,1089,99,1057,67,1077,101,1045,69,1053,72,305,105,1050,75,1052,77,1086,111,1054,79,1009,112,1088,112,1056,80,1075,114,1058,84,215,120,1093,120,1061,88,1091,121,1059,89,65283,35,65288,40,65289,41,65292,44,65307,59,65311,63],\"fr\":[65374,126,65306,58,65281,33,8216,96,8245,96,12494,47,1047,51,1073,54,1072,97,1040,65,1068,98,1042,66,1089,99,1057,67,1077,101,1045,69,1053,72,305,105,1050,75,921,73,1052,77,1086,111,1054,79,1009,112,1088,112,1056,80,1075,114,1058,84,215,120,1093,120,1061,88,1091,121,1059,89,65283,35,65288,40,65289,41,65292,44,65307,59,65311,63],\"it\":[160,32,8211,45,65374,126,65306,58,65281,33,8216,96,8245,96,180,96,12494,47,1047,51,1073,54,1072,97,1040,65,1068,98,1042,66,1089,99,1057,67,1077,101,1045,69,1053,72,305,105,1050,75,921,73,1052,77,1086,111,1054,79,1009,112,1088,112,1056,80,1075,114,1058,84,215,120,1093,120,1061,88,1091,121,1059,89,65283,35,65288,40,65289,41,65292,44,65307,59,65311,63],\"ja\":[8211,45,65306,58,65281,33,8216,96,8217,96,8245,96,180,96,1047,51,1073,54,1072,97,1040,65,1068,98,1042,66,1089,99,1057,67,1077,101,1045,69,1053,72,305,105,1050,75,921,73,1052,77,1086,111,1054,79,1009,112,1088,112,1056,80,1075,114,1058,84,215,120,1093,120,1061,88,1091,121,1059,89,65283,35,65292,44,65307,59],\"ko\":[8211,45,65374,126,65306,58,65281,33,8245,96,180,96,12494,47,1047,51,1073,54,1072,97,1040,65,1068,98,1042,66,1089,99,1057,67,1077,101,1045,69,1053,72,305,105,1050,75,921,73,1052,77,1086,111,1054,79,1009,112,1088,112,1056,80,1075,114,1058,84,215,120,1093,120,1061,88,1091,121,1059,89,65283,35,65288,40,65289,41,65292,44,65307,59,65311,63],\"pl\":[65374,126,65306,58,65281,33,8216,96,8217,96,8245,96,180,96,12494,47,1047,51,1073,54,1072,97,1040,65,1068,98,1042,66,1089,99,1057,67,1077,101,1045,69,1053,72,305,105,1050,75,921,73,1052,77,1086,111,1054,79,1009,112,1088,112,1056,80,1075,114,1058,84,215,120,1093,120,1061,88,1091,121,1059,89,65283,35,65288,40,65289,41,65292,44,65307,59,65311,63],\"pt-BR\":[65374,126,65306,58,65281,33,8216,96,8217,96,8245,96,180,96,12494,47,1047,51,1073,54,1072,97,1040,65,1068,98,1042,66,1089,99,1057,67,1077,101,1045,69,1053,72,305,105,1050,75,921,73,1052,77,1086,111,1054,79,1009,112,1088,112,1056,80,1075,114,1058,84,215,120,1093,120,1061,88,1091,121,1059,89,65283,35,65288,40,65289,41,65292,44,65307,59,65311,63],\"qps-ploc\":[160,32,8211,45,65374,126,65306,58,65281,33,8216,96,8217,96,8245,96,180,96,12494,47,1047,51,1073,54,1072,97,1040,65,1068,98,1042,66,1089,99,1057,67,1077,101,1045,69,1053,72,305,105,1050,75,921,73,1052,77,1086,111,1054,79,1088,112,1056,80,1075,114,1058,84,215,120,1093,120,1061,88,1091,121,1059,89,65283,35,65288,40,65289,41,65292,44,65307,59,65311,63],\"ru\":[65374,126,65306,58,65281,33,8216,96,8217,96,8245,96,180,96,12494,47,305,105,921,73,1009,112,215,120,65283,35,65288,40,65289,41,65292,44,65307,59,65311,63],\"tr\":[160,32,8211,45,65374,126,65306,58,65281,33,8245,96,180,96,12494,47,1047,51,1073,54,1072,97,1040,65,1068,98,1042,66,1089,99,1057,67,1077,101,1045,69,1053,72,1050,75,921,73,1052,77,1086,111,1054,79,1009,112,1088,112,1056,80,1075,114,1058,84,215,120,1093,120,1061,88,1091,121,1059,89,65283,35,65288,40,65289,41,65292,44,65307,59,65311,63],\"zh-hans\":[65374,126,65306,58,65281,33,8245,96,180,96,12494,47,1047,51,1073,54,1072,97,1040,65,1068,98,1042,66,1089,99,1057,67,1077,101,1045,69,1053,72,305,105,1050,75,921,73,1052,77,1086,111,1054,79,1009,112,1088,112,1056,80,1075,114,1058,84,215,120,1093,120,1061,88,1091,121,1059,89,65288,40,65289,41],\"zh-hant\":[8211,45,65374,126,180,96,12494,47,1047,51,1073,54,1072,97,1040,65,1068,98,1042,66,1089,99,1057,67,1077,101,1045,69,1053,72,305,105,1050,75,921,73,1052,77,1086,111,1054,79,1009,112,1088,112,1056,80,1075,114,1058,84,215,120,1093,120,1061,88,1091,121,1059,89,65283,35,65307,59]}"
\ No newline at end of file
diff --git a/modules/charset/ambiguous/generate.go b/modules/charset/ambiguous/generate.go
new file mode 100644
index 0000000000..7dd2821aae
--- /dev/null
+++ b/modules/charset/ambiguous/generate.go
@@ -0,0 +1,189 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "bytes"
+ "flag"
+ "fmt"
+ "go/format"
+ "os"
+ "sort"
+ "text/template"
+ "unicode"
+
+ "code.gitea.io/gitea/modules/json"
+
+ "golang.org/x/text/unicode/rangetable"
+)
+
+// ambiguous.json provides a one to one mapping of ambiguous characters to other characters
+// See https://github.com/hediet/vscode-unicode-data/blob/main/out/ambiguous.json
+
+type AmbiguousTable struct {
+ Confusable []rune
+ With []rune
+ Locale string
+ RangeTable *unicode.RangeTable
+}
+
+type RunePair struct {
+ Confusable rune
+ With rune
+}
+
+var verbose bool
+
+func main() {
+ flag.Usage = func() {
+ fmt.Fprintf(os.Stderr, `%s: Generate AmbiguousCharacter
+
+Usage: %[1]s [-v] [-o output.go] ambiguous.json
+`, os.Args[0])
+ flag.PrintDefaults()
+ }
+
+ output := ""
+ flag.BoolVar(&verbose, "v", false, "verbose output")
+ flag.StringVar(&output, "o", "ambiguous_gen.go", "file to output to")
+ flag.Parse()
+ input := flag.Arg(0)
+ if input == "" {
+ input = "ambiguous.json"
+ }
+
+ bs, err := os.ReadFile(input)
+ if err != nil {
+ fatalf("Unable to read: %s Err: %v", input, err)
+ }
+
+ var unwrapped string
+ if err := json.Unmarshal(bs, &unwrapped); err != nil {
+ fatalf("Unable to unwrap content in: %s Err: %v", input, err)
+ }
+
+ fromJSON := map[string][]uint32{}
+ if err := json.Unmarshal([]byte(unwrapped), &fromJSON); err != nil {
+ fatalf("Unable to unmarshal content in: %s Err: %v", input, err)
+ }
+
+ tables := make([]*AmbiguousTable, 0, len(fromJSON))
+ for locale, chars := range fromJSON {
+ table := &AmbiguousTable{Locale: locale}
+ table.Confusable = make([]rune, 0, len(chars)/2)
+ table.With = make([]rune, 0, len(chars)/2)
+ pairs := make([]RunePair, len(chars)/2)
+ for i := 0; i < len(chars); i += 2 {
+ pairs[i/2].Confusable, pairs[i/2].With = rune(chars[i]), rune(chars[i+1])
+ }
+ sort.Slice(pairs, func(i, j int) bool {
+ return pairs[i].Confusable < pairs[j].Confusable
+ })
+ for _, pair := range pairs {
+ table.Confusable = append(table.Confusable, pair.Confusable)
+ table.With = append(table.With, pair.With)
+ }
+ table.RangeTable = rangetable.New(table.Confusable...)
+ tables = append(tables, table)
+ }
+ sort.Slice(tables, func(i, j int) bool {
+ return tables[i].Locale < tables[j].Locale
+ })
+ data := map[string]interface{}{
+ "Tables": tables,
+ }
+
+ if err := runTemplate(generatorTemplate, output, &data); err != nil {
+ fatalf("Unable to run template: %v", err)
+ }
+}
+
+func runTemplate(t *template.Template, filename string, data interface{}) error {
+ buf := bytes.NewBuffer(nil)
+ if err := t.Execute(buf, data); err != nil {
+ return fmt.Errorf("unable to execute template: %w", err)
+ }
+ bs, err := format.Source(buf.Bytes())
+ if err != nil {
+ verbosef("Bad source:\n%s", buf.String())
+ return fmt.Errorf("unable to format source: %w", err)
+ }
+
+ old, err := os.ReadFile(filename)
+ if err != nil && !os.IsNotExist(err) {
+ return fmt.Errorf("failed to read old file %s because %w", filename, err)
+ } else if err == nil {
+ if bytes.Equal(bs, old) {
+ // files are the same don't rewrite it.
+ return nil
+ }
+ }
+
+ file, err := os.Create(filename)
+ if err != nil {
+ return fmt.Errorf("failed to create file %s because %w", filename, err)
+ }
+ defer file.Close()
+ _, err = file.Write(bs)
+ if err != nil {
+ return fmt.Errorf("unable to write generated source: %w", err)
+ }
+ return nil
+}
+
+var generatorTemplate = template.Must(template.New("ambiguousTemplate").Parse(`// This file is generated by modules/charset/ambiguous/generate.go DO NOT EDIT
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package charset
+
+import "unicode"
+
+// This file is generated from https://github.com/hediet/vscode-unicode-data/blob/main/out/ambiguous.json
+
+// AmbiguousTable matches a confusable rune with its partner for the Locale
+type AmbiguousTable struct {
+ Confusable []rune
+ With []rune
+ Locale string
+ RangeTable *unicode.RangeTable
+}
+
+// AmbiguousCharacters provides a map by locale name to the confusable characters in that locale
+var AmbiguousCharacters = map[string]*AmbiguousTable{
+ {{range .Tables}}{{printf "%q:" .Locale}} {
+ Confusable: []rune{ {{range .Confusable}}{{.}},{{end}} },
+ With: []rune{ {{range .With}}{{.}},{{end}} },
+ Locale: {{printf "%q" .Locale}},
+ RangeTable: &unicode.RangeTable{
+ R16: []unicode.Range16{
+ {{range .RangeTable.R16 }} {Lo:{{.Lo}}, Hi:{{.Hi}}, Stride: {{.Stride}}},
+ {{end}} },
+ R32: []unicode.Range32{
+ {{range .RangeTable.R32}} {Lo:{{.Lo}}, Hi:{{.Hi}}, Stride: {{.Stride}}},
+ {{end}} },
+ LatinOffset: {{.RangeTable.LatinOffset}},
+ },
+ },
+ {{end}}
+}
+
+`))
+
+func logf(format string, args ...interface{}) {
+ fmt.Fprintf(os.Stderr, format+"\n", args...)
+}
+
+func verbosef(format string, args ...interface{}) {
+ if verbose {
+ logf(format, args...)
+ }
+}
+
+func fatalf(format string, args ...interface{}) {
+ logf("fatal: "+format+"\n", args...)
+ os.Exit(1)
+}
diff --git a/modules/charset/ambiguous_gen.go b/modules/charset/ambiguous_gen.go
new file mode 100644
index 0000000000..cc270affac
--- /dev/null
+++ b/modules/charset/ambiguous_gen.go
@@ -0,0 +1,837 @@
+// This file is generated by modules/charset/ambiguous/generate.go DO NOT EDIT
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package charset
+
+import "unicode"
+
+// This file is generated from https://github.com/hediet/vscode-unicode-data/blob/main/out/ambiguous.json
+
+// AmbiguousTable matches a confusable rune with its partner for the Locale
+type AmbiguousTable struct {
+ Confusable []rune
+ With []rune
+ Locale string
+ RangeTable *unicode.RangeTable
+}
+
+// AmbiguousCharacters provides a map by locale name to the confusable characters in that locale
+var AmbiguousCharacters = map[string]*AmbiguousTable{
+ "_common": {
+ Confusable: []rune{184, 383, 388, 397, 422, 423, 439, 444, 445, 448, 451, 540, 546, 547, 577, 593, 609, 611, 617, 618, 623, 651, 655, 660, 697, 699, 700, 701, 702, 706, 707, 708, 710, 712, 714, 715, 720, 727, 731, 732, 756, 760, 884, 890, 894, 895, 900, 913, 914, 917, 918, 919, 922, 924, 925, 927, 929, 932, 933, 935, 945, 947, 953, 957, 959, 961, 963, 965, 978, 988, 1000, 1010, 1011, 1017, 1018, 1029, 1030, 1032, 1109, 1110, 1112, 1121, 1140, 1141, 1198, 1199, 1211, 1213, 1216, 1231, 1248, 1281, 1292, 1307, 1308, 1309, 1357, 1359, 1365, 1370, 1373, 1377, 1379, 1382, 1392, 1400, 1404, 1405, 1409, 1412, 1413, 1417, 1472, 1475, 1493, 1496, 1497, 1503, 1505, 1523, 1549, 1575, 1607, 1632, 1633, 1637, 1639, 1643, 1645, 1726, 1729, 1748, 1749, 1776, 1777, 1781, 1783, 1793, 1794, 1795, 1796, 1984, 1994, 2036, 2037, 2042, 2307, 2406, 2429, 2534, 2538, 2541, 2662, 2663, 2666, 2691, 2790, 2819, 2848, 2918, 2920, 3046, 3074, 3174, 3202, 3302, 3330, 3360, 3430, 3437, 3458, 3664, 3792, 4125, 4160, 4327, 4351, 4608, 4816, 5024, 5025, 5026, 5029, 5033, 5034, 5035, 5036, 5038, 5043, 5047, 5051, 5053, 5056, 5058, 5059, 5070, 5071, 5074, 5076, 5077, 5081, 5082, 5086, 5087, 5090, 5094, 5095, 5102, 5107, 5108, 5120, 5167, 5171, 5176, 5194, 5196, 5229, 5231, 5234, 5261, 5290, 5311, 5441, 5500, 5501, 5511, 5551, 5556, 5573, 5598, 5610, 5616, 5623, 5741, 5742, 5760, 5810, 5815, 5825, 5836, 5845, 5846, 5868, 5869, 5941, 6147, 6153, 7428, 7439, 7441, 7452, 7456, 7457, 7458, 7462, 7555, 7564, 7837, 7935, 8125, 8126, 8127, 8128, 8175, 8189, 8190, 8192, 8193, 8194, 8195, 8196, 8197, 8198, 8199, 8200, 8201, 8202, 8208, 8209, 8210, 8218, 8219, 8228, 8232, 8233, 8239, 8242, 8249, 8250, 8257, 8259, 8260, 8270, 8275, 8282, 8287, 8450, 8458, 8459, 8460, 8461, 8462, 8464, 8465, 8466, 8467, 8469, 8473, 8474, 8475, 8476, 8477, 8484, 8488, 8490, 8492, 8493, 8494, 8495, 8496, 8497, 8499, 8500, 8505, 8509, 8517, 8518, 8519, 8520, 8521, 8544, 8548, 8553, 8556, 8557, 8558, 8559, 8560, 8564, 8569, 8572, 8573, 8574, 8722, 8725, 8726, 8727, 8739, 8744, 8746, 8758, 8764, 8868, 8897, 8899, 8959, 9075, 9076, 9082, 9213, 9585, 9587, 10088, 10089, 10094, 10095, 10098, 10099, 10100, 10101, 10133, 10134, 10187, 10189, 10201, 10539, 10540, 10741, 10744, 10745, 10799, 11397, 11406, 11410, 11412, 11416, 11418, 11422, 11423, 11426, 11427, 11428, 11429, 11430, 11432, 11436, 11450, 11462, 11466, 11468, 11472, 11474, 11576, 11577, 11599, 11601, 11604, 11605, 11613, 11840, 12034, 12035, 12295, 12308, 12309, 12339, 12448, 12755, 12756, 20022, 20031, 42192, 42193, 42194, 42195, 42196, 42198, 42199, 42201, 42202, 42204, 42205, 42207, 42208, 42209, 42210, 42211, 42214, 42215, 42218, 42219, 42220, 42222, 42224, 42226, 42227, 42228, 42232, 42233, 42237, 42239, 42510, 42564, 42567, 42719, 42731, 42735, 42801, 42842, 42858, 42862, 42872, 42889, 42892, 42904, 42905, 42911, 42923, 42930, 42931, 42932, 43826, 43829, 43837, 43847, 43848, 43854, 43858, 43866, 43893, 43905, 43907, 43923, 43945, 43946, 43951, 64422, 64423, 64424, 64425, 64426, 64427, 64428, 64429, 64830, 64831, 65072, 65101, 65102, 65103, 65112, 65128, 65165, 65166, 65257, 65258, 65259, 65260, 65282, 65284, 65285, 65286, 65287, 65290, 65291, 65293, 65294, 65295, 65296, 65297, 65298, 65299, 65300, 65301, 65302, 65303, 65304, 65305, 65308, 65309, 65310, 65312, 65313, 65314, 65315, 65316, 65317, 65318, 65319, 65320, 65321, 65322, 65323, 65324, 65325, 65326, 65327, 65328, 65329, 65330, 65331, 65332, 65333, 65334, 65335, 65336, 65337, 65338, 65339, 65340, 65341, 65342, 65343, 65344, 65345, 65346, 65347, 65348, 65349, 65350, 65351, 65352, 65353, 65354, 65355, 65356, 65357, 65358, 65359, 65360, 65361, 65362, 65363, 65364, 65365, 65366, 65367, 65368, 65369, 65370, 65371, 65372, 65373, 65512, 66178, 66182, 66183, 66186, 66192, 66194, 66197, 66198, 66199, 66203, 66208, 66209, 66210, 66213, 66219, 66224, 66225, 66226, 66228, 66255, 66293, 66305, 66306, 66313, 66321, 66325, 66327, 66330, 66335, 66336, 66338, 66564, 66581, 66587, 66592, 66604, 66621, 66632, 66740, 66754, 66766, 66770, 66794, 66806, 66835, 66838, 66840, 66844, 66845, 66853, 66854, 66855, 68176, 70864, 71430, 71434, 71438, 71439, 71840, 71842, 71843, 71844, 71846, 71849, 71852, 71854, 71855, 71858, 71861, 71864, 71867, 71868, 71872, 71873, 71874, 71875, 71876, 71878, 71880, 71882, 71884, 71893, 71894, 71895, 71896, 71900, 71904, 71909, 71910, 71913, 71916, 71919, 71922, 93960, 93962, 93974, 93992, 94005, 94010, 94011, 94015, 94016, 94018, 94019, 94033, 94034, 119060, 119149, 119302, 119309, 119311, 119314, 119315, 119318, 119338, 119350, 119351, 119354, 119355, 119808, 119809, 119810, 119811, 119812, 119813, 119814, 119815, 119816, 119817, 119818, 119819, 119820, 119821, 119822, 119823, 119824, 119825, 119826, 119827, 119828, 119829, 119830, 119831, 119832, 119833, 119834, 119835, 119836, 119837, 119838, 119839, 119840, 119841, 119842, 119843, 119844, 119845, 119847, 119848, 119849, 119850, 119851, 119852, 119853, 119854, 119855, 119856, 119857, 119858, 119859, 119860, 119861, 119862, 119863, 119864, 119865, 119866, 119867, 119868, 119869, 119870, 119871, 119872, 119873, 119874, 119875, 119876, 119877, 119878, 119879, 119880, 119881, 119882, 119883, 119884, 119885, 119886, 119887, 119888, 119889, 119890, 119891, 119892, 119894, 119895, 119896, 119897, 119899, 119900, 119901, 119902, 119903, 119904, 119905, 119906, 119907, 119908, 119909, 119910, 119911, 119912, 119913, 119914, 119915, 119916, 119917, 119918, 119919, 119920, 119921, 119922, 119923, 119924, 119925, 119926, 119927, 119928, 119929, 119930, 119931, 119932, 119933, 119934, 119935, 119936, 119937, 119938, 119939, 119940, 119941, 119942, 119943, 119944, 119945, 119946, 119947, 119948, 119949, 119951, 119952, 119953, 119954, 119955, 119956, 119957, 119958, 119959, 119960, 119961, 119962, 119963, 119964, 119966, 119967, 119970, 119973, 119974, 119977, 119978, 119979, 119980, 119982, 119983, 119984, 119985, 119986, 119987, 119988, 119989, 119990, 119991, 119992, 119993, 119995, 119997, 119998, 119999, 120000, 120001, 120003, 120005, 120006, 120007, 120008, 120009, 120010, 120011, 120012, 120013, 120014, 120015, 120016, 120017, 120018, 120019, 120020, 120021, 120022, 120023, 120024, 120025, 120026, 120027, 120028, 120029, 120030, 120031, 120032, 120033, 120034, 120035, 120036, 120037, 120038, 120039, 120040, 120041, 120042, 120043, 120044, 120045, 120046, 120047, 120048, 120049, 120050, 120051, 120052, 120053, 120055, 120056, 120057, 120058, 120059, 120060, 120061, 120062, 120063, 120064, 120065, 120066, 120067, 120068, 120069, 120071, 120072, 120073, 120074, 120077, 120078, 120079, 120080, 120081, 120082, 120083, 120084, 120086, 120087, 120088, 120089, 120090, 120091, 120092, 120094, 120095, 120096, 120097, 120098, 120099, 120100, 120101, 120102, 120103, 120104, 120105, 120107, 120108, 120109, 120110, 120111, 120112, 120113, 120114, 120115, 120116, 120117, 120118, 120119, 120120, 120121, 120123, 120124, 120125, 120126, 120128, 120129, 120130, 120131, 120132, 120134, 120138, 120139, 120140, 120141, 120142, 120143, 120144, 120146, 120147, 120148, 120149, 120150, 120151, 120152, 120153, 120154, 120155, 120156, 120157, 120159, 120160, 120161, 120162, 120163, 120164, 120165, 120166, 120167, 120168, 120169, 120170, 120171, 120172, 120173, 120174, 120175, 120176, 120177, 120178, 120179, 120180, 120181, 120182, 120183, 120184, 120185, 120186, 120187, 120188, 120189, 120190, 120191, 120192, 120193, 120194, 120195, 120196, 120197, 120198, 120199, 120200, 120201, 120202, 120203, 120204, 120205, 120206, 120207, 120208, 120209, 120211, 120212, 120213, 120214, 120215, 120216, 120217, 120218, 120219, 120220, 120221, 120222, 120223, 120224, 120225, 120226, 120227, 120228, 120229, 120230, 120231, 120232, 120233, 120234, 120235, 120236, 120237, 120238, 120239, 120240, 120241, 120242, 120243, 120244, 120245, 120246, 120247, 120248, 120249, 120250, 120251, 120252, 120253, 120254, 120255, 120256, 120257, 120258, 120259, 120260, 120261, 120263, 120264, 120265, 120266, 120267, 120268, 120269, 120270, 120271, 120272, 120273, 120274, 120275, 120276, 120277, 120278, 120279, 120280, 120281, 120282, 120283, 120284, 120285, 120286, 120287, 120288, 120289, 120290, 120291, 120292, 120293, 120294, 120295, 120296, 120297, 120298, 120299, 120300, 120301, 120302, 120303, 120304, 120305, 120306, 120307, 120308, 120309, 120310, 120311, 120312, 120313, 120315, 120316, 120317, 120318, 120319, 120320, 120321, 120322, 120323, 120324, 120325, 120326, 120327, 120328, 120329, 120330, 120331, 120332, 120333, 120334, 120335, 120336, 120337, 120338, 120339, 120340, 120341, 120342, 120343, 120344, 120345, 120346, 120347, 120348, 120349, 120350, 120351, 120352, 120353, 120354, 120355, 120356, 120357, 120358, 120359, 120360, 120361, 120362, 120363, 120364, 120365, 120367, 120368, 120369, 120370, 120371, 120372, 120373, 120374, 120375, 120376, 120377, 120378, 120379, 120380, 120381, 120382, 120383, 120384, 120385, 120386, 120387, 120388, 120389, 120390, 120391, 120392, 120393, 120394, 120395, 120396, 120397, 120398, 120399, 120400, 120401, 120402, 120403, 120404, 120405, 120406, 120407, 120408, 120409, 120410, 120411, 120412, 120413, 120414, 120415, 120416, 120417, 120419, 120420, 120421, 120422, 120423, 120424, 120425, 120426, 120427, 120428, 120429, 120430, 120431, 120432, 120433, 120434, 120435, 120436, 120437, 120438, 120439, 120440, 120441, 120442, 120443, 120444, 120445, 120446, 120447, 120448, 120449, 120450, 120451, 120452, 120453, 120454, 120455, 120456, 120457, 120458, 120459, 120460, 120461, 120462, 120463, 120464, 120465, 120466, 120467, 120468, 120469, 120471, 120472, 120473, 120474, 120475, 120476, 120477, 120478, 120479, 120480, 120481, 120482, 120483, 120484, 120488, 120489, 120492, 120493, 120494, 120496, 120497, 120499, 120500, 120502, 120504, 120507, 120508, 120510, 120514, 120516, 120522, 120526, 120528, 120530, 120532, 120534, 120544, 120546, 120547, 120550, 120551, 120552, 120554, 120555, 120557, 120558, 120560, 120562, 120565, 120566, 120568, 120572, 120574, 120580, 120584, 120586, 120588, 120590, 120592, 120602, 120604, 120605, 120608, 120609, 120610, 120612, 120613, 120615, 120616, 120618, 120620, 120623, 120624, 120626, 120630, 120632, 120638, 120642, 120644, 120646, 120648, 120650, 120660, 120662, 120663, 120666, 120667, 120668, 120670, 120671, 120673, 120674, 120676, 120678, 120681, 120682, 120684, 120688, 120690, 120696, 120700, 120702, 120704, 120706, 120708, 120718, 120720, 120721, 120724, 120725, 120726, 120728, 120729, 120731, 120732, 120734, 120736, 120739, 120740, 120742, 120746, 120748, 120754, 120758, 120760, 120762, 120764, 120766, 120776, 120778, 120782, 120783, 120784, 120785, 120786, 120787, 120788, 120789, 120790, 120791, 120792, 120793, 120794, 120795, 120796, 120797, 120798, 120799, 120800, 120801, 120802, 120803, 120804, 120805, 120806, 120807, 120808, 120809, 120810, 120811, 120812, 120813, 120814, 120815, 120816, 120817, 120818, 120819, 120820, 120821, 120822, 120823, 120824, 120825, 120826, 120827, 120828, 120829, 120830, 120831, 125127, 125131, 126464, 126500, 126564, 126592, 126596, 128844, 128872, 130032, 130033, 130034, 130035, 130036, 130037, 130038, 130039, 130040, 130041},
+ With: []rune{44, 102, 98, 103, 82, 50, 51, 53, 115, 73, 33, 51, 56, 56, 63, 97, 103, 121, 105, 105, 119, 117, 121, 63, 96, 96, 96, 96, 96, 60, 62, 94, 94, 96, 96, 96, 58, 45, 105, 126, 96, 58, 96, 105, 59, 74, 96, 65, 66, 69, 90, 72, 75, 77, 78, 79, 80, 84, 89, 88, 97, 121, 105, 118, 111, 112, 111, 117, 89, 70, 50, 99, 106, 67, 77, 83, 73, 74, 115, 105, 106, 119, 86, 118, 89, 121, 104, 101, 73, 105, 51, 100, 71, 113, 87, 119, 85, 83, 79, 96, 96, 119, 113, 113, 104, 110, 110, 117, 103, 102, 111, 58, 108, 58, 108, 118, 96, 108, 111, 96, 44, 108, 111, 46, 108, 111, 86, 44, 42, 111, 111, 45, 111, 46, 73, 111, 86, 46, 46, 58, 58, 79, 108, 96, 96, 95, 58, 111, 63, 79, 56, 57, 111, 57, 56, 58, 111, 56, 79, 79, 57, 111, 111, 111, 111, 111, 111, 111, 111, 57, 111, 111, 111, 111, 111, 121, 111, 85, 79, 68, 82, 84, 105, 89, 65, 74, 69, 63, 87, 77, 72, 89, 71, 104, 90, 52, 98, 82, 87, 83, 86, 83, 76, 67, 80, 75, 100, 54, 71, 66, 61, 86, 62, 60, 96, 85, 80, 100, 98, 74, 76, 50, 120, 72, 120, 82, 98, 70, 65, 68, 68, 77, 66, 88, 120, 32, 60, 88, 73, 96, 75, 77, 58, 43, 47, 58, 58, 99, 111, 111, 117, 118, 119, 122, 114, 103, 121, 102, 121, 96, 105, 96, 126, 96, 96, 96, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 45, 45, 45, 44, 96, 46, 32, 32, 32, 96, 60, 62, 47, 45, 47, 42, 126, 58, 32, 67, 103, 72, 72, 72, 104, 73, 73, 76, 108, 78, 80, 81, 82, 82, 82, 90, 90, 75, 66, 67, 101, 101, 69, 70, 77, 111, 105, 121, 68, 100, 101, 105, 106, 73, 86, 88, 76, 67, 68, 77, 105, 118, 120, 73, 99, 100, 45, 47, 92, 42, 73, 118, 85, 58, 126, 84, 118, 85, 69, 105, 112, 97, 73, 47, 88, 40, 41, 60, 62, 40, 41, 123, 125, 43, 45, 47, 92, 84, 120, 120, 92, 47, 92, 120, 114, 72, 73, 75, 77, 78, 79, 111, 80, 112, 67, 99, 84, 89, 88, 45, 47, 57, 51, 76, 54, 86, 69, 73, 33, 79, 81, 88, 61, 92, 47, 79, 40, 41, 47, 61, 47, 92, 92, 47, 66, 80, 100, 68, 84, 71, 75, 74, 67, 90, 70, 77, 78, 76, 83, 82, 86, 72, 87, 88, 89, 65, 69, 73, 79, 85, 46, 44, 58, 61, 46, 50, 105, 86, 63, 50, 115, 50, 51, 57, 38, 58, 96, 70, 102, 117, 51, 74, 88, 66, 101, 102, 111, 114, 114, 117, 117, 121, 105, 114, 119, 122, 118, 115, 99, 111, 111, 111, 111, 111, 111, 111, 111, 40, 41, 58, 95, 95, 95, 45, 92, 108, 108, 111, 111, 111, 111, 34, 36, 37, 38, 96, 42, 43, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 60, 61, 62, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 73, 66, 69, 70, 124, 88, 79, 80, 83, 84, 43, 65, 66, 67, 70, 79, 77, 84, 89, 88, 72, 90, 66, 67, 124, 77, 84, 88, 56, 42, 108, 88, 79, 67, 76, 83, 111, 99, 115, 82, 79, 85, 55, 111, 117, 78, 79, 75, 67, 86, 70, 76, 88, 46, 79, 118, 119, 119, 119, 86, 70, 76, 89, 69, 90, 57, 69, 52, 76, 79, 85, 53, 84, 118, 115, 70, 105, 122, 55, 111, 51, 57, 54, 57, 111, 117, 121, 79, 90, 87, 67, 88, 87, 67, 86, 84, 76, 73, 82, 83, 51, 62, 65, 85, 89, 96, 96, 123, 46, 51, 86, 92, 55, 70, 82, 76, 60, 62, 47, 92, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 97, 98, 99, 100, 101, 102, 103, 105, 106, 107, 108, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 65, 67, 68, 71, 74, 75, 78, 79, 80, 81, 83, 84, 85, 86, 87, 88, 89, 90, 97, 98, 99, 100, 102, 104, 105, 106, 107, 108, 110, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 65, 66, 68, 69, 70, 71, 74, 75, 76, 77, 78, 79, 80, 81, 83, 84, 85, 86, 87, 88, 89, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 73, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 65, 66, 68, 69, 70, 71, 73, 74, 75, 76, 77, 79, 83, 84, 85, 86, 87, 88, 89, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 73, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 73, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 73, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 73, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 73, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 73, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 73, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 105, 65, 66, 69, 90, 72, 73, 75, 77, 78, 79, 80, 84, 89, 88, 97, 121, 105, 118, 111, 112, 111, 117, 112, 65, 66, 69, 90, 72, 73, 75, 77, 78, 79, 80, 84, 89, 88, 97, 121, 105, 118, 111, 112, 111, 117, 112, 65, 66, 69, 90, 72, 73, 75, 77, 78, 79, 80, 84, 89, 88, 97, 121, 105, 118, 111, 112, 111, 117, 112, 65, 66, 69, 90, 72, 73, 75, 77, 78, 79, 80, 84, 89, 88, 97, 121, 105, 118, 111, 112, 111, 117, 112, 65, 66, 69, 90, 72, 73, 75, 77, 78, 79, 80, 84, 89, 88, 97, 121, 105, 118, 111, 112, 111, 117, 112, 70, 79, 73, 50, 51, 52, 53, 54, 55, 56, 57, 79, 73, 50, 51, 52, 53, 54, 55, 56, 57, 79, 73, 50, 51, 52, 53, 54, 55, 56, 57, 79, 73, 50, 51, 52, 53, 54, 55, 56, 57, 79, 73, 50, 51, 52, 53, 54, 55, 56, 57, 108, 56, 108, 111, 111, 108, 111, 67, 84, 79, 73, 50, 51, 52, 53, 54, 55, 56, 57},
+ Locale: "_common",
+ RangeTable: &unicode.RangeTable{
+ R16: []unicode.Range16{
+ {Lo: 184, Hi: 383, Stride: 199},
+ {Lo: 388, Hi: 397, Stride: 9},
+ {Lo: 422, Hi: 423, Stride: 1},
+ {Lo: 439, Hi: 444, Stride: 5},
+ {Lo: 445, Hi: 451, Stride: 3},
+ {Lo: 540, Hi: 546, Stride: 6},
+ {Lo: 547, Hi: 577, Stride: 30},
+ {Lo: 593, Hi: 609, Stride: 16},
+ {Lo: 611, Hi: 617, Stride: 6},
+ {Lo: 618, Hi: 623, Stride: 5},
+ {Lo: 651, Hi: 655, Stride: 4},
+ {Lo: 660, Hi: 697, Stride: 37},
+ {Lo: 699, Hi: 702, Stride: 1},
+ {Lo: 706, Hi: 708, Stride: 1},
+ {Lo: 710, Hi: 714, Stride: 2},
+ {Lo: 715, Hi: 720, Stride: 5},
+ {Lo: 727, Hi: 731, Stride: 4},
+ {Lo: 732, Hi: 756, Stride: 24},
+ {Lo: 760, Hi: 884, Stride: 124},
+ {Lo: 890, Hi: 894, Stride: 4},
+ {Lo: 895, Hi: 900, Stride: 5},
+ {Lo: 913, Hi: 914, Stride: 1},
+ {Lo: 917, Hi: 919, Stride: 1},
+ {Lo: 922, Hi: 924, Stride: 2},
+ {Lo: 925, Hi: 929, Stride: 2},
+ {Lo: 932, Hi: 933, Stride: 1},
+ {Lo: 935, Hi: 945, Stride: 10},
+ {Lo: 947, Hi: 953, Stride: 6},
+ {Lo: 957, Hi: 965, Stride: 2},
+ {Lo: 978, Hi: 988, Stride: 10},
+ {Lo: 1000, Hi: 1010, Stride: 10},
+ {Lo: 1011, Hi: 1017, Stride: 6},
+ {Lo: 1018, Hi: 1029, Stride: 11},
+ {Lo: 1030, Hi: 1032, Stride: 2},
+ {Lo: 1109, Hi: 1110, Stride: 1},
+ {Lo: 1112, Hi: 1121, Stride: 9},
+ {Lo: 1140, Hi: 1141, Stride: 1},
+ {Lo: 1198, Hi: 1199, Stride: 1},
+ {Lo: 1211, Hi: 1213, Stride: 2},
+ {Lo: 1216, Hi: 1231, Stride: 15},
+ {Lo: 1248, Hi: 1281, Stride: 33},
+ {Lo: 1292, Hi: 1307, Stride: 15},
+ {Lo: 1308, Hi: 1309, Stride: 1},
+ {Lo: 1357, Hi: 1359, Stride: 2},
+ {Lo: 1365, Hi: 1370, Stride: 5},
+ {Lo: 1373, Hi: 1377, Stride: 4},
+ {Lo: 1379, Hi: 1382, Stride: 3},
+ {Lo: 1392, Hi: 1400, Stride: 8},
+ {Lo: 1404, Hi: 1405, Stride: 1},
+ {Lo: 1409, Hi: 1412, Stride: 3},
+ {Lo: 1413, Hi: 1417, Stride: 4},
+ {Lo: 1472, Hi: 1475, Stride: 3},
+ {Lo: 1493, Hi: 1496, Stride: 3},
+ {Lo: 1497, Hi: 1503, Stride: 6},
+ {Lo: 1505, Hi: 1523, Stride: 18},
+ {Lo: 1549, Hi: 1575, Stride: 26},
+ {Lo: 1607, Hi: 1632, Stride: 25},
+ {Lo: 1633, Hi: 1637, Stride: 4},
+ {Lo: 1639, Hi: 1643, Stride: 4},
+ {Lo: 1645, Hi: 1726, Stride: 81},
+ {Lo: 1729, Hi: 1748, Stride: 19},
+ {Lo: 1749, Hi: 1776, Stride: 27},
+ {Lo: 1777, Hi: 1781, Stride: 4},
+ {Lo: 1783, Hi: 1793, Stride: 10},
+ {Lo: 1794, Hi: 1796, Stride: 1},
+ {Lo: 1984, Hi: 1994, Stride: 10},
+ {Lo: 2036, Hi: 2037, Stride: 1},
+ {Lo: 2042, Hi: 2307, Stride: 265},
+ {Lo: 2406, Hi: 2429, Stride: 23},
+ {Lo: 2534, Hi: 2538, Stride: 4},
+ {Lo: 2541, Hi: 2662, Stride: 121},
+ {Lo: 2663, Hi: 2666, Stride: 3},
+ {Lo: 2691, Hi: 2790, Stride: 99},
+ {Lo: 2819, Hi: 2848, Stride: 29},
+ {Lo: 2918, Hi: 2920, Stride: 2},
+ {Lo: 3046, Hi: 3074, Stride: 28},
+ {Lo: 3174, Hi: 3202, Stride: 28},
+ {Lo: 3302, Hi: 3330, Stride: 28},
+ {Lo: 3360, Hi: 3430, Stride: 70},
+ {Lo: 3437, Hi: 3458, Stride: 21},
+ {Lo: 3664, Hi: 3792, Stride: 128},
+ {Lo: 4125, Hi: 4160, Stride: 35},
+ {Lo: 4327, Hi: 4351, Stride: 24},
+ {Lo: 4608, Hi: 5024, Stride: 208},
+ {Lo: 5025, Hi: 5026, Stride: 1},
+ {Lo: 5029, Hi: 5033, Stride: 4},
+ {Lo: 5034, Hi: 5036, Stride: 1},
+ {Lo: 5038, Hi: 5043, Stride: 5},
+ {Lo: 5047, Hi: 5051, Stride: 4},
+ {Lo: 5053, Hi: 5056, Stride: 3},
+ {Lo: 5058, Hi: 5059, Stride: 1},
+ {Lo: 5070, Hi: 5071, Stride: 1},
+ {Lo: 5074, Hi: 5076, Stride: 2},
+ {Lo: 5077, Hi: 5081, Stride: 4},
+ {Lo: 5082, Hi: 5086, Stride: 4},
+ {Lo: 5087, Hi: 5090, Stride: 3},
+ {Lo: 5094, Hi: 5095, Stride: 1},
+ {Lo: 5102, Hi: 5107, Stride: 5},
+ {Lo: 5108, Hi: 5120, Stride: 12},
+ {Lo: 5167, Hi: 5171, Stride: 4},
+ {Lo: 5176, Hi: 5194, Stride: 18},
+ {Lo: 5196, Hi: 5229, Stride: 33},
+ {Lo: 5231, Hi: 5234, Stride: 3},
+ {Lo: 5261, Hi: 5290, Stride: 29},
+ {Lo: 5311, Hi: 5441, Stride: 130},
+ {Lo: 5500, Hi: 5501, Stride: 1},
+ {Lo: 5511, Hi: 5551, Stride: 40},
+ {Lo: 5556, Hi: 5573, Stride: 17},
+ {Lo: 5598, Hi: 5610, Stride: 12},
+ {Lo: 5616, Hi: 5623, Stride: 7},
+ {Lo: 5741, Hi: 5742, Stride: 1},
+ {Lo: 5760, Hi: 5810, Stride: 50},
+ {Lo: 5815, Hi: 5825, Stride: 10},
+ {Lo: 5836, Hi: 5845, Stride: 9},
+ {Lo: 5846, Hi: 5868, Stride: 22},
+ {Lo: 5869, Hi: 5941, Stride: 72},
+ {Lo: 6147, Hi: 6153, Stride: 6},
+ {Lo: 7428, Hi: 7439, Stride: 11},
+ {Lo: 7441, Hi: 7452, Stride: 11},
+ {Lo: 7456, Hi: 7458, Stride: 1},
+ {Lo: 7462, Hi: 7555, Stride: 93},
+ {Lo: 7564, Hi: 7837, Stride: 273},
+ {Lo: 7935, Hi: 8125, Stride: 190},
+ {Lo: 8126, Hi: 8128, Stride: 1},
+ {Lo: 8175, Hi: 8189, Stride: 14},
+ {Lo: 8190, Hi: 8192, Stride: 2},
+ {Lo: 8193, Hi: 8202, Stride: 1},
+ {Lo: 8208, Hi: 8210, Stride: 1},
+ {Lo: 8218, Hi: 8219, Stride: 1},
+ {Lo: 8228, Hi: 8232, Stride: 4},
+ {Lo: 8233, Hi: 8239, Stride: 6},
+ {Lo: 8242, Hi: 8249, Stride: 7},
+ {Lo: 8250, Hi: 8257, Stride: 7},
+ {Lo: 8259, Hi: 8260, Stride: 1},
+ {Lo: 8270, Hi: 8275, Stride: 5},
+ {Lo: 8282, Hi: 8287, Stride: 5},
+ {Lo: 8450, Hi: 8458, Stride: 8},
+ {Lo: 8459, Hi: 8462, Stride: 1},
+ {Lo: 8464, Hi: 8467, Stride: 1},
+ {Lo: 8469, Hi: 8473, Stride: 4},
+ {Lo: 8474, Hi: 8477, Stride: 1},
+ {Lo: 8484, Hi: 8488, Stride: 4},
+ {Lo: 8490, Hi: 8492, Stride: 2},
+ {Lo: 8493, Hi: 8497, Stride: 1},
+ {Lo: 8499, Hi: 8500, Stride: 1},
+ {Lo: 8505, Hi: 8509, Stride: 4},
+ {Lo: 8517, Hi: 8521, Stride: 1},
+ {Lo: 8544, Hi: 8548, Stride: 4},
+ {Lo: 8553, Hi: 8556, Stride: 3},
+ {Lo: 8557, Hi: 8560, Stride: 1},
+ {Lo: 8564, Hi: 8569, Stride: 5},
+ {Lo: 8572, Hi: 8574, Stride: 1},
+ {Lo: 8722, Hi: 8725, Stride: 3},
+ {Lo: 8726, Hi: 8727, Stride: 1},
+ {Lo: 8739, Hi: 8744, Stride: 5},
+ {Lo: 8746, Hi: 8758, Stride: 12},
+ {Lo: 8764, Hi: 8868, Stride: 104},
+ {Lo: 8897, Hi: 8899, Stride: 2},
+ {Lo: 8959, Hi: 9075, Stride: 116},
+ {Lo: 9076, Hi: 9082, Stride: 6},
+ {Lo: 9213, Hi: 9585, Stride: 372},
+ {Lo: 9587, Hi: 10088, Stride: 501},
+ {Lo: 10089, Hi: 10094, Stride: 5},
+ {Lo: 10095, Hi: 10098, Stride: 3},
+ {Lo: 10099, Hi: 10101, Stride: 1},
+ {Lo: 10133, Hi: 10134, Stride: 1},
+ {Lo: 10187, Hi: 10189, Stride: 2},
+ {Lo: 10201, Hi: 10539, Stride: 338},
+ {Lo: 10540, Hi: 10741, Stride: 201},
+ {Lo: 10744, Hi: 10745, Stride: 1},
+ {Lo: 10799, Hi: 11397, Stride: 598},
+ {Lo: 11406, Hi: 11410, Stride: 4},
+ {Lo: 11412, Hi: 11416, Stride: 4},
+ {Lo: 11418, Hi: 11422, Stride: 4},
+ {Lo: 11423, Hi: 11426, Stride: 3},
+ {Lo: 11427, Hi: 11430, Stride: 1},
+ {Lo: 11432, Hi: 11436, Stride: 4},
+ {Lo: 11450, Hi: 11462, Stride: 12},
+ {Lo: 11466, Hi: 11468, Stride: 2},
+ {Lo: 11472, Hi: 11474, Stride: 2},
+ {Lo: 11576, Hi: 11577, Stride: 1},
+ {Lo: 11599, Hi: 11601, Stride: 2},
+ {Lo: 11604, Hi: 11605, Stride: 1},
+ {Lo: 11613, Hi: 11840, Stride: 227},
+ {Lo: 12034, Hi: 12035, Stride: 1},
+ {Lo: 12295, Hi: 12308, Stride: 13},
+ {Lo: 12309, Hi: 12339, Stride: 30},
+ {Lo: 12448, Hi: 12755, Stride: 307},
+ {Lo: 12756, Hi: 20022, Stride: 7266},
+ {Lo: 20031, Hi: 42192, Stride: 22161},
+ {Lo: 42193, Hi: 42196, Stride: 1},
+ {Lo: 42198, Hi: 42199, Stride: 1},
+ {Lo: 42201, Hi: 42202, Stride: 1},
+ {Lo: 42204, Hi: 42205, Stride: 1},
+ {Lo: 42207, Hi: 42211, Stride: 1},
+ {Lo: 42214, Hi: 42215, Stride: 1},
+ {Lo: 42218, Hi: 42220, Stride: 1},
+ {Lo: 42222, Hi: 42226, Stride: 2},
+ {Lo: 42227, Hi: 42228, Stride: 1},
+ {Lo: 42232, Hi: 42233, Stride: 1},
+ {Lo: 42237, Hi: 42239, Stride: 2},
+ {Lo: 42510, Hi: 42564, Stride: 54},
+ {Lo: 42567, Hi: 42719, Stride: 152},
+ {Lo: 42731, Hi: 42735, Stride: 4},
+ {Lo: 42801, Hi: 42842, Stride: 41},
+ {Lo: 42858, Hi: 42862, Stride: 4},
+ {Lo: 42872, Hi: 42889, Stride: 17},
+ {Lo: 42892, Hi: 42904, Stride: 12},
+ {Lo: 42905, Hi: 42911, Stride: 6},
+ {Lo: 42923, Hi: 42930, Stride: 7},
+ {Lo: 42931, Hi: 42932, Stride: 1},
+ {Lo: 43826, Hi: 43829, Stride: 3},
+ {Lo: 43837, Hi: 43847, Stride: 10},
+ {Lo: 43848, Hi: 43854, Stride: 6},
+ {Lo: 43858, Hi: 43866, Stride: 8},
+ {Lo: 43893, Hi: 43905, Stride: 12},
+ {Lo: 43907, Hi: 43923, Stride: 16},
+ {Lo: 43945, Hi: 43946, Stride: 1},
+ {Lo: 43951, Hi: 64422, Stride: 20471},
+ {Lo: 64423, Hi: 64429, Stride: 1},
+ {Lo: 64830, Hi: 64831, Stride: 1},
+ {Lo: 65072, Hi: 65101, Stride: 29},
+ {Lo: 65102, Hi: 65103, Stride: 1},
+ {Lo: 65112, Hi: 65128, Stride: 16},
+ {Lo: 65165, Hi: 65166, Stride: 1},
+ {Lo: 65257, Hi: 65260, Stride: 1},
+ {Lo: 65282, Hi: 65284, Stride: 2},
+ {Lo: 65285, Hi: 65287, Stride: 1},
+ {Lo: 65290, Hi: 65291, Stride: 1},
+ {Lo: 65293, Hi: 65305, Stride: 1},
+ {Lo: 65308, Hi: 65310, Stride: 1},
+ {Lo: 65312, Hi: 65373, Stride: 1},
+ {Lo: 65512, Hi: 65512, Stride: 1},
+ },
+ R32: []unicode.Range32{
+ {Lo: 66178, Hi: 66182, Stride: 4},
+ {Lo: 66183, Hi: 66186, Stride: 3},
+ {Lo: 66192, Hi: 66194, Stride: 2},
+ {Lo: 66197, Hi: 66199, Stride: 1},
+ {Lo: 66203, Hi: 66208, Stride: 5},
+ {Lo: 66209, Hi: 66210, Stride: 1},
+ {Lo: 66213, Hi: 66219, Stride: 6},
+ {Lo: 66224, Hi: 66226, Stride: 1},
+ {Lo: 66228, Hi: 66255, Stride: 27},
+ {Lo: 66293, Hi: 66305, Stride: 12},
+ {Lo: 66306, Hi: 66313, Stride: 7},
+ {Lo: 66321, Hi: 66325, Stride: 4},
+ {Lo: 66327, Hi: 66330, Stride: 3},
+ {Lo: 66335, Hi: 66336, Stride: 1},
+ {Lo: 66338, Hi: 66564, Stride: 226},
+ {Lo: 66581, Hi: 66587, Stride: 6},
+ {Lo: 66592, Hi: 66604, Stride: 12},
+ {Lo: 66621, Hi: 66632, Stride: 11},
+ {Lo: 66740, Hi: 66754, Stride: 14},
+ {Lo: 66766, Hi: 66770, Stride: 4},
+ {Lo: 66794, Hi: 66806, Stride: 12},
+ {Lo: 66835, Hi: 66838, Stride: 3},
+ {Lo: 66840, Hi: 66844, Stride: 4},
+ {Lo: 66845, Hi: 66853, Stride: 8},
+ {Lo: 66854, Hi: 66855, Stride: 1},
+ {Lo: 68176, Hi: 70864, Stride: 2688},
+ {Lo: 71430, Hi: 71438, Stride: 4},
+ {Lo: 71439, Hi: 71840, Stride: 401},
+ {Lo: 71842, Hi: 71844, Stride: 1},
+ {Lo: 71846, Hi: 71852, Stride: 3},
+ {Lo: 71854, Hi: 71855, Stride: 1},
+ {Lo: 71858, Hi: 71867, Stride: 3},
+ {Lo: 71868, Hi: 71872, Stride: 4},
+ {Lo: 71873, Hi: 71876, Stride: 1},
+ {Lo: 71878, Hi: 71884, Stride: 2},
+ {Lo: 71893, Hi: 71896, Stride: 1},
+ {Lo: 71900, Hi: 71904, Stride: 4},
+ {Lo: 71909, Hi: 71910, Stride: 1},
+ {Lo: 71913, Hi: 71922, Stride: 3},
+ {Lo: 93960, Hi: 93962, Stride: 2},
+ {Lo: 93974, Hi: 93992, Stride: 18},
+ {Lo: 94005, Hi: 94010, Stride: 5},
+ {Lo: 94011, Hi: 94015, Stride: 4},
+ {Lo: 94016, Hi: 94018, Stride: 2},
+ {Lo: 94019, Hi: 94033, Stride: 14},
+ {Lo: 94034, Hi: 119060, Stride: 25026},
+ {Lo: 119149, Hi: 119302, Stride: 153},
+ {Lo: 119309, Hi: 119311, Stride: 2},
+ {Lo: 119314, Hi: 119315, Stride: 1},
+ {Lo: 119318, Hi: 119338, Stride: 20},
+ {Lo: 119350, Hi: 119351, Stride: 1},
+ {Lo: 119354, Hi: 119355, Stride: 1},
+ {Lo: 119808, Hi: 119845, Stride: 1},
+ {Lo: 119847, Hi: 119892, Stride: 1},
+ {Lo: 119894, Hi: 119897, Stride: 1},
+ {Lo: 119899, Hi: 119949, Stride: 1},
+ {Lo: 119951, Hi: 119964, Stride: 1},
+ {Lo: 119966, Hi: 119967, Stride: 1},
+ {Lo: 119970, Hi: 119973, Stride: 3},
+ {Lo: 119974, Hi: 119977, Stride: 3},
+ {Lo: 119978, Hi: 119980, Stride: 1},
+ {Lo: 119982, Hi: 119993, Stride: 1},
+ {Lo: 119995, Hi: 119997, Stride: 2},
+ {Lo: 119998, Hi: 120001, Stride: 1},
+ {Lo: 120003, Hi: 120005, Stride: 2},
+ {Lo: 120006, Hi: 120053, Stride: 1},
+ {Lo: 120055, Hi: 120069, Stride: 1},
+ {Lo: 120071, Hi: 120074, Stride: 1},
+ {Lo: 120077, Hi: 120084, Stride: 1},
+ {Lo: 120086, Hi: 120092, Stride: 1},
+ {Lo: 120094, Hi: 120105, Stride: 1},
+ {Lo: 120107, Hi: 120121, Stride: 1},
+ {Lo: 120123, Hi: 120126, Stride: 1},
+ {Lo: 120128, Hi: 120132, Stride: 1},
+ {Lo: 120134, Hi: 120138, Stride: 4},
+ {Lo: 120139, Hi: 120144, Stride: 1},
+ {Lo: 120146, Hi: 120157, Stride: 1},
+ {Lo: 120159, Hi: 120209, Stride: 1},
+ {Lo: 120211, Hi: 120261, Stride: 1},
+ {Lo: 120263, Hi: 120313, Stride: 1},
+ {Lo: 120315, Hi: 120365, Stride: 1},
+ {Lo: 120367, Hi: 120417, Stride: 1},
+ {Lo: 120419, Hi: 120469, Stride: 1},
+ {Lo: 120471, Hi: 120484, Stride: 1},
+ {Lo: 120488, Hi: 120489, Stride: 1},
+ {Lo: 120492, Hi: 120494, Stride: 1},
+ {Lo: 120496, Hi: 120497, Stride: 1},
+ {Lo: 120499, Hi: 120500, Stride: 1},
+ {Lo: 120502, Hi: 120504, Stride: 2},
+ {Lo: 120507, Hi: 120508, Stride: 1},
+ {Lo: 120510, Hi: 120514, Stride: 4},
+ {Lo: 120516, Hi: 120522, Stride: 6},
+ {Lo: 120526, Hi: 120534, Stride: 2},
+ {Lo: 120544, Hi: 120546, Stride: 2},
+ {Lo: 120547, Hi: 120550, Stride: 3},
+ {Lo: 120551, Hi: 120552, Stride: 1},
+ {Lo: 120554, Hi: 120555, Stride: 1},
+ {Lo: 120557, Hi: 120558, Stride: 1},
+ {Lo: 120560, Hi: 120562, Stride: 2},
+ {Lo: 120565, Hi: 120566, Stride: 1},
+ {Lo: 120568, Hi: 120572, Stride: 4},
+ {Lo: 120574, Hi: 120580, Stride: 6},
+ {Lo: 120584, Hi: 120592, Stride: 2},
+ {Lo: 120602, Hi: 120604, Stride: 2},
+ {Lo: 120605, Hi: 120608, Stride: 3},
+ {Lo: 120609, Hi: 120610, Stride: 1},
+ {Lo: 120612, Hi: 120613, Stride: 1},
+ {Lo: 120615, Hi: 120616, Stride: 1},
+ {Lo: 120618, Hi: 120620, Stride: 2},
+ {Lo: 120623, Hi: 120624, Stride: 1},
+ {Lo: 120626, Hi: 120630, Stride: 4},
+ {Lo: 120632, Hi: 120638, Stride: 6},
+ {Lo: 120642, Hi: 120650, Stride: 2},
+ {Lo: 120660, Hi: 120662, Stride: 2},
+ {Lo: 120663, Hi: 120666, Stride: 3},
+ {Lo: 120667, Hi: 120668, Stride: 1},
+ {Lo: 120670, Hi: 120671, Stride: 1},
+ {Lo: 120673, Hi: 120674, Stride: 1},
+ {Lo: 120676, Hi: 120678, Stride: 2},
+ {Lo: 120681, Hi: 120682, Stride: 1},
+ {Lo: 120684, Hi: 120688, Stride: 4},
+ {Lo: 120690, Hi: 120696, Stride: 6},
+ {Lo: 120700, Hi: 120708, Stride: 2},
+ {Lo: 120718, Hi: 120720, Stride: 2},
+ {Lo: 120721, Hi: 120724, Stride: 3},
+ {Lo: 120725, Hi: 120726, Stride: 1},
+ {Lo: 120728, Hi: 120729, Stride: 1},
+ {Lo: 120731, Hi: 120732, Stride: 1},
+ {Lo: 120734, Hi: 120736, Stride: 2},
+ {Lo: 120739, Hi: 120740, Stride: 1},
+ {Lo: 120742, Hi: 120746, Stride: 4},
+ {Lo: 120748, Hi: 120754, Stride: 6},
+ {Lo: 120758, Hi: 120766, Stride: 2},
+ {Lo: 120776, Hi: 120778, Stride: 2},
+ {Lo: 120782, Hi: 120831, Stride: 1},
+ {Lo: 125127, Hi: 125131, Stride: 4},
+ {Lo: 126464, Hi: 126500, Stride: 36},
+ {Lo: 126564, Hi: 126592, Stride: 28},
+ {Lo: 126596, Hi: 128844, Stride: 2248},
+ {Lo: 128872, Hi: 130032, Stride: 1160},
+ {Lo: 130033, Hi: 130041, Stride: 1},
+ },
+ LatinOffset: 0,
+ },
+ },
+ "_default": {
+ Confusable: []rune{160, 180, 215, 305, 921, 1009, 1040, 1042, 1045, 1047, 1050, 1052, 1053, 1054, 1056, 1057, 1058, 1059, 1061, 1068, 1072, 1073, 1075, 1077, 1086, 1088, 1089, 1091, 1093, 8211, 8216, 8217, 8245, 12494, 65281, 65283, 65288, 65289, 65292, 65306, 65307, 65311, 65374},
+ With: []rune{32, 96, 120, 105, 73, 112, 65, 66, 69, 51, 75, 77, 72, 79, 80, 67, 84, 89, 88, 98, 97, 54, 114, 101, 111, 112, 99, 121, 120, 45, 96, 96, 96, 47, 33, 35, 40, 41, 44, 58, 59, 63, 126},
+ Locale: "_default",
+ RangeTable: &unicode.RangeTable{
+ R16: []unicode.Range16{
+ {Lo: 160, Hi: 180, Stride: 20},
+ {Lo: 215, Hi: 305, Stride: 90},
+ {Lo: 921, Hi: 1009, Stride: 88},
+ {Lo: 1040, Hi: 1042, Stride: 2},
+ {Lo: 1045, Hi: 1047, Stride: 2},
+ {Lo: 1050, Hi: 1052, Stride: 2},
+ {Lo: 1053, Hi: 1054, Stride: 1},
+ {Lo: 1056, Hi: 1059, Stride: 1},
+ {Lo: 1061, Hi: 1068, Stride: 7},
+ {Lo: 1072, Hi: 1073, Stride: 1},
+ {Lo: 1075, Hi: 1077, Stride: 2},
+ {Lo: 1086, Hi: 1088, Stride: 2},
+ {Lo: 1089, Hi: 1093, Stride: 2},
+ {Lo: 8211, Hi: 8216, Stride: 5},
+ {Lo: 8217, Hi: 8245, Stride: 28},
+ {Lo: 12494, Hi: 65281, Stride: 52787},
+ {Lo: 65283, Hi: 65288, Stride: 5},
+ {Lo: 65289, Hi: 65292, Stride: 3},
+ {Lo: 65306, Hi: 65307, Stride: 1},
+ {Lo: 65311, Hi: 65374, Stride: 63},
+ },
+ R32: []unicode.Range32{},
+ LatinOffset: 1,
+ },
+ },
+ "cs": {
+ Confusable: []rune{180, 305, 921, 1009, 1040, 1042, 1045, 1047, 1050, 1052, 1053, 1054, 1056, 1057, 1058, 1059, 1061, 1068, 1072, 1073, 1075, 1077, 1086, 1088, 1089, 1091, 1093, 8216, 8217, 8245, 12494, 65281, 65283, 65288, 65289, 65292, 65306, 65307, 65311, 65374},
+ With: []rune{96, 105, 73, 112, 65, 66, 69, 51, 75, 77, 72, 79, 80, 67, 84, 89, 88, 98, 97, 54, 114, 101, 111, 112, 99, 121, 120, 96, 96, 96, 47, 33, 35, 40, 41, 44, 58, 59, 63, 126},
+ Locale: "cs",
+ RangeTable: &unicode.RangeTable{
+ R16: []unicode.Range16{
+ {Lo: 180, Hi: 305, Stride: 125},
+ {Lo: 921, Hi: 1009, Stride: 88},
+ {Lo: 1040, Hi: 1042, Stride: 2},
+ {Lo: 1045, Hi: 1047, Stride: 2},
+ {Lo: 1050, Hi: 1052, Stride: 2},
+ {Lo: 1053, Hi: 1054, Stride: 1},
+ {Lo: 1056, Hi: 1059, Stride: 1},
+ {Lo: 1061, Hi: 1068, Stride: 7},
+ {Lo: 1072, Hi: 1073, Stride: 1},
+ {Lo: 1075, Hi: 1077, Stride: 2},
+ {Lo: 1086, Hi: 1088, Stride: 2},
+ {Lo: 1089, Hi: 1093, Stride: 2},
+ {Lo: 8216, Hi: 8217, Stride: 1},
+ {Lo: 8245, Hi: 12494, Stride: 4249},
+ {Lo: 65281, Hi: 65283, Stride: 2},
+ {Lo: 65288, Hi: 65289, Stride: 1},
+ {Lo: 65292, Hi: 65306, Stride: 14},
+ {Lo: 65307, Hi: 65311, Stride: 4},
+ {Lo: 65374, Hi: 65374, Stride: 1},
+ },
+ R32: []unicode.Range32{},
+ LatinOffset: 0,
+ },
+ },
+ "de": {
+ Confusable: []rune{180, 305, 921, 1009, 1040, 1042, 1045, 1047, 1050, 1052, 1053, 1054, 1056, 1057, 1058, 1059, 1061, 1068, 1072, 1073, 1075, 1077, 1086, 1088, 1089, 1091, 1093, 8216, 8217, 8245, 12494, 65281, 65283, 65288, 65289, 65292, 65306, 65307, 65311, 65374},
+ With: []rune{96, 105, 73, 112, 65, 66, 69, 51, 75, 77, 72, 79, 80, 67, 84, 89, 88, 98, 97, 54, 114, 101, 111, 112, 99, 121, 120, 96, 96, 96, 47, 33, 35, 40, 41, 44, 58, 59, 63, 126},
+ Locale: "de",
+ RangeTable: &unicode.RangeTable{
+ R16: []unicode.Range16{
+ {Lo: 180, Hi: 305, Stride: 125},
+ {Lo: 921, Hi: 1009, Stride: 88},
+ {Lo: 1040, Hi: 1042, Stride: 2},
+ {Lo: 1045, Hi: 1047, Stride: 2},
+ {Lo: 1050, Hi: 1052, Stride: 2},
+ {Lo: 1053, Hi: 1054, Stride: 1},
+ {Lo: 1056, Hi: 1059, Stride: 1},
+ {Lo: 1061, Hi: 1068, Stride: 7},
+ {Lo: 1072, Hi: 1073, Stride: 1},
+ {Lo: 1075, Hi: 1077, Stride: 2},
+ {Lo: 1086, Hi: 1088, Stride: 2},
+ {Lo: 1089, Hi: 1093, Stride: 2},
+ {Lo: 8216, Hi: 8217, Stride: 1},
+ {Lo: 8245, Hi: 12494, Stride: 4249},
+ {Lo: 65281, Hi: 65283, Stride: 2},
+ {Lo: 65288, Hi: 65289, Stride: 1},
+ {Lo: 65292, Hi: 65306, Stride: 14},
+ {Lo: 65307, Hi: 65311, Stride: 4},
+ {Lo: 65374, Hi: 65374, Stride: 1},
+ },
+ R32: []unicode.Range32{},
+ LatinOffset: 0,
+ },
+ },
+ "es": {
+ Confusable: []rune{180, 215, 305, 1009, 1040, 1042, 1045, 1047, 1050, 1052, 1053, 1054, 1056, 1057, 1058, 1059, 1061, 1068, 1072, 1073, 1075, 1077, 1086, 1088, 1089, 1091, 1093, 8211, 8245, 12494, 65281, 65283, 65288, 65289, 65292, 65306, 65307, 65311, 65374},
+ With: []rune{96, 120, 105, 112, 65, 66, 69, 51, 75, 77, 72, 79, 80, 67, 84, 89, 88, 98, 97, 54, 114, 101, 111, 112, 99, 121, 120, 45, 96, 47, 33, 35, 40, 41, 44, 58, 59, 63, 126},
+ Locale: "es",
+ RangeTable: &unicode.RangeTable{
+ R16: []unicode.Range16{
+ {Lo: 180, Hi: 215, Stride: 35},
+ {Lo: 305, Hi: 1009, Stride: 704},
+ {Lo: 1040, Hi: 1042, Stride: 2},
+ {Lo: 1045, Hi: 1047, Stride: 2},
+ {Lo: 1050, Hi: 1052, Stride: 2},
+ {Lo: 1053, Hi: 1054, Stride: 1},
+ {Lo: 1056, Hi: 1059, Stride: 1},
+ {Lo: 1061, Hi: 1068, Stride: 7},
+ {Lo: 1072, Hi: 1073, Stride: 1},
+ {Lo: 1075, Hi: 1077, Stride: 2},
+ {Lo: 1086, Hi: 1088, Stride: 2},
+ {Lo: 1089, Hi: 1093, Stride: 2},
+ {Lo: 8211, Hi: 8245, Stride: 34},
+ {Lo: 12494, Hi: 65281, Stride: 52787},
+ {Lo: 65283, Hi: 65288, Stride: 5},
+ {Lo: 65289, Hi: 65292, Stride: 3},
+ {Lo: 65306, Hi: 65307, Stride: 1},
+ {Lo: 65311, Hi: 65374, Stride: 63},
+ },
+ R32: []unicode.Range32{},
+ LatinOffset: 1,
+ },
+ },
+ "fr": {
+ Confusable: []rune{215, 305, 921, 1009, 1040, 1042, 1045, 1047, 1050, 1052, 1053, 1054, 1056, 1057, 1058, 1059, 1061, 1068, 1072, 1073, 1075, 1077, 1086, 1088, 1089, 1091, 1093, 8216, 8245, 12494, 65281, 65283, 65288, 65289, 65292, 65306, 65307, 65311, 65374},
+ With: []rune{120, 105, 73, 112, 65, 66, 69, 51, 75, 77, 72, 79, 80, 67, 84, 89, 88, 98, 97, 54, 114, 101, 111, 112, 99, 121, 120, 96, 96, 47, 33, 35, 40, 41, 44, 58, 59, 63, 126},
+ Locale: "fr",
+ RangeTable: &unicode.RangeTable{
+ R16: []unicode.Range16{
+ {Lo: 215, Hi: 305, Stride: 90},
+ {Lo: 921, Hi: 1009, Stride: 88},
+ {Lo: 1040, Hi: 1042, Stride: 2},
+ {Lo: 1045, Hi: 1047, Stride: 2},
+ {Lo: 1050, Hi: 1052, Stride: 2},
+ {Lo: 1053, Hi: 1054, Stride: 1},
+ {Lo: 1056, Hi: 1059, Stride: 1},
+ {Lo: 1061, Hi: 1068, Stride: 7},
+ {Lo: 1072, Hi: 1073, Stride: 1},
+ {Lo: 1075, Hi: 1077, Stride: 2},
+ {Lo: 1086, Hi: 1088, Stride: 2},
+ {Lo: 1089, Hi: 1093, Stride: 2},
+ {Lo: 8216, Hi: 8245, Stride: 29},
+ {Lo: 12494, Hi: 65281, Stride: 52787},
+ {Lo: 65283, Hi: 65288, Stride: 5},
+ {Lo: 65289, Hi: 65292, Stride: 3},
+ {Lo: 65306, Hi: 65307, Stride: 1},
+ {Lo: 65311, Hi: 65374, Stride: 63},
+ },
+ R32: []unicode.Range32{},
+ LatinOffset: 0,
+ },
+ },
+ "it": {
+ Confusable: []rune{160, 180, 215, 305, 921, 1009, 1040, 1042, 1045, 1047, 1050, 1052, 1053, 1054, 1056, 1057, 1058, 1059, 1061, 1068, 1072, 1073, 1075, 1077, 1086, 1088, 1089, 1091, 1093, 8211, 8216, 8245, 12494, 65281, 65283, 65288, 65289, 65292, 65306, 65307, 65311, 65374},
+ With: []rune{32, 96, 120, 105, 73, 112, 65, 66, 69, 51, 75, 77, 72, 79, 80, 67, 84, 89, 88, 98, 97, 54, 114, 101, 111, 112, 99, 121, 120, 45, 96, 96, 47, 33, 35, 40, 41, 44, 58, 59, 63, 126},
+ Locale: "it",
+ RangeTable: &unicode.RangeTable{
+ R16: []unicode.Range16{
+ {Lo: 160, Hi: 180, Stride: 20},
+ {Lo: 215, Hi: 305, Stride: 90},
+ {Lo: 921, Hi: 1009, Stride: 88},
+ {Lo: 1040, Hi: 1042, Stride: 2},
+ {Lo: 1045, Hi: 1047, Stride: 2},
+ {Lo: 1050, Hi: 1052, Stride: 2},
+ {Lo: 1053, Hi: 1054, Stride: 1},
+ {Lo: 1056, Hi: 1059, Stride: 1},
+ {Lo: 1061, Hi: 1068, Stride: 7},
+ {Lo: 1072, Hi: 1073, Stride: 1},
+ {Lo: 1075, Hi: 1077, Stride: 2},
+ {Lo: 1086, Hi: 1088, Stride: 2},
+ {Lo: 1089, Hi: 1093, Stride: 2},
+ {Lo: 8211, Hi: 8216, Stride: 5},
+ {Lo: 8245, Hi: 12494, Stride: 4249},
+ {Lo: 65281, Hi: 65283, Stride: 2},
+ {Lo: 65288, Hi: 65289, Stride: 1},
+ {Lo: 65292, Hi: 65306, Stride: 14},
+ {Lo: 65307, Hi: 65311, Stride: 4},
+ {Lo: 65374, Hi: 65374, Stride: 1},
+ },
+ R32: []unicode.Range32{},
+ LatinOffset: 1,
+ },
+ },
+ "ja": {
+ Confusable: []rune{180, 215, 305, 921, 1009, 1040, 1042, 1045, 1047, 1050, 1052, 1053, 1054, 1056, 1057, 1058, 1059, 1061, 1068, 1072, 1073, 1075, 1077, 1086, 1088, 1089, 1091, 1093, 8211, 8216, 8217, 8245, 65281, 65283, 65292, 65306, 65307},
+ With: []rune{96, 120, 105, 73, 112, 65, 66, 69, 51, 75, 77, 72, 79, 80, 67, 84, 89, 88, 98, 97, 54, 114, 101, 111, 112, 99, 121, 120, 45, 96, 96, 96, 33, 35, 44, 58, 59},
+ Locale: "ja",
+ RangeTable: &unicode.RangeTable{
+ R16: []unicode.Range16{
+ {Lo: 180, Hi: 215, Stride: 35},
+ {Lo: 305, Hi: 921, Stride: 616},
+ {Lo: 1009, Hi: 1040, Stride: 31},
+ {Lo: 1042, Hi: 1045, Stride: 3},
+ {Lo: 1047, Hi: 1050, Stride: 3},
+ {Lo: 1052, Hi: 1054, Stride: 1},
+ {Lo: 1056, Hi: 1059, Stride: 1},
+ {Lo: 1061, Hi: 1068, Stride: 7},
+ {Lo: 1072, Hi: 1073, Stride: 1},
+ {Lo: 1075, Hi: 1077, Stride: 2},
+ {Lo: 1086, Hi: 1088, Stride: 2},
+ {Lo: 1089, Hi: 1093, Stride: 2},
+ {Lo: 8211, Hi: 8216, Stride: 5},
+ {Lo: 8217, Hi: 8245, Stride: 28},
+ {Lo: 65281, Hi: 65283, Stride: 2},
+ {Lo: 65292, Hi: 65306, Stride: 14},
+ {Lo: 65307, Hi: 65307, Stride: 1},
+ },
+ R32: []unicode.Range32{},
+ LatinOffset: 1,
+ },
+ },
+ "ko": {
+ Confusable: []rune{180, 215, 305, 921, 1009, 1040, 1042, 1045, 1047, 1050, 1052, 1053, 1054, 1056, 1057, 1058, 1059, 1061, 1068, 1072, 1073, 1075, 1077, 1086, 1088, 1089, 1091, 1093, 8211, 8245, 12494, 65281, 65283, 65288, 65289, 65292, 65306, 65307, 65311, 65374},
+ With: []rune{96, 120, 105, 73, 112, 65, 66, 69, 51, 75, 77, 72, 79, 80, 67, 84, 89, 88, 98, 97, 54, 114, 101, 111, 112, 99, 121, 120, 45, 96, 47, 33, 35, 40, 41, 44, 58, 59, 63, 126},
+ Locale: "ko",
+ RangeTable: &unicode.RangeTable{
+ R16: []unicode.Range16{
+ {Lo: 180, Hi: 215, Stride: 35},
+ {Lo: 305, Hi: 921, Stride: 616},
+ {Lo: 1009, Hi: 1040, Stride: 31},
+ {Lo: 1042, Hi: 1045, Stride: 3},
+ {Lo: 1047, Hi: 1050, Stride: 3},
+ {Lo: 1052, Hi: 1054, Stride: 1},
+ {Lo: 1056, Hi: 1059, Stride: 1},
+ {Lo: 1061, Hi: 1068, Stride: 7},
+ {Lo: 1072, Hi: 1073, Stride: 1},
+ {Lo: 1075, Hi: 1077, Stride: 2},
+ {Lo: 1086, Hi: 1088, Stride: 2},
+ {Lo: 1089, Hi: 1093, Stride: 2},
+ {Lo: 8211, Hi: 8245, Stride: 34},
+ {Lo: 12494, Hi: 65281, Stride: 52787},
+ {Lo: 65283, Hi: 65288, Stride: 5},
+ {Lo: 65289, Hi: 65292, Stride: 3},
+ {Lo: 65306, Hi: 65307, Stride: 1},
+ {Lo: 65311, Hi: 65374, Stride: 63},
+ },
+ R32: []unicode.Range32{},
+ LatinOffset: 1,
+ },
+ },
+ "pl": {
+ Confusable: []rune{180, 215, 305, 921, 1009, 1040, 1042, 1045, 1047, 1050, 1052, 1053, 1054, 1056, 1057, 1058, 1059, 1061, 1068, 1072, 1073, 1075, 1077, 1086, 1088, 1089, 1091, 1093, 8216, 8217, 8245, 12494, 65281, 65283, 65288, 65289, 65292, 65306, 65307, 65311, 65374},
+ With: []rune{96, 120, 105, 73, 112, 65, 66, 69, 51, 75, 77, 72, 79, 80, 67, 84, 89, 88, 98, 97, 54, 114, 101, 111, 112, 99, 121, 120, 96, 96, 96, 47, 33, 35, 40, 41, 44, 58, 59, 63, 126},
+ Locale: "pl",
+ RangeTable: &unicode.RangeTable{
+ R16: []unicode.Range16{
+ {Lo: 180, Hi: 215, Stride: 35},
+ {Lo: 305, Hi: 921, Stride: 616},
+ {Lo: 1009, Hi: 1040, Stride: 31},
+ {Lo: 1042, Hi: 1045, Stride: 3},
+ {Lo: 1047, Hi: 1050, Stride: 3},
+ {Lo: 1052, Hi: 1054, Stride: 1},
+ {Lo: 1056, Hi: 1059, Stride: 1},
+ {Lo: 1061, Hi: 1068, Stride: 7},
+ {Lo: 1072, Hi: 1073, Stride: 1},
+ {Lo: 1075, Hi: 1077, Stride: 2},
+ {Lo: 1086, Hi: 1088, Stride: 2},
+ {Lo: 1089, Hi: 1093, Stride: 2},
+ {Lo: 8216, Hi: 8217, Stride: 1},
+ {Lo: 8245, Hi: 12494, Stride: 4249},
+ {Lo: 65281, Hi: 65283, Stride: 2},
+ {Lo: 65288, Hi: 65289, Stride: 1},
+ {Lo: 65292, Hi: 65306, Stride: 14},
+ {Lo: 65307, Hi: 65311, Stride: 4},
+ {Lo: 65374, Hi: 65374, Stride: 1},
+ },
+ R32: []unicode.Range32{},
+ LatinOffset: 1,
+ },
+ },
+ "pt-BR": {
+ Confusable: []rune{180, 215, 305, 921, 1009, 1040, 1042, 1045, 1047, 1050, 1052, 1053, 1054, 1056, 1057, 1058, 1059, 1061, 1068, 1072, 1073, 1075, 1077, 1086, 1088, 1089, 1091, 1093, 8216, 8217, 8245, 12494, 65281, 65283, 65288, 65289, 65292, 65306, 65307, 65311, 65374},
+ With: []rune{96, 120, 105, 73, 112, 65, 66, 69, 51, 75, 77, 72, 79, 80, 67, 84, 89, 88, 98, 97, 54, 114, 101, 111, 112, 99, 121, 120, 96, 96, 96, 47, 33, 35, 40, 41, 44, 58, 59, 63, 126},
+ Locale: "pt-BR",
+ RangeTable: &unicode.RangeTable{
+ R16: []unicode.Range16{
+ {Lo: 180, Hi: 215, Stride: 35},
+ {Lo: 305, Hi: 921, Stride: 616},
+ {Lo: 1009, Hi: 1040, Stride: 31},
+ {Lo: 1042, Hi: 1045, Stride: 3},
+ {Lo: 1047, Hi: 1050, Stride: 3},
+ {Lo: 1052, Hi: 1054, Stride: 1},
+ {Lo: 1056, Hi: 1059, Stride: 1},
+ {Lo: 1061, Hi: 1068, Stride: 7},
+ {Lo: 1072, Hi: 1073, Stride: 1},
+ {Lo: 1075, Hi: 1077, Stride: 2},
+ {Lo: 1086, Hi: 1088, Stride: 2},
+ {Lo: 1089, Hi: 1093, Stride: 2},
+ {Lo: 8216, Hi: 8217, Stride: 1},
+ {Lo: 8245, Hi: 12494, Stride: 4249},
+ {Lo: 65281, Hi: 65283, Stride: 2},
+ {Lo: 65288, Hi: 65289, Stride: 1},
+ {Lo: 65292, Hi: 65306, Stride: 14},
+ {Lo: 65307, Hi: 65311, Stride: 4},
+ {Lo: 65374, Hi: 65374, Stride: 1},
+ },
+ R32: []unicode.Range32{},
+ LatinOffset: 1,
+ },
+ },
+ "qps-ploc": {
+ Confusable: []rune{160, 180, 215, 305, 921, 1040, 1042, 1045, 1047, 1050, 1052, 1053, 1054, 1056, 1057, 1058, 1059, 1061, 1068, 1072, 1073, 1075, 1077, 1086, 1088, 1089, 1091, 1093, 8211, 8216, 8217, 8245, 12494, 65281, 65283, 65288, 65289, 65292, 65306, 65307, 65311, 65374},
+ With: []rune{32, 96, 120, 105, 73, 65, 66, 69, 51, 75, 77, 72, 79, 80, 67, 84, 89, 88, 98, 97, 54, 114, 101, 111, 112, 99, 121, 120, 45, 96, 96, 96, 47, 33, 35, 40, 41, 44, 58, 59, 63, 126},
+ Locale: "qps-ploc",
+ RangeTable: &unicode.RangeTable{
+ R16: []unicode.Range16{
+ {Lo: 160, Hi: 180, Stride: 20},
+ {Lo: 215, Hi: 305, Stride: 90},
+ {Lo: 921, Hi: 1040, Stride: 119},
+ {Lo: 1042, Hi: 1045, Stride: 3},
+ {Lo: 1047, Hi: 1050, Stride: 3},
+ {Lo: 1052, Hi: 1054, Stride: 1},
+ {Lo: 1056, Hi: 1059, Stride: 1},
+ {Lo: 1061, Hi: 1068, Stride: 7},
+ {Lo: 1072, Hi: 1073, Stride: 1},
+ {Lo: 1075, Hi: 1077, Stride: 2},
+ {Lo: 1086, Hi: 1088, Stride: 2},
+ {Lo: 1089, Hi: 1093, Stride: 2},
+ {Lo: 8211, Hi: 8216, Stride: 5},
+ {Lo: 8217, Hi: 8245, Stride: 28},
+ {Lo: 12494, Hi: 65281, Stride: 52787},
+ {Lo: 65283, Hi: 65288, Stride: 5},
+ {Lo: 65289, Hi: 65292, Stride: 3},
+ {Lo: 65306, Hi: 65307, Stride: 1},
+ {Lo: 65311, Hi: 65374, Stride: 63},
+ },
+ R32: []unicode.Range32{},
+ LatinOffset: 1,
+ },
+ },
+ "ru": {
+ Confusable: []rune{180, 215, 305, 921, 1009, 8216, 8217, 8245, 12494, 65281, 65283, 65288, 65289, 65292, 65306, 65307, 65311, 65374},
+ With: []rune{96, 120, 105, 73, 112, 96, 96, 96, 47, 33, 35, 40, 41, 44, 58, 59, 63, 126},
+ Locale: "ru",
+ RangeTable: &unicode.RangeTable{
+ R16: []unicode.Range16{
+ {Lo: 180, Hi: 215, Stride: 35},
+ {Lo: 305, Hi: 921, Stride: 616},
+ {Lo: 1009, Hi: 8216, Stride: 7207},
+ {Lo: 8217, Hi: 8245, Stride: 28},
+ {Lo: 12494, Hi: 65281, Stride: 52787},
+ {Lo: 65283, Hi: 65288, Stride: 5},
+ {Lo: 65289, Hi: 65292, Stride: 3},
+ {Lo: 65306, Hi: 65307, Stride: 1},
+ {Lo: 65311, Hi: 65374, Stride: 63},
+ },
+ R32: []unicode.Range32{},
+ LatinOffset: 1,
+ },
+ },
+ "tr": {
+ Confusable: []rune{160, 180, 215, 921, 1009, 1040, 1042, 1045, 1047, 1050, 1052, 1053, 1054, 1056, 1057, 1058, 1059, 1061, 1068, 1072, 1073, 1075, 1077, 1086, 1088, 1089, 1091, 1093, 8211, 8245, 12494, 65281, 65283, 65288, 65289, 65292, 65306, 65307, 65311, 65374},
+ With: []rune{32, 96, 120, 73, 112, 65, 66, 69, 51, 75, 77, 72, 79, 80, 67, 84, 89, 88, 98, 97, 54, 114, 101, 111, 112, 99, 121, 120, 45, 96, 47, 33, 35, 40, 41, 44, 58, 59, 63, 126},
+ Locale: "tr",
+ RangeTable: &unicode.RangeTable{
+ R16: []unicode.Range16{
+ {Lo: 160, Hi: 180, Stride: 20},
+ {Lo: 215, Hi: 921, Stride: 706},
+ {Lo: 1009, Hi: 1040, Stride: 31},
+ {Lo: 1042, Hi: 1045, Stride: 3},
+ {Lo: 1047, Hi: 1050, Stride: 3},
+ {Lo: 1052, Hi: 1054, Stride: 1},
+ {Lo: 1056, Hi: 1059, Stride: 1},
+ {Lo: 1061, Hi: 1068, Stride: 7},
+ {Lo: 1072, Hi: 1073, Stride: 1},
+ {Lo: 1075, Hi: 1077, Stride: 2},
+ {Lo: 1086, Hi: 1088, Stride: 2},
+ {Lo: 1089, Hi: 1093, Stride: 2},
+ {Lo: 8211, Hi: 8245, Stride: 34},
+ {Lo: 12494, Hi: 65281, Stride: 52787},
+ {Lo: 65283, Hi: 65288, Stride: 5},
+ {Lo: 65289, Hi: 65292, Stride: 3},
+ {Lo: 65306, Hi: 65307, Stride: 1},
+ {Lo: 65311, Hi: 65374, Stride: 63},
+ },
+ R32: []unicode.Range32{},
+ LatinOffset: 1,
+ },
+ },
+ "zh-hans": {
+ Confusable: []rune{180, 215, 305, 921, 1009, 1040, 1042, 1045, 1047, 1050, 1052, 1053, 1054, 1056, 1057, 1058, 1059, 1061, 1068, 1072, 1073, 1075, 1077, 1086, 1088, 1089, 1091, 1093, 8245, 12494, 65281, 65288, 65289, 65306, 65374},
+ With: []rune{96, 120, 105, 73, 112, 65, 66, 69, 51, 75, 77, 72, 79, 80, 67, 84, 89, 88, 98, 97, 54, 114, 101, 111, 112, 99, 121, 120, 96, 47, 33, 40, 41, 58, 126},
+ Locale: "zh-hans",
+ RangeTable: &unicode.RangeTable{
+ R16: []unicode.Range16{
+ {Lo: 180, Hi: 215, Stride: 35},
+ {Lo: 305, Hi: 921, Stride: 616},
+ {Lo: 1009, Hi: 1040, Stride: 31},
+ {Lo: 1042, Hi: 1045, Stride: 3},
+ {Lo: 1047, Hi: 1050, Stride: 3},
+ {Lo: 1052, Hi: 1054, Stride: 1},
+ {Lo: 1056, Hi: 1059, Stride: 1},
+ {Lo: 1061, Hi: 1068, Stride: 7},
+ {Lo: 1072, Hi: 1073, Stride: 1},
+ {Lo: 1075, Hi: 1077, Stride: 2},
+ {Lo: 1086, Hi: 1088, Stride: 2},
+ {Lo: 1089, Hi: 1093, Stride: 2},
+ {Lo: 8245, Hi: 12494, Stride: 4249},
+ {Lo: 65281, Hi: 65288, Stride: 7},
+ {Lo: 65289, Hi: 65306, Stride: 17},
+ {Lo: 65374, Hi: 65374, Stride: 1},
+ },
+ R32: []unicode.Range32{},
+ LatinOffset: 1,
+ },
+ },
+ "zh-hant": {
+ Confusable: []rune{180, 215, 305, 921, 1009, 1040, 1042, 1045, 1047, 1050, 1052, 1053, 1054, 1056, 1057, 1058, 1059, 1061, 1068, 1072, 1073, 1075, 1077, 1086, 1088, 1089, 1091, 1093, 8211, 12494, 65283, 65307, 65374},
+ With: []rune{96, 120, 105, 73, 112, 65, 66, 69, 51, 75, 77, 72, 79, 80, 67, 84, 89, 88, 98, 97, 54, 114, 101, 111, 112, 99, 121, 120, 45, 47, 35, 59, 126},
+ Locale: "zh-hant",
+ RangeTable: &unicode.RangeTable{
+ R16: []unicode.Range16{
+ {Lo: 180, Hi: 215, Stride: 35},
+ {Lo: 305, Hi: 921, Stride: 616},
+ {Lo: 1009, Hi: 1040, Stride: 31},
+ {Lo: 1042, Hi: 1045, Stride: 3},
+ {Lo: 1047, Hi: 1050, Stride: 3},
+ {Lo: 1052, Hi: 1054, Stride: 1},
+ {Lo: 1056, Hi: 1059, Stride: 1},
+ {Lo: 1061, Hi: 1068, Stride: 7},
+ {Lo: 1072, Hi: 1073, Stride: 1},
+ {Lo: 1075, Hi: 1077, Stride: 2},
+ {Lo: 1086, Hi: 1088, Stride: 2},
+ {Lo: 1089, Hi: 1093, Stride: 2},
+ {Lo: 8211, Hi: 12494, Stride: 4283},
+ {Lo: 65283, Hi: 65307, Stride: 24},
+ {Lo: 65374, Hi: 65374, Stride: 1},
+ },
+ R32: []unicode.Range32{},
+ LatinOffset: 1,
+ },
+ },
+}
diff --git a/modules/charset/ambiguous_gen_test.go b/modules/charset/ambiguous_gen_test.go
new file mode 100644
index 0000000000..bd64e1c5b1
--- /dev/null
+++ b/modules/charset/ambiguous_gen_test.go
@@ -0,0 +1,32 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package charset
+
+import (
+ "sort"
+ "testing"
+ "unicode"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestAmbiguousCharacters(t *testing.T) {
+ for locale, ambiguous := range AmbiguousCharacters {
+ assert.Equal(t, locale, ambiguous.Locale)
+ assert.Equal(t, len(ambiguous.Confusable), len(ambiguous.With))
+ assert.True(t, sort.SliceIsSorted(ambiguous.Confusable, func(i, j int) bool {
+ return ambiguous.Confusable[i] < ambiguous.Confusable[j]
+ }))
+
+ for _, confusable := range ambiguous.Confusable {
+ assert.True(t, unicode.Is(ambiguous.RangeTable, confusable))
+ i := sort.Search(len(ambiguous.Confusable), func(j int) bool {
+ return ambiguous.Confusable[j] >= confusable
+ })
+ found := i < len(ambiguous.Confusable) && ambiguous.Confusable[i] == confusable
+ assert.True(t, found, "%c is not in %d", confusable, i)
+ }
+ }
+}
diff --git a/modules/charset/breakwriter.go b/modules/charset/breakwriter.go
new file mode 100644
index 0000000000..619826ff21
--- /dev/null
+++ b/modules/charset/breakwriter.go
@@ -0,0 +1,44 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package charset
+
+import (
+ "bytes"
+ "io"
+)
+
+// BreakWriter wraps an io.Writer to always write '\n' as '
'
+type BreakWriter struct {
+ io.Writer
+}
+
+// Write writes the provided byte slice transparently replacing '\n' with '
'
+func (b *BreakWriter) Write(bs []byte) (n int, err error) {
+ pos := 0
+ for pos < len(bs) {
+ idx := bytes.IndexByte(bs[pos:], '\n')
+ if idx < 0 {
+ wn, err := b.Writer.Write(bs[pos:])
+ return n + wn, err
+ }
+
+ if idx > 0 {
+ wn, err := b.Writer.Write(bs[pos : pos+idx])
+ n += wn
+ if err != nil {
+ return n, err
+ }
+ }
+
+ if _, err = b.Writer.Write([]byte("
")); err != nil {
+ return n, err
+ }
+ pos += idx + 1
+
+ n++
+ }
+
+ return n, err
+}
diff --git a/modules/charset/breakwriter_test.go b/modules/charset/breakwriter_test.go
new file mode 100644
index 0000000000..6bbed42ea5
--- /dev/null
+++ b/modules/charset/breakwriter_test.go
@@ -0,0 +1,69 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package charset
+
+import (
+ "strings"
+ "testing"
+)
+
+func TestBreakWriter_Write(t *testing.T) {
+ tests := []struct {
+ name string
+ kase string
+ expect string
+ wantErr bool
+ }{
+ {
+ name: "noline",
+ kase: "abcdefghijklmnopqrstuvwxyz",
+ expect: "abcdefghijklmnopqrstuvwxyz",
+ },
+ {
+ name: "endline",
+ kase: "abcdefghijklmnopqrstuvwxyz\n",
+ expect: "abcdefghijklmnopqrstuvwxyz
",
+ },
+ {
+ name: "startline",
+ kase: "\nabcdefghijklmnopqrstuvwxyz",
+ expect: "
abcdefghijklmnopqrstuvwxyz",
+ },
+ {
+ name: "onlyline",
+ kase: "\n\n\n",
+ expect: "
",
+ },
+ {
+ name: "empty",
+ kase: "",
+ expect: "",
+ },
+ {
+ name: "midline",
+ kase: "\nabc\ndefghijkl\nmnopqrstuvwxy\nz",
+ expect: "
abc
defghijkl
mnopqrstuvwxy
z",
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ buf := &strings.Builder{}
+ b := &BreakWriter{
+ Writer: buf,
+ }
+ n, err := b.Write([]byte(tt.kase))
+ if (err != nil) != tt.wantErr {
+ t.Errorf("BreakWriter.Write() error = %v, wantErr %v", err, tt.wantErr)
+ return
+ }
+ if n != len(tt.kase) {
+ t.Errorf("BreakWriter.Write() = %v, want %v", n, len(tt.kase))
+ }
+ if buf.String() != tt.expect {
+ t.Errorf("BreakWriter.Write() wrote %q, want %v", buf.String(), tt.expect)
+ }
+ })
+ }
+}
diff --git a/modules/charset/escape.go b/modules/charset/escape.go
index 9c1baafba3..b264a569ff 100644
--- a/modules/charset/escape.go
+++ b/modules/charset/escape.go
@@ -1,236 +1,58 @@
-// Copyright 2021 The Gitea Authors. All rights reserved.
+// Copyright 2022 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
+//go:generate go run invisible/generate.go -v -o ./invisible_gen.go
+
+//go:generate go run ambiguous/generate.go -v -o ./ambiguous_gen.go ambiguous/ambiguous.json
+
package charset
import (
- "bytes"
- "fmt"
"io"
"strings"
- "unicode"
- "unicode/utf8"
- "golang.org/x/text/unicode/bidi"
+ "code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/translation"
)
-// EscapeStatus represents the findings of the unicode escaper
-type EscapeStatus struct {
- Escaped bool
- HasError bool
- HasBadRunes bool
- HasControls bool
- HasSpaces bool
- HasMarks bool
- HasBIDI bool
- BadBIDI bool
- HasRTLScript bool
- HasLTRScript bool
+// RuneNBSP is the codepoint for NBSP
+const RuneNBSP = 0xa0
+
+// EscapeControlHTML escapes the unicode control sequences in a provided html document
+func EscapeControlHTML(text string, locale translation.Locale, allowed ...rune) (escaped *EscapeStatus, output string) {
+ sb := &strings.Builder{}
+ outputStream := &HTMLStreamerWriter{Writer: sb}
+ streamer := NewEscapeStreamer(locale, outputStream, allowed...).(*escapeStreamer)
+
+ if err := StreamHTML(strings.NewReader(text), streamer); err != nil {
+ streamer.escaped.HasError = true
+ log.Error("Error whilst escaping: %v", err)
+ }
+ return streamer.escaped, sb.String()
}
-// Or combines two EscapeStatus structs into one representing the conjunction of the two
-func (status EscapeStatus) Or(other EscapeStatus) EscapeStatus {
- st := status
- st.Escaped = st.Escaped || other.Escaped
- st.HasError = st.HasError || other.HasError
- st.HasBadRunes = st.HasBadRunes || other.HasBadRunes
- st.HasControls = st.HasControls || other.HasControls
- st.HasSpaces = st.HasSpaces || other.HasSpaces
- st.HasMarks = st.HasMarks || other.HasMarks
- st.HasBIDI = st.HasBIDI || other.HasBIDI
- st.BadBIDI = st.BadBIDI || other.BadBIDI
- st.HasRTLScript = st.HasRTLScript || other.HasRTLScript
- st.HasLTRScript = st.HasLTRScript || other.HasLTRScript
- return st
+// EscapeControlReaders escapes the unicode control sequences in a provider reader and writer in a locale and returns the findings as an EscapeStatus and the escaped []byte
+func EscapeControlReader(reader io.Reader, writer io.Writer, locale translation.Locale, allowed ...rune) (escaped *EscapeStatus, err error) {
+ outputStream := &HTMLStreamerWriter{Writer: writer}
+ streamer := NewEscapeStreamer(locale, outputStream, allowed...).(*escapeStreamer)
+
+ if err = StreamHTML(reader, streamer); err != nil {
+ streamer.escaped.HasError = true
+ log.Error("Error whilst escaping: %v", err)
+ }
+ return streamer.escaped, err
}
// EscapeControlString escapes the unicode control sequences in a provided string and returns the findings as an EscapeStatus and the escaped string
-func EscapeControlString(text string) (EscapeStatus, string) {
+func EscapeControlString(text string, locale translation.Locale, allowed ...rune) (escaped *EscapeStatus, output string) {
sb := &strings.Builder{}
- escaped, _ := EscapeControlReader(strings.NewReader(text), sb)
- return escaped, sb.String()
-}
+ outputStream := &HTMLStreamerWriter{Writer: sb}
+ streamer := NewEscapeStreamer(locale, outputStream, allowed...).(*escapeStreamer)
-// EscapeControlBytes escapes the unicode control sequences a provided []byte and returns the findings as an EscapeStatus and the escaped []byte
-func EscapeControlBytes(text []byte) (EscapeStatus, []byte) {
- buf := &bytes.Buffer{}
- escaped, _ := EscapeControlReader(bytes.NewReader(text), buf)
- return escaped, buf.Bytes()
-}
-
-// EscapeControlReader escapes the unicode control sequences a provided Reader writing the escaped output to the output and returns the findings as an EscapeStatus and an error
-func EscapeControlReader(text io.Reader, output io.Writer) (escaped EscapeStatus, err error) {
- buf := make([]byte, 4096)
- readStart := 0
- runeCount := 0
- var n int
- var writePos int
-
- lineHasBIDI := false
- lineHasRTLScript := false
- lineHasLTRScript := false
-
-readingloop:
- for err == nil {
- n, err = text.Read(buf[readStart:])
- bs := buf[:n+readStart]
- n = len(bs)
- i := 0
-
- for i < len(bs) {
- r, size := utf8.DecodeRune(bs[i:])
- runeCount++
-
- // Now handle the codepoints
- switch {
- case r == utf8.RuneError:
- if writePos < i {
- if _, err = output.Write(bs[writePos:i]); err != nil {
- escaped.HasError = true
- return
- }
- writePos = i
- }
- // runes can be at most 4 bytes - so...
- if len(bs)-i <= 3 {
- // if not request more data
- copy(buf, bs[i:])
- readStart = n - i
- writePos = 0
- continue readingloop
- }
- // this is a real broken rune
- escaped.HasBadRunes = true
- escaped.Escaped = true
- if err = writeBroken(output, bs[i:i+size]); err != nil {
- escaped.HasError = true
- return
- }
- writePos += size
- case r == '\n':
- if lineHasBIDI && !lineHasRTLScript && lineHasLTRScript {
- escaped.BadBIDI = true
- }
- lineHasBIDI = false
- lineHasRTLScript = false
- lineHasLTRScript = false
-
- case runeCount == 1 && r == 0xFEFF: // UTF BOM
- // the first BOM is safe
- case r == '\r' || r == '\t' || r == ' ':
- // These are acceptable control characters and space characters
- case unicode.IsSpace(r):
- escaped.HasSpaces = true
- escaped.Escaped = true
- if writePos < i {
- if _, err = output.Write(bs[writePos:i]); err != nil {
- escaped.HasError = true
- return
- }
- }
- if err = writeEscaped(output, r); err != nil {
- escaped.HasError = true
- return
- }
- writePos = i + size
- case unicode.Is(unicode.Bidi_Control, r):
- escaped.Escaped = true
- escaped.HasBIDI = true
- if writePos < i {
- if _, err = output.Write(bs[writePos:i]); err != nil {
- escaped.HasError = true
- return
- }
- }
- lineHasBIDI = true
- if err = writeEscaped(output, r); err != nil {
- escaped.HasError = true
- return
- }
- writePos = i + size
- case unicode.Is(unicode.C, r):
- escaped.Escaped = true
- escaped.HasControls = true
- if writePos < i {
- if _, err = output.Write(bs[writePos:i]); err != nil {
- escaped.HasError = true
- return
- }
- }
- if err = writeEscaped(output, r); err != nil {
- escaped.HasError = true
- return
- }
- writePos = i + size
- case unicode.Is(unicode.M, r):
- escaped.Escaped = true
- escaped.HasMarks = true
- if writePos < i {
- if _, err = output.Write(bs[writePos:i]); err != nil {
- escaped.HasError = true
- return
- }
- }
- if err = writeEscaped(output, r); err != nil {
- escaped.HasError = true
- return
- }
- writePos = i + size
- default:
- p, _ := bidi.Lookup(bs[i : i+size])
- c := p.Class()
- if c == bidi.R || c == bidi.AL {
- lineHasRTLScript = true
- escaped.HasRTLScript = true
- } else if c == bidi.L {
- lineHasLTRScript = true
- escaped.HasLTRScript = true
- }
- }
- i += size
- }
- if n > 0 {
- // we read something...
- // write everything unwritten
- if writePos < i {
- if _, err = output.Write(bs[writePos:i]); err != nil {
- escaped.HasError = true
- return
- }
- }
-
- // reset the starting positions for the next read
- readStart = 0
- writePos = 0
- }
+ if err := streamer.Text(text); err != nil {
+ streamer.escaped.HasError = true
+ log.Error("Error whilst escaping: %v", err)
}
- if readStart > 0 {
- // this means that there is an incomplete or broken rune at 0-readStart and we read nothing on the last go round
- escaped.Escaped = true
- escaped.HasBadRunes = true
- if err = writeBroken(output, buf[:readStart]); err != nil {
- escaped.HasError = true
- return
- }
- }
- if err == io.EOF {
- if lineHasBIDI && !lineHasRTLScript && lineHasLTRScript {
- escaped.BadBIDI = true
- }
- err = nil
- return
- }
- escaped.HasError = true
- return escaped, err
-}
-
-func writeBroken(output io.Writer, bs []byte) (err error) {
- _, err = fmt.Fprintf(output, `<%X>`, bs)
- return err
-}
-
-func writeEscaped(output io.Writer, r rune) (err error) {
- _, err = fmt.Fprintf(output, `%c`, r, r)
- return err
+ return streamer.escaped, sb.String()
}
diff --git a/modules/charset/escape_status.go b/modules/charset/escape_status.go
new file mode 100644
index 0000000000..7ff0ef112b
--- /dev/null
+++ b/modules/charset/escape_status.go
@@ -0,0 +1,28 @@
+// Copyright 2021 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package charset
+
+// EscapeStatus represents the findings of the unicode escaper
+type EscapeStatus struct {
+ Escaped bool
+ HasError bool
+ HasBadRunes bool
+ HasInvisible bool
+ HasAmbiguous bool
+}
+
+// Or combines two EscapeStatus structs into one representing the conjunction of the two
+func (status *EscapeStatus) Or(other *EscapeStatus) *EscapeStatus {
+ st := status
+ if status == nil {
+ st = &EscapeStatus{}
+ }
+ st.Escaped = st.Escaped || other.Escaped
+ st.HasError = st.HasError || other.HasError
+ st.HasBadRunes = st.HasBadRunes || other.HasBadRunes
+ st.HasAmbiguous = st.HasAmbiguous || other.HasAmbiguous
+ st.HasInvisible = st.HasInvisible || other.HasInvisible
+ return st
+}
diff --git a/modules/charset/escape_stream.go b/modules/charset/escape_stream.go
new file mode 100644
index 0000000000..e5f303d26f
--- /dev/null
+++ b/modules/charset/escape_stream.go
@@ -0,0 +1,298 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package charset
+
+import (
+ "fmt"
+ "regexp"
+ "sort"
+ "strings"
+ "unicode"
+ "unicode/utf8"
+
+ "code.gitea.io/gitea/modules/translation"
+
+ "golang.org/x/net/html"
+)
+
+// VScode defaultWordRegexp
+var defaultWordRegexp = regexp.MustCompile(`(-?\d*\.\d\w*)|([^\` + "`" + `\~\!\@\#\$\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s\x00-\x1f]+)`)
+
+func NewEscapeStreamer(locale translation.Locale, next HTMLStreamer, allowed ...rune) HTMLStreamer {
+ return &escapeStreamer{
+ escaped: &EscapeStatus{},
+ PassthroughHTMLStreamer: *NewPassthroughStreamer(next),
+ locale: locale,
+ ambiguousTables: AmbiguousTablesForLocale(locale),
+ allowed: allowed,
+ }
+}
+
+type escapeStreamer struct {
+ PassthroughHTMLStreamer
+ escaped *EscapeStatus
+ locale translation.Locale
+ ambiguousTables []*AmbiguousTable
+ allowed []rune
+}
+
+func (e *escapeStreamer) EscapeStatus() *EscapeStatus {
+ return e.escaped
+}
+
+// Text tells the next streamer there is a text
+func (e *escapeStreamer) Text(data string) error {
+ sb := &strings.Builder{}
+ pos, until, next := 0, 0, 0
+ if len(data) > len(UTF8BOM) && data[:len(UTF8BOM)] == string(UTF8BOM) {
+ _, _ = sb.WriteString(data[:len(UTF8BOM)])
+ pos = len(UTF8BOM)
+ }
+ dataBytes := []byte(data)
+ for pos < len(data) {
+ nextIdxs := defaultWordRegexp.FindStringIndex(data[pos:])
+ if nextIdxs == nil {
+ until = len(data)
+ next = until
+ } else {
+ until, next = nextIdxs[0]+pos, nextIdxs[1]+pos
+ }
+
+ // from pos until until we know that the runes are not \r\t\n or even ' '
+ runes := make([]rune, 0, next-until)
+ positions := make([]int, 0, next-until+1)
+
+ for pos < until {
+ r, sz := utf8.DecodeRune(dataBytes[pos:])
+ positions = positions[:0]
+ positions = append(positions, pos, pos+sz)
+ types, confusables, _ := e.runeTypes(r)
+ if err := e.handleRunes(dataBytes, []rune{r}, positions, types, confusables, sb); err != nil {
+ return err
+ }
+ pos += sz
+ }
+
+ for i := pos; i < next; {
+ r, sz := utf8.DecodeRune(dataBytes[i:])
+ runes = append(runes, r)
+ positions = append(positions, i)
+ i += sz
+ }
+ positions = append(positions, next)
+ types, confusables, runeCounts := e.runeTypes(runes...)
+ if runeCounts.needsEscape() {
+ if err := e.handleRunes(dataBytes, runes, positions, types, confusables, sb); err != nil {
+ return err
+ }
+ } else {
+ _, _ = sb.Write(dataBytes[pos:next])
+ }
+ pos = next
+ }
+ if sb.Len() > 0 {
+ if err := e.PassthroughHTMLStreamer.Text(sb.String()); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func (e *escapeStreamer) handleRunes(data []byte, runes []rune, positions []int, types []runeType, confusables []rune, sb *strings.Builder) error {
+ for i, r := range runes {
+ switch types[i] {
+ case brokenRuneType:
+ if sb.Len() > 0 {
+ if err := e.PassthroughHTMLStreamer.Text(sb.String()); err != nil {
+ return err
+ }
+ sb.Reset()
+ }
+ end := positions[i+1]
+ start := positions[i]
+ if err := e.brokenRune(data[start:end]); err != nil {
+ return err
+ }
+ case ambiguousRuneType:
+ if sb.Len() > 0 {
+ if err := e.PassthroughHTMLStreamer.Text(sb.String()); err != nil {
+ return err
+ }
+ sb.Reset()
+ }
+ if err := e.ambiguousRune(r, confusables[0]); err != nil {
+ return err
+ }
+ confusables = confusables[1:]
+ case invisibleRuneType:
+ if sb.Len() > 0 {
+ if err := e.PassthroughHTMLStreamer.Text(sb.String()); err != nil {
+ return err
+ }
+ sb.Reset()
+ }
+ if err := e.invisibleRune(r); err != nil {
+ return err
+ }
+ default:
+ _, _ = sb.WriteRune(r)
+ }
+ }
+ return nil
+}
+
+func (e *escapeStreamer) brokenRune(bs []byte) error {
+ e.escaped.Escaped = true
+ e.escaped.HasBadRunes = true
+
+ if err := e.PassthroughHTMLStreamer.StartTag("span", html.Attribute{
+ Key: "class",
+ Val: "broken-code-point",
+ }); err != nil {
+ return err
+ }
+ if err := e.PassthroughHTMLStreamer.Text(fmt.Sprintf("<%X>", bs)); err != nil {
+ return err
+ }
+
+ return e.PassthroughHTMLStreamer.EndTag("span")
+}
+
+func (e *escapeStreamer) ambiguousRune(r, c rune) error {
+ e.escaped.Escaped = true
+ e.escaped.HasAmbiguous = true
+
+ if err := e.PassthroughHTMLStreamer.StartTag("span", html.Attribute{
+ Key: "class",
+ Val: "ambiguous-code-point tooltip",
+ }, html.Attribute{
+ Key: "data-content",
+ Val: e.locale.Tr("repo.ambiguous_character", r, c),
+ }); err != nil {
+ return err
+ }
+ if err := e.PassthroughHTMLStreamer.StartTag("span", html.Attribute{
+ Key: "class",
+ Val: "char",
+ }); err != nil {
+ return err
+ }
+ if err := e.PassthroughHTMLStreamer.Text(string(r)); err != nil {
+ return err
+ }
+ if err := e.PassthroughHTMLStreamer.EndTag("span"); err != nil {
+ return err
+ }
+
+ return e.PassthroughHTMLStreamer.EndTag("span")
+}
+
+func (e *escapeStreamer) invisibleRune(r rune) error {
+ e.escaped.Escaped = true
+ e.escaped.HasInvisible = true
+
+ if err := e.PassthroughHTMLStreamer.StartTag("span", html.Attribute{
+ Key: "class",
+ Val: "escaped-code-point",
+ }, html.Attribute{
+ Key: "data-escaped",
+ Val: fmt.Sprintf("[U+%04X]", r),
+ }); err != nil {
+ return err
+ }
+ if err := e.PassthroughHTMLStreamer.StartTag("span", html.Attribute{
+ Key: "class",
+ Val: "char",
+ }); err != nil {
+ return err
+ }
+ if err := e.PassthroughHTMLStreamer.Text(string(r)); err != nil {
+ return err
+ }
+ if err := e.PassthroughHTMLStreamer.EndTag("span"); err != nil {
+ return err
+ }
+
+ return e.PassthroughHTMLStreamer.EndTag("span")
+}
+
+type runeCountType struct {
+ numBasicRunes int
+ numNonConfusingNonBasicRunes int
+ numAmbiguousRunes int
+ numInvisibleRunes int
+ numBrokenRunes int
+}
+
+func (counts runeCountType) needsEscape() bool {
+ if counts.numBrokenRunes > 0 {
+ return true
+ }
+ if counts.numBasicRunes == 0 &&
+ counts.numNonConfusingNonBasicRunes > 0 {
+ return false
+ }
+ return counts.numAmbiguousRunes > 0 || counts.numInvisibleRunes > 0
+}
+
+type runeType int
+
+const (
+ basicASCIIRuneType runeType = iota //nolint // <- This is technically deadcode but its self-documenting so it should stay
+ brokenRuneType
+ nonBasicASCIIRuneType
+ ambiguousRuneType
+ invisibleRuneType
+)
+
+func (e *escapeStreamer) runeTypes(runes ...rune) (types []runeType, confusables []rune, runeCounts runeCountType) {
+ types = make([]runeType, len(runes))
+ for i, r := range runes {
+ var confusable rune
+ switch {
+ case r == utf8.RuneError:
+ types[i] = brokenRuneType
+ runeCounts.numBrokenRunes++
+ case r == ' ' || r == '\t' || r == '\n':
+ runeCounts.numBasicRunes++
+ case e.isAllowed(r):
+ if r > 0x7e || r < 0x20 {
+ types[i] = nonBasicASCIIRuneType
+ runeCounts.numNonConfusingNonBasicRunes++
+ } else {
+ runeCounts.numBasicRunes++
+ }
+ case unicode.Is(InvisibleRanges, r):
+ types[i] = invisibleRuneType
+ runeCounts.numInvisibleRunes++
+ case unicode.IsControl(r):
+ types[i] = invisibleRuneType
+ runeCounts.numInvisibleRunes++
+ case isAmbiguous(r, &confusable, e.ambiguousTables...):
+ confusables = append(confusables, confusable)
+ types[i] = ambiguousRuneType
+ runeCounts.numAmbiguousRunes++
+ case r > 0x7e || r < 0x20:
+ types[i] = nonBasicASCIIRuneType
+ runeCounts.numNonConfusingNonBasicRunes++
+ default:
+ runeCounts.numBasicRunes++
+ }
+ }
+ return types, confusables, runeCounts
+}
+
+func (e *escapeStreamer) isAllowed(r rune) bool {
+ if len(e.allowed) == 0 {
+ return false
+ }
+ if len(e.allowed) == 1 {
+ return e.allowed[0] == r
+ }
+
+ return sort.Search(len(e.allowed), func(i int) bool {
+ return e.allowed[i] >= r
+ }) >= 0
+}
diff --git a/modules/charset/escape_test.go b/modules/charset/escape_test.go
index 01ccca7724..a7232a4658 100644
--- a/modules/charset/escape_test.go
+++ b/modules/charset/escape_test.go
@@ -8,6 +8,8 @@ import (
"reflect"
"strings"
"testing"
+
+ "code.gitea.io/gitea/modules/translation"
)
type escapeControlTest struct {
@@ -25,37 +27,37 @@ var escapeControlTests = []escapeControlTest{
name: "single line western",
text: "single line western",
result: "single line western",
- status: EscapeStatus{HasLTRScript: true},
+ status: EscapeStatus{},
},
{
name: "multi line western",
text: "single line western\nmulti line western\n",
result: "single line western\nmulti line western\n",
- status: EscapeStatus{HasLTRScript: true},
+ status: EscapeStatus{},
},
{
name: "multi line western non-breaking space",
text: "single line western\nmulti line western\n",
result: `single line western` + "\n" + `multi line western` + "\n",
- status: EscapeStatus{Escaped: true, HasLTRScript: true, HasSpaces: true},
+ status: EscapeStatus{Escaped: true, HasInvisible: true},
},
{
name: "mixed scripts: western + japanese",
text: "日属秘ぞしちゅ。Then some western.",
result: "日属秘ぞしちゅ。Then some western.",
- status: EscapeStatus{HasLTRScript: true},
+ status: EscapeStatus{},
},
{
name: "japanese",
text: "日属秘ぞしちゅ。",
result: "日属秘ぞしちゅ。",
- status: EscapeStatus{HasLTRScript: true},
+ status: EscapeStatus{},
},
{
name: "hebrew",
text: "עד תקופת יוון העתיקה היה העיסוק במתמטיקה תכליתי בלבד: היא שימשה כאוסף של נוסחאות לחישוב קרקע, אוכלוסין וכו'. פריצת הדרך של היוונים, פרט לתרומותיהם הגדולות לידע המתמטי, הייתה בלימוד המתמטיקה כשלעצמה, מתוקף ערכה הרוחני. יחסם של חלק מהיוונים הקדמונים למתמטיקה היה דתי - למשל, הכת שאסף סביבו פיתגורס האמינה כי המתמטיקה היא הבסיס לכל הדברים. היוונים נחשבים ליוצרי מושג ההוכחה המתמטית, וכן לראשונים שעסקו במתמטיקה לשם עצמה, כלומר כתחום מחקרי עיוני ומופשט ולא רק כעזר שימושי. עם זאת, לצדה",
- result: "עד תקופת יוון העתיקה היה העיסוק במתמטיקה תכליתי בלבד: היא שימשה כאוסף של נוסחאות לחישוב קרקע, אוכלוסין וכו'. פריצת הדרך של היוונים, פרט לתרומותיהם הגדולות לידע המתמטי, הייתה בלימוד המתמטיקה כשלעצמה, מתוקף ערכה הרוחני. יחסם של חלק מהיוונים הקדמונים למתמטיקה היה דתי - למשל, הכת שאסף סביבו פיתגורס האמינה כי המתמטיקה היא הבסיס לכל הדברים. היוונים נחשבים ליוצרי מושג ההוכחה המתמטית, וכן לראשונים שעסקו במתמטיקה לשם עצמה, כלומר כתחום מחקרי עיוני ומופשט ולא רק כעזר שימושי. עם זאת, לצדה",
- status: EscapeStatus{HasRTLScript: true},
+ result: `עד תקופת יוון העתיקה היה העיסוק במתמטיקה תכליתי בלבד: היא שימשה כאוסף של נוסחאות לחישוב קרקע, אוכלוסין וכו'. פריצת הדרך של היוונים, פרט לתרומותיהם הגדולות לידע המתמטי, הייתה בלימוד המתמטיקה כשלעצמה, מתוקף ערכה הרוחני. יחסם של חלק מהיוונים הקדמונים למתמטיקה היה דתי - למשל, הכת שאסף סביבו פיתגורס האמינה כי המתמטיקה היא הבסיס לכל הדברים. היוונים נחשבים ליוצרי מושג ההוכחה המתמטית, וכן לראשונים שעסקו במתמטיקה לשם עצמה, כלומר כתחום מחקרי עיוני ומופשט ולא רק כעזר שימושי. עם זאת, לצדה`,
+ status: EscapeStatus{Escaped: true, HasAmbiguous: true},
},
{
name: "more hebrew",
@@ -64,12 +66,12 @@ var escapeControlTests = []escapeControlTest{
המתמטיקאי הבולט הראשון ביוון העתיקה, ויש האומרים בתולדות האנושות, הוא תאלס (624 לפנה"ס - 546 לפנה"ס בקירוב).[1] לא יהיה זה משולל יסוד להניח שהוא האדם הראשון שהוכיח משפט מתמטי, ולא רק גילה אותו. תאלס הוכיח שישרים מקבילים חותכים מצד אחד של שוקי זווית קטעים בעלי יחסים שווים (משפט תאלס הראשון), שהזווית המונחת על קוטר במעגל היא זווית ישרה (משפט תאלס השני), שהקוטר מחלק את המעגל לשני חלקים שווים, ושזוויות הבסיס במשולש שווה-שוקיים שוות זו לזו. מיוחסות לו גם שיטות למדידת גובהן של הפירמידות בעזרת מדידת צילן ולקביעת מיקומה של ספינה הנראית מן החוף.
בשנים 582 לפנה"ס עד 496 לפנה"ס, בקירוב, חי מתמטיקאי חשוב במיוחד - פיתגורס. המקורות הראשוניים עליו מועטים, וההיסטוריונים מתקשים להפריד את העובדות משכבת המסתורין והאגדות שנקשרו בו. ידוע שסביבו התקבצה האסכולה הפיתגוראית מעין כת פסבדו-מתמטית שהאמינה ש"הכל מספר", או ליתר דיוק הכל ניתן לכימות, וייחסה למספרים משמעויות מיסטיות. ככל הנראה הפיתגוראים ידעו לבנות את הגופים האפלטוניים, הכירו את הממוצע האריתמטי, הממוצע הגאומטרי והממוצע ההרמוני והגיעו להישגים חשובים נוספים. ניתן לומר שהפיתגוראים גילו את היותו של השורש הריבועי של 2, שהוא גם האלכסון בריבוע שאורך צלעותיו 1, אי רציונלי, אך תגליתם הייתה למעשה רק שהקטעים "חסרי מידה משותפת", ומושג המספר האי רציונלי מאוחר יותר.[2] אזכור ראשון לקיומם של קטעים חסרי מידה משותפת מופיע בדיאלוג "תאיטיטוס" של אפלטון, אך רעיון זה היה מוכר עוד קודם לכן, במאה החמישית לפנה"ס להיפאסוס, בן האסכולה הפיתגוראית, ואולי לפיתגורס עצמו.[3]`,
- result: `בתקופה מאוחרת יותר, השתמשו היוונים בשיטת סימון מתקדמת יותר, שבה הוצגו המספרים לפי 22 אותיות האלפבית היווני. לסימון המספרים בין 1 ל-9 נקבעו תשע האותיות הראשונות, בתוספת גרש ( ' ) בצד ימין של האות, למעלה; תשע האותיות הבאות ייצגו את העשרות מ-10 עד 90, והבאות את המאות. לסימון הספרות בין 1000 ל-900,000, השתמשו היוונים באותן אותיות, אך הוסיפו לאותיות את הגרש דווקא מצד שמאל של האותיות, למטה. ממיליון ומעלה, כנראה השתמשו היוונים בשני תגים במקום אחד.
+ result: `בתקופה מאוחרת יותר, השתמשו היוונים בשיטת סימון מתקדמת יותר, שבה הוצגו המספרים לפי 22 אותיות האלפבית היווני. לסימון המספרים בין 1 ל-9 נקבעו תשע האותיות הראשונות, בתוספת גרש ( ' ) בצד ימין של האות, למעלה; תשע האותיות הבאות ייצגו את העשרות מ-10 עד 90, והבאות את המאות. לסימון הספרות בין 1000 ל-900,000, השתמשו היוונים באותן אותיות, אך הוסיפו לאותיות את הגרש דווקא מצד שמאל של האותיות, למטה. ממיליון ומעלה, כנראה השתמשו היוונים בשני תגים במקום אחד.
- המתמטיקאי הבולט הראשון ביוון העתיקה, ויש האומרים בתולדות האנושות, הוא תאלס (624 לפנה"ס - 546 לפנה"ס בקירוב).[1] לא יהיה זה משולל יסוד להניח שהוא האדם הראשון שהוכיח משפט מתמטי, ולא רק גילה אותו. תאלס הוכיח שישרים מקבילים חותכים מצד אחד של שוקי זווית קטעים בעלי יחסים שווים (משפט תאלס הראשון), שהזווית המונחת על קוטר במעגל היא זווית ישרה (משפט תאלס השני), שהקוטר מחלק את המעגל לשני חלקים שווים, ושזוויות הבסיס במשולש שווה-שוקיים שוות זו לזו. מיוחסות לו גם שיטות למדידת גובהן של הפירמידות בעזרת מדידת צילן ולקביעת מיקומה של ספינה הנראית מן החוף.
+ המתמטיקאי הבולט הראשון ביוון העתיקה, ויש האומרים בתולדות האנושות, הוא תאלס (624 לפנה"ס - 546 לפנה"ס בקירוב).[1] לא יהיה זה משולל יסוד להניח שהוא האדם הראשון שהוכיח משפט מתמטי, ולא רק גילה אותו. תאלס הוכיח שישרים מקבילים חותכים מצד אחד של שוקי זווית קטעים בעלי יחסים שווים (משפט תאלס הראשון), שהזווית המונחת על קוטר במעגל היא זווית ישרה (משפט תאלס השני), שהקוטר מחלק את המעגל לשני חלקים שווים, ושזוויות הבסיס במשולש שווה-שוקיים שוות זו לזו. מיוחסות לו גם שיטות למדידת גובהן של הפירמידות בעזרת מדידת צילן ולקביעת מיקומה של ספינה הנראית מן החוף.
- בשנים 582 לפנה"ס עד 496 לפנה"ס, בקירוב, חי מתמטיקאי חשוב במיוחד - פיתגורס. המקורות הראשוניים עליו מועטים, וההיסטוריונים מתקשים להפריד את העובדות משכבת המסתורין והאגדות שנקשרו בו. ידוע שסביבו התקבצה האסכולה הפיתגוראית מעין כת פסבדו-מתמטית שהאמינה ש"הכל מספר", או ליתר דיוק הכל ניתן לכימות, וייחסה למספרים משמעויות מיסטיות. ככל הנראה הפיתגוראים ידעו לבנות את הגופים האפלטוניים, הכירו את הממוצע האריתמטי, הממוצע הגאומטרי והממוצע ההרמוני והגיעו להישגים חשובים נוספים. ניתן לומר שהפיתגוראים גילו את היותו של השורש הריבועי של 2, שהוא גם האלכסון בריבוע שאורך צלעותיו 1, אי רציונלי, אך תגליתם הייתה למעשה רק שהקטעים "חסרי מידה משותפת", ומושג המספר האי רציונלי מאוחר יותר.[2] אזכור ראשון לקיומם של קטעים חסרי מידה משותפת מופיע בדיאלוג "תאיטיטוס" של אפלטון, אך רעיון זה היה מוכר עוד קודם לכן, במאה החמישית לפנה"ס להיפאסוס, בן האסכולה הפיתגוראית, ואולי לפיתגורס עצמו.[3]`,
- status: EscapeStatus{HasRTLScript: true},
+ בשנים 582 לפנה"ס עד 496 לפנה"ס, בקירוב, חי מתמטיקאי חשוב במיוחד - פיתגורס. המקורות הראשוניים עליו מועטים, וההיסטוריונים מתקשים להפריד את העובדות משכבת המסתורין והאגדות שנקשרו בו. ידוע שסביבו התקבצה האסכולה הפיתגוראית מעין כת פסבדו-מתמטית שהאמינה ש"הכל מספר", או ליתר דיוק הכל ניתן לכימות, וייחסה למספרים משמעויות מיסטיות. ככל הנראה הפיתגוראים ידעו לבנות את הגופים האפלטוניים, הכירו את הממוצע האריתמטי, הממוצע הגאומטרי והממוצע ההרמוני והגיעו להישגים חשובים נוספים. ניתן לומר שהפיתגוראים גילו את היותו של השורש הריבועי של 2, שהוא גם האלכסון בריבוע שאורך צלעותיו 1, אי רציונלי, אך תגליתם הייתה למעשה רק שהקטעים "חסרי מידה משותפת", ומושג המספר האי רציונלי מאוחר יותר.[2] אזכור ראשון לקיומם של קטעים חסרי מידה משותפת מופיע בדיאלוג "תאיטיטוס" של אפלטון, אך רעיון זה היה מוכר עוד קודם לכן, במאה החמישית לפנה"ס להיפאסוס, בן האסכולה הפיתגוראית, ואולי לפיתגורס עצמו.[3]`,
+ status: EscapeStatus{Escaped: true, HasAmbiguous: true},
},
{
name: "Mixed RTL+LTR",
@@ -79,10 +81,7 @@ then resh (ר), and finally heh (ה) (which should appear leftmost).`,
result: `Many computer programs fail to display bidirectional text correctly.
For example, the Hebrew name Sarah (שרה) is spelled: sin (ש) (which appears rightmost),
then resh (ר), and finally heh (ה) (which should appear leftmost).`,
- status: EscapeStatus{
- HasRTLScript: true,
- HasLTRScript: true,
- },
+ status: EscapeStatus{},
},
{
name: "Mixed RTL+LTR+BIDI",
@@ -90,32 +89,27 @@ then resh (ר), and finally heh (ה) (which should appear leftmost).`,
For example, the Hebrew name Sarah ` + "\u2067" + `שרה` + "\u2066\n" +
`sin (ש) (which appears rightmost), then resh (ר), and finally heh (ה) (which should appear leftmost).`,
result: `Many computer programs fail to display bidirectional text correctly.
- For example, the Hebrew name Sarah ` + "\u2067" + `שרה` + "\u2066" + `` + "\n" +
+ For example, the Hebrew name Sarah ` + "\u2067" + `שרה` + "\u2066\n" +
`sin (ש) (which appears rightmost), then resh (ר), and finally heh (ה) (which should appear leftmost).`,
- status: EscapeStatus{
- Escaped: true,
- HasBIDI: true,
- HasRTLScript: true,
- HasLTRScript: true,
- },
+ status: EscapeStatus{},
},
{
name: "Accented characters",
text: string([]byte{0xc3, 0xa1, 0xc3, 0xa9, 0xc3, 0xad, 0xc3, 0xb3, 0xc3, 0xba}),
result: string([]byte{0xc3, 0xa1, 0xc3, 0xa9, 0xc3, 0xad, 0xc3, 0xb3, 0xc3, 0xba}),
- status: EscapeStatus{HasLTRScript: true},
+ status: EscapeStatus{},
},
{
name: "Program",
text: "string([]byte{0xc3, 0xa1, 0xc3, 0xa9, 0xc3, 0xad, 0xc3, 0xb3, 0xc3, 0xba})",
result: "string([]byte{0xc3, 0xa1, 0xc3, 0xa9, 0xc3, 0xad, 0xc3, 0xb3, 0xc3, 0xba})",
- status: EscapeStatus{HasLTRScript: true},
+ status: EscapeStatus{},
},
{
name: "CVE testcase",
text: "if access_level != \"user\u202E \u2066// Check if admin\u2069 \u2066\" {",
- result: `if access_level != "user` + "\u202e" + ` ` + "\u2066" + `// Check if admin` + "\u2069" + ` ` + "\u2066" + `" {`,
- status: EscapeStatus{Escaped: true, HasBIDI: true, BadBIDI: true, HasLTRScript: true},
+ result: `if access_level != "user` + "\u202e" + ` ` + "\u2066" + `// Check if admin` + "\u2069" + ` ` + "\u2066" + `" {`,
+ status: EscapeStatus{Escaped: true, HasInvisible: true},
},
{
name: "Mixed testcase with fail",
@@ -124,10 +118,10 @@ then resh (ר), and finally heh (ה) (which should appear leftmost).`,
`sin (ש) (which appears rightmost), then resh (ר), and finally heh (ה) (which should appear leftmost).` +
"\nif access_level != \"user\u202E \u2066// Check if admin\u2069 \u2066\" {\n",
result: `Many computer programs fail to display bidirectional text correctly.
- For example, the Hebrew name Sarah ` + "\u2067" + `שרה` + "\u2066" + `` + "\n" +
+ For example, the Hebrew name Sarah ` + "\u2067" + `שרה` + "\u2066\n" +
`sin (ש) (which appears rightmost), then resh (ר), and finally heh (ה) (which should appear leftmost).` +
- "\n" + `if access_level != "user` + "\u202e" + ` ` + "\u2066" + `// Check if admin` + "\u2069" + ` ` + "\u2066" + `" {` + "\n",
- status: EscapeStatus{Escaped: true, HasBIDI: true, BadBIDI: true, HasLTRScript: true, HasRTLScript: true},
+ "\n" + `if access_level != "user` + "\u202e" + ` ` + "\u2066" + `// Check if admin` + "\u2069" + ` ` + "\u2066" + `" {` + "\n",
+ status: EscapeStatus{Escaped: true, HasInvisible: true},
},
{
// UTF-8/16/32 all use the same codepoint for BOM
@@ -135,15 +129,23 @@ then resh (ר), and finally heh (ה) (which should appear leftmost).`,
name: "UTF BOM",
text: "\xef\xbb\xbftest",
result: "\xef\xbb\xbftest",
- status: EscapeStatus{HasLTRScript: true},
+ status: EscapeStatus{},
},
}
+type nullLocale struct{}
+
+func (nullLocale) Language() string { return "" }
+func (nullLocale) Tr(key string, _ ...interface{}) string { return key }
+func (nullLocale) TrN(cnt interface{}, key1, keyN string, args ...interface{}) string { return "" }
+
+var _ (translation.Locale) = nullLocale{}
+
func TestEscapeControlString(t *testing.T) {
for _, tt := range escapeControlTests {
t.Run(tt.name, func(t *testing.T) {
- status, result := EscapeControlString(tt.text)
- if !reflect.DeepEqual(status, tt.status) {
+ status, result := EscapeControlString(tt.text, nullLocale{})
+ if !reflect.DeepEqual(*status, tt.status) {
t.Errorf("EscapeControlString() status = %v, wanted= %v", status, tt.status)
}
if result != tt.result {
@@ -153,20 +155,6 @@ func TestEscapeControlString(t *testing.T) {
}
}
-func TestEscapeControlBytes(t *testing.T) {
- for _, tt := range escapeControlTests {
- t.Run(tt.name, func(t *testing.T) {
- status, result := EscapeControlBytes([]byte(tt.text))
- if !reflect.DeepEqual(status, tt.status) {
- t.Errorf("EscapeControlBytes() status = %v, wanted= %v", status, tt.status)
- }
- if string(result) != tt.result {
- t.Errorf("EscapeControlBytes()\nresult= %v,\nwanted= %v", result, tt.result)
- }
- })
- }
-}
-
func TestEscapeControlReader(t *testing.T) {
// lets add some control characters to the tests
tests := make([]escapeControlTest, 0, len(escapeControlTests)*3)
@@ -184,16 +172,7 @@ func TestEscapeControlReader(t *testing.T) {
test.text = addPrefix("\u001E", test.text)
test.result = addPrefix(``+"\u001e"+``, test.result)
test.status.Escaped = true
- test.status.HasControls = true
- tests = append(tests, test)
- }
-
- for _, test := range escapeControlTests {
- test.name += " (+Mark)"
- test.text = addPrefix("\u0300", test.text)
- test.result = addPrefix(``+"\u0300"+``, test.result)
- test.status.Escaped = true
- test.status.HasMarks = true
+ test.status.HasInvisible = true
tests = append(tests, test)
}
@@ -201,13 +180,13 @@ func TestEscapeControlReader(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
input := strings.NewReader(tt.text)
output := &strings.Builder{}
- status, err := EscapeControlReader(input, output)
+ status, err := EscapeControlReader(input, output, nullLocale{})
result := output.String()
if err != nil {
t.Errorf("EscapeControlReader(): err = %v", err)
}
- if !reflect.DeepEqual(status, tt.status) {
+ if !reflect.DeepEqual(*status, tt.status) {
t.Errorf("EscapeControlReader() status = %v, wanted= %v", status, tt.status)
}
if result != tt.result {
@@ -223,5 +202,5 @@ func TestEscapeControlReader_panic(t *testing.T) {
for i := 0; i < 6826; i++ {
bs = append(bs, []byte("—")...)
}
- _, _ = EscapeControlBytes(bs)
+ _, _ = EscapeControlString(string(bs), nullLocale{})
}
diff --git a/modules/charset/htmlstream.go b/modules/charset/htmlstream.go
new file mode 100644
index 0000000000..b354ce6a48
--- /dev/null
+++ b/modules/charset/htmlstream.go
@@ -0,0 +1,201 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package charset
+
+import (
+ "fmt"
+ "io"
+
+ "golang.org/x/net/html"
+)
+
+// HTMLStreamer represents a SAX-like interface for HTML
+type HTMLStreamer interface {
+ Error(err error) error
+ Doctype(data string) error
+ Comment(data string) error
+ StartTag(data string, attrs ...html.Attribute) error
+ SelfClosingTag(data string, attrs ...html.Attribute) error
+ EndTag(data string) error
+ Text(data string) error
+}
+
+// PassthroughHTMLStreamer is a passthrough streamer
+type PassthroughHTMLStreamer struct {
+ next HTMLStreamer
+}
+
+func NewPassthroughStreamer(next HTMLStreamer) *PassthroughHTMLStreamer {
+ return &PassthroughHTMLStreamer{next: next}
+}
+
+var _ (HTMLStreamer) = &PassthroughHTMLStreamer{}
+
+// Error tells the next streamer in line that there is an error
+func (p *PassthroughHTMLStreamer) Error(err error) error {
+ return p.next.Error(err)
+}
+
+// Doctype tells the next streamer what the doctype is
+func (p *PassthroughHTMLStreamer) Doctype(data string) error {
+ return p.next.Doctype(data)
+}
+
+// Comment tells the next streamer there is a comment
+func (p *PassthroughHTMLStreamer) Comment(data string) error {
+ return p.next.Comment(data)
+}
+
+// StartTag tells the next streamer there is a starting tag
+func (p *PassthroughHTMLStreamer) StartTag(data string, attrs ...html.Attribute) error {
+ return p.next.StartTag(data, attrs...)
+}
+
+// SelfClosingTag tells the next streamer there is a self-closing tag
+func (p *PassthroughHTMLStreamer) SelfClosingTag(data string, attrs ...html.Attribute) error {
+ return p.next.SelfClosingTag(data, attrs...)
+}
+
+// EndTag tells the next streamer there is a end tag
+func (p *PassthroughHTMLStreamer) EndTag(data string) error {
+ return p.next.EndTag(data)
+}
+
+// Text tells the next streamer there is a text
+func (p *PassthroughHTMLStreamer) Text(data string) error {
+ return p.next.Text(data)
+}
+
+// HTMLStreamWriter acts as a writing sink
+type HTMLStreamerWriter struct {
+ io.Writer
+ err error
+}
+
+// Write implements io.Writer
+func (h *HTMLStreamerWriter) Write(data []byte) (int, error) {
+ if h.err != nil {
+ return 0, h.err
+ }
+ return h.Writer.Write(data)
+}
+
+// Write implements io.StringWriter
+func (h *HTMLStreamerWriter) WriteString(data string) (int, error) {
+ if h.err != nil {
+ return 0, h.err
+ }
+ return h.Writer.Write([]byte(data))
+}
+
+// Error tells the next streamer in line that there is an error
+func (h *HTMLStreamerWriter) Error(err error) error {
+ if h.err == nil {
+ h.err = err
+ }
+ return h.err
+}
+
+// Doctype tells the next streamer what the doctype is
+func (h *HTMLStreamerWriter) Doctype(data string) error {
+ _, h.err = h.WriteString("")
+ return h.err
+}
+
+// Comment tells the next streamer there is a comment
+func (h *HTMLStreamerWriter) Comment(data string) error {
+ _, h.err = h.WriteString("")
+ return h.err
+}
+
+// StartTag tells the next streamer there is a starting tag
+func (h *HTMLStreamerWriter) StartTag(data string, attrs ...html.Attribute) error {
+ return h.startTag(data, attrs, false)
+}
+
+// SelfClosingTag tells the next streamer there is a self-closing tag
+func (h *HTMLStreamerWriter) SelfClosingTag(data string, attrs ...html.Attribute) error {
+ return h.startTag(data, attrs, true)
+}
+
+func (h *HTMLStreamerWriter) startTag(data string, attrs []html.Attribute, selfclosing bool) error {
+ if _, h.err = h.WriteString("<" + data); h.err != nil {
+ return h.err
+ }
+ for _, attr := range attrs {
+ if _, h.err = h.WriteString(" " + attr.Key + "=\"" + html.EscapeString(attr.Val) + "\""); h.err != nil {
+ return h.err
+ }
+ }
+ if selfclosing {
+ if _, h.err = h.WriteString("/>"); h.err != nil {
+ return h.err
+ }
+ } else {
+ if _, h.err = h.WriteString(">"); h.err != nil {
+ return h.err
+ }
+ }
+ return h.err
+}
+
+// EndTag tells the next streamer there is a end tag
+func (h *HTMLStreamerWriter) EndTag(data string) error {
+ _, h.err = h.WriteString("" + data + ">")
+ return h.err
+}
+
+// Text tells the next streamer there is a text
+func (h *HTMLStreamerWriter) Text(data string) error {
+ _, h.err = h.WriteString(html.EscapeString(data))
+ return h.err
+}
+
+// StreamHTML streams an html to a provided streamer
+func StreamHTML(source io.Reader, streamer HTMLStreamer) error {
+ tokenizer := html.NewTokenizer(source)
+ for {
+ tt := tokenizer.Next()
+ switch tt {
+ case html.ErrorToken:
+ if tokenizer.Err() != io.EOF {
+ return tokenizer.Err()
+ }
+ return nil
+ case html.DoctypeToken:
+ token := tokenizer.Token()
+ if err := streamer.Doctype(token.Data); err != nil {
+ return err
+ }
+ case html.CommentToken:
+ token := tokenizer.Token()
+ if err := streamer.Comment(token.Data); err != nil {
+ return err
+ }
+ case html.StartTagToken:
+ token := tokenizer.Token()
+ if err := streamer.StartTag(token.Data, token.Attr...); err != nil {
+ return err
+ }
+ case html.SelfClosingTagToken:
+ token := tokenizer.Token()
+ if err := streamer.StartTag(token.Data, token.Attr...); err != nil {
+ return err
+ }
+ case html.EndTagToken:
+ token := tokenizer.Token()
+ if err := streamer.EndTag(token.Data); err != nil {
+ return err
+ }
+ case html.TextToken:
+ token := tokenizer.Token()
+ if err := streamer.Text(token.Data); err != nil {
+ return err
+ }
+ default:
+ return fmt.Errorf("unknown type of token: %d", tt)
+ }
+ }
+}
diff --git a/modules/charset/invisible/generate.go b/modules/charset/invisible/generate.go
new file mode 100644
index 0000000000..39eddd58dd
--- /dev/null
+++ b/modules/charset/invisible/generate.go
@@ -0,0 +1,122 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package main
+
+import (
+ "bytes"
+ "flag"
+ "fmt"
+ "go/format"
+ "os"
+ "text/template"
+
+ "golang.org/x/text/unicode/rangetable"
+)
+
+// InvisibleRunes these are runes that vscode has assigned to be invisible
+// See https://github.com/hediet/vscode-unicode-data
+var InvisibleRunes = []rune{
+ 9, 10, 11, 12, 13, 32, 127, 160, 173, 847, 1564, 4447, 4448, 6068, 6069, 6155, 6156, 6157, 6158, 7355, 7356, 8192, 8193, 8194, 8195, 8196, 8197, 8198, 8199, 8200, 8201, 8202, 8203, 8204, 8205, 8206, 8207, 8234, 8235, 8236, 8237, 8238, 8239, 8287, 8288, 8289, 8290, 8291, 8292, 8293, 8294, 8295, 8296, 8297, 8298, 8299, 8300, 8301, 8302, 8303, 10240, 12288, 12644, 65024, 65025, 65026, 65027, 65028, 65029, 65030, 65031, 65032, 65033, 65034, 65035, 65036, 65037, 65038, 65039, 65279, 65440, 65520, 65521, 65522, 65523, 65524, 65525, 65526, 65527, 65528, 65532, 78844, 119155, 119156, 119157, 119158, 119159, 119160, 119161, 119162, 917504, 917505, 917506, 917507, 917508, 917509, 917510, 917511, 917512, 917513, 917514, 917515, 917516, 917517, 917518, 917519, 917520, 917521, 917522, 917523, 917524, 917525, 917526, 917527, 917528, 917529, 917530, 917531, 917532, 917533, 917534, 917535, 917536, 917537, 917538, 917539, 917540, 917541, 917542, 917543, 917544, 917545, 917546, 917547, 917548, 917549, 917550, 917551, 917552, 917553, 917554, 917555, 917556, 917557, 917558, 917559, 917560, 917561, 917562, 917563, 917564, 917565, 917566, 917567, 917568, 917569, 917570, 917571, 917572, 917573, 917574, 917575, 917576, 917577, 917578, 917579, 917580, 917581, 917582, 917583, 917584, 917585, 917586, 917587, 917588, 917589, 917590, 917591, 917592, 917593, 917594, 917595, 917596, 917597, 917598, 917599, 917600, 917601, 917602, 917603, 917604, 917605, 917606, 917607, 917608, 917609, 917610, 917611, 917612, 917613, 917614, 917615, 917616, 917617, 917618, 917619, 917620, 917621, 917622, 917623, 917624, 917625, 917626, 917627, 917628, 917629, 917630, 917631, 917760, 917761, 917762, 917763, 917764, 917765, 917766, 917767, 917768, 917769, 917770, 917771, 917772, 917773, 917774, 917775, 917776, 917777, 917778, 917779, 917780, 917781, 917782, 917783, 917784, 917785, 917786, 917787, 917788, 917789, 917790, 917791, 917792, 917793, 917794, 917795, 917796, 917797, 917798, 917799, 917800, 917801, 917802, 917803, 917804, 917805, 917806, 917807, 917808, 917809, 917810, 917811, 917812, 917813, 917814, 917815, 917816, 917817, 917818, 917819, 917820, 917821, 917822, 917823, 917824, 917825, 917826, 917827, 917828, 917829, 917830, 917831, 917832, 917833, 917834, 917835, 917836, 917837, 917838, 917839, 917840, 917841, 917842, 917843, 917844, 917845, 917846, 917847, 917848, 917849, 917850, 917851, 917852, 917853, 917854, 917855, 917856, 917857, 917858, 917859, 917860, 917861, 917862, 917863, 917864, 917865, 917866, 917867, 917868, 917869, 917870, 917871, 917872, 917873, 917874, 917875, 917876, 917877, 917878, 917879, 917880, 917881, 917882, 917883, 917884, 917885, 917886, 917887, 917888, 917889, 917890, 917891, 917892, 917893, 917894, 917895, 917896, 917897, 917898, 917899, 917900, 917901, 917902, 917903, 917904, 917905, 917906, 917907, 917908, 917909, 917910, 917911, 917912, 917913, 917914, 917915, 917916, 917917, 917918, 917919, 917920, 917921, 917922, 917923, 917924, 917925, 917926, 917927, 917928, 917929, 917930, 917931, 917932, 917933, 917934, 917935, 917936, 917937, 917938, 917939, 917940, 917941, 917942, 917943, 917944, 917945, 917946, 917947, 917948, 917949, 917950, 917951, 917952, 917953, 917954, 917955, 917956, 917957, 917958, 917959, 917960, 917961, 917962, 917963, 917964, 917965, 917966, 917967, 917968, 917969, 917970, 917971, 917972, 917973, 917974, 917975, 917976, 917977, 917978, 917979, 917980, 917981, 917982, 917983, 917984, 917985, 917986, 917987, 917988, 917989, 917990, 917991, 917992, 917993, 917994, 917995, 917996, 917997, 917998, 917999,
+}
+
+var verbose bool
+
+func main() {
+ flag.Usage = func() {
+ fmt.Fprintf(os.Stderr, `%s: Generate InvisibleRunesRange
+
+Usage: %[1]s [-v] [-o output.go]
+`, os.Args[0])
+ flag.PrintDefaults()
+ }
+
+ output := ""
+ flag.BoolVar(&verbose, "v", false, "verbose output")
+ flag.StringVar(&output, "o", "invisible_gen.go", "file to output to")
+ flag.Parse()
+
+ // First we filter the runes to remove
+ //
+ filtered := make([]rune, 0, len(InvisibleRunes))
+ for _, r := range InvisibleRunes {
+ if r == ' ' || r == '\t' || r == '\n' {
+ continue
+ }
+ filtered = append(filtered, r)
+ }
+
+ table := rangetable.New(filtered...)
+ if err := runTemplate(generatorTemplate, output, table); err != nil {
+ fatalf("Unable to run template: %v", err)
+ }
+}
+
+func runTemplate(t *template.Template, filename string, data interface{}) error {
+ buf := bytes.NewBuffer(nil)
+ if err := t.Execute(buf, data); err != nil {
+ return fmt.Errorf("unable to execute template: %w", err)
+ }
+ bs, err := format.Source(buf.Bytes())
+ if err != nil {
+ verbosef("Bad source:\n%s", buf.String())
+ return fmt.Errorf("unable to format source: %w", err)
+ }
+
+ old, err := os.ReadFile(filename)
+ if err != nil && !os.IsNotExist(err) {
+ return fmt.Errorf("failed to read old file %s because %w", filename, err)
+ } else if err == nil {
+ if bytes.Equal(bs, old) {
+ // files are the same don't rewrite it.
+ return nil
+ }
+ }
+
+ file, err := os.Create(filename)
+ if err != nil {
+ return fmt.Errorf("failed to create file %s because %w", filename, err)
+ }
+ defer file.Close()
+ _, err = file.Write(bs)
+ if err != nil {
+ return fmt.Errorf("unable to write generated source: %w", err)
+ }
+ return nil
+}
+
+var generatorTemplate = template.Must(template.New("invisibleTemplate").Parse(`// This file is generated by modules/charset/invisible/generate.go DO NOT EDIT
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package charset
+
+import "unicode"
+
+var InvisibleRanges = &unicode.RangeTable{
+ R16: []unicode.Range16{
+{{range .R16 }} {Lo:{{.Lo}}, Hi:{{.Hi}}, Stride: {{.Stride}}},
+{{end}} },
+ R32: []unicode.Range32{
+{{range .R32}} {Lo:{{.Lo}}, Hi:{{.Hi}}, Stride: {{.Stride}}},
+{{end}} },
+ LatinOffset: {{.LatinOffset}},
+}
+`))
+
+func logf(format string, args ...interface{}) {
+ fmt.Fprintf(os.Stderr, format+"\n", args...)
+}
+
+func verbosef(format string, args ...interface{}) {
+ if verbose {
+ logf(format, args...)
+ }
+}
+
+func fatalf(format string, args ...interface{}) {
+ logf("fatal: "+format+"\n", args...)
+ os.Exit(1)
+}
diff --git a/modules/charset/invisible_gen.go b/modules/charset/invisible_gen.go
new file mode 100644
index 0000000000..b3bfebe0c0
--- /dev/null
+++ b/modules/charset/invisible_gen.go
@@ -0,0 +1,37 @@
+// This file is generated by modules/charset/invisible/generate.go DO NOT EDIT
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package charset
+
+import "unicode"
+
+var InvisibleRanges = &unicode.RangeTable{
+ R16: []unicode.Range16{
+ {Lo: 11, Hi: 13, Stride: 1},
+ {Lo: 127, Hi: 160, Stride: 33},
+ {Lo: 173, Hi: 847, Stride: 674},
+ {Lo: 1564, Hi: 4447, Stride: 2883},
+ {Lo: 4448, Hi: 6068, Stride: 1620},
+ {Lo: 6069, Hi: 6155, Stride: 86},
+ {Lo: 6156, Hi: 6158, Stride: 1},
+ {Lo: 7355, Hi: 7356, Stride: 1},
+ {Lo: 8192, Hi: 8207, Stride: 1},
+ {Lo: 8234, Hi: 8239, Stride: 1},
+ {Lo: 8287, Hi: 8303, Stride: 1},
+ {Lo: 10240, Hi: 12288, Stride: 2048},
+ {Lo: 12644, Hi: 65024, Stride: 52380},
+ {Lo: 65025, Hi: 65039, Stride: 1},
+ {Lo: 65279, Hi: 65440, Stride: 161},
+ {Lo: 65520, Hi: 65528, Stride: 1},
+ {Lo: 65532, Hi: 65532, Stride: 1},
+ },
+ R32: []unicode.Range32{
+ {Lo: 78844, Hi: 119155, Stride: 40311},
+ {Lo: 119156, Hi: 119162, Stride: 1},
+ {Lo: 917504, Hi: 917631, Stride: 1},
+ {Lo: 917760, Hi: 917999, Stride: 1},
+ },
+ LatinOffset: 2,
+}
diff --git a/modules/container/map.go b/modules/container/map.go
deleted file mode 100644
index 3519de0951..0000000000
--- a/modules/container/map.go
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright 2022 The Gitea Authors. All rights reserved.
-// Use of this source code is governed by a MIT-style
-// license that can be found in the LICENSE file.
-
-package container
-
-// KeysInt64 returns keys slice for a map with int64 key
-func KeysInt64(m map[int64]struct{}) []int64 {
- keys := make([]int64, 0, len(m))
- for k := range m {
- keys = append(keys, k)
- }
- return keys
-}
diff --git a/modules/container/set.go b/modules/container/set.go
new file mode 100644
index 0000000000..4b4c74525d
--- /dev/null
+++ b/modules/container/set.go
@@ -0,0 +1,57 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package container
+
+type Set[T comparable] map[T]struct{}
+
+// SetOf creates a set and adds the specified elements to it.
+func SetOf[T comparable](values ...T) Set[T] {
+ s := make(Set[T], len(values))
+ s.AddMultiple(values...)
+ return s
+}
+
+// Add adds the specified element to a set.
+// Returns true if the element is added; false if the element is already present.
+func (s Set[T]) Add(value T) bool {
+ if _, has := s[value]; !has {
+ s[value] = struct{}{}
+ return true
+ }
+ return false
+}
+
+// AddMultiple adds the specified elements to a set.
+func (s Set[T]) AddMultiple(values ...T) {
+ for _, value := range values {
+ s.Add(value)
+ }
+}
+
+// Contains determines whether a set contains the specified element.
+// Returns true if the set contains the specified element; otherwise, false.
+func (s Set[T]) Contains(value T) bool {
+ _, has := s[value]
+ return has
+}
+
+// Remove removes the specified element.
+// Returns true if the element is successfully found and removed; otherwise, false.
+func (s Set[T]) Remove(value T) bool {
+ if _, has := s[value]; has {
+ delete(s, value)
+ return true
+ }
+ return false
+}
+
+// Values gets a list of all elements in the set.
+func (s Set[T]) Values() []T {
+ keys := make([]T, 0, len(s))
+ for k := range s {
+ keys = append(keys, k)
+ }
+ return keys
+}
diff --git a/modules/container/set_test.go b/modules/container/set_test.go
new file mode 100644
index 0000000000..6654763e56
--- /dev/null
+++ b/modules/container/set_test.go
@@ -0,0 +1,37 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package container
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestSet(t *testing.T) {
+ s := make(Set[string])
+
+ assert.True(t, s.Add("key1"))
+ assert.False(t, s.Add("key1"))
+ assert.True(t, s.Add("key2"))
+
+ assert.True(t, s.Contains("key1"))
+ assert.True(t, s.Contains("key2"))
+ assert.False(t, s.Contains("key3"))
+
+ assert.True(t, s.Remove("key2"))
+ assert.False(t, s.Contains("key2"))
+
+ assert.False(t, s.Remove("key3"))
+
+ s.AddMultiple("key4", "key5")
+ assert.True(t, s.Contains("key4"))
+ assert.True(t, s.Contains("key5"))
+
+ s = SetOf("key6", "key7")
+ assert.False(t, s.Contains("key1"))
+ assert.True(t, s.Contains("key6"))
+ assert.True(t, s.Contains("key7"))
+}
diff --git a/modules/context/context.go b/modules/context/context.go
index 8824911619..4b6a21b217 100644
--- a/modules/context/context.go
+++ b/modules/context/context.go
@@ -224,7 +224,7 @@ func (ctx *Context) HTML(status int, name base.TplName) {
ctx.Data["TemplateLoadTimes"] = func() string {
return strconv.FormatInt(time.Since(tmplStartTime).Nanoseconds()/1e6, 10) + "ms"
}
- if err := ctx.Render.HTML(ctx.Resp, status, string(name), ctx.Data); err != nil {
+ if err := ctx.Render.HTML(ctx.Resp, status, string(name), templates.BaseVars().Merge(ctx.Data)); err != nil {
if status == http.StatusInternalServerError && name == base.TplName("status/500") {
ctx.PlainText(http.StatusInternalServerError, "Unable to find status/500 template")
return
@@ -358,14 +358,7 @@ func (ctx *Context) SetServeHeaders(filename string) {
}
// ServeContent serves content to http request
-func (ctx *Context) ServeContent(name string, r io.ReadSeeker, params ...interface{}) {
- modTime := time.Now()
- for _, p := range params {
- switch v := p.(type) {
- case time.Time:
- modTime = v
- }
- }
+func (ctx *Context) ServeContent(name string, r io.ReadSeeker, modTime time.Time) {
ctx.SetServeHeaders(name)
http.ServeContent(ctx.Resp, ctx.Req, name, modTime, r)
}
@@ -382,15 +375,6 @@ func (ctx *Context) ServeFile(file string, names ...string) {
http.ServeFile(ctx.Resp, ctx.Req, file)
}
-// ServeStream serves file via io stream
-func (ctx *Context) ServeStream(rd io.Reader, name string) {
- ctx.SetServeHeaders(name)
- _, err := io.Copy(ctx.Resp, rd)
- if err != nil {
- ctx.ServerError("Download file failed", err)
- }
-}
-
// UploadStream returns the request body or the first form file
// Only form files need to get closed.
func (ctx *Context) UploadStream() (rd io.ReadCloser, needToClose bool, err error) {
@@ -674,8 +658,8 @@ func Auth(authMethod auth.Method) func(*Context) {
}
// Contexter initializes a classic context for a request.
-func Contexter() func(next http.Handler) http.Handler {
- rnd := templates.HTMLRenderer()
+func Contexter(ctx context.Context) func(next http.Handler) http.Handler {
+ _, rnd := templates.HTMLRenderer(ctx)
csrfOpts := getCsrfOpts()
if !setting.IsProd {
CsrfTokenRegenerationInterval = 5 * time.Second // in dev, re-generate the tokens more aggressively for debug purpose
diff --git a/modules/context/org.go b/modules/context/org.go
index d020befa40..89260b8654 100644
--- a/modules/context/org.go
+++ b/modules/context/org.go
@@ -130,6 +130,7 @@ func HandleOrgAssignment(ctx *Context, args ...bool) {
ctx.Data["IsOrganizationOwner"] = ctx.Org.IsOwner
ctx.Data["IsOrganizationMember"] = ctx.Org.IsMember
ctx.Data["IsPackageEnabled"] = setting.Packages.Enabled
+ ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
ctx.Data["IsPublicMember"] = func(uid int64) bool {
is, _ := organization.IsPublicMembership(ctx.Org.Organization.ID, uid)
return is
diff --git a/modules/context/package.go b/modules/context/package.go
index 4c52907dc5..d12bdc4913 100644
--- a/modules/context/package.go
+++ b/modules/context/package.go
@@ -5,14 +5,18 @@
package context
import (
+ gocontext "context"
"fmt"
"net/http"
"code.gitea.io/gitea/models/organization"
packages_model "code.gitea.io/gitea/models/packages"
"code.gitea.io/gitea/models/perm"
+ "code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/structs"
+ "code.gitea.io/gitea/modules/templates"
)
// Package contains owner, access mode and optional the package descriptor
@@ -51,31 +55,11 @@ func packageAssignment(ctx *Context, errCb func(int, string, interface{})) {
Owner: ctx.ContextUser,
}
- if ctx.Package.Owner.IsOrganization() {
- // 1. Get user max authorize level for the org (may be none, if user is not member of the org)
- if ctx.Doer != nil {
- var err error
- ctx.Package.AccessMode, err = organization.OrgFromUser(ctx.Package.Owner).GetOrgUserMaxAuthorizeLevel(ctx.Doer.ID)
- if err != nil {
- errCb(http.StatusInternalServerError, "GetOrgUserMaxAuthorizeLevel", err)
- return
- }
- }
- // 2. If authorize level is none, check if org is visible to user
- if ctx.Package.AccessMode == perm.AccessModeNone && organization.HasOrgOrUserVisible(ctx, ctx.Package.Owner, ctx.Doer) {
- ctx.Package.AccessMode = perm.AccessModeRead
- }
- } else {
- if ctx.Doer != nil && !ctx.Doer.IsGhost() {
- // 1. Check if user is package owner
- if ctx.Doer.ID == ctx.Package.Owner.ID {
- ctx.Package.AccessMode = perm.AccessModeOwner
- } else if ctx.Package.Owner.Visibility == structs.VisibleTypePublic || ctx.Package.Owner.Visibility == structs.VisibleTypeLimited { // 2. Check if package owner is public or limited
- ctx.Package.AccessMode = perm.AccessModeRead
- }
- } else if ctx.Package.Owner.Visibility == structs.VisibleTypePublic { // 3. Check if package owner is public
- ctx.Package.AccessMode = perm.AccessModeRead
- }
+ var err error
+ ctx.Package.AccessMode, err = determineAccessMode(ctx)
+ if err != nil {
+ errCb(http.StatusInternalServerError, "determineAccessMode", err)
+ return
}
packageType := ctx.Params("type")
@@ -100,13 +84,66 @@ func packageAssignment(ctx *Context, errCb func(int, string, interface{})) {
}
}
+func determineAccessMode(ctx *Context) (perm.AccessMode, error) {
+ accessMode := perm.AccessModeNone
+
+ if setting.Service.RequireSignInView && ctx.Doer == nil {
+ return accessMode, nil
+ }
+
+ if ctx.Package.Owner.IsOrganization() {
+ org := organization.OrgFromUser(ctx.Package.Owner)
+
+ // 1. Get user max authorize level for the org (may be none, if user is not member of the org)
+ if ctx.Doer != nil {
+ var err error
+ accessMode, err = org.GetOrgUserMaxAuthorizeLevel(ctx.Doer.ID)
+ if err != nil {
+ return accessMode, err
+ }
+ // If access mode is less than write check every team for more permissions
+ if accessMode < perm.AccessModeWrite {
+ teams, err := organization.GetUserOrgTeams(ctx, org.ID, ctx.Doer.ID)
+ if err != nil {
+ return accessMode, err
+ }
+ for _, t := range teams {
+ perm := t.UnitAccessModeCtx(ctx, unit.TypePackages)
+ if accessMode < perm {
+ accessMode = perm
+ }
+ }
+ }
+ }
+ // 2. If authorize level is none, check if org is visible to user
+ if accessMode == perm.AccessModeNone && organization.HasOrgOrUserVisible(ctx, ctx.Package.Owner, ctx.Doer) {
+ accessMode = perm.AccessModeRead
+ }
+ } else {
+ if ctx.Doer != nil && !ctx.Doer.IsGhost() {
+ // 1. Check if user is package owner
+ if ctx.Doer.ID == ctx.Package.Owner.ID {
+ accessMode = perm.AccessModeOwner
+ } else if ctx.Package.Owner.Visibility == structs.VisibleTypePublic || ctx.Package.Owner.Visibility == structs.VisibleTypeLimited { // 2. Check if package owner is public or limited
+ accessMode = perm.AccessModeRead
+ }
+ } else if ctx.Package.Owner.Visibility == structs.VisibleTypePublic { // 3. Check if package owner is public
+ accessMode = perm.AccessModeRead
+ }
+ }
+
+ return accessMode, nil
+}
+
// PackageContexter initializes a package context for a request.
-func PackageContexter() func(next http.Handler) http.Handler {
+func PackageContexter(ctx gocontext.Context) func(next http.Handler) http.Handler {
+ _, rnd := templates.HTMLRenderer(ctx)
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
ctx := Context{
- Resp: NewResponse(resp),
- Data: map[string]interface{}{},
+ Resp: NewResponse(resp),
+ Data: map[string]interface{}{},
+ Render: rnd,
}
defer ctx.Close()
diff --git a/modules/context/repo.go b/modules/context/repo.go
index ddb63150b1..7021e4fb7b 100644
--- a/modules/context/repo.go
+++ b/modules/context/repo.go
@@ -9,7 +9,6 @@ import (
"context"
"fmt"
"html"
- "io"
"net/http"
"net/url"
"path"
@@ -26,8 +25,8 @@ import (
"code.gitea.io/gitea/modules/cache"
"code.gitea.io/gitea/modules/git"
code_indexer "code.gitea.io/gitea/modules/indexer/code"
+ "code.gitea.io/gitea/modules/issue/template"
"code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/markup/markdown"
repo_module "code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
@@ -118,7 +117,8 @@ type CanCommitToBranchResults struct {
}
// CanCommitToBranch returns true if repository is editable and user has proper access level
-// and branch is not protected for push
+//
+// and branch is not protected for push
func (r *Repository) CanCommitToBranch(ctx context.Context, doer *user_model.User) (CanCommitToBranchResults, error) {
protectedBranch, err := git_model.GetProtectedBranchBy(ctx, r.Repository.ID, r.BranchName)
if err != nil {
@@ -393,7 +393,7 @@ func repoAssignment(ctx *Context, repo *repo_model.Repository) {
}
}
- pushMirrors, err := repo_model.GetPushMirrorsByRepoID(repo.ID)
+ pushMirrors, _, err := repo_model.GetPushMirrorsByRepoID(ctx, repo.ID, db.ListOptions{})
if err != nil {
ctx.ServerError("GetPushMirrorsByRepoID", err)
return
@@ -451,11 +451,20 @@ func RepoAssignment(ctx *Context) (cancel context.CancelFunc) {
owner, err = user_model.GetUserByName(ctx, userName)
if err != nil {
if user_model.IsErrUserNotExist(err) {
+ // go-get does not support redirects
+ // https://github.com/golang/go/issues/19760
if ctx.FormString("go-get") == "1" {
EarlyResponseForGoGetMeta(ctx)
return
}
- ctx.NotFound("GetUserByName", nil)
+
+ if redirectUserID, err := user_model.LookupUserRedirect(userName); err == nil {
+ RedirectToUser(ctx, userName, redirectUserID)
+ } else if user_model.IsErrUserRedirectNotExist(err) {
+ ctx.NotFound("GetUserByName", nil)
+ } else {
+ ctx.ServerError("LookupUserRedirect", err)
+ }
} else {
ctx.ServerError("GetUserByName", err)
}
@@ -523,14 +532,16 @@ func RepoAssignment(ctx *Context) (cancel context.CancelFunc) {
ctx.Data["RepoExternalIssuesLink"] = unit.ExternalTrackerConfig().ExternalTrackerURL
}
- ctx.Data["NumTags"], err = models.GetReleaseCountByRepoID(ctx.Repo.Repository.ID, models.FindReleasesOptions{
- IncludeTags: true,
+ ctx.Data["NumTags"], err = repo_model.GetReleaseCountByRepoID(ctx.Repo.Repository.ID, repo_model.FindReleasesOptions{
+ IncludeDrafts: true,
+ IncludeTags: true,
+ HasSha1: util.OptionalBoolTrue, // only draft releases which are created with existing tags
})
if err != nil {
ctx.ServerError("GetReleaseCountByRepoID", err)
return
}
- ctx.Data["NumReleases"], err = models.GetReleaseCountByRepoID(ctx.Repo.Repository.ID, models.FindReleasesOptions{})
+ ctx.Data["NumReleases"], err = repo_model.GetReleaseCountByRepoID(ctx.Repo.Repository.ID, repo_model.FindReleasesOptions{})
if err != nil {
ctx.ServerError("GetReleaseCountByRepoID", err)
return
@@ -986,6 +997,7 @@ func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context
}
ctx.Data["BranchName"] = ctx.Repo.BranchName
+ ctx.Data["RefName"] = ctx.Repo.RefName
ctx.Data["BranchNameSubURL"] = ctx.Repo.BranchNameSubURL()
ctx.Data["TagName"] = ctx.Repo.TagName
ctx.Data["CommitID"] = ctx.Repo.CommitID
@@ -1001,6 +1013,8 @@ func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context
return
}
ctx.Data["CommitsCount"] = ctx.Repo.CommitsCount
+ ctx.Repo.GitRepo.LastCommitCache = git.NewLastCommitCache(ctx.Repo.CommitsCount, ctx.Repo.Repository.FullName(), ctx.Repo.GitRepo, cache.GetCache())
+
return cancel
}
}
@@ -1030,73 +1044,55 @@ func UnitTypes() func(ctx *Context) {
}
}
-// IssueTemplatesFromDefaultBranch checks for issue templates in the repo's default branch
-func (ctx *Context) IssueTemplatesFromDefaultBranch() []api.IssueTemplate {
- var issueTemplates []api.IssueTemplate
+// IssueTemplatesFromDefaultBranch checks for valid issue templates in the repo's default branch,
+func (ctx *Context) IssueTemplatesFromDefaultBranch() []*api.IssueTemplate {
+ ret, _ := ctx.IssueTemplatesErrorsFromDefaultBranch()
+ return ret
+}
+
+// IssueTemplatesErrorsFromDefaultBranch checks for issue templates in the repo's default branch,
+// returns valid templates and the errors of invalid template files.
+func (ctx *Context) IssueTemplatesErrorsFromDefaultBranch() ([]*api.IssueTemplate, map[string]error) {
+ var issueTemplates []*api.IssueTemplate
if ctx.Repo.Repository.IsEmpty {
- return issueTemplates
+ return issueTemplates, nil
}
if ctx.Repo.Commit == nil {
var err error
ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(ctx.Repo.Repository.DefaultBranch)
if err != nil {
- return issueTemplates
+ return issueTemplates, nil
}
}
+ invalidFiles := map[string]error{}
for _, dirName := range IssueTemplateDirCandidates {
tree, err := ctx.Repo.Commit.SubTree(dirName)
if err != nil {
+ log.Debug("get sub tree of %s: %v", dirName, err)
continue
}
entries, err := tree.ListEntries()
if err != nil {
- return issueTemplates
+ log.Debug("list entries in %s: %v", dirName, err)
+ return issueTemplates, nil
}
for _, entry := range entries {
- if strings.HasSuffix(entry.Name(), ".md") {
- if entry.Blob().Size() >= setting.UI.MaxDisplayFileSize {
- log.Debug("Issue template is too large: %s", entry.Name())
- continue
- }
- r, err := entry.Blob().DataAsync()
- if err != nil {
- log.Debug("DataAsync: %v", err)
- continue
- }
- closed := false
- defer func() {
- if !closed {
- _ = r.Close()
- }
- }()
- data, err := io.ReadAll(r)
- if err != nil {
- log.Debug("ReadAll: %v", err)
- continue
- }
- _ = r.Close()
- var it api.IssueTemplate
- content, err := markdown.ExtractMetadata(string(data), &it)
- if err != nil {
- log.Debug("ExtractMetadata: %v", err)
- continue
- }
- it.Content = content
- it.FileName = entry.Name()
+ if !template.CouldBe(entry.Name()) {
+ continue
+ }
+ fullName := path.Join(dirName, entry.Name())
+ if it, err := template.UnmarshalFromEntry(entry, dirName); err != nil {
+ invalidFiles[fullName] = err
+ } else {
if !strings.HasPrefix(it.Ref, "refs/") { // Assume that the ref intended is always a branch - for tags users should use refs/tags/[
- it.Ref = "refs/heads/" + it.Ref
- }
- if it.Valid() {
- issueTemplates = append(issueTemplates, it)
+ it.Ref = git.BranchPrefix + it.Ref
}
+ issueTemplates = append(issueTemplates, it)
}
}
- if len(issueTemplates) > 0 {
- return issueTemplates
- }
}
- return issueTemplates
+ return issueTemplates, invalidFiles
}
diff --git a/modules/convert/convert.go b/modules/convert/convert.go
index c62b4303ed..187c67fa76 100644
--- a/modules/convert/convert.go
+++ b/modules/convert/convert.go
@@ -27,6 +27,7 @@ import (
"code.gitea.io/gitea/modules/log"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
+ "code.gitea.io/gitea/services/gitdiff"
webhook_service "code.gitea.io/gitea/services/webhook"
)
@@ -295,6 +296,7 @@ func ToOrganization(org *organization.Organization) *api.Organization {
return &api.Organization{
ID: org.ID,
AvatarURL: org.AsUser().AvatarLink(),
+ Name: org.Name,
UserName: org.Name,
FullName: org.FullName,
Description: org.Description,
@@ -410,7 +412,40 @@ func ToLFSLock(l *git_model.LFSLock) *api.LFSLock {
Path: l.Path,
LockedAt: l.Created.Round(time.Second),
Owner: &api.LFSLockOwner{
- Name: u.DisplayName(),
+ Name: u.Name,
},
}
}
+
+// ToChangedFile convert a gitdiff.DiffFile to api.ChangedFile
+func ToChangedFile(f *gitdiff.DiffFile, repo *repo_model.Repository, commit string) *api.ChangedFile {
+ status := "changed"
+ if f.IsDeleted {
+ status = "deleted"
+ } else if f.IsCreated {
+ status = "added"
+ } else if f.IsRenamed && f.Type == gitdiff.DiffFileCopy {
+ status = "copied"
+ } else if f.IsRenamed && f.Type == gitdiff.DiffFileRename {
+ status = "renamed"
+ } else if f.Addition == 0 && f.Deletion == 0 {
+ status = "unchanged"
+ }
+
+ file := &api.ChangedFile{
+ Filename: f.GetDiffFileName(),
+ Status: status,
+ Additions: f.Addition,
+ Deletions: f.Deletion,
+ Changes: f.Addition + f.Deletion,
+ HTMLURL: fmt.Sprint(repo.HTMLURL(), "/src/commit/", commit, "/", util.PathEscapeSegments(f.GetDiffFileName())),
+ ContentsURL: fmt.Sprint(repo.APIURL(), "/contents/", util.PathEscapeSegments(f.GetDiffFileName()), "?ref=", commit),
+ RawURL: fmt.Sprint(repo.HTMLURL(), "/raw/commit/", commit, "/", util.PathEscapeSegments(f.GetDiffFileName())),
+ }
+
+ if status == "rename" {
+ file.PreviousFilename = f.OldName
+ }
+
+ return file
+}
diff --git a/modules/convert/git_commit.go b/modules/convert/git_commit.go
index dfd6cb080c..6015a73712 100644
--- a/modules/convert/git_commit.go
+++ b/modules/convert/git_commit.go
@@ -73,7 +73,7 @@ func ToPayloadCommit(repo *repo_model.Repository, c *git.Commit) *api.PayloadCom
}
// ToCommit convert a git.Commit to api.Commit
-func ToCommit(repo *repo_model.Repository, gitRepo *git.Repository, commit *git.Commit, userCache map[string]*user_model.User) (*api.Commit, error) {
+func ToCommit(repo *repo_model.Repository, gitRepo *git.Repository, commit *git.Commit, userCache map[string]*user_model.User, stat bool) (*api.Commit, error) {
var apiAuthor, apiCommitter *api.User
// Retrieve author and committer information
@@ -133,28 +133,7 @@ func ToCommit(repo *repo_model.Repository, gitRepo *git.Repository, commit *git.
}
}
- // Retrieve files affected by the commit
- fileStatus, err := git.GetCommitFileStatus(gitRepo.Ctx, repo.RepoPath(), commit.ID.String())
- if err != nil {
- return nil, err
- }
- affectedFileList := make([]*api.CommitAffectedFiles, 0, len(fileStatus.Added)+len(fileStatus.Removed)+len(fileStatus.Modified))
- for _, files := range [][]string{fileStatus.Added, fileStatus.Removed, fileStatus.Modified} {
- for _, filename := range files {
- affectedFileList = append(affectedFileList, &api.CommitAffectedFiles{
- Filename: filename,
- })
- }
- }
-
- diff, err := gitdiff.GetDiff(gitRepo, &gitdiff.DiffOptions{
- AfterCommitID: commit.ID.String(),
- })
- if err != nil {
- return nil, err
- }
-
- return &api.Commit{
+ res := &api.Commit{
CommitMeta: &api.CommitMeta{
URL: repo.APIURL() + "/git/commits/" + url.PathEscape(commit.ID.String()),
SHA: commit.ID.String(),
@@ -188,11 +167,37 @@ func ToCommit(repo *repo_model.Repository, gitRepo *git.Repository, commit *git.
Author: apiAuthor,
Committer: apiCommitter,
Parents: apiParents,
- Files: affectedFileList,
- Stats: &api.CommitStats{
+ }
+
+ // Retrieve files affected by the commit
+ if stat {
+ fileStatus, err := git.GetCommitFileStatus(gitRepo.Ctx, repo.RepoPath(), commit.ID.String())
+ if err != nil {
+ return nil, err
+ }
+ affectedFileList := make([]*api.CommitAffectedFiles, 0, len(fileStatus.Added)+len(fileStatus.Removed)+len(fileStatus.Modified))
+ for _, files := range [][]string{fileStatus.Added, fileStatus.Removed, fileStatus.Modified} {
+ for _, filename := range files {
+ affectedFileList = append(affectedFileList, &api.CommitAffectedFiles{
+ Filename: filename,
+ })
+ }
+ }
+
+ diff, err := gitdiff.GetDiff(gitRepo, &gitdiff.DiffOptions{
+ AfterCommitID: commit.ID.String(),
+ })
+ if err != nil {
+ return nil, err
+ }
+
+ res.Files = affectedFileList
+ res.Stats = &api.CommitStats{
Total: diff.TotalAddition + diff.TotalDeletion,
Additions: diff.TotalAddition,
Deletions: diff.TotalDeletion,
- },
- }, nil
+ }
+ }
+
+ return res, nil
}
diff --git a/modules/convert/git_commit_test.go b/modules/convert/git_commit_test.go
index 118ba3a007..0bba0e502e 100644
--- a/modules/convert/git_commit_test.go
+++ b/modules/convert/git_commit_test.go
@@ -19,7 +19,7 @@ import (
func TestToCommitMeta(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- headRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository)
+ headRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
sha1, _ := git.NewIDFromString("0000000000000000000000000000000000000000")
signature := &git.Signature{Name: "Test Signature", Email: "test@email.com", When: time.Unix(0, 0)}
tag := &git.Tag{
diff --git a/modules/convert/issue_comment.go b/modules/convert/issue_comment.go
index ccc94b2496..73ad345fa4 100644
--- a/modules/convert/issue_comment.go
+++ b/modules/convert/issue_comment.go
@@ -101,6 +101,12 @@ func ToTimelineComment(c *issues_model.Comment, doer *user_model.User) *api.Time
}
if c.Time != nil {
+ err = c.Time.LoadAttributes()
+ if err != nil {
+ log.Error("Time.LoadAttributes: %v", err)
+ return nil
+ }
+
comment.TrackedTime = ToTrackedTime(c.Time)
}
diff --git a/modules/convert/issue_test.go b/modules/convert/issue_test.go
index 5bf04bcb52..ec672abad2 100644
--- a/modules/convert/issue_test.go
+++ b/modules/convert/issue_test.go
@@ -21,8 +21,8 @@ import (
func TestLabel_ToLabel(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- label := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 1}).(*issues_model.Label)
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: label.RepoID}).(*repo_model.Repository)
+ label := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 1})
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: label.RepoID})
assert.Equal(t, &api.Label{
ID: label.ID,
Name: label.Name,
diff --git a/modules/convert/mirror.go b/modules/convert/mirror.go
new file mode 100644
index 0000000000..b2414f4677
--- /dev/null
+++ b/modules/convert/mirror.go
@@ -0,0 +1,39 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package convert
+
+import (
+ repo_model "code.gitea.io/gitea/models/repo"
+ "code.gitea.io/gitea/modules/git"
+ api "code.gitea.io/gitea/modules/structs"
+)
+
+// ToPushMirror convert from repo_model.PushMirror and remoteAddress to api.TopicResponse
+func ToPushMirror(pm *repo_model.PushMirror) (*api.PushMirror, error) {
+ repo := pm.GetRepository()
+ remoteAddress, err := getRemoteAddress(repo, pm.RemoteName)
+ if err != nil {
+ return nil, err
+ }
+ return &api.PushMirror{
+ RepoName: repo.Name,
+ RemoteName: pm.RemoteName,
+ RemoteAddress: remoteAddress,
+ CreatedUnix: pm.CreatedUnix.FormatLong(),
+ LastUpdateUnix: pm.LastUpdateUnix.FormatLong(),
+ LastError: pm.LastError,
+ Interval: pm.Interval.String(),
+ }, nil
+}
+
+func getRemoteAddress(repo *repo_model.Repository, remoteName string) (string, error) {
+ url, err := git.GetRemoteURL(git.DefaultContext, repo.RepoPath(), remoteName)
+ if err != nil {
+ return "", err
+ }
+ // remove confidential information
+ url.User = nil
+ return url.String(), nil
+}
diff --git a/modules/convert/notification.go b/modules/convert/notification.go
index 1efba5745c..55f782f8f6 100644
--- a/modules/convert/notification.go
+++ b/modules/convert/notification.go
@@ -7,17 +7,17 @@ package convert
import (
"net/url"
- "code.gitea.io/gitea/models"
+ activities_model "code.gitea.io/gitea/models/activities"
"code.gitea.io/gitea/models/perm"
api "code.gitea.io/gitea/modules/structs"
)
// ToNotificationThread convert a Notification to api.NotificationThread
-func ToNotificationThread(n *models.Notification) *api.NotificationThread {
+func ToNotificationThread(n *activities_model.Notification) *api.NotificationThread {
result := &api.NotificationThread{
ID: n.ID,
- Unread: !(n.Status == models.NotificationStatusRead || n.Status == models.NotificationStatusPinned),
- Pinned: n.Status == models.NotificationStatusPinned,
+ Unread: !(n.Status == activities_model.NotificationStatusRead || n.Status == activities_model.NotificationStatusPinned),
+ Pinned: n.Status == activities_model.NotificationStatusPinned,
UpdatedAt: n.UpdatedUnix.AsTime(),
URL: n.APIURL(),
}
@@ -34,7 +34,7 @@ func ToNotificationThread(n *models.Notification) *api.NotificationThread {
// handle Subject
switch n.Source {
- case models.NotificationSourceIssue:
+ case activities_model.NotificationSourceIssue:
result.Subject = &api.NotificationSubject{Type: api.NotifySubjectIssue}
if n.Issue != nil {
result.Subject.Title = n.Issue.Title
@@ -47,7 +47,7 @@ func ToNotificationThread(n *models.Notification) *api.NotificationThread {
result.Subject.LatestCommentHTMLURL = comment.HTMLURL()
}
}
- case models.NotificationSourcePullRequest:
+ case activities_model.NotificationSourcePullRequest:
result.Subject = &api.NotificationSubject{Type: api.NotifySubjectPull}
if n.Issue != nil {
result.Subject.Title = n.Issue.Title
@@ -65,7 +65,7 @@ func ToNotificationThread(n *models.Notification) *api.NotificationThread {
result.Subject.State = "merged"
}
}
- case models.NotificationSourceCommit:
+ case activities_model.NotificationSourceCommit:
url := n.Repository.HTMLURL() + "/commit/" + url.PathEscape(n.CommitID)
result.Subject = &api.NotificationSubject{
Type: api.NotifySubjectCommit,
@@ -73,7 +73,7 @@ func ToNotificationThread(n *models.Notification) *api.NotificationThread {
URL: url,
HTMLURL: url,
}
- case models.NotificationSourceRepository:
+ case activities_model.NotificationSourceRepository:
result.Subject = &api.NotificationSubject{
Type: api.NotifySubjectRepository,
Title: n.Repository.FullName(),
@@ -87,7 +87,7 @@ func ToNotificationThread(n *models.Notification) *api.NotificationThread {
}
// ToNotifications convert list of Notification to api.NotificationThread list
-func ToNotifications(nl models.NotificationList) []*api.NotificationThread {
+func ToNotifications(nl activities_model.NotificationList) []*api.NotificationThread {
result := make([]*api.NotificationThread, 0, len(nl))
for _, n := range nl {
result = append(result, ToNotificationThread(n))
diff --git a/modules/convert/pull_test.go b/modules/convert/pull_test.go
index 10ef311399..a6ccbaca58 100644
--- a/modules/convert/pull_test.go
+++ b/modules/convert/pull_test.go
@@ -20,8 +20,8 @@ import (
func TestPullRequest_APIFormat(t *testing.T) {
// with HeadRepo
assert.NoError(t, unittest.PrepareTestDatabase())
- headRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository)
- pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1}).(*issues_model.PullRequest)
+ headRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
+ pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1})
assert.NoError(t, pr.LoadAttributes())
assert.NoError(t, pr.LoadIssue())
apiPullRequest := ToAPIPullRequest(git.DefaultContext, pr, nil)
@@ -35,7 +35,7 @@ func TestPullRequest_APIFormat(t *testing.T) {
}, apiPullRequest.Head)
// withOut HeadRepo
- pr = unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1}).(*issues_model.PullRequest)
+ pr = unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1})
assert.NoError(t, pr.LoadIssue())
assert.NoError(t, pr.LoadAttributes())
// simulate fork deletion
diff --git a/modules/convert/release.go b/modules/convert/release.go
index 955d3ff05f..5fc95dab72 100644
--- a/modules/convert/release.go
+++ b/modules/convert/release.go
@@ -5,13 +5,12 @@
package convert
import (
- "code.gitea.io/gitea/models"
repo_model "code.gitea.io/gitea/models/repo"
api "code.gitea.io/gitea/modules/structs"
)
-// ToRelease convert a models.Release to api.Release
-func ToRelease(r *models.Release) *api.Release {
+// ToRelease convert a repo_model.Release to api.Release
+func ToRelease(r *repo_model.Release) *api.Release {
assets := make([]*api.Attachment, 0)
for _, att := range r.Attachments {
assets = append(assets, ToReleaseAttachment(att))
diff --git a/modules/convert/repository.go b/modules/convert/repository.go
index d333c124b5..09b84afa6c 100644
--- a/modules/convert/repository.go
+++ b/modules/convert/repository.go
@@ -56,9 +56,10 @@ func innerToRepo(repo *repo_model.Repository, mode perm.AccessMode, isParent boo
config := unit.ExternalTrackerConfig()
hasIssues = true
externalTracker = &api.ExternalTracker{
- ExternalTrackerURL: config.ExternalTrackerURL,
- ExternalTrackerFormat: config.ExternalTrackerFormat,
- ExternalTrackerStyle: config.ExternalTrackerStyle,
+ ExternalTrackerURL: config.ExternalTrackerURL,
+ ExternalTrackerFormat: config.ExternalTrackerFormat,
+ ExternalTrackerStyle: config.ExternalTrackerStyle,
+ ExternalTrackerRegexpPattern: config.ExternalTrackerRegexpPattern,
}
}
hasWiki := false
@@ -102,7 +103,7 @@ func innerToRepo(repo *repo_model.Repository, mode perm.AccessMode, isParent boo
return nil
}
- numReleases, _ := models.GetReleaseCountByRepoID(repo.ID, models.FindReleasesOptions{IncludeDrafts: false, IncludeTags: false})
+ numReleases, _ := repo_model.GetReleaseCountByRepoID(repo.ID, repo_model.FindReleasesOptions{IncludeDrafts: false, IncludeTags: false})
mirrorInterval := ""
var mirrorUpdated time.Time
diff --git a/modules/convert/user_test.go b/modules/convert/user_test.go
index 2ed962950f..89d912e460 100644
--- a/modules/convert/user_test.go
+++ b/modules/convert/user_test.go
@@ -17,13 +17,13 @@ import (
func TestUser_ToUser(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1, IsAdmin: true}).(*user_model.User)
+ user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1, IsAdmin: true})
apiUser := toUser(user1, true, true)
assert.True(t, apiUser.IsAdmin)
assert.Contains(t, apiUser.AvatarURL, "://")
- user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2, IsAdmin: false}).(*user_model.User)
+ user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2, IsAdmin: false})
apiUser = toUser(user2, true, true)
assert.False(t, apiUser.IsAdmin)
@@ -32,7 +32,7 @@ func TestUser_ToUser(t *testing.T) {
assert.False(t, apiUser.IsAdmin)
assert.EqualValues(t, api.VisibleTypePublic.String(), apiUser.Visibility)
- user31 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 31, IsAdmin: false, Visibility: api.VisibleTypePrivate}).(*user_model.User)
+ user31 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 31, IsAdmin: false, Visibility: api.VisibleTypePrivate})
apiUser = toUser(user31, true, true)
assert.False(t, apiUser.IsAdmin)
diff --git a/modules/csv/csv_test.go b/modules/csv/csv_test.go
index 9d0848ae5b..5a8e13c811 100644
--- a/modules/csv/csv_test.go
+++ b/modules/csv/csv_test.go
@@ -322,7 +322,7 @@ func TestGuessDelimiter(t *testing.T) {
},
// case 3 - tab delimited
{
- csv: "1 2",
+ csv: "1\t2",
expectedDelimiter: '\t',
},
// case 4 - pipe delimited
diff --git a/modules/doctor/authorizedkeys.go b/modules/doctor/authorizedkeys.go
index 34dfe939d3..d4ceef87c0 100644
--- a/modules/doctor/authorizedkeys.go
+++ b/modules/doctor/authorizedkeys.go
@@ -14,6 +14,7 @@ import (
"strings"
asymkey_model "code.gitea.io/gitea/models/asymkey"
+ "code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
)
@@ -40,7 +41,7 @@ func checkAuthorizedKeys(ctx context.Context, logger log.Logger, autofix bool) e
}
defer f.Close()
- linesInAuthorizedKeys := map[string]bool{}
+ linesInAuthorizedKeys := make(container.Set[string])
scanner := bufio.NewScanner(f)
for scanner.Scan() {
@@ -48,7 +49,7 @@ func checkAuthorizedKeys(ctx context.Context, logger log.Logger, autofix bool) e
if strings.HasPrefix(line, tplCommentPrefix) {
continue
}
- linesInAuthorizedKeys[line] = true
+ linesInAuthorizedKeys.Add(line)
}
f.Close()
@@ -64,7 +65,7 @@ func checkAuthorizedKeys(ctx context.Context, logger log.Logger, autofix bool) e
if strings.HasPrefix(line, tplCommentPrefix) {
continue
}
- if ok := linesInAuthorizedKeys[line]; ok {
+ if linesInAuthorizedKeys.Contains(line) {
continue
}
if !autofix {
diff --git a/modules/doctor/dbconsistency.go b/modules/doctor/dbconsistency.go
index f8b62e898a..7ae349908e 100644
--- a/modules/doctor/dbconsistency.go
+++ b/modules/doctor/dbconsistency.go
@@ -7,7 +7,7 @@ package doctor
import (
"context"
- "code.gitea.io/gitea/models"
+ activities_model "code.gitea.io/gitea/models/activities"
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/migrations"
@@ -121,8 +121,8 @@ func checkDBConsistency(ctx context.Context, logger log.Logger, autofix bool) er
// find null archived repositories
{
Name: "Repositories with is_archived IS NULL",
- Counter: models.CountNullArchivedRepository,
- Fixer: models.FixNullArchivedRepository,
+ Counter: repo_model.CountNullArchivedRepository,
+ Fixer: repo_model.FixNullArchivedRepository,
FixedMessage: "Fixed",
},
// find label comments with empty labels
@@ -148,8 +148,8 @@ func checkDBConsistency(ctx context.Context, logger log.Logger, autofix bool) er
},
{
Name: "Action with created_unix set as an empty string",
- Counter: models.CountActionCreatedUnixString,
- Fixer: models.FixActionCreatedUnixString,
+ Counter: activities_model.CountActionCreatedUnixString,
+ Fixer: activities_model.FixActionCreatedUnixString,
FixedMessage: "Set to zero",
},
}
diff --git a/modules/doctor/doctor.go b/modules/doctor/doctor.go
index c8975a788e..5d14cef55c 100644
--- a/modules/doctor/doctor.go
+++ b/modules/doctor/doctor.go
@@ -11,6 +11,7 @@ import (
"strings"
"code.gitea.io/gitea/models/db"
+ "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
)
@@ -49,7 +50,11 @@ func initDBDisableConsole(ctx context.Context, disableConsole bool) error {
setting.NewXORMLogService(disableConsole)
if err := db.InitEngine(ctx); err != nil {
- return fmt.Errorf("models.SetEngine: %v", err)
+ return fmt.Errorf("db.InitEngine: %w", err)
+ }
+ // some doctor sub-commands need to use git command
+ if err := git.InitFull(ctx); err != nil {
+ return fmt.Errorf("git.InitFull: %w", err)
}
return nil
}
diff --git a/modules/doctor/heads.go b/modules/doctor/heads.go
new file mode 100644
index 0000000000..ec14aa4ead
--- /dev/null
+++ b/modules/doctor/heads.go
@@ -0,0 +1,91 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package doctor
+
+import (
+ "context"
+
+ repo_model "code.gitea.io/gitea/models/repo"
+ "code.gitea.io/gitea/modules/git"
+ "code.gitea.io/gitea/modules/log"
+)
+
+func synchronizeRepoHeads(ctx context.Context, logger log.Logger, autofix bool) error {
+ numRepos := 0
+ numHeadsBroken := 0
+ numDefaultBranchesBroken := 0
+ numReposUpdated := 0
+ err := iterateRepositories(ctx, func(repo *repo_model.Repository) error {
+ numRepos++
+ runOpts := &git.RunOpts{Dir: repo.RepoPath()}
+
+ _, _, defaultBranchErr := git.NewCommand(ctx, "rev-parse", "--", repo.DefaultBranch).RunStdString(runOpts)
+
+ head, _, headErr := git.NewCommand(ctx, "symbolic-ref", "--short", "HEAD").RunStdString(runOpts)
+
+ // what we expect: default branch is valid, and HEAD points to it
+ if headErr == nil && defaultBranchErr == nil && head == repo.DefaultBranch {
+ return nil
+ }
+
+ if headErr != nil {
+ numHeadsBroken++
+ }
+ if defaultBranchErr != nil {
+ numDefaultBranchesBroken++
+ }
+
+ // if default branch is broken, let the user fix that in the UI
+ if defaultBranchErr != nil {
+ logger.Warn("Default branch for %s/%s doesn't point to a valid commit", repo.OwnerName, repo.Name)
+ return nil
+ }
+
+ // if we're not autofixing, that's all we can do
+ if !autofix {
+ return nil
+ }
+
+ // otherwise, let's try fixing HEAD
+ err := git.NewCommand(ctx, "symbolic-ref", "--", "HEAD", repo.DefaultBranch).Run(runOpts)
+ if err != nil {
+ logger.Warn("Failed to fix HEAD for %s/%s: %v", repo.OwnerName, repo.Name, err)
+ return nil
+ }
+ numReposUpdated++
+ return nil
+ })
+ if err != nil {
+ logger.Critical("Error when fixing repo HEADs: %v", err)
+ }
+
+ if autofix {
+ logger.Info("Out of %d repos, HEADs for %d are now fixed and HEADS for %d are still broken", numRepos, numReposUpdated, numDefaultBranchesBroken+numHeadsBroken-numReposUpdated)
+ } else {
+ if numHeadsBroken == 0 && numDefaultBranchesBroken == 0 {
+ logger.Info("All %d repos have their HEADs in the correct state")
+ } else {
+ if numHeadsBroken == 0 && numDefaultBranchesBroken != 0 {
+ logger.Critical("Default branches are broken for %d/%d repos", numDefaultBranchesBroken, numRepos)
+ } else if numHeadsBroken != 0 && numDefaultBranchesBroken == 0 {
+ logger.Warn("HEADs are broken for %d/%d repos", numHeadsBroken, numRepos)
+ } else {
+ logger.Critical("Out of %d repos, HEADS are broken for %d and default branches are broken for %d", numRepos, numHeadsBroken, numDefaultBranchesBroken)
+ }
+ }
+ }
+
+ return err
+}
+
+func init() {
+ Register(&Check{
+ Title: "Synchronize repo HEADs",
+ Name: "synchronize-repo-heads",
+ IsDefault: true,
+ Run: synchronizeRepoHeads,
+ Priority: 7,
+ })
+}
diff --git a/modules/doctor/mergebase.go b/modules/doctor/mergebase.go
index 2da91cdcc3..46369290a1 100644
--- a/modules/doctor/mergebase.go
+++ b/modules/doctor/mergebase.go
@@ -30,9 +30,6 @@ func iteratePRs(ctx context.Context, repo *repo_model.Repository, each func(*rep
}
func checkPRMergeBase(ctx context.Context, logger log.Logger, autofix bool) error {
- if err := git.InitOnceWithSync(ctx); err != nil {
- return err
- }
numRepos := 0
numPRs := 0
numPRsUpdated := 0
diff --git a/modules/doctor/misc.go b/modules/doctor/misc.go
index 24175fcaf4..2d2bcb910d 100644
--- a/modules/doctor/misc.go
+++ b/modules/doctor/misc.go
@@ -190,10 +190,6 @@ func checkDaemonExport(ctx context.Context, logger log.Logger, autofix bool) err
}
func checkCommitGraph(ctx context.Context, logger log.Logger, autofix bool) error {
- if err := git.InitOnceWithSync(ctx); err != nil {
- return err
- }
-
numRepos := 0
numNeedUpdate := 0
numWritten := 0
diff --git a/modules/doctor/usertype.go b/modules/doctor/usertype.go
index 34e12afe72..166e38bd24 100644
--- a/modules/doctor/usertype.go
+++ b/modules/doctor/usertype.go
@@ -7,19 +7,19 @@ package doctor
import (
"context"
- "code.gitea.io/gitea/models"
+ user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/log"
)
func checkUserType(ctx context.Context, logger log.Logger, autofix bool) error {
- count, err := models.CountWrongUserType()
+ count, err := user_model.CountWrongUserType()
if err != nil {
logger.Critical("Error: %v whilst counting wrong user types")
return err
}
if count > 0 {
if autofix {
- if count, err = models.FixWrongUserType(); err != nil {
+ if count, err = user_model.FixWrongUserType(); err != nil {
logger.Critical("Error: %v whilst fixing wrong user types")
return err
}
diff --git a/modules/emoji/emoji_data.go b/modules/emoji/emoji_data.go
index 1fb08767bb..1e14d3de6b 100644
--- a/modules/emoji/emoji_data.go
+++ b/modules/emoji/emoji_data.go
@@ -4,9 +4,8 @@
package emoji
-// Code generated by gen.go. DO NOT EDIT.
+// Code generated by build/generate-emoji.go. DO NOT EDIT.
// Sourced from https://raw.githubusercontent.com/github/gemoji/master/db/emoji.json
-//
var GemojiData = Gemoji{
{"\U0001f44d", "thumbs up", []string{"+1", "thumbsup"}, "6.0", true},
{"\U0001f44e", "thumbs down", []string{"-1", "thumbsdown"}, "6.0", true},
@@ -129,7 +128,7 @@ var GemojiData = Gemoji{
{"\U0001f50b", "battery", []string{"battery"}, "6.0", false},
{"\U0001f3d6\ufe0f", "beach with umbrella", []string{"beach_umbrella"}, "7.0", false},
{"\U0001f43b", "bear", []string{"bear"}, "6.0", false},
- {"\U0001f9d4", "man: beard", []string{"bearded_person"}, "11.0", true},
+ {"\U0001f9d4", "person: beard", []string{"bearded_person"}, "11.0", true},
{"\U0001f6cf\ufe0f", "bed", []string{"bed"}, "7.0", false},
{"\U0001f41d", "honeybee", []string{"bee", "honeybee"}, "6.0", false},
{"\U0001f37a", "beer mug", []string{"beer"}, "6.0", false},
@@ -377,14 +376,14 @@ var GemojiData = Gemoji{
{"\U0001f1e8\U0001f1ee", "flag: Côte d’Ivoire", []string{"cote_divoire"}, "6.0", false},
{"\U0001f6cb\ufe0f", "couch and lamp", []string{"couch_and_lamp"}, "7.0", false},
{"\U0001f46b", "woman and man holding hands", []string{"couple"}, "6.0", true},
- {"\U0001f491", "couple with heart", []string{"couple_with_heart"}, "6.0", false},
- {"\U0001f468\u200d\u2764\ufe0f\u200d\U0001f468", "couple with heart: man, man", []string{"couple_with_heart_man_man"}, "6.0", false},
- {"\U0001f469\u200d\u2764\ufe0f\u200d\U0001f468", "couple with heart: woman, man", []string{"couple_with_heart_woman_man"}, "11.0", false},
- {"\U0001f469\u200d\u2764\ufe0f\u200d\U0001f469", "couple with heart: woman, woman", []string{"couple_with_heart_woman_woman"}, "6.0", false},
- {"\U0001f48f", "kiss", []string{"couplekiss"}, "6.0", false},
- {"\U0001f468\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468", "kiss: man, man", []string{"couplekiss_man_man"}, "6.0", false},
- {"\U0001f469\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468", "kiss: woman, man", []string{"couplekiss_man_woman"}, "11.0", false},
- {"\U0001f469\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469", "kiss: woman, woman", []string{"couplekiss_woman_woman"}, "6.0", false},
+ {"\U0001f491", "couple with heart", []string{"couple_with_heart"}, "6.0", true},
+ {"\U0001f468\u200d\u2764\ufe0f\u200d\U0001f468", "couple with heart: man, man", []string{"couple_with_heart_man_man"}, "6.0", true},
+ {"\U0001f469\u200d\u2764\ufe0f\u200d\U0001f468", "couple with heart: woman, man", []string{"couple_with_heart_woman_man"}, "11.0", true},
+ {"\U0001f469\u200d\u2764\ufe0f\u200d\U0001f469", "couple with heart: woman, woman", []string{"couple_with_heart_woman_woman"}, "6.0", true},
+ {"\U0001f48f", "kiss", []string{"couplekiss"}, "6.0", true},
+ {"\U0001f468\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468", "kiss: man, man", []string{"couplekiss_man_man"}, "6.0", true},
+ {"\U0001f469\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468", "kiss: woman, man", []string{"couplekiss_man_woman"}, "11.0", true},
+ {"\U0001f469\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469", "kiss: woman, woman", []string{"couplekiss_woman_woman"}, "6.0", true},
{"\U0001f42e", "cow face", []string{"cow"}, "6.0", false},
{"\U0001f404", "cow", []string{"cow2"}, "6.0", false},
{"\U0001f920", "cowboy hat face", []string{"cowboy_hat_face"}, "9.0", false},
@@ -429,7 +428,7 @@ var GemojiData = Gemoji{
{"\U0001f46f\u200d\u2640\ufe0f", "women with bunny ears", []string{"dancing_women"}, "11.0", false},
{"\U0001f361", "dango", []string{"dango"}, "6.0", false},
{"\U0001f576\ufe0f", "sunglasses", []string{"dark_sunglasses"}, "7.0", false},
- {"\U0001f3af", "direct hit", []string{"dart"}, "6.0", false},
+ {"\U0001f3af", "bullseye", []string{"dart"}, "6.0", false},
{"\U0001f4a8", "dashing away", []string{"dash"}, "6.0", false},
{"\U0001f4c5", "calendar", []string{"date"}, "6.0", false},
{"\U0001f1e9\U0001f1ea", "flag: Germany", []string{"de"}, "6.0", false},
@@ -453,7 +452,7 @@ var GemojiData = Gemoji{
{"\U0001f93f", "diving mask", []string{"diving_mask"}, "12.0", false},
{"\U0001fa94", "diya lamp", []string{"diya_lamp"}, "12.0", false},
{"\U0001f4ab", "dizzy", []string{"dizzy"}, "6.0", false},
- {"\U0001f635", "dizzy face", []string{"dizzy_face"}, "6.0", false},
+ {"\U0001f635", "knocked-out face", []string{"dizzy_face"}, "6.0", false},
{"\U0001f1e9\U0001f1ef", "flag: Djibouti", []string{"djibouti"}, "6.0", false},
{"\U0001f9ec", "dna", []string{"dna"}, "11.0", false},
{"\U0001f6af", "no littering", []string{"do_not_litter"}, "6.0", false},
@@ -478,7 +477,6 @@ var GemojiData = Gemoji{
{"\U0001f986", "duck", []string{"duck"}, "9.0", false},
{"\U0001f95f", "dumpling", []string{"dumpling"}, "11.0", false},
{"\U0001f4c0", "dvd", []string{"dvd"}, "6.0", false},
- {"\U0001f4e7", "e-mail", []string{"e-mail"}, "6.0", false},
{"\U0001f985", "eagle", []string{"eagle"}, "9.0", false},
{"\U0001f442", "ear", []string{"ear"}, "6.0", true},
{"\U0001f33e", "sheaf of rice", []string{"ear_of_rice"}, "6.0", false},
@@ -500,9 +498,10 @@ var GemojiData = Gemoji{
{"\U0001f9dd", "elf", []string{"elf"}, "11.0", true},
{"\U0001f9dd\u200d\u2642\ufe0f", "man elf", []string{"elf_man"}, "11.0", true},
{"\U0001f9dd\u200d\u2640\ufe0f", "woman elf", []string{"elf_woman"}, "11.0", true},
- {"\u2709\ufe0f", "envelope", []string{"email", "envelope"}, "", false},
+ {"\U0001f4e7", "e-mail", []string{"email", "e-mail"}, "6.0", false},
{"\U0001f51a", "END arrow", []string{"end"}, "6.0", false},
{"\U0001f3f4\U000e0067\U000e0062\U000e0065\U000e006e\U000e0067\U000e007f", "flag: England", []string{"england"}, "11.0", false},
+ {"\u2709\ufe0f", "envelope", []string{"envelope"}, "", false},
{"\U0001f4e9", "envelope with arrow", []string{"envelope_with_arrow"}, "6.0", false},
{"\U0001f1ec\U0001f1f6", "flag: Equatorial Guinea", []string{"equatorial_guinea"}, "6.0", false},
{"\U0001f1ea\U0001f1f7", "flag: Eritrea", []string{"eritrea"}, "6.0", false},
@@ -514,7 +513,7 @@ var GemojiData = Gemoji{
{"\U0001f3f0", "castle", []string{"european_castle"}, "6.0", false},
{"\U0001f3e4", "post office", []string{"european_post_office"}, "6.0", false},
{"\U0001f332", "evergreen tree", []string{"evergreen_tree"}, "6.0", false},
- {"\u2757", "exclamation mark", []string{"exclamation", "heavy_exclamation_mark"}, "5.2", false},
+ {"\u2757", "red exclamation mark", []string{"exclamation", "heavy_exclamation_mark"}, "5.2", false},
{"\U0001f92f", "exploding head", []string{"exploding_head"}, "11.0", false},
{"\U0001f611", "expressionless face", []string{"expressionless"}, "6.1", false},
{"\U0001f441\ufe0f", "eye", []string{"eye"}, "7.0", false},
@@ -689,7 +688,7 @@ var GemojiData = Gemoji{
{"\U0001f1ec\U0001f1f3", "flag: Guinea", []string{"guinea"}, "6.0", false},
{"\U0001f1ec\U0001f1fc", "flag: Guinea-Bissau", []string{"guinea_bissau"}, "6.0", false},
{"\U0001f3b8", "guitar", []string{"guitar"}, "6.0", false},
- {"\U0001f52b", "pistol", []string{"gun"}, "6.0", false},
+ {"\U0001f52b", "water pistol", []string{"gun"}, "6.0", false},
{"\U0001f1ec\U0001f1fe", "flag: Guyana", []string{"guyana"}, "6.0", false},
{"\U0001f487", "person getting haircut", []string{"haircut"}, "6.0", true},
{"\U0001f487\u200d\u2642\ufe0f", "man getting haircut", []string{"haircut_man"}, "6.0", true},
@@ -1228,7 +1227,7 @@ var GemojiData = Gemoji{
{"\U0001f4cc", "pushpin", []string{"pushpin"}, "6.0", false},
{"\U0001f6ae", "litter in bin sign", []string{"put_litter_in_its_place"}, "6.0", false},
{"\U0001f1f6\U0001f1e6", "flag: Qatar", []string{"qatar"}, "6.0", false},
- {"\u2753", "question mark", []string{"question"}, "6.0", false},
+ {"\u2753", "red question mark", []string{"question"}, "6.0", false},
{"\U0001f430", "rabbit face", []string{"rabbit"}, "6.0", false},
{"\U0001f407", "rabbit", []string{"rabbit2"}, "6.0", false},
{"\U0001f99d", "raccoon", []string{"raccoon"}, "11.0", false},
@@ -1751,61 +1750,61 @@ var GemojiData = Gemoji{
{"\U0001f44d\U0001f3fc", "thumbs up: Medium-Light Skin Tone", []string{"+1_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f44d\U0001f3fd", "thumbs up: Medium Skin Tone", []string{"+1_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f44d\U0001f3fe", "thumbs up: Medium-Dark Skin Tone", []string{"+1_Medium-Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f44e\U0001f3ff", "thumbs down: Dark Skin Tone", []string{"-1_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f44e\U0001f3fb", "thumbs down: Light Skin Tone", []string{"-1_Light_Skin_Tone"}, "12.0", false},
{"\U0001f44e\U0001f3fc", "thumbs down: Medium-Light Skin Tone", []string{"-1_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f44e\U0001f3fd", "thumbs down: Medium Skin Tone", []string{"-1_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f44e\U0001f3fe", "thumbs down: Medium-Dark Skin Tone", []string{"-1_Medium-Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f44e\U0001f3ff", "thumbs down: Dark Skin Tone", []string{"-1_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f9d1\U0001f3fc", "person: Medium-Light Skin Tone", []string{"adult_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3fd", "person: Medium Skin Tone", []string{"adult_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3fe", "person: Medium-Dark Skin Tone", []string{"adult_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3ff", "person: Dark Skin Tone", []string{"adult_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3fb", "person: Light Skin Tone", []string{"adult_Light_Skin_Tone"}, "12.0", false},
- {"\U0001f9d1\U0001f3fc", "person: Medium-Light Skin Tone", []string{"adult_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f47c\U0001f3fb", "baby angel: Light Skin Tone", []string{"angel_Light_Skin_Tone"}, "12.0", false},
{"\U0001f47c\U0001f3fc", "baby angel: Medium-Light Skin Tone", []string{"angel_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f47c\U0001f3fd", "baby angel: Medium Skin Tone", []string{"angel_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f47c\U0001f3fe", "baby angel: Medium-Dark Skin Tone", []string{"angel_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f47c\U0001f3ff", "baby angel: Dark Skin Tone", []string{"angel_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f9d1\U0001f3fe\u200d\U0001f3a8", "artist: Medium-Dark Skin Tone", []string{"artist_Medium-Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f9d1\U0001f3ff\u200d\U0001f3a8", "artist: Dark Skin Tone", []string{"artist_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3fb\u200d\U0001f3a8", "artist: Light Skin Tone", []string{"artist_Light_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3fc\u200d\U0001f3a8", "artist: Medium-Light Skin Tone", []string{"artist_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3fd\u200d\U0001f3a8", "artist: Medium Skin Tone", []string{"artist_Medium_Skin_Tone"}, "12.0", false},
- {"\U0001f9d1\U0001f3fb\u200d\U0001f680", "astronaut: Light Skin Tone", []string{"astronaut_Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f9d1\U0001f3fe\u200d\U0001f3a8", "artist: Medium-Dark Skin Tone", []string{"artist_Medium-Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f9d1\U0001f3ff\u200d\U0001f3a8", "artist: Dark Skin Tone", []string{"artist_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3fc\u200d\U0001f680", "astronaut: Medium-Light Skin Tone", []string{"astronaut_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3fd\u200d\U0001f680", "astronaut: Medium Skin Tone", []string{"astronaut_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3fe\u200d\U0001f680", "astronaut: Medium-Dark Skin Tone", []string{"astronaut_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3ff\u200d\U0001f680", "astronaut: Dark Skin Tone", []string{"astronaut_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f9d1\U0001f3fb\u200d\U0001f680", "astronaut: Light Skin Tone", []string{"astronaut_Light_Skin_Tone"}, "12.0", false},
{"\U0001f476\U0001f3fb", "baby: Light Skin Tone", []string{"baby_Light_Skin_Tone"}, "12.0", false},
{"\U0001f476\U0001f3fc", "baby: Medium-Light Skin Tone", []string{"baby_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f476\U0001f3fd", "baby: Medium Skin Tone", []string{"baby_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f476\U0001f3fe", "baby: Medium-Dark Skin Tone", []string{"baby_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f476\U0001f3ff", "baby: Dark Skin Tone", []string{"baby_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f468\U0001f3fe\u200d\U0001f9b2", "man: bald: Medium-Dark Skin Tone", []string{"bald_man_Medium-Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f468\U0001f3ff\u200d\U0001f9b2", "man: bald: Dark Skin Tone", []string{"bald_man_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3fb\u200d\U0001f9b2", "man: bald: Light Skin Tone", []string{"bald_man_Light_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3fc\u200d\U0001f9b2", "man: bald: Medium-Light Skin Tone", []string{"bald_man_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3fd\u200d\U0001f9b2", "man: bald: Medium Skin Tone", []string{"bald_man_Medium_Skin_Tone"}, "12.0", false},
- {"\U0001f468\U0001f3fe\u200d\U0001f9b2", "man: bald: Medium-Dark Skin Tone", []string{"bald_man_Medium-Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f468\U0001f3ff\u200d\U0001f9b2", "man: bald: Dark Skin Tone", []string{"bald_man_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f469\U0001f3ff\u200d\U0001f9b2", "woman: bald: Dark Skin Tone", []string{"bald_woman_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f469\U0001f3fb\u200d\U0001f9b2", "woman: bald: Light Skin Tone", []string{"bald_woman_Light_Skin_Tone"}, "12.0", false},
{"\U0001f469\U0001f3fc\u200d\U0001f9b2", "woman: bald: Medium-Light Skin Tone", []string{"bald_woman_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f469\U0001f3fd\u200d\U0001f9b2", "woman: bald: Medium Skin Tone", []string{"bald_woman_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f469\U0001f3fe\u200d\U0001f9b2", "woman: bald: Medium-Dark Skin Tone", []string{"bald_woman_Medium-Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f469\U0001f3ff\u200d\U0001f9b2", "woman: bald: Dark Skin Tone", []string{"bald_woman_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f469\U0001f3fb\u200d\U0001f9b2", "woman: bald: Light Skin Tone", []string{"bald_woman_Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f6c0\U0001f3fe", "person taking bath: Medium-Dark Skin Tone", []string{"bath_Medium-Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f6c0\U0001f3ff", "person taking bath: Dark Skin Tone", []string{"bath_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f6c0\U0001f3fb", "person taking bath: Light Skin Tone", []string{"bath_Light_Skin_Tone"}, "12.0", false},
{"\U0001f6c0\U0001f3fc", "person taking bath: Medium-Light Skin Tone", []string{"bath_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f6c0\U0001f3fd", "person taking bath: Medium Skin Tone", []string{"bath_Medium_Skin_Tone"}, "12.0", false},
- {"\U0001f6c0\U0001f3fe", "person taking bath: Medium-Dark Skin Tone", []string{"bath_Medium-Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f6c0\U0001f3ff", "person taking bath: Dark Skin Tone", []string{"bath_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f9d4\U0001f3fb", "man: beard: Light Skin Tone", []string{"bearded_person_Light_Skin_Tone"}, "12.0", false},
- {"\U0001f9d4\U0001f3fc", "man: beard: Medium-Light Skin Tone", []string{"bearded_person_Medium-Light_Skin_Tone"}, "12.0", false},
- {"\U0001f9d4\U0001f3fd", "man: beard: Medium Skin Tone", []string{"bearded_person_Medium_Skin_Tone"}, "12.0", false},
- {"\U0001f9d4\U0001f3fe", "man: beard: Medium-Dark Skin Tone", []string{"bearded_person_Medium-Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f9d4\U0001f3ff", "man: beard: Dark Skin Tone", []string{"bearded_person_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f9d4\U0001f3fd", "person: beard: Medium Skin Tone", []string{"bearded_person_Medium_Skin_Tone"}, "12.0", false},
+ {"\U0001f9d4\U0001f3fe", "person: beard: Medium-Dark Skin Tone", []string{"bearded_person_Medium-Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f9d4\U0001f3ff", "person: beard: Dark Skin Tone", []string{"bearded_person_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f9d4\U0001f3fb", "person: beard: Light Skin Tone", []string{"bearded_person_Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f9d4\U0001f3fc", "person: beard: Medium-Light Skin Tone", []string{"bearded_person_Medium-Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f6b4\U0001f3fd", "person biking: Medium Skin Tone", []string{"bicyclist_Medium_Skin_Tone"}, "12.0", false},
+ {"\U0001f6b4\U0001f3fe", "person biking: Medium-Dark Skin Tone", []string{"bicyclist_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f6b4\U0001f3ff", "person biking: Dark Skin Tone", []string{"bicyclist_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f6b4\U0001f3fb", "person biking: Light Skin Tone", []string{"bicyclist_Light_Skin_Tone"}, "12.0", false},
{"\U0001f6b4\U0001f3fc", "person biking: Medium-Light Skin Tone", []string{"bicyclist_Medium-Light_Skin_Tone"}, "12.0", false},
- {"\U0001f6b4\U0001f3fd", "person biking: Medium Skin Tone", []string{"bicyclist_Medium_Skin_Tone"}, "12.0", false},
- {"\U0001f6b4\U0001f3fe", "person biking: Medium-Dark Skin Tone", []string{"bicyclist_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f6b4\U0001f3fe\u200d\u2642\ufe0f", "man biking: Medium-Dark Skin Tone", []string{"biking_man_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f6b4\U0001f3ff\u200d\u2642\ufe0f", "man biking: Dark Skin Tone", []string{"biking_man_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f6b4\U0001f3fb\u200d\u2642\ufe0f", "man biking: Light Skin Tone", []string{"biking_man_Light_Skin_Tone"}, "12.0", false},
@@ -1821,21 +1820,21 @@ var GemojiData = Gemoji{
{"\U0001f471\U0001f3fd\u200d\u2642\ufe0f", "man: blond hair: Medium Skin Tone", []string{"blond_haired_man_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f471\U0001f3fe\u200d\u2642\ufe0f", "man: blond hair: Medium-Dark Skin Tone", []string{"blond_haired_man_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f471\U0001f3ff\u200d\u2642\ufe0f", "man: blond hair: Dark Skin Tone", []string{"blond_haired_man_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f471\U0001f3fb", "person: blond hair: Light Skin Tone", []string{"blond_haired_person_Light_Skin_Tone"}, "12.0", false},
- {"\U0001f471\U0001f3fc", "person: blond hair: Medium-Light Skin Tone", []string{"blond_haired_person_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f471\U0001f3fd", "person: blond hair: Medium Skin Tone", []string{"blond_haired_person_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f471\U0001f3fe", "person: blond hair: Medium-Dark Skin Tone", []string{"blond_haired_person_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f471\U0001f3ff", "person: blond hair: Dark Skin Tone", []string{"blond_haired_person_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f471\U0001f3fb", "person: blond hair: Light Skin Tone", []string{"blond_haired_person_Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f471\U0001f3fc", "person: blond hair: Medium-Light Skin Tone", []string{"blond_haired_person_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f471\U0001f3fb\u200d\u2640\ufe0f", "woman: blond hair: Light Skin Tone", []string{"blond_haired_woman_Light_Skin_Tone"}, "12.0", false},
{"\U0001f471\U0001f3fc\u200d\u2640\ufe0f", "woman: blond hair: Medium-Light Skin Tone", []string{"blond_haired_woman_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f471\U0001f3fd\u200d\u2640\ufe0f", "woman: blond hair: Medium Skin Tone", []string{"blond_haired_woman_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f471\U0001f3fe\u200d\u2640\ufe0f", "woman: blond hair: Medium-Dark Skin Tone", []string{"blond_haired_woman_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f471\U0001f3ff\u200d\u2640\ufe0f", "woman: blond hair: Dark Skin Tone", []string{"blond_haired_woman_Dark_Skin_Tone"}, "12.0", false},
- {"\u26f9\U0001f3fe\ufe0f\u200d\u2642\ufe0f", "man bouncing ball: Medium-Dark Skin Tone", []string{"bouncing_ball_man_Medium-Dark_Skin_Tone"}, "12.0", false},
- {"\u26f9\U0001f3ff\ufe0f\u200d\u2642\ufe0f", "man bouncing ball: Dark Skin Tone", []string{"bouncing_ball_man_Dark_Skin_Tone"}, "12.0", false},
{"\u26f9\U0001f3fb\ufe0f\u200d\u2642\ufe0f", "man bouncing ball: Light Skin Tone", []string{"bouncing_ball_man_Light_Skin_Tone"}, "12.0", false},
{"\u26f9\U0001f3fc\ufe0f\u200d\u2642\ufe0f", "man bouncing ball: Medium-Light Skin Tone", []string{"bouncing_ball_man_Medium-Light_Skin_Tone"}, "12.0", false},
{"\u26f9\U0001f3fd\ufe0f\u200d\u2642\ufe0f", "man bouncing ball: Medium Skin Tone", []string{"bouncing_ball_man_Medium_Skin_Tone"}, "12.0", false},
+ {"\u26f9\U0001f3fe\ufe0f\u200d\u2642\ufe0f", "man bouncing ball: Medium-Dark Skin Tone", []string{"bouncing_ball_man_Medium-Dark_Skin_Tone"}, "12.0", false},
+ {"\u26f9\U0001f3ff\ufe0f\u200d\u2642\ufe0f", "man bouncing ball: Dark Skin Tone", []string{"bouncing_ball_man_Dark_Skin_Tone"}, "12.0", false},
{"\u26f9\U0001f3fb\ufe0f", "person bouncing ball: Light Skin Tone", []string{"bouncing_ball_person_Light_Skin_Tone"}, "12.0", false},
{"\u26f9\U0001f3fc\ufe0f", "person bouncing ball: Medium-Light Skin Tone", []string{"bouncing_ball_person_Medium-Light_Skin_Tone"}, "12.0", false},
{"\u26f9\U0001f3fd\ufe0f", "person bouncing ball: Medium Skin Tone", []string{"bouncing_ball_person_Medium_Skin_Tone"}, "12.0", false},
@@ -1846,31 +1845,31 @@ var GemojiData = Gemoji{
{"\u26f9\U0001f3fb\ufe0f\u200d\u2640\ufe0f", "woman bouncing ball: Light Skin Tone", []string{"bouncing_ball_woman_Light_Skin_Tone"}, "12.0", false},
{"\u26f9\U0001f3fc\ufe0f\u200d\u2640\ufe0f", "woman bouncing ball: Medium-Light Skin Tone", []string{"bouncing_ball_woman_Medium-Light_Skin_Tone"}, "12.0", false},
{"\u26f9\U0001f3fd\ufe0f\u200d\u2640\ufe0f", "woman bouncing ball: Medium Skin Tone", []string{"bouncing_ball_woman_Medium_Skin_Tone"}, "12.0", false},
- {"\U0001f647\U0001f3fb", "person bowing: Light Skin Tone", []string{"bow_Light_Skin_Tone"}, "12.0", false},
- {"\U0001f647\U0001f3fc", "person bowing: Medium-Light Skin Tone", []string{"bow_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f647\U0001f3fd", "person bowing: Medium Skin Tone", []string{"bow_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f647\U0001f3fe", "person bowing: Medium-Dark Skin Tone", []string{"bow_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f647\U0001f3ff", "person bowing: Dark Skin Tone", []string{"bow_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f647\U0001f3fb", "person bowing: Light Skin Tone", []string{"bow_Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f647\U0001f3fc", "person bowing: Medium-Light Skin Tone", []string{"bow_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f647\U0001f3ff\u200d\u2642\ufe0f", "man bowing: Dark Skin Tone", []string{"bowing_man_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f647\U0001f3fb\u200d\u2642\ufe0f", "man bowing: Light Skin Tone", []string{"bowing_man_Light_Skin_Tone"}, "12.0", false},
{"\U0001f647\U0001f3fc\u200d\u2642\ufe0f", "man bowing: Medium-Light Skin Tone", []string{"bowing_man_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f647\U0001f3fd\u200d\u2642\ufe0f", "man bowing: Medium Skin Tone", []string{"bowing_man_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f647\U0001f3fe\u200d\u2642\ufe0f", "man bowing: Medium-Dark Skin Tone", []string{"bowing_man_Medium-Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f647\U0001f3fe\u200d\u2640\ufe0f", "woman bowing: Medium-Dark Skin Tone", []string{"bowing_woman_Medium-Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f647\U0001f3ff\u200d\u2640\ufe0f", "woman bowing: Dark Skin Tone", []string{"bowing_woman_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f647\U0001f3fb\u200d\u2640\ufe0f", "woman bowing: Light Skin Tone", []string{"bowing_woman_Light_Skin_Tone"}, "12.0", false},
{"\U0001f647\U0001f3fc\u200d\u2640\ufe0f", "woman bowing: Medium-Light Skin Tone", []string{"bowing_woman_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f647\U0001f3fd\u200d\u2640\ufe0f", "woman bowing: Medium Skin Tone", []string{"bowing_woman_Medium_Skin_Tone"}, "12.0", false},
+ {"\U0001f647\U0001f3fe\u200d\u2640\ufe0f", "woman bowing: Medium-Dark Skin Tone", []string{"bowing_woman_Medium-Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f647\U0001f3ff\u200d\u2640\ufe0f", "woman bowing: Dark Skin Tone", []string{"bowing_woman_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f466\U0001f3fb", "boy: Light Skin Tone", []string{"boy_Light_Skin_Tone"}, "12.0", false},
{"\U0001f466\U0001f3fc", "boy: Medium-Light Skin Tone", []string{"boy_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f466\U0001f3fd", "boy: Medium Skin Tone", []string{"boy_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f466\U0001f3fe", "boy: Medium-Dark Skin Tone", []string{"boy_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f466\U0001f3ff", "boy: Dark Skin Tone", []string{"boy_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f931\U0001f3fe", "breast-feeding: Medium-Dark Skin Tone", []string{"breast_feeding_Medium-Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f931\U0001f3ff", "breast-feeding: Dark Skin Tone", []string{"breast_feeding_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f931\U0001f3fb", "breast-feeding: Light Skin Tone", []string{"breast_feeding_Light_Skin_Tone"}, "12.0", false},
{"\U0001f931\U0001f3fc", "breast-feeding: Medium-Light Skin Tone", []string{"breast_feeding_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f931\U0001f3fd", "breast-feeding: Medium Skin Tone", []string{"breast_feeding_Medium_Skin_Tone"}, "12.0", false},
+ {"\U0001f931\U0001f3fe", "breast-feeding: Medium-Dark Skin Tone", []string{"breast_feeding_Medium-Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f931\U0001f3ff", "breast-feeding: Dark Skin Tone", []string{"breast_feeding_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f574\U0001f3fb\ufe0f", "person in suit levitating: Light Skin Tone", []string{"business_suit_levitating_Light_Skin_Tone"}, "12.0", false},
{"\U0001f574\U0001f3fc\ufe0f", "person in suit levitating: Medium-Light Skin Tone", []string{"business_suit_levitating_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f574\U0001f3fd\ufe0f", "person in suit levitating: Medium Skin Tone", []string{"business_suit_levitating_Medium_Skin_Tone"}, "12.0", false},
@@ -1881,116 +1880,156 @@ var GemojiData = Gemoji{
{"\U0001f919\U0001f3fd", "call me hand: Medium Skin Tone", []string{"call_me_hand_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f919\U0001f3fe", "call me hand: Medium-Dark Skin Tone", []string{"call_me_hand_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f919\U0001f3ff", "call me hand: Dark Skin Tone", []string{"call_me_hand_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f938\U0001f3ff", "person cartwheeling: Dark Skin Tone", []string{"cartwheeling_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f938\U0001f3fb", "person cartwheeling: Light Skin Tone", []string{"cartwheeling_Light_Skin_Tone"}, "12.0", false},
{"\U0001f938\U0001f3fc", "person cartwheeling: Medium-Light Skin Tone", []string{"cartwheeling_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f938\U0001f3fd", "person cartwheeling: Medium Skin Tone", []string{"cartwheeling_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f938\U0001f3fe", "person cartwheeling: Medium-Dark Skin Tone", []string{"cartwheeling_Medium-Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f938\U0001f3ff", "person cartwheeling: Dark Skin Tone", []string{"cartwheeling_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f938\U0001f3fb", "person cartwheeling: Light Skin Tone", []string{"cartwheeling_Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f9d2\U0001f3fb", "child: Light Skin Tone", []string{"child_Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f9d2\U0001f3fc", "child: Medium-Light Skin Tone", []string{"child_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f9d2\U0001f3fd", "child: Medium Skin Tone", []string{"child_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f9d2\U0001f3fe", "child: Medium-Dark Skin Tone", []string{"child_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9d2\U0001f3ff", "child: Dark Skin Tone", []string{"child_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f9d2\U0001f3fb", "child: Light Skin Tone", []string{"child_Light_Skin_Tone"}, "12.0", false},
- {"\U0001f9d2\U0001f3fc", "child: Medium-Light Skin Tone", []string{"child_Medium-Light_Skin_Tone"}, "12.0", false},
- {"\U0001f44f\U0001f3fb", "clapping hands: Light Skin Tone", []string{"clap_Light_Skin_Tone"}, "12.0", false},
- {"\U0001f44f\U0001f3fc", "clapping hands: Medium-Light Skin Tone", []string{"clap_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f44f\U0001f3fd", "clapping hands: Medium Skin Tone", []string{"clap_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f44f\U0001f3fe", "clapping hands: Medium-Dark Skin Tone", []string{"clap_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f44f\U0001f3ff", "clapping hands: Dark Skin Tone", []string{"clap_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f44f\U0001f3fb", "clapping hands: Light Skin Tone", []string{"clap_Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f44f\U0001f3fc", "clapping hands: Medium-Light Skin Tone", []string{"clap_Medium-Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f9d7\U0001f3fb", "person climbing: Light Skin Tone", []string{"climbing_Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f9d7\U0001f3fc", "person climbing: Medium-Light Skin Tone", []string{"climbing_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f9d7\U0001f3fd", "person climbing: Medium Skin Tone", []string{"climbing_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f9d7\U0001f3fe", "person climbing: Medium-Dark Skin Tone", []string{"climbing_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9d7\U0001f3ff", "person climbing: Dark Skin Tone", []string{"climbing_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f9d7\U0001f3fb", "person climbing: Light Skin Tone", []string{"climbing_Light_Skin_Tone"}, "12.0", false},
- {"\U0001f9d7\U0001f3fc", "person climbing: Medium-Light Skin Tone", []string{"climbing_Medium-Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f9d7\U0001f3ff\u200d\u2642\ufe0f", "man climbing: Dark Skin Tone", []string{"climbing_man_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9d7\U0001f3fb\u200d\u2642\ufe0f", "man climbing: Light Skin Tone", []string{"climbing_man_Light_Skin_Tone"}, "12.0", false},
{"\U0001f9d7\U0001f3fc\u200d\u2642\ufe0f", "man climbing: Medium-Light Skin Tone", []string{"climbing_man_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f9d7\U0001f3fd\u200d\u2642\ufe0f", "man climbing: Medium Skin Tone", []string{"climbing_man_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f9d7\U0001f3fe\u200d\u2642\ufe0f", "man climbing: Medium-Dark Skin Tone", []string{"climbing_man_Medium-Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f9d7\U0001f3ff\u200d\u2642\ufe0f", "man climbing: Dark Skin Tone", []string{"climbing_man_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9d7\U0001f3fb\u200d\u2640\ufe0f", "woman climbing: Light Skin Tone", []string{"climbing_woman_Light_Skin_Tone"}, "12.0", false},
{"\U0001f9d7\U0001f3fc\u200d\u2640\ufe0f", "woman climbing: Medium-Light Skin Tone", []string{"climbing_woman_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f9d7\U0001f3fd\u200d\u2640\ufe0f", "woman climbing: Medium Skin Tone", []string{"climbing_woman_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f9d7\U0001f3fe\u200d\u2640\ufe0f", "woman climbing: Medium-Dark Skin Tone", []string{"climbing_woman_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9d7\U0001f3ff\u200d\u2640\ufe0f", "woman climbing: Dark Skin Tone", []string{"climbing_woman_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f477\U0001f3fb", "construction worker: Light Skin Tone", []string{"construction_worker_Light_Skin_Tone"}, "12.0", false},
- {"\U0001f477\U0001f3fc", "construction worker: Medium-Light Skin Tone", []string{"construction_worker_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f477\U0001f3fd", "construction worker: Medium Skin Tone", []string{"construction_worker_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f477\U0001f3fe", "construction worker: Medium-Dark Skin Tone", []string{"construction_worker_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f477\U0001f3ff", "construction worker: Dark Skin Tone", []string{"construction_worker_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f477\U0001f3fb", "construction worker: Light Skin Tone", []string{"construction_worker_Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f477\U0001f3fc", "construction worker: Medium-Light Skin Tone", []string{"construction_worker_Medium-Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f477\U0001f3ff\u200d\u2642\ufe0f", "man construction worker: Dark Skin Tone", []string{"construction_worker_man_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f477\U0001f3fb\u200d\u2642\ufe0f", "man construction worker: Light Skin Tone", []string{"construction_worker_man_Light_Skin_Tone"}, "12.0", false},
{"\U0001f477\U0001f3fc\u200d\u2642\ufe0f", "man construction worker: Medium-Light Skin Tone", []string{"construction_worker_man_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f477\U0001f3fd\u200d\u2642\ufe0f", "man construction worker: Medium Skin Tone", []string{"construction_worker_man_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f477\U0001f3fe\u200d\u2642\ufe0f", "man construction worker: Medium-Dark Skin Tone", []string{"construction_worker_man_Medium-Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f477\U0001f3ff\u200d\u2642\ufe0f", "man construction worker: Dark Skin Tone", []string{"construction_worker_man_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f477\U0001f3ff\u200d\u2640\ufe0f", "woman construction worker: Dark Skin Tone", []string{"construction_worker_woman_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f477\U0001f3fb\u200d\u2640\ufe0f", "woman construction worker: Light Skin Tone", []string{"construction_worker_woman_Light_Skin_Tone"}, "12.0", false},
{"\U0001f477\U0001f3fc\u200d\u2640\ufe0f", "woman construction worker: Medium-Light Skin Tone", []string{"construction_worker_woman_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f477\U0001f3fd\u200d\u2640\ufe0f", "woman construction worker: Medium Skin Tone", []string{"construction_worker_woman_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f477\U0001f3fe\u200d\u2640\ufe0f", "woman construction worker: Medium-Dark Skin Tone", []string{"construction_worker_woman_Medium-Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f477\U0001f3ff\u200d\u2640\ufe0f", "woman construction worker: Dark Skin Tone", []string{"construction_worker_woman_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f9d1\U0001f3fb\u200d\U0001f373", "cook: Light Skin Tone", []string{"cook_Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f9d1\U0001f3fc\u200d\U0001f373", "cook: Medium-Light Skin Tone", []string{"cook_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3fd\u200d\U0001f373", "cook: Medium Skin Tone", []string{"cook_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3fe\u200d\U0001f373", "cook: Medium-Dark Skin Tone", []string{"cook_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3ff\u200d\U0001f373", "cook: Dark Skin Tone", []string{"cook_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f9d1\U0001f3fb\u200d\U0001f373", "cook: Light Skin Tone", []string{"cook_Light_Skin_Tone"}, "12.0", false},
- {"\U0001f9d1\U0001f3fc\u200d\U0001f373", "cook: Medium-Light Skin Tone", []string{"cook_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f46b\U0001f3fb", "woman and man holding hands: Light Skin Tone", []string{"couple_Light_Skin_Tone"}, "12.0", false},
{"\U0001f46b\U0001f3fc", "woman and man holding hands: Medium-Light Skin Tone", []string{"couple_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f46b\U0001f3fd", "woman and man holding hands: Medium Skin Tone", []string{"couple_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f46b\U0001f3fe", "woman and man holding hands: Medium-Dark Skin Tone", []string{"couple_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f46b\U0001f3ff", "woman and man holding hands: Dark Skin Tone", []string{"couple_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f491\U0001f3fd", "couple with heart: Medium Skin Tone", []string{"couple_with_heart_Medium_Skin_Tone"}, "12.0", false},
+ {"\U0001f491\U0001f3fe", "couple with heart: Medium-Dark Skin Tone", []string{"couple_with_heart_Medium-Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f491\U0001f3ff", "couple with heart: Dark Skin Tone", []string{"couple_with_heart_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f491\U0001f3fb", "couple with heart: Light Skin Tone", []string{"couple_with_heart_Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f491\U0001f3fc", "couple with heart: Medium-Light Skin Tone", []string{"couple_with_heart_Medium-Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f468\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f468", "couple with heart: man, man: Medium Skin Tone", []string{"couple_with_heart_man_man_Medium_Skin_Tone"}, "12.0", false},
+ {"\U0001f468\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f468", "couple with heart: man, man: Medium-Dark Skin Tone", []string{"couple_with_heart_man_man_Medium-Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f468\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f468", "couple with heart: man, man: Dark Skin Tone", []string{"couple_with_heart_man_man_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f468\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f468", "couple with heart: man, man: Light Skin Tone", []string{"couple_with_heart_man_man_Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f468\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f468", "couple with heart: man, man: Medium-Light Skin Tone", []string{"couple_with_heart_man_man_Medium-Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f469\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f468", "couple with heart: woman, man: Light Skin Tone", []string{"couple_with_heart_woman_man_Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f469\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f468", "couple with heart: woman, man: Medium-Light Skin Tone", []string{"couple_with_heart_woman_man_Medium-Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f469\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f468", "couple with heart: woman, man: Medium Skin Tone", []string{"couple_with_heart_woman_man_Medium_Skin_Tone"}, "12.0", false},
+ {"\U0001f469\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f468", "couple with heart: woman, man: Medium-Dark Skin Tone", []string{"couple_with_heart_woman_man_Medium-Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f469\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f468", "couple with heart: woman, man: Dark Skin Tone", []string{"couple_with_heart_woman_man_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f469\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f469", "couple with heart: woman, woman: Medium Skin Tone", []string{"couple_with_heart_woman_woman_Medium_Skin_Tone"}, "12.0", false},
+ {"\U0001f469\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f469", "couple with heart: woman, woman: Medium-Dark Skin Tone", []string{"couple_with_heart_woman_woman_Medium-Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f469\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f469", "couple with heart: woman, woman: Dark Skin Tone", []string{"couple_with_heart_woman_woman_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f469\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f469", "couple with heart: woman, woman: Light Skin Tone", []string{"couple_with_heart_woman_woman_Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f469\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f469", "couple with heart: woman, woman: Medium-Light Skin Tone", []string{"couple_with_heart_woman_woman_Medium-Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f48f\U0001f3fe", "kiss: Medium-Dark Skin Tone", []string{"couplekiss_Medium-Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f48f\U0001f3ff", "kiss: Dark Skin Tone", []string{"couplekiss_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f48f\U0001f3fb", "kiss: Light Skin Tone", []string{"couplekiss_Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f48f\U0001f3fc", "kiss: Medium-Light Skin Tone", []string{"couplekiss_Medium-Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f48f\U0001f3fd", "kiss: Medium Skin Tone", []string{"couplekiss_Medium_Skin_Tone"}, "12.0", false},
+ {"\U0001f468\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468", "kiss: man, man: Dark Skin Tone", []string{"couplekiss_man_man_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f468\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468", "kiss: man, man: Light Skin Tone", []string{"couplekiss_man_man_Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f468\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468", "kiss: man, man: Medium-Light Skin Tone", []string{"couplekiss_man_man_Medium-Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f468\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468", "kiss: man, man: Medium Skin Tone", []string{"couplekiss_man_man_Medium_Skin_Tone"}, "12.0", false},
+ {"\U0001f468\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468", "kiss: man, man: Medium-Dark Skin Tone", []string{"couplekiss_man_man_Medium-Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f469\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468", "kiss: woman, man: Light Skin Tone", []string{"couplekiss_man_woman_Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f469\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468", "kiss: woman, man: Medium-Light Skin Tone", []string{"couplekiss_man_woman_Medium-Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f469\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468", "kiss: woman, man: Medium Skin Tone", []string{"couplekiss_man_woman_Medium_Skin_Tone"}, "12.0", false},
+ {"\U0001f469\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468", "kiss: woman, man: Medium-Dark Skin Tone", []string{"couplekiss_man_woman_Medium-Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f469\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f468", "kiss: woman, man: Dark Skin Tone", []string{"couplekiss_man_woman_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f469\U0001f3fb\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469", "kiss: woman, woman: Light Skin Tone", []string{"couplekiss_woman_woman_Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f469\U0001f3fc\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469", "kiss: woman, woman: Medium-Light Skin Tone", []string{"couplekiss_woman_woman_Medium-Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f469\U0001f3fd\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469", "kiss: woman, woman: Medium Skin Tone", []string{"couplekiss_woman_woman_Medium_Skin_Tone"}, "12.0", false},
+ {"\U0001f469\U0001f3fe\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469", "kiss: woman, woman: Medium-Dark Skin Tone", []string{"couplekiss_woman_woman_Medium-Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f469\U0001f3ff\u200d\u2764\ufe0f\u200d\U0001f48b\u200d\U0001f469", "kiss: woman, woman: Dark Skin Tone", []string{"couplekiss_woman_woman_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f91e\U0001f3fe", "crossed fingers: Medium-Dark Skin Tone", []string{"crossed_fingers_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f91e\U0001f3ff", "crossed fingers: Dark Skin Tone", []string{"crossed_fingers_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f91e\U0001f3fb", "crossed fingers: Light Skin Tone", []string{"crossed_fingers_Light_Skin_Tone"}, "12.0", false},
{"\U0001f91e\U0001f3fc", "crossed fingers: Medium-Light Skin Tone", []string{"crossed_fingers_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f91e\U0001f3fd", "crossed fingers: Medium Skin Tone", []string{"crossed_fingers_Medium_Skin_Tone"}, "12.0", false},
- {"\U0001f91e\U0001f3fe", "crossed fingers: Medium-Dark Skin Tone", []string{"crossed_fingers_Medium-Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f468\U0001f3fd\u200d\U0001f9b1", "man: curly hair: Medium Skin Tone", []string{"curly_haired_man_Medium_Skin_Tone"}, "12.0", false},
- {"\U0001f468\U0001f3fe\u200d\U0001f9b1", "man: curly hair: Medium-Dark Skin Tone", []string{"curly_haired_man_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3ff\u200d\U0001f9b1", "man: curly hair: Dark Skin Tone", []string{"curly_haired_man_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3fb\u200d\U0001f9b1", "man: curly hair: Light Skin Tone", []string{"curly_haired_man_Light_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3fc\u200d\U0001f9b1", "man: curly hair: Medium-Light Skin Tone", []string{"curly_haired_man_Medium-Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f468\U0001f3fd\u200d\U0001f9b1", "man: curly hair: Medium Skin Tone", []string{"curly_haired_man_Medium_Skin_Tone"}, "12.0", false},
+ {"\U0001f468\U0001f3fe\u200d\U0001f9b1", "man: curly hair: Medium-Dark Skin Tone", []string{"curly_haired_man_Medium-Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f469\U0001f3ff\u200d\U0001f9b1", "woman: curly hair: Dark Skin Tone", []string{"curly_haired_woman_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f469\U0001f3fb\u200d\U0001f9b1", "woman: curly hair: Light Skin Tone", []string{"curly_haired_woman_Light_Skin_Tone"}, "12.0", false},
{"\U0001f469\U0001f3fc\u200d\U0001f9b1", "woman: curly hair: Medium-Light Skin Tone", []string{"curly_haired_woman_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f469\U0001f3fd\u200d\U0001f9b1", "woman: curly hair: Medium Skin Tone", []string{"curly_haired_woman_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f469\U0001f3fe\u200d\U0001f9b1", "woman: curly hair: Medium-Dark Skin Tone", []string{"curly_haired_woman_Medium-Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f469\U0001f3ff\u200d\U0001f9b1", "woman: curly hair: Dark Skin Tone", []string{"curly_haired_woman_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f9cf\U0001f3ff\u200d\u2642\ufe0f", "deaf man: Dark Skin Tone", []string{"deaf_man_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9cf\U0001f3fb\u200d\u2642\ufe0f", "deaf man: Light Skin Tone", []string{"deaf_man_Light_Skin_Tone"}, "12.0", false},
{"\U0001f9cf\U0001f3fc\u200d\u2642\ufe0f", "deaf man: Medium-Light Skin Tone", []string{"deaf_man_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f9cf\U0001f3fd\u200d\u2642\ufe0f", "deaf man: Medium Skin Tone", []string{"deaf_man_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f9cf\U0001f3fe\u200d\u2642\ufe0f", "deaf man: Medium-Dark Skin Tone", []string{"deaf_man_Medium-Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f9cf\U0001f3ff\u200d\u2642\ufe0f", "deaf man: Dark Skin Tone", []string{"deaf_man_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f9cf\U0001f3fb", "deaf person: Light Skin Tone", []string{"deaf_person_Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f9cf\U0001f3fc", "deaf person: Medium-Light Skin Tone", []string{"deaf_person_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f9cf\U0001f3fd", "deaf person: Medium Skin Tone", []string{"deaf_person_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f9cf\U0001f3fe", "deaf person: Medium-Dark Skin Tone", []string{"deaf_person_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9cf\U0001f3ff", "deaf person: Dark Skin Tone", []string{"deaf_person_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f9cf\U0001f3fb", "deaf person: Light Skin Tone", []string{"deaf_person_Light_Skin_Tone"}, "12.0", false},
- {"\U0001f9cf\U0001f3fc", "deaf person: Medium-Light Skin Tone", []string{"deaf_person_Medium-Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f9cf\U0001f3fe\u200d\u2640\ufe0f", "deaf woman: Medium-Dark Skin Tone", []string{"deaf_woman_Medium-Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f9cf\U0001f3ff\u200d\u2640\ufe0f", "deaf woman: Dark Skin Tone", []string{"deaf_woman_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9cf\U0001f3fb\u200d\u2640\ufe0f", "deaf woman: Light Skin Tone", []string{"deaf_woman_Light_Skin_Tone"}, "12.0", false},
{"\U0001f9cf\U0001f3fc\u200d\u2640\ufe0f", "deaf woman: Medium-Light Skin Tone", []string{"deaf_woman_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f9cf\U0001f3fd\u200d\u2640\ufe0f", "deaf woman: Medium Skin Tone", []string{"deaf_woman_Medium_Skin_Tone"}, "12.0", false},
- {"\U0001f9cf\U0001f3fe\u200d\u2640\ufe0f", "deaf woman: Medium-Dark Skin Tone", []string{"deaf_woman_Medium-Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f9cf\U0001f3ff\u200d\u2640\ufe0f", "deaf woman: Dark Skin Tone", []string{"deaf_woman_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f575\U0001f3fe\ufe0f", "detective: Medium-Dark Skin Tone", []string{"detective_Medium-Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f575\U0001f3ff\ufe0f", "detective: Dark Skin Tone", []string{"detective_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f575\U0001f3fb\ufe0f", "detective: Light Skin Tone", []string{"detective_Light_Skin_Tone"}, "12.0", false},
{"\U0001f575\U0001f3fc\ufe0f", "detective: Medium-Light Skin Tone", []string{"detective_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f575\U0001f3fd\ufe0f", "detective: Medium Skin Tone", []string{"detective_Medium_Skin_Tone"}, "12.0", false},
- {"\U0001f442\U0001f3fc", "ear: Medium-Light Skin Tone", []string{"ear_Medium-Light_Skin_Tone"}, "12.0", false},
- {"\U0001f442\U0001f3fd", "ear: Medium Skin Tone", []string{"ear_Medium_Skin_Tone"}, "12.0", false},
+ {"\U0001f575\U0001f3fe\ufe0f", "detective: Medium-Dark Skin Tone", []string{"detective_Medium-Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f575\U0001f3ff\ufe0f", "detective: Dark Skin Tone", []string{"detective_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f442\U0001f3fe", "ear: Medium-Dark Skin Tone", []string{"ear_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f442\U0001f3ff", "ear: Dark Skin Tone", []string{"ear_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f442\U0001f3fb", "ear: Light Skin Tone", []string{"ear_Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f442\U0001f3fc", "ear: Medium-Light Skin Tone", []string{"ear_Medium-Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f442\U0001f3fd", "ear: Medium Skin Tone", []string{"ear_Medium_Skin_Tone"}, "12.0", false},
+ {"\U0001f9bb\U0001f3fe", "ear with hearing aid: Medium-Dark Skin Tone", []string{"ear_with_hearing_aid_Medium-Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f9bb\U0001f3ff", "ear with hearing aid: Dark Skin Tone", []string{"ear_with_hearing_aid_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9bb\U0001f3fb", "ear with hearing aid: Light Skin Tone", []string{"ear_with_hearing_aid_Light_Skin_Tone"}, "12.0", false},
{"\U0001f9bb\U0001f3fc", "ear with hearing aid: Medium-Light Skin Tone", []string{"ear_with_hearing_aid_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f9bb\U0001f3fd", "ear with hearing aid: Medium Skin Tone", []string{"ear_with_hearing_aid_Medium_Skin_Tone"}, "12.0", false},
- {"\U0001f9bb\U0001f3fe", "ear with hearing aid: Medium-Dark Skin Tone", []string{"ear_with_hearing_aid_Medium-Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f9bb\U0001f3ff", "ear with hearing aid: Dark Skin Tone", []string{"ear_with_hearing_aid_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9dd\U0001f3fb", "elf: Light Skin Tone", []string{"elf_Light_Skin_Tone"}, "12.0", false},
{"\U0001f9dd\U0001f3fc", "elf: Medium-Light Skin Tone", []string{"elf_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f9dd\U0001f3fd", "elf: Medium Skin Tone", []string{"elf_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f9dd\U0001f3fe", "elf: Medium-Dark Skin Tone", []string{"elf_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9dd\U0001f3ff", "elf: Dark Skin Tone", []string{"elf_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f9dd\U0001f3ff\u200d\u2642\ufe0f", "man elf: Dark Skin Tone", []string{"elf_man_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9dd\U0001f3fb\u200d\u2642\ufe0f", "man elf: Light Skin Tone", []string{"elf_man_Light_Skin_Tone"}, "12.0", false},
{"\U0001f9dd\U0001f3fc\u200d\u2642\ufe0f", "man elf: Medium-Light Skin Tone", []string{"elf_man_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f9dd\U0001f3fd\u200d\u2642\ufe0f", "man elf: Medium Skin Tone", []string{"elf_man_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f9dd\U0001f3fe\u200d\u2642\ufe0f", "man elf: Medium-Dark Skin Tone", []string{"elf_man_Medium-Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f9dd\U0001f3ff\u200d\u2642\ufe0f", "man elf: Dark Skin Tone", []string{"elf_man_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9dd\U0001f3fb\u200d\u2640\ufe0f", "woman elf: Light Skin Tone", []string{"elf_woman_Light_Skin_Tone"}, "12.0", false},
{"\U0001f9dd\U0001f3fc\u200d\u2640\ufe0f", "woman elf: Medium-Light Skin Tone", []string{"elf_woman_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f9dd\U0001f3fd\u200d\u2640\ufe0f", "woman elf: Medium Skin Tone", []string{"elf_woman_Medium_Skin_Tone"}, "12.0", false},
@@ -2001,111 +2040,111 @@ var GemojiData = Gemoji{
{"\U0001f926\U0001f3fd", "person facepalming: Medium Skin Tone", []string{"facepalm_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f926\U0001f3fe", "person facepalming: Medium-Dark Skin Tone", []string{"facepalm_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f926\U0001f3ff", "person facepalming: Dark Skin Tone", []string{"facepalm_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f9d1\U0001f3ff\u200d\U0001f3ed", "factory worker: Dark Skin Tone", []string{"factory_worker_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3fb\u200d\U0001f3ed", "factory worker: Light Skin Tone", []string{"factory_worker_Light_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3fc\u200d\U0001f3ed", "factory worker: Medium-Light Skin Tone", []string{"factory_worker_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3fd\u200d\U0001f3ed", "factory worker: Medium Skin Tone", []string{"factory_worker_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3fe\u200d\U0001f3ed", "factory worker: Medium-Dark Skin Tone", []string{"factory_worker_Medium-Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f9d1\U0001f3ff\u200d\U0001f3ed", "factory worker: Dark Skin Tone", []string{"factory_worker_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f9da\U0001f3fe", "fairy: Medium-Dark Skin Tone", []string{"fairy_Medium-Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f9da\U0001f3ff", "fairy: Dark Skin Tone", []string{"fairy_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9da\U0001f3fb", "fairy: Light Skin Tone", []string{"fairy_Light_Skin_Tone"}, "12.0", false},
{"\U0001f9da\U0001f3fc", "fairy: Medium-Light Skin Tone", []string{"fairy_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f9da\U0001f3fd", "fairy: Medium Skin Tone", []string{"fairy_Medium_Skin_Tone"}, "12.0", false},
- {"\U0001f9da\U0001f3fe", "fairy: Medium-Dark Skin Tone", []string{"fairy_Medium-Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f9da\U0001f3ff", "fairy: Dark Skin Tone", []string{"fairy_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9da\U0001f3fb\u200d\u2642\ufe0f", "man fairy: Light Skin Tone", []string{"fairy_man_Light_Skin_Tone"}, "12.0", false},
{"\U0001f9da\U0001f3fc\u200d\u2642\ufe0f", "man fairy: Medium-Light Skin Tone", []string{"fairy_man_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f9da\U0001f3fd\u200d\u2642\ufe0f", "man fairy: Medium Skin Tone", []string{"fairy_man_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f9da\U0001f3fe\u200d\u2642\ufe0f", "man fairy: Medium-Dark Skin Tone", []string{"fairy_man_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9da\U0001f3ff\u200d\u2642\ufe0f", "man fairy: Dark Skin Tone", []string{"fairy_man_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f9da\U0001f3fe\u200d\u2640\ufe0f", "woman fairy: Medium-Dark Skin Tone", []string{"fairy_woman_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9da\U0001f3ff\u200d\u2640\ufe0f", "woman fairy: Dark Skin Tone", []string{"fairy_woman_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9da\U0001f3fb\u200d\u2640\ufe0f", "woman fairy: Light Skin Tone", []string{"fairy_woman_Light_Skin_Tone"}, "12.0", false},
{"\U0001f9da\U0001f3fc\u200d\u2640\ufe0f", "woman fairy: Medium-Light Skin Tone", []string{"fairy_woman_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f9da\U0001f3fd\u200d\u2640\ufe0f", "woman fairy: Medium Skin Tone", []string{"fairy_woman_Medium_Skin_Tone"}, "12.0", false},
- {"\U0001f9d1\U0001f3fb\u200d\U0001f33e", "farmer: Light Skin Tone", []string{"farmer_Light_Skin_Tone"}, "12.0", false},
- {"\U0001f9d1\U0001f3fc\u200d\U0001f33e", "farmer: Medium-Light Skin Tone", []string{"farmer_Medium-Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f9da\U0001f3fe\u200d\u2640\ufe0f", "woman fairy: Medium-Dark Skin Tone", []string{"fairy_woman_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3fd\u200d\U0001f33e", "farmer: Medium Skin Tone", []string{"farmer_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3fe\u200d\U0001f33e", "farmer: Medium-Dark Skin Tone", []string{"farmer_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3ff\u200d\U0001f33e", "farmer: Dark Skin Tone", []string{"farmer_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f9d1\U0001f3fb\u200d\U0001f33e", "farmer: Light Skin Tone", []string{"farmer_Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f9d1\U0001f3fc\u200d\U0001f33e", "farmer: Medium-Light Skin Tone", []string{"farmer_Medium-Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f575\U0001f3ff\ufe0f\u200d\u2640\ufe0f", "woman detective: Dark Skin Tone", []string{"female_detective_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f575\U0001f3fb\ufe0f\u200d\u2640\ufe0f", "woman detective: Light Skin Tone", []string{"female_detective_Light_Skin_Tone"}, "12.0", false},
{"\U0001f575\U0001f3fc\ufe0f\u200d\u2640\ufe0f", "woman detective: Medium-Light Skin Tone", []string{"female_detective_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f575\U0001f3fd\ufe0f\u200d\u2640\ufe0f", "woman detective: Medium Skin Tone", []string{"female_detective_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f575\U0001f3fe\ufe0f\u200d\u2640\ufe0f", "woman detective: Medium-Dark Skin Tone", []string{"female_detective_Medium-Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f575\U0001f3ff\ufe0f\u200d\u2640\ufe0f", "woman detective: Dark Skin Tone", []string{"female_detective_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f575\U0001f3fb\ufe0f\u200d\u2640\ufe0f", "woman detective: Light Skin Tone", []string{"female_detective_Light_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3fc\u200d\U0001f692", "firefighter: Medium-Light Skin Tone", []string{"firefighter_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3fd\u200d\U0001f692", "firefighter: Medium Skin Tone", []string{"firefighter_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3fe\u200d\U0001f692", "firefighter: Medium-Dark Skin Tone", []string{"firefighter_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3ff\u200d\U0001f692", "firefighter: Dark Skin Tone", []string{"firefighter_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3fb\u200d\U0001f692", "firefighter: Light Skin Tone", []string{"firefighter_Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f91b\U0001f3fb", "left-facing fist: Light Skin Tone", []string{"fist_left_Light_Skin_Tone"}, "12.0", false},
{"\U0001f91b\U0001f3fc", "left-facing fist: Medium-Light Skin Tone", []string{"fist_left_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f91b\U0001f3fd", "left-facing fist: Medium Skin Tone", []string{"fist_left_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f91b\U0001f3fe", "left-facing fist: Medium-Dark Skin Tone", []string{"fist_left_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f91b\U0001f3ff", "left-facing fist: Dark Skin Tone", []string{"fist_left_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f91b\U0001f3fb", "left-facing fist: Light Skin Tone", []string{"fist_left_Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f44a\U0001f3fb", "oncoming fist: Light Skin Tone", []string{"fist_oncoming_Light_Skin_Tone"}, "12.0", false},
{"\U0001f44a\U0001f3fc", "oncoming fist: Medium-Light Skin Tone", []string{"fist_oncoming_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f44a\U0001f3fd", "oncoming fist: Medium Skin Tone", []string{"fist_oncoming_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f44a\U0001f3fe", "oncoming fist: Medium-Dark Skin Tone", []string{"fist_oncoming_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f44a\U0001f3ff", "oncoming fist: Dark Skin Tone", []string{"fist_oncoming_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f44a\U0001f3fb", "oncoming fist: Light Skin Tone", []string{"fist_oncoming_Light_Skin_Tone"}, "12.0", false},
+ {"\u270a\U0001f3fb", "raised fist: Light Skin Tone", []string{"fist_raised_Light_Skin_Tone"}, "12.0", false},
{"\u270a\U0001f3fc", "raised fist: Medium-Light Skin Tone", []string{"fist_raised_Medium-Light_Skin_Tone"}, "12.0", false},
{"\u270a\U0001f3fd", "raised fist: Medium Skin Tone", []string{"fist_raised_Medium_Skin_Tone"}, "12.0", false},
{"\u270a\U0001f3fe", "raised fist: Medium-Dark Skin Tone", []string{"fist_raised_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\u270a\U0001f3ff", "raised fist: Dark Skin Tone", []string{"fist_raised_Dark_Skin_Tone"}, "12.0", false},
- {"\u270a\U0001f3fb", "raised fist: Light Skin Tone", []string{"fist_raised_Light_Skin_Tone"}, "12.0", false},
{"\U0001f91c\U0001f3fb", "right-facing fist: Light Skin Tone", []string{"fist_right_Light_Skin_Tone"}, "12.0", false},
{"\U0001f91c\U0001f3fc", "right-facing fist: Medium-Light Skin Tone", []string{"fist_right_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f91c\U0001f3fd", "right-facing fist: Medium Skin Tone", []string{"fist_right_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f91c\U0001f3fe", "right-facing fist: Medium-Dark Skin Tone", []string{"fist_right_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f91c\U0001f3ff", "right-facing fist: Dark Skin Tone", []string{"fist_right_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f9b6\U0001f3fc", "foot: Medium-Light Skin Tone", []string{"foot_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f9b6\U0001f3fd", "foot: Medium Skin Tone", []string{"foot_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f9b6\U0001f3fe", "foot: Medium-Dark Skin Tone", []string{"foot_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9b6\U0001f3ff", "foot: Dark Skin Tone", []string{"foot_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9b6\U0001f3fb", "foot: Light Skin Tone", []string{"foot_Light_Skin_Tone"}, "12.0", false},
- {"\U0001f9b6\U0001f3fc", "foot: Medium-Light Skin Tone", []string{"foot_Medium-Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f64d\U0001f3fd\u200d\u2642\ufe0f", "man frowning: Medium Skin Tone", []string{"frowning_man_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f64d\U0001f3fe\u200d\u2642\ufe0f", "man frowning: Medium-Dark Skin Tone", []string{"frowning_man_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f64d\U0001f3ff\u200d\u2642\ufe0f", "man frowning: Dark Skin Tone", []string{"frowning_man_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f64d\U0001f3fb\u200d\u2642\ufe0f", "man frowning: Light Skin Tone", []string{"frowning_man_Light_Skin_Tone"}, "12.0", false},
{"\U0001f64d\U0001f3fc\u200d\u2642\ufe0f", "man frowning: Medium-Light Skin Tone", []string{"frowning_man_Medium-Light_Skin_Tone"}, "12.0", false},
- {"\U0001f64d\U0001f3fd\u200d\u2642\ufe0f", "man frowning: Medium Skin Tone", []string{"frowning_man_Medium_Skin_Tone"}, "12.0", false},
+ {"\U0001f64d\U0001f3fb", "person frowning: Light Skin Tone", []string{"frowning_person_Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f64d\U0001f3fc", "person frowning: Medium-Light Skin Tone", []string{"frowning_person_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f64d\U0001f3fd", "person frowning: Medium Skin Tone", []string{"frowning_person_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f64d\U0001f3fe", "person frowning: Medium-Dark Skin Tone", []string{"frowning_person_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f64d\U0001f3ff", "person frowning: Dark Skin Tone", []string{"frowning_person_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f64d\U0001f3fb", "person frowning: Light Skin Tone", []string{"frowning_person_Light_Skin_Tone"}, "12.0", false},
- {"\U0001f64d\U0001f3fc", "person frowning: Medium-Light Skin Tone", []string{"frowning_person_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f64d\U0001f3fb\u200d\u2640\ufe0f", "woman frowning: Light Skin Tone", []string{"frowning_woman_Light_Skin_Tone"}, "12.0", false},
{"\U0001f64d\U0001f3fc\u200d\u2640\ufe0f", "woman frowning: Medium-Light Skin Tone", []string{"frowning_woman_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f64d\U0001f3fd\u200d\u2640\ufe0f", "woman frowning: Medium Skin Tone", []string{"frowning_woman_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f64d\U0001f3fe\u200d\u2640\ufe0f", "woman frowning: Medium-Dark Skin Tone", []string{"frowning_woman_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f64d\U0001f3ff\u200d\u2640\ufe0f", "woman frowning: Dark Skin Tone", []string{"frowning_woman_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f467\U0001f3fc", "girl: Medium-Light Skin Tone", []string{"girl_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f467\U0001f3fd", "girl: Medium Skin Tone", []string{"girl_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f467\U0001f3fe", "girl: Medium-Dark Skin Tone", []string{"girl_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f467\U0001f3ff", "girl: Dark Skin Tone", []string{"girl_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f467\U0001f3fb", "girl: Light Skin Tone", []string{"girl_Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f467\U0001f3fc", "girl: Medium-Light Skin Tone", []string{"girl_Medium-Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f3cc\U0001f3ff\ufe0f", "person golfing: Dark Skin Tone", []string{"golfing_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f3cc\U0001f3fb\ufe0f", "person golfing: Light Skin Tone", []string{"golfing_Light_Skin_Tone"}, "12.0", false},
{"\U0001f3cc\U0001f3fc\ufe0f", "person golfing: Medium-Light Skin Tone", []string{"golfing_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f3cc\U0001f3fd\ufe0f", "person golfing: Medium Skin Tone", []string{"golfing_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f3cc\U0001f3fe\ufe0f", "person golfing: Medium-Dark Skin Tone", []string{"golfing_Medium-Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f3cc\U0001f3ff\ufe0f", "person golfing: Dark Skin Tone", []string{"golfing_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f3cc\U0001f3fb\ufe0f\u200d\u2642\ufe0f", "man golfing: Light Skin Tone", []string{"golfing_man_Light_Skin_Tone"}, "12.0", false},
{"\U0001f3cc\U0001f3fc\ufe0f\u200d\u2642\ufe0f", "man golfing: Medium-Light Skin Tone", []string{"golfing_man_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f3cc\U0001f3fd\ufe0f\u200d\u2642\ufe0f", "man golfing: Medium Skin Tone", []string{"golfing_man_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f3cc\U0001f3fe\ufe0f\u200d\u2642\ufe0f", "man golfing: Medium-Dark Skin Tone", []string{"golfing_man_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f3cc\U0001f3ff\ufe0f\u200d\u2642\ufe0f", "man golfing: Dark Skin Tone", []string{"golfing_man_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f3cc\U0001f3fb\ufe0f\u200d\u2640\ufe0f", "woman golfing: Light Skin Tone", []string{"golfing_woman_Light_Skin_Tone"}, "12.0", false},
- {"\U0001f3cc\U0001f3fc\ufe0f\u200d\u2640\ufe0f", "woman golfing: Medium-Light Skin Tone", []string{"golfing_woman_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f3cc\U0001f3fd\ufe0f\u200d\u2640\ufe0f", "woman golfing: Medium Skin Tone", []string{"golfing_woman_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f3cc\U0001f3fe\ufe0f\u200d\u2640\ufe0f", "woman golfing: Medium-Dark Skin Tone", []string{"golfing_woman_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f3cc\U0001f3ff\ufe0f\u200d\u2640\ufe0f", "woman golfing: Dark Skin Tone", []string{"golfing_woman_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f3cc\U0001f3fb\ufe0f\u200d\u2640\ufe0f", "woman golfing: Light Skin Tone", []string{"golfing_woman_Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f3cc\U0001f3fc\ufe0f\u200d\u2640\ufe0f", "woman golfing: Medium-Light Skin Tone", []string{"golfing_woman_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f482\U0001f3fb", "guard: Light Skin Tone", []string{"guard_Light_Skin_Tone"}, "12.0", false},
{"\U0001f482\U0001f3fc", "guard: Medium-Light Skin Tone", []string{"guard_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f482\U0001f3fd", "guard: Medium Skin Tone", []string{"guard_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f482\U0001f3fe", "guard: Medium-Dark Skin Tone", []string{"guard_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f482\U0001f3ff", "guard: Dark Skin Tone", []string{"guard_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f482\U0001f3fd\u200d\u2642\ufe0f", "man guard: Medium Skin Tone", []string{"guardsman_Medium_Skin_Tone"}, "12.0", false},
+ {"\U0001f482\U0001f3fe\u200d\u2642\ufe0f", "man guard: Medium-Dark Skin Tone", []string{"guardsman_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f482\U0001f3ff\u200d\u2642\ufe0f", "man guard: Dark Skin Tone", []string{"guardsman_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f482\U0001f3fb\u200d\u2642\ufe0f", "man guard: Light Skin Tone", []string{"guardsman_Light_Skin_Tone"}, "12.0", false},
{"\U0001f482\U0001f3fc\u200d\u2642\ufe0f", "man guard: Medium-Light Skin Tone", []string{"guardsman_Medium-Light_Skin_Tone"}, "12.0", false},
- {"\U0001f482\U0001f3fd\u200d\u2642\ufe0f", "man guard: Medium Skin Tone", []string{"guardsman_Medium_Skin_Tone"}, "12.0", false},
- {"\U0001f482\U0001f3fe\u200d\u2642\ufe0f", "man guard: Medium-Dark Skin Tone", []string{"guardsman_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f482\U0001f3fb\u200d\u2640\ufe0f", "woman guard: Light Skin Tone", []string{"guardswoman_Light_Skin_Tone"}, "12.0", false},
{"\U0001f482\U0001f3fc\u200d\u2640\ufe0f", "woman guard: Medium-Light Skin Tone", []string{"guardswoman_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f482\U0001f3fd\u200d\u2640\ufe0f", "woman guard: Medium Skin Tone", []string{"guardswoman_Medium_Skin_Tone"}, "12.0", false},
@@ -2116,131 +2155,131 @@ var GemojiData = Gemoji{
{"\U0001f487\U0001f3fb", "person getting haircut: Light Skin Tone", []string{"haircut_Light_Skin_Tone"}, "12.0", false},
{"\U0001f487\U0001f3fc", "person getting haircut: Medium-Light Skin Tone", []string{"haircut_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f487\U0001f3fd", "person getting haircut: Medium Skin Tone", []string{"haircut_Medium_Skin_Tone"}, "12.0", false},
- {"\U0001f487\U0001f3fb\u200d\u2642\ufe0f", "man getting haircut: Light Skin Tone", []string{"haircut_man_Light_Skin_Tone"}, "12.0", false},
{"\U0001f487\U0001f3fc\u200d\u2642\ufe0f", "man getting haircut: Medium-Light Skin Tone", []string{"haircut_man_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f487\U0001f3fd\u200d\u2642\ufe0f", "man getting haircut: Medium Skin Tone", []string{"haircut_man_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f487\U0001f3fe\u200d\u2642\ufe0f", "man getting haircut: Medium-Dark Skin Tone", []string{"haircut_man_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f487\U0001f3ff\u200d\u2642\ufe0f", "man getting haircut: Dark Skin Tone", []string{"haircut_man_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f487\U0001f3fb\u200d\u2642\ufe0f", "man getting haircut: Light Skin Tone", []string{"haircut_man_Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f487\U0001f3ff\u200d\u2640\ufe0f", "woman getting haircut: Dark Skin Tone", []string{"haircut_woman_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f487\U0001f3fb\u200d\u2640\ufe0f", "woman getting haircut: Light Skin Tone", []string{"haircut_woman_Light_Skin_Tone"}, "12.0", false},
{"\U0001f487\U0001f3fc\u200d\u2640\ufe0f", "woman getting haircut: Medium-Light Skin Tone", []string{"haircut_woman_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f487\U0001f3fd\u200d\u2640\ufe0f", "woman getting haircut: Medium Skin Tone", []string{"haircut_woman_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f487\U0001f3fe\u200d\u2640\ufe0f", "woman getting haircut: Medium-Dark Skin Tone", []string{"haircut_woman_Medium-Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f487\U0001f3ff\u200d\u2640\ufe0f", "woman getting haircut: Dark Skin Tone", []string{"haircut_woman_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f487\U0001f3fb\u200d\u2640\ufe0f", "woman getting haircut: Light Skin Tone", []string{"haircut_woman_Light_Skin_Tone"}, "12.0", false},
+ {"\u270b\U0001f3ff", "raised hand: Dark Skin Tone", []string{"hand_Dark_Skin_Tone"}, "12.0", false},
+ {"\u270b\U0001f3fb", "raised hand: Light Skin Tone", []string{"hand_Light_Skin_Tone"}, "12.0", false},
{"\u270b\U0001f3fc", "raised hand: Medium-Light Skin Tone", []string{"hand_Medium-Light_Skin_Tone"}, "12.0", false},
{"\u270b\U0001f3fd", "raised hand: Medium Skin Tone", []string{"hand_Medium_Skin_Tone"}, "12.0", false},
{"\u270b\U0001f3fe", "raised hand: Medium-Dark Skin Tone", []string{"hand_Medium-Dark_Skin_Tone"}, "12.0", false},
- {"\u270b\U0001f3ff", "raised hand: Dark Skin Tone", []string{"hand_Dark_Skin_Tone"}, "12.0", false},
- {"\u270b\U0001f3fb", "raised hand: Light Skin Tone", []string{"hand_Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f93e\U0001f3fd", "person playing handball: Medium Skin Tone", []string{"handball_person_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f93e\U0001f3fe", "person playing handball: Medium-Dark Skin Tone", []string{"handball_person_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f93e\U0001f3ff", "person playing handball: Dark Skin Tone", []string{"handball_person_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f93e\U0001f3fb", "person playing handball: Light Skin Tone", []string{"handball_person_Light_Skin_Tone"}, "12.0", false},
{"\U0001f93e\U0001f3fc", "person playing handball: Medium-Light Skin Tone", []string{"handball_person_Medium-Light_Skin_Tone"}, "12.0", false},
- {"\U0001f93e\U0001f3fd", "person playing handball: Medium Skin Tone", []string{"handball_person_Medium_Skin_Tone"}, "12.0", false},
- {"\U0001f9d1\U0001f3ff\u200d\u2695\ufe0f", "health worker: Dark Skin Tone", []string{"health_worker_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3fb\u200d\u2695\ufe0f", "health worker: Light Skin Tone", []string{"health_worker_Light_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3fc\u200d\u2695\ufe0f", "health worker: Medium-Light Skin Tone", []string{"health_worker_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3fd\u200d\u2695\ufe0f", "health worker: Medium Skin Tone", []string{"health_worker_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3fe\u200d\u2695\ufe0f", "health worker: Medium-Dark Skin Tone", []string{"health_worker_Medium-Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f9d1\U0001f3ff\u200d\u2695\ufe0f", "health worker: Dark Skin Tone", []string{"health_worker_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f3c7\U0001f3fb", "horse racing: Light Skin Tone", []string{"horse_racing_Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f3c7\U0001f3fc", "horse racing: Medium-Light Skin Tone", []string{"horse_racing_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f3c7\U0001f3fd", "horse racing: Medium Skin Tone", []string{"horse_racing_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f3c7\U0001f3fe", "horse racing: Medium-Dark Skin Tone", []string{"horse_racing_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f3c7\U0001f3ff", "horse racing: Dark Skin Tone", []string{"horse_racing_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f3c7\U0001f3fb", "horse racing: Light Skin Tone", []string{"horse_racing_Light_Skin_Tone"}, "12.0", false},
- {"\U0001f3c7\U0001f3fc", "horse racing: Medium-Light Skin Tone", []string{"horse_racing_Medium-Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f9d1\U0001f3ff\u200d\u2696\ufe0f", "judge: Dark Skin Tone", []string{"judge_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f9d1\U0001f3fb\u200d\u2696\ufe0f", "judge: Light Skin Tone", []string{"judge_Light_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3fc\u200d\u2696\ufe0f", "judge: Medium-Light Skin Tone", []string{"judge_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3fd\u200d\u2696\ufe0f", "judge: Medium Skin Tone", []string{"judge_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3fe\u200d\u2696\ufe0f", "judge: Medium-Dark Skin Tone", []string{"judge_Medium-Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f9d1\U0001f3ff\u200d\u2696\ufe0f", "judge: Dark Skin Tone", []string{"judge_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f9d1\U0001f3fb\u200d\u2696\ufe0f", "judge: Light Skin Tone", []string{"judge_Light_Skin_Tone"}, "12.0", false},
{"\U0001f939\U0001f3fd", "person juggling: Medium Skin Tone", []string{"juggling_person_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f939\U0001f3fe", "person juggling: Medium-Dark Skin Tone", []string{"juggling_person_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f939\U0001f3ff", "person juggling: Dark Skin Tone", []string{"juggling_person_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f939\U0001f3fb", "person juggling: Light Skin Tone", []string{"juggling_person_Light_Skin_Tone"}, "12.0", false},
{"\U0001f939\U0001f3fc", "person juggling: Medium-Light Skin Tone", []string{"juggling_person_Medium-Light_Skin_Tone"}, "12.0", false},
- {"\U0001f9ce\U0001f3fb\u200d\u2642\ufe0f", "man kneeling: Light Skin Tone", []string{"kneeling_man_Light_Skin_Tone"}, "12.0", false},
- {"\U0001f9ce\U0001f3fc\u200d\u2642\ufe0f", "man kneeling: Medium-Light Skin Tone", []string{"kneeling_man_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f9ce\U0001f3fd\u200d\u2642\ufe0f", "man kneeling: Medium Skin Tone", []string{"kneeling_man_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f9ce\U0001f3fe\u200d\u2642\ufe0f", "man kneeling: Medium-Dark Skin Tone", []string{"kneeling_man_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9ce\U0001f3ff\u200d\u2642\ufe0f", "man kneeling: Dark Skin Tone", []string{"kneeling_man_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f9ce\U0001f3fb\u200d\u2642\ufe0f", "man kneeling: Light Skin Tone", []string{"kneeling_man_Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f9ce\U0001f3fc\u200d\u2642\ufe0f", "man kneeling: Medium-Light Skin Tone", []string{"kneeling_man_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f9ce\U0001f3fb", "person kneeling: Light Skin Tone", []string{"kneeling_person_Light_Skin_Tone"}, "12.0", false},
{"\U0001f9ce\U0001f3fc", "person kneeling: Medium-Light Skin Tone", []string{"kneeling_person_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f9ce\U0001f3fd", "person kneeling: Medium Skin Tone", []string{"kneeling_person_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f9ce\U0001f3fe", "person kneeling: Medium-Dark Skin Tone", []string{"kneeling_person_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9ce\U0001f3ff", "person kneeling: Dark Skin Tone", []string{"kneeling_person_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f9ce\U0001f3ff\u200d\u2640\ufe0f", "woman kneeling: Dark Skin Tone", []string{"kneeling_woman_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f9ce\U0001f3fb\u200d\u2640\ufe0f", "woman kneeling: Light Skin Tone", []string{"kneeling_woman_Light_Skin_Tone"}, "12.0", false},
{"\U0001f9ce\U0001f3fc\u200d\u2640\ufe0f", "woman kneeling: Medium-Light Skin Tone", []string{"kneeling_woman_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f9ce\U0001f3fd\u200d\u2640\ufe0f", "woman kneeling: Medium Skin Tone", []string{"kneeling_woman_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f9ce\U0001f3fe\u200d\u2640\ufe0f", "woman kneeling: Medium-Dark Skin Tone", []string{"kneeling_woman_Medium-Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f9ce\U0001f3ff\u200d\u2640\ufe0f", "woman kneeling: Dark Skin Tone", []string{"kneeling_woman_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f9ce\U0001f3fb\u200d\u2640\ufe0f", "woman kneeling: Light Skin Tone", []string{"kneeling_woman_Light_Skin_Tone"}, "12.0", false},
{"\U0001f9b5\U0001f3fb", "leg: Light Skin Tone", []string{"leg_Light_Skin_Tone"}, "12.0", false},
{"\U0001f9b5\U0001f3fc", "leg: Medium-Light Skin Tone", []string{"leg_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f9b5\U0001f3fd", "leg: Medium Skin Tone", []string{"leg_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f9b5\U0001f3fe", "leg: Medium-Dark Skin Tone", []string{"leg_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9b5\U0001f3ff", "leg: Dark Skin Tone", []string{"leg_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f9d8\U0001f3fb", "person in lotus position: Light Skin Tone", []string{"lotus_position_Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f9d8\U0001f3fc", "person in lotus position: Medium-Light Skin Tone", []string{"lotus_position_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f9d8\U0001f3fd", "person in lotus position: Medium Skin Tone", []string{"lotus_position_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f9d8\U0001f3fe", "person in lotus position: Medium-Dark Skin Tone", []string{"lotus_position_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9d8\U0001f3ff", "person in lotus position: Dark Skin Tone", []string{"lotus_position_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f9d8\U0001f3fb", "person in lotus position: Light Skin Tone", []string{"lotus_position_Light_Skin_Tone"}, "12.0", false},
- {"\U0001f9d8\U0001f3fc", "person in lotus position: Medium-Light Skin Tone", []string{"lotus_position_Medium-Light_Skin_Tone"}, "12.0", false},
- {"\U0001f9d8\U0001f3fd\u200d\u2642\ufe0f", "man in lotus position: Medium Skin Tone", []string{"lotus_position_man_Medium_Skin_Tone"}, "12.0", false},
- {"\U0001f9d8\U0001f3fe\u200d\u2642\ufe0f", "man in lotus position: Medium-Dark Skin Tone", []string{"lotus_position_man_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9d8\U0001f3ff\u200d\u2642\ufe0f", "man in lotus position: Dark Skin Tone", []string{"lotus_position_man_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9d8\U0001f3fb\u200d\u2642\ufe0f", "man in lotus position: Light Skin Tone", []string{"lotus_position_man_Light_Skin_Tone"}, "12.0", false},
{"\U0001f9d8\U0001f3fc\u200d\u2642\ufe0f", "man in lotus position: Medium-Light Skin Tone", []string{"lotus_position_man_Medium-Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f9d8\U0001f3fd\u200d\u2642\ufe0f", "man in lotus position: Medium Skin Tone", []string{"lotus_position_man_Medium_Skin_Tone"}, "12.0", false},
+ {"\U0001f9d8\U0001f3fe\u200d\u2642\ufe0f", "man in lotus position: Medium-Dark Skin Tone", []string{"lotus_position_man_Medium-Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f9d8\U0001f3fb\u200d\u2640\ufe0f", "woman in lotus position: Light Skin Tone", []string{"lotus_position_woman_Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f9d8\U0001f3fc\u200d\u2640\ufe0f", "woman in lotus position: Medium-Light Skin Tone", []string{"lotus_position_woman_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f9d8\U0001f3fd\u200d\u2640\ufe0f", "woman in lotus position: Medium Skin Tone", []string{"lotus_position_woman_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f9d8\U0001f3fe\u200d\u2640\ufe0f", "woman in lotus position: Medium-Dark Skin Tone", []string{"lotus_position_woman_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9d8\U0001f3ff\u200d\u2640\ufe0f", "woman in lotus position: Dark Skin Tone", []string{"lotus_position_woman_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f9d8\U0001f3fb\u200d\u2640\ufe0f", "woman in lotus position: Light Skin Tone", []string{"lotus_position_woman_Light_Skin_Tone"}, "12.0", false},
- {"\U0001f9d8\U0001f3fc\u200d\u2640\ufe0f", "woman in lotus position: Medium-Light Skin Tone", []string{"lotus_position_woman_Medium-Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f91f\U0001f3ff", "love-you gesture: Dark Skin Tone", []string{"love_you_gesture_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f91f\U0001f3fb", "love-you gesture: Light Skin Tone", []string{"love_you_gesture_Light_Skin_Tone"}, "12.0", false},
{"\U0001f91f\U0001f3fc", "love-you gesture: Medium-Light Skin Tone", []string{"love_you_gesture_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f91f\U0001f3fd", "love-you gesture: Medium Skin Tone", []string{"love_you_gesture_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f91f\U0001f3fe", "love-you gesture: Medium-Dark Skin Tone", []string{"love_you_gesture_Medium-Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f91f\U0001f3ff", "love-you gesture: Dark Skin Tone", []string{"love_you_gesture_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f91f\U0001f3fb", "love-you gesture: Light Skin Tone", []string{"love_you_gesture_Light_Skin_Tone"}, "12.0", false},
{"\U0001f9d9\U0001f3fb", "mage: Light Skin Tone", []string{"mage_Light_Skin_Tone"}, "12.0", false},
{"\U0001f9d9\U0001f3fc", "mage: Medium-Light Skin Tone", []string{"mage_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f9d9\U0001f3fd", "mage: Medium Skin Tone", []string{"mage_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f9d9\U0001f3fe", "mage: Medium-Dark Skin Tone", []string{"mage_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9d9\U0001f3ff", "mage: Dark Skin Tone", []string{"mage_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f9d9\U0001f3fe\u200d\u2642\ufe0f", "man mage: Medium-Dark Skin Tone", []string{"mage_man_Medium-Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f9d9\U0001f3ff\u200d\u2642\ufe0f", "man mage: Dark Skin Tone", []string{"mage_man_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9d9\U0001f3fb\u200d\u2642\ufe0f", "man mage: Light Skin Tone", []string{"mage_man_Light_Skin_Tone"}, "12.0", false},
{"\U0001f9d9\U0001f3fc\u200d\u2642\ufe0f", "man mage: Medium-Light Skin Tone", []string{"mage_man_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f9d9\U0001f3fd\u200d\u2642\ufe0f", "man mage: Medium Skin Tone", []string{"mage_man_Medium_Skin_Tone"}, "12.0", false},
- {"\U0001f9d9\U0001f3fb\u200d\u2640\ufe0f", "woman mage: Light Skin Tone", []string{"mage_woman_Light_Skin_Tone"}, "12.0", false},
- {"\U0001f9d9\U0001f3fc\u200d\u2640\ufe0f", "woman mage: Medium-Light Skin Tone", []string{"mage_woman_Medium-Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f9d9\U0001f3fe\u200d\u2642\ufe0f", "man mage: Medium-Dark Skin Tone", []string{"mage_man_Medium-Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f9d9\U0001f3ff\u200d\u2642\ufe0f", "man mage: Dark Skin Tone", []string{"mage_man_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9d9\U0001f3fd\u200d\u2640\ufe0f", "woman mage: Medium Skin Tone", []string{"mage_woman_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f9d9\U0001f3fe\u200d\u2640\ufe0f", "woman mage: Medium-Dark Skin Tone", []string{"mage_woman_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9d9\U0001f3ff\u200d\u2640\ufe0f", "woman mage: Dark Skin Tone", []string{"mage_woman_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f9d9\U0001f3fb\u200d\u2640\ufe0f", "woman mage: Light Skin Tone", []string{"mage_woman_Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f9d9\U0001f3fc\u200d\u2640\ufe0f", "woman mage: Medium-Light Skin Tone", []string{"mage_woman_Medium-Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f575\U0001f3fb\ufe0f\u200d\u2642\ufe0f", "man detective: Light Skin Tone", []string{"male_detective_Light_Skin_Tone"}, "12.0", false},
{"\U0001f575\U0001f3fc\ufe0f\u200d\u2642\ufe0f", "man detective: Medium-Light Skin Tone", []string{"male_detective_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f575\U0001f3fd\ufe0f\u200d\u2642\ufe0f", "man detective: Medium Skin Tone", []string{"male_detective_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f575\U0001f3fe\ufe0f\u200d\u2642\ufe0f", "man detective: Medium-Dark Skin Tone", []string{"male_detective_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f575\U0001f3ff\ufe0f\u200d\u2642\ufe0f", "man detective: Dark Skin Tone", []string{"male_detective_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f575\U0001f3fb\ufe0f\u200d\u2642\ufe0f", "man detective: Light Skin Tone", []string{"male_detective_Light_Skin_Tone"}, "12.0", false},
- {"\U0001f468\U0001f3fe", "man: Medium-Dark Skin Tone", []string{"man_Medium-Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f468\U0001f3ff", "man: Dark Skin Tone", []string{"man_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3fb", "man: Light Skin Tone", []string{"man_Light_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3fc", "man: Medium-Light Skin Tone", []string{"man_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3fd", "man: Medium Skin Tone", []string{"man_Medium_Skin_Tone"}, "12.0", false},
+ {"\U0001f468\U0001f3fe", "man: Medium-Dark Skin Tone", []string{"man_Medium-Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f468\U0001f3ff", "man: Dark Skin Tone", []string{"man_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f468\U0001f3fd\u200d\U0001f3a8", "man artist: Medium Skin Tone", []string{"man_artist_Medium_Skin_Tone"}, "12.0", false},
+ {"\U0001f468\U0001f3fe\u200d\U0001f3a8", "man artist: Medium-Dark Skin Tone", []string{"man_artist_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3ff\u200d\U0001f3a8", "man artist: Dark Skin Tone", []string{"man_artist_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3fb\u200d\U0001f3a8", "man artist: Light Skin Tone", []string{"man_artist_Light_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3fc\u200d\U0001f3a8", "man artist: Medium-Light Skin Tone", []string{"man_artist_Medium-Light_Skin_Tone"}, "12.0", false},
- {"\U0001f468\U0001f3fd\u200d\U0001f3a8", "man artist: Medium Skin Tone", []string{"man_artist_Medium_Skin_Tone"}, "12.0", false},
- {"\U0001f468\U0001f3fe\u200d\U0001f3a8", "man artist: Medium-Dark Skin Tone", []string{"man_artist_Medium-Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f468\U0001f3fc\u200d\U0001f680", "man astronaut: Medium-Light Skin Tone", []string{"man_astronaut_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3fd\u200d\U0001f680", "man astronaut: Medium Skin Tone", []string{"man_astronaut_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3fe\u200d\U0001f680", "man astronaut: Medium-Dark Skin Tone", []string{"man_astronaut_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3ff\u200d\U0001f680", "man astronaut: Dark Skin Tone", []string{"man_astronaut_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3fb\u200d\U0001f680", "man astronaut: Light Skin Tone", []string{"man_astronaut_Light_Skin_Tone"}, "12.0", false},
- {"\U0001f468\U0001f3fc\u200d\U0001f680", "man astronaut: Medium-Light Skin Tone", []string{"man_astronaut_Medium-Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f938\U0001f3fb\u200d\u2642\ufe0f", "man cartwheeling: Light Skin Tone", []string{"man_cartwheeling_Light_Skin_Tone"}, "12.0", false},
{"\U0001f938\U0001f3fc\u200d\u2642\ufe0f", "man cartwheeling: Medium-Light Skin Tone", []string{"man_cartwheeling_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f938\U0001f3fd\u200d\u2642\ufe0f", "man cartwheeling: Medium Skin Tone", []string{"man_cartwheeling_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f938\U0001f3fe\u200d\u2642\ufe0f", "man cartwheeling: Medium-Dark Skin Tone", []string{"man_cartwheeling_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f938\U0001f3ff\u200d\u2642\ufe0f", "man cartwheeling: Dark Skin Tone", []string{"man_cartwheeling_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f938\U0001f3fb\u200d\u2642\ufe0f", "man cartwheeling: Light Skin Tone", []string{"man_cartwheeling_Light_Skin_Tone"}, "12.0", false},
- {"\U0001f468\U0001f3fe\u200d\U0001f373", "man cook: Medium-Dark Skin Tone", []string{"man_cook_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3ff\u200d\U0001f373", "man cook: Dark Skin Tone", []string{"man_cook_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3fb\u200d\U0001f373", "man cook: Light Skin Tone", []string{"man_cook_Light_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3fc\u200d\U0001f373", "man cook: Medium-Light Skin Tone", []string{"man_cook_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3fd\u200d\U0001f373", "man cook: Medium Skin Tone", []string{"man_cook_Medium_Skin_Tone"}, "12.0", false},
+ {"\U0001f468\U0001f3fe\u200d\U0001f373", "man cook: Medium-Dark Skin Tone", []string{"man_cook_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f57a\U0001f3fb", "man dancing: Light Skin Tone", []string{"man_dancing_Light_Skin_Tone"}, "12.0", false},
{"\U0001f57a\U0001f3fc", "man dancing: Medium-Light Skin Tone", []string{"man_dancing_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f57a\U0001f3fd", "man dancing: Medium Skin Tone", []string{"man_dancing_Medium_Skin_Tone"}, "12.0", false},
@@ -2251,61 +2290,61 @@ var GemojiData = Gemoji{
{"\U0001f926\U0001f3fb\u200d\u2642\ufe0f", "man facepalming: Light Skin Tone", []string{"man_facepalming_Light_Skin_Tone"}, "12.0", false},
{"\U0001f926\U0001f3fc\u200d\u2642\ufe0f", "man facepalming: Medium-Light Skin Tone", []string{"man_facepalming_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f926\U0001f3fd\u200d\u2642\ufe0f", "man facepalming: Medium Skin Tone", []string{"man_facepalming_Medium_Skin_Tone"}, "12.0", false},
- {"\U0001f468\U0001f3ff\u200d\U0001f3ed", "man factory worker: Dark Skin Tone", []string{"man_factory_worker_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3fb\u200d\U0001f3ed", "man factory worker: Light Skin Tone", []string{"man_factory_worker_Light_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3fc\u200d\U0001f3ed", "man factory worker: Medium-Light Skin Tone", []string{"man_factory_worker_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3fd\u200d\U0001f3ed", "man factory worker: Medium Skin Tone", []string{"man_factory_worker_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3fe\u200d\U0001f3ed", "man factory worker: Medium-Dark Skin Tone", []string{"man_factory_worker_Medium-Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f468\U0001f3ff\u200d\U0001f3ed", "man factory worker: Dark Skin Tone", []string{"man_factory_worker_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3fb\u200d\U0001f33e", "man farmer: Light Skin Tone", []string{"man_farmer_Light_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3fc\u200d\U0001f33e", "man farmer: Medium-Light Skin Tone", []string{"man_farmer_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3fd\u200d\U0001f33e", "man farmer: Medium Skin Tone", []string{"man_farmer_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3fe\u200d\U0001f33e", "man farmer: Medium-Dark Skin Tone", []string{"man_farmer_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3ff\u200d\U0001f33e", "man farmer: Dark Skin Tone", []string{"man_farmer_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f468\U0001f3ff\u200d\U0001f692", "man firefighter: Dark Skin Tone", []string{"man_firefighter_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3fb\u200d\U0001f692", "man firefighter: Light Skin Tone", []string{"man_firefighter_Light_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3fc\u200d\U0001f692", "man firefighter: Medium-Light Skin Tone", []string{"man_firefighter_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3fd\u200d\U0001f692", "man firefighter: Medium Skin Tone", []string{"man_firefighter_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3fe\u200d\U0001f692", "man firefighter: Medium-Dark Skin Tone", []string{"man_firefighter_Medium-Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f468\U0001f3ff\u200d\U0001f692", "man firefighter: Dark Skin Tone", []string{"man_firefighter_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f468\U0001f3fe\u200d\u2695\ufe0f", "man health worker: Medium-Dark Skin Tone", []string{"man_health_worker_Medium-Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f468\U0001f3ff\u200d\u2695\ufe0f", "man health worker: Dark Skin Tone", []string{"man_health_worker_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3fb\u200d\u2695\ufe0f", "man health worker: Light Skin Tone", []string{"man_health_worker_Light_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3fc\u200d\u2695\ufe0f", "man health worker: Medium-Light Skin Tone", []string{"man_health_worker_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3fd\u200d\u2695\ufe0f", "man health worker: Medium Skin Tone", []string{"man_health_worker_Medium_Skin_Tone"}, "12.0", false},
- {"\U0001f468\U0001f3fe\u200d\u2695\ufe0f", "man health worker: Medium-Dark Skin Tone", []string{"man_health_worker_Medium-Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f468\U0001f3ff\u200d\u2695\ufe0f", "man health worker: Dark Skin Tone", []string{"man_health_worker_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f468\U0001f3fb\u200d\U0001f9bd", "man in manual wheelchair: Light Skin Tone", []string{"man_in_manual_wheelchair_Light_Skin_Tone"}, "12.0", false},
- {"\U0001f468\U0001f3fc\u200d\U0001f9bd", "man in manual wheelchair: Medium-Light Skin Tone", []string{"man_in_manual_wheelchair_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3fd\u200d\U0001f9bd", "man in manual wheelchair: Medium Skin Tone", []string{"man_in_manual_wheelchair_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3fe\u200d\U0001f9bd", "man in manual wheelchair: Medium-Dark Skin Tone", []string{"man_in_manual_wheelchair_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3ff\u200d\U0001f9bd", "man in manual wheelchair: Dark Skin Tone", []string{"man_in_manual_wheelchair_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f468\U0001f3fb\u200d\U0001f9bc", "man in motorized wheelchair: Light Skin Tone", []string{"man_in_motorized_wheelchair_Light_Skin_Tone"}, "12.0", false},
- {"\U0001f468\U0001f3fc\u200d\U0001f9bc", "man in motorized wheelchair: Medium-Light Skin Tone", []string{"man_in_motorized_wheelchair_Medium-Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f468\U0001f3fb\u200d\U0001f9bd", "man in manual wheelchair: Light Skin Tone", []string{"man_in_manual_wheelchair_Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f468\U0001f3fc\u200d\U0001f9bd", "man in manual wheelchair: Medium-Light Skin Tone", []string{"man_in_manual_wheelchair_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3fd\u200d\U0001f9bc", "man in motorized wheelchair: Medium Skin Tone", []string{"man_in_motorized_wheelchair_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3fe\u200d\U0001f9bc", "man in motorized wheelchair: Medium-Dark Skin Tone", []string{"man_in_motorized_wheelchair_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3ff\u200d\U0001f9bc", "man in motorized wheelchair: Dark Skin Tone", []string{"man_in_motorized_wheelchair_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f468\U0001f3fb\u200d\U0001f9bc", "man in motorized wheelchair: Light Skin Tone", []string{"man_in_motorized_wheelchair_Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f468\U0001f3fc\u200d\U0001f9bc", "man in motorized wheelchair: Medium-Light Skin Tone", []string{"man_in_motorized_wheelchair_Medium-Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f468\U0001f3fb\u200d\u2696\ufe0f", "man judge: Light Skin Tone", []string{"man_judge_Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f468\U0001f3fc\u200d\u2696\ufe0f", "man judge: Medium-Light Skin Tone", []string{"man_judge_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3fd\u200d\u2696\ufe0f", "man judge: Medium Skin Tone", []string{"man_judge_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3fe\u200d\u2696\ufe0f", "man judge: Medium-Dark Skin Tone", []string{"man_judge_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3ff\u200d\u2696\ufe0f", "man judge: Dark Skin Tone", []string{"man_judge_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f468\U0001f3fb\u200d\u2696\ufe0f", "man judge: Light Skin Tone", []string{"man_judge_Light_Skin_Tone"}, "12.0", false},
- {"\U0001f468\U0001f3fc\u200d\u2696\ufe0f", "man judge: Medium-Light Skin Tone", []string{"man_judge_Medium-Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f939\U0001f3fc\u200d\u2642\ufe0f", "man juggling: Medium-Light Skin Tone", []string{"man_juggling_Medium-Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f939\U0001f3fd\u200d\u2642\ufe0f", "man juggling: Medium Skin Tone", []string{"man_juggling_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f939\U0001f3fe\u200d\u2642\ufe0f", "man juggling: Medium-Dark Skin Tone", []string{"man_juggling_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f939\U0001f3ff\u200d\u2642\ufe0f", "man juggling: Dark Skin Tone", []string{"man_juggling_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f939\U0001f3fb\u200d\u2642\ufe0f", "man juggling: Light Skin Tone", []string{"man_juggling_Light_Skin_Tone"}, "12.0", false},
- {"\U0001f939\U0001f3fc\u200d\u2642\ufe0f", "man juggling: Medium-Light Skin Tone", []string{"man_juggling_Medium-Light_Skin_Tone"}, "12.0", false},
- {"\U0001f939\U0001f3fd\u200d\u2642\ufe0f", "man juggling: Medium Skin Tone", []string{"man_juggling_Medium_Skin_Tone"}, "12.0", false},
- {"\U0001f468\U0001f3fb\u200d\U0001f527", "man mechanic: Light Skin Tone", []string{"man_mechanic_Light_Skin_Tone"}, "12.0", false},
- {"\U0001f468\U0001f3fc\u200d\U0001f527", "man mechanic: Medium-Light Skin Tone", []string{"man_mechanic_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3fd\u200d\U0001f527", "man mechanic: Medium Skin Tone", []string{"man_mechanic_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3fe\u200d\U0001f527", "man mechanic: Medium-Dark Skin Tone", []string{"man_mechanic_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3ff\u200d\U0001f527", "man mechanic: Dark Skin Tone", []string{"man_mechanic_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f468\U0001f3fb\u200d\U0001f527", "man mechanic: Light Skin Tone", []string{"man_mechanic_Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f468\U0001f3fc\u200d\U0001f527", "man mechanic: Medium-Light Skin Tone", []string{"man_mechanic_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3fb\u200d\U0001f4bc", "man office worker: Light Skin Tone", []string{"man_office_worker_Light_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3fc\u200d\U0001f4bc", "man office worker: Medium-Light Skin Tone", []string{"man_office_worker_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3fd\u200d\U0001f4bc", "man office worker: Medium Skin Tone", []string{"man_office_worker_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3fe\u200d\U0001f4bc", "man office worker: Medium-Dark Skin Tone", []string{"man_office_worker_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3ff\u200d\U0001f4bc", "man office worker: Dark Skin Tone", []string{"man_office_worker_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f468\U0001f3fb\u200d\u2708\ufe0f", "man pilot: Light Skin Tone", []string{"man_pilot_Light_Skin_Tone"}, "12.0", false},
- {"\U0001f468\U0001f3fc\u200d\u2708\ufe0f", "man pilot: Medium-Light Skin Tone", []string{"man_pilot_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3fd\u200d\u2708\ufe0f", "man pilot: Medium Skin Tone", []string{"man_pilot_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3fe\u200d\u2708\ufe0f", "man pilot: Medium-Dark Skin Tone", []string{"man_pilot_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3ff\u200d\u2708\ufe0f", "man pilot: Dark Skin Tone", []string{"man_pilot_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f468\U0001f3fb\u200d\u2708\ufe0f", "man pilot: Light Skin Tone", []string{"man_pilot_Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f468\U0001f3fc\u200d\u2708\ufe0f", "man pilot: Medium-Light Skin Tone", []string{"man_pilot_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f93e\U0001f3fb\u200d\u2642\ufe0f", "man playing handball: Light Skin Tone", []string{"man_playing_handball_Light_Skin_Tone"}, "12.0", false},
{"\U0001f93e\U0001f3fc\u200d\u2642\ufe0f", "man playing handball: Medium-Light Skin Tone", []string{"man_playing_handball_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f93e\U0001f3fd\u200d\u2642\ufe0f", "man playing handball: Medium Skin Tone", []string{"man_playing_handball_Medium_Skin_Tone"}, "12.0", false},
@@ -2321,91 +2360,91 @@ var GemojiData = Gemoji{
{"\U0001f468\U0001f3fd\u200d\U0001f52c", "man scientist: Medium Skin Tone", []string{"man_scientist_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3fe\u200d\U0001f52c", "man scientist: Medium-Dark Skin Tone", []string{"man_scientist_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3ff\u200d\U0001f52c", "man scientist: Dark Skin Tone", []string{"man_scientist_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f937\U0001f3fe\u200d\u2642\ufe0f", "man shrugging: Medium-Dark Skin Tone", []string{"man_shrugging_Medium-Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f937\U0001f3ff\u200d\u2642\ufe0f", "man shrugging: Dark Skin Tone", []string{"man_shrugging_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f937\U0001f3fb\u200d\u2642\ufe0f", "man shrugging: Light Skin Tone", []string{"man_shrugging_Light_Skin_Tone"}, "12.0", false},
{"\U0001f937\U0001f3fc\u200d\u2642\ufe0f", "man shrugging: Medium-Light Skin Tone", []string{"man_shrugging_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f937\U0001f3fd\u200d\u2642\ufe0f", "man shrugging: Medium Skin Tone", []string{"man_shrugging_Medium_Skin_Tone"}, "12.0", false},
- {"\U0001f937\U0001f3fe\u200d\u2642\ufe0f", "man shrugging: Medium-Dark Skin Tone", []string{"man_shrugging_Medium-Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f937\U0001f3ff\u200d\u2642\ufe0f", "man shrugging: Dark Skin Tone", []string{"man_shrugging_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3fb\u200d\U0001f3a4", "man singer: Light Skin Tone", []string{"man_singer_Light_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3fc\u200d\U0001f3a4", "man singer: Medium-Light Skin Tone", []string{"man_singer_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3fd\u200d\U0001f3a4", "man singer: Medium Skin Tone", []string{"man_singer_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3fe\u200d\U0001f3a4", "man singer: Medium-Dark Skin Tone", []string{"man_singer_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3ff\u200d\U0001f3a4", "man singer: Dark Skin Tone", []string{"man_singer_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f468\U0001f3fe\u200d\U0001f393", "man student: Medium-Dark Skin Tone", []string{"man_student_Medium-Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f468\U0001f3ff\u200d\U0001f393", "man student: Dark Skin Tone", []string{"man_student_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3fb\u200d\U0001f393", "man student: Light Skin Tone", []string{"man_student_Light_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3fc\u200d\U0001f393", "man student: Medium-Light Skin Tone", []string{"man_student_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3fd\u200d\U0001f393", "man student: Medium Skin Tone", []string{"man_student_Medium_Skin_Tone"}, "12.0", false},
+ {"\U0001f468\U0001f3fe\u200d\U0001f393", "man student: Medium-Dark Skin Tone", []string{"man_student_Medium-Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f468\U0001f3ff\u200d\U0001f393", "man student: Dark Skin Tone", []string{"man_student_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f468\U0001f3ff\u200d\U0001f3eb", "man teacher: Dark Skin Tone", []string{"man_teacher_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f468\U0001f3fb\u200d\U0001f3eb", "man teacher: Light Skin Tone", []string{"man_teacher_Light_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3fc\u200d\U0001f3eb", "man teacher: Medium-Light Skin Tone", []string{"man_teacher_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3fd\u200d\U0001f3eb", "man teacher: Medium Skin Tone", []string{"man_teacher_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3fe\u200d\U0001f3eb", "man teacher: Medium-Dark Skin Tone", []string{"man_teacher_Medium-Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f468\U0001f3ff\u200d\U0001f3eb", "man teacher: Dark Skin Tone", []string{"man_teacher_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f468\U0001f3fb\u200d\U0001f3eb", "man teacher: Light Skin Tone", []string{"man_teacher_Light_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3fb\u200d\U0001f4bb", "man technologist: Light Skin Tone", []string{"man_technologist_Light_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3fc\u200d\U0001f4bb", "man technologist: Medium-Light Skin Tone", []string{"man_technologist_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3fd\u200d\U0001f4bb", "man technologist: Medium Skin Tone", []string{"man_technologist_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3fe\u200d\U0001f4bb", "man technologist: Medium-Dark Skin Tone", []string{"man_technologist_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3ff\u200d\U0001f4bb", "man technologist: Dark Skin Tone", []string{"man_technologist_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f472\U0001f3fe", "person with skullcap: Medium-Dark Skin Tone", []string{"man_with_gua_pi_mao_Medium-Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f472\U0001f3ff", "person with skullcap: Dark Skin Tone", []string{"man_with_gua_pi_mao_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f472\U0001f3fb", "person with skullcap: Light Skin Tone", []string{"man_with_gua_pi_mao_Light_Skin_Tone"}, "12.0", false},
{"\U0001f472\U0001f3fc", "person with skullcap: Medium-Light Skin Tone", []string{"man_with_gua_pi_mao_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f472\U0001f3fd", "person with skullcap: Medium Skin Tone", []string{"man_with_gua_pi_mao_Medium_Skin_Tone"}, "12.0", false},
- {"\U0001f472\U0001f3fe", "person with skullcap: Medium-Dark Skin Tone", []string{"man_with_gua_pi_mao_Medium-Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f472\U0001f3ff", "person with skullcap: Dark Skin Tone", []string{"man_with_gua_pi_mao_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f468\U0001f3fe\u200d\U0001f9af", "man with white cane: Medium-Dark Skin Tone", []string{"man_with_probing_cane_Medium-Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f468\U0001f3ff\u200d\U0001f9af", "man with white cane: Dark Skin Tone", []string{"man_with_probing_cane_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3fb\u200d\U0001f9af", "man with white cane: Light Skin Tone", []string{"man_with_probing_cane_Light_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3fc\u200d\U0001f9af", "man with white cane: Medium-Light Skin Tone", []string{"man_with_probing_cane_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3fd\u200d\U0001f9af", "man with white cane: Medium Skin Tone", []string{"man_with_probing_cane_Medium_Skin_Tone"}, "12.0", false},
+ {"\U0001f468\U0001f3fe\u200d\U0001f9af", "man with white cane: Medium-Dark Skin Tone", []string{"man_with_probing_cane_Medium-Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f468\U0001f3ff\u200d\U0001f9af", "man with white cane: Dark Skin Tone", []string{"man_with_probing_cane_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f473\U0001f3fc\u200d\u2642\ufe0f", "man wearing turban: Medium-Light Skin Tone", []string{"man_with_turban_Medium-Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f473\U0001f3fd\u200d\u2642\ufe0f", "man wearing turban: Medium Skin Tone", []string{"man_with_turban_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f473\U0001f3fe\u200d\u2642\ufe0f", "man wearing turban: Medium-Dark Skin Tone", []string{"man_with_turban_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f473\U0001f3ff\u200d\u2642\ufe0f", "man wearing turban: Dark Skin Tone", []string{"man_with_turban_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f473\U0001f3fb\u200d\u2642\ufe0f", "man wearing turban: Light Skin Tone", []string{"man_with_turban_Light_Skin_Tone"}, "12.0", false},
- {"\U0001f473\U0001f3fc\u200d\u2642\ufe0f", "man wearing turban: Medium-Light Skin Tone", []string{"man_with_turban_Medium-Light_Skin_Tone"}, "12.0", false},
- {"\U0001f473\U0001f3fd\u200d\u2642\ufe0f", "man wearing turban: Medium Skin Tone", []string{"man_with_turban_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f486\U0001f3fb", "person getting massage: Light Skin Tone", []string{"massage_Light_Skin_Tone"}, "12.0", false},
{"\U0001f486\U0001f3fc", "person getting massage: Medium-Light Skin Tone", []string{"massage_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f486\U0001f3fd", "person getting massage: Medium Skin Tone", []string{"massage_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f486\U0001f3fe", "person getting massage: Medium-Dark Skin Tone", []string{"massage_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f486\U0001f3ff", "person getting massage: Dark Skin Tone", []string{"massage_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f486\U0001f3fe\u200d\u2642\ufe0f", "man getting massage: Medium-Dark Skin Tone", []string{"massage_man_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f486\U0001f3ff\u200d\u2642\ufe0f", "man getting massage: Dark Skin Tone", []string{"massage_man_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f486\U0001f3fb\u200d\u2642\ufe0f", "man getting massage: Light Skin Tone", []string{"massage_man_Light_Skin_Tone"}, "12.0", false},
{"\U0001f486\U0001f3fc\u200d\u2642\ufe0f", "man getting massage: Medium-Light Skin Tone", []string{"massage_man_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f486\U0001f3fd\u200d\u2642\ufe0f", "man getting massage: Medium Skin Tone", []string{"massage_man_Medium_Skin_Tone"}, "12.0", false},
+ {"\U0001f486\U0001f3fe\u200d\u2642\ufe0f", "man getting massage: Medium-Dark Skin Tone", []string{"massage_man_Medium-Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f486\U0001f3fc\u200d\u2640\ufe0f", "woman getting massage: Medium-Light Skin Tone", []string{"massage_woman_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f486\U0001f3fd\u200d\u2640\ufe0f", "woman getting massage: Medium Skin Tone", []string{"massage_woman_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f486\U0001f3fe\u200d\u2640\ufe0f", "woman getting massage: Medium-Dark Skin Tone", []string{"massage_woman_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f486\U0001f3ff\u200d\u2640\ufe0f", "woman getting massage: Dark Skin Tone", []string{"massage_woman_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f486\U0001f3fb\u200d\u2640\ufe0f", "woman getting massage: Light Skin Tone", []string{"massage_woman_Light_Skin_Tone"}, "12.0", false},
- {"\U0001f486\U0001f3fc\u200d\u2640\ufe0f", "woman getting massage: Medium-Light Skin Tone", []string{"massage_woman_Medium-Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f9d1\U0001f3fe\u200d\U0001f527", "mechanic: Medium-Dark Skin Tone", []string{"mechanic_Medium-Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f9d1\U0001f3ff\u200d\U0001f527", "mechanic: Dark Skin Tone", []string{"mechanic_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3fb\u200d\U0001f527", "mechanic: Light Skin Tone", []string{"mechanic_Light_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3fc\u200d\U0001f527", "mechanic: Medium-Light Skin Tone", []string{"mechanic_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3fd\u200d\U0001f527", "mechanic: Medium Skin Tone", []string{"mechanic_Medium_Skin_Tone"}, "12.0", false},
- {"\U0001f9d1\U0001f3fe\u200d\U0001f527", "mechanic: Medium-Dark Skin Tone", []string{"mechanic_Medium-Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f9d1\U0001f3ff\u200d\U0001f527", "mechanic: Dark Skin Tone", []string{"mechanic_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f9dc\U0001f3ff\u200d\u2640\ufe0f", "mermaid: Dark Skin Tone", []string{"mermaid_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9dc\U0001f3fb\u200d\u2640\ufe0f", "mermaid: Light Skin Tone", []string{"mermaid_Light_Skin_Tone"}, "12.0", false},
{"\U0001f9dc\U0001f3fc\u200d\u2640\ufe0f", "mermaid: Medium-Light Skin Tone", []string{"mermaid_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f9dc\U0001f3fd\u200d\u2640\ufe0f", "mermaid: Medium Skin Tone", []string{"mermaid_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f9dc\U0001f3fe\u200d\u2640\ufe0f", "mermaid: Medium-Dark Skin Tone", []string{"mermaid_Medium-Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f9dc\U0001f3ff\u200d\u2640\ufe0f", "mermaid: Dark Skin Tone", []string{"mermaid_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f9dc\U0001f3fe\u200d\u2642\ufe0f", "merman: Medium-Dark Skin Tone", []string{"merman_Medium-Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f9dc\U0001f3ff\u200d\u2642\ufe0f", "merman: Dark Skin Tone", []string{"merman_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9dc\U0001f3fb\u200d\u2642\ufe0f", "merman: Light Skin Tone", []string{"merman_Light_Skin_Tone"}, "12.0", false},
{"\U0001f9dc\U0001f3fc\u200d\u2642\ufe0f", "merman: Medium-Light Skin Tone", []string{"merman_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f9dc\U0001f3fd\u200d\u2642\ufe0f", "merman: Medium Skin Tone", []string{"merman_Medium_Skin_Tone"}, "12.0", false},
- {"\U0001f9dc\U0001f3fe\u200d\u2642\ufe0f", "merman: Medium-Dark Skin Tone", []string{"merman_Medium-Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f9dc\U0001f3ff\u200d\u2642\ufe0f", "merman: Dark Skin Tone", []string{"merman_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f9dc\U0001f3fb", "merperson: Light Skin Tone", []string{"merperson_Light_Skin_Tone"}, "12.0", false},
{"\U0001f9dc\U0001f3fc", "merperson: Medium-Light Skin Tone", []string{"merperson_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f9dc\U0001f3fd", "merperson: Medium Skin Tone", []string{"merperson_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f9dc\U0001f3fe", "merperson: Medium-Dark Skin Tone", []string{"merperson_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9dc\U0001f3ff", "merperson: Dark Skin Tone", []string{"merperson_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f9dc\U0001f3fb", "merperson: Light Skin Tone", []string{"merperson_Light_Skin_Tone"}, "12.0", false},
{"\U0001f918\U0001f3fc", "sign of the horns: Medium-Light Skin Tone", []string{"metal_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f918\U0001f3fd", "sign of the horns: Medium Skin Tone", []string{"metal_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f918\U0001f3fe", "sign of the horns: Medium-Dark Skin Tone", []string{"metal_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f918\U0001f3ff", "sign of the horns: Dark Skin Tone", []string{"metal_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f918\U0001f3fb", "sign of the horns: Light Skin Tone", []string{"metal_Light_Skin_Tone"}, "12.0", false},
- {"\U0001f595\U0001f3fb", "middle finger: Light Skin Tone", []string{"middle_finger_Light_Skin_Tone"}, "12.0", false},
{"\U0001f595\U0001f3fc", "middle finger: Medium-Light Skin Tone", []string{"middle_finger_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f595\U0001f3fd", "middle finger: Medium Skin Tone", []string{"middle_finger_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f595\U0001f3fe", "middle finger: Medium-Dark Skin Tone", []string{"middle_finger_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f595\U0001f3ff", "middle finger: Dark Skin Tone", []string{"middle_finger_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f595\U0001f3fb", "middle finger: Light Skin Tone", []string{"middle_finger_Light_Skin_Tone"}, "12.0", false},
{"\U0001f6b5\U0001f3fe", "person mountain biking: Medium-Dark Skin Tone", []string{"mountain_bicyclist_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f6b5\U0001f3ff", "person mountain biking: Dark Skin Tone", []string{"mountain_bicyclist_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f6b5\U0001f3fb", "person mountain biking: Light Skin Tone", []string{"mountain_bicyclist_Light_Skin_Tone"}, "12.0", false},
@@ -2416,36 +2455,36 @@ var GemojiData = Gemoji{
{"\U0001f6b5\U0001f3fd\u200d\u2642\ufe0f", "man mountain biking: Medium Skin Tone", []string{"mountain_biking_man_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f6b5\U0001f3fe\u200d\u2642\ufe0f", "man mountain biking: Medium-Dark Skin Tone", []string{"mountain_biking_man_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f6b5\U0001f3ff\u200d\u2642\ufe0f", "man mountain biking: Dark Skin Tone", []string{"mountain_biking_man_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f6b5\U0001f3fe\u200d\u2640\ufe0f", "woman mountain biking: Medium-Dark Skin Tone", []string{"mountain_biking_woman_Medium-Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f6b5\U0001f3ff\u200d\u2640\ufe0f", "woman mountain biking: Dark Skin Tone", []string{"mountain_biking_woman_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f6b5\U0001f3fb\u200d\u2640\ufe0f", "woman mountain biking: Light Skin Tone", []string{"mountain_biking_woman_Light_Skin_Tone"}, "12.0", false},
{"\U0001f6b5\U0001f3fc\u200d\u2640\ufe0f", "woman mountain biking: Medium-Light Skin Tone", []string{"mountain_biking_woman_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f6b5\U0001f3fd\u200d\u2640\ufe0f", "woman mountain biking: Medium Skin Tone", []string{"mountain_biking_woman_Medium_Skin_Tone"}, "12.0", false},
- {"\U0001f6b5\U0001f3fe\u200d\u2640\ufe0f", "woman mountain biking: Medium-Dark Skin Tone", []string{"mountain_biking_woman_Medium-Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f6b5\U0001f3ff\u200d\u2640\ufe0f", "woman mountain biking: Dark Skin Tone", []string{"mountain_biking_woman_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f936\U0001f3fb", "Mrs. Claus: Light Skin Tone", []string{"mrs_claus_Light_Skin_Tone"}, "12.0", false},
{"\U0001f936\U0001f3fc", "Mrs. Claus: Medium-Light Skin Tone", []string{"mrs_claus_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f936\U0001f3fd", "Mrs. Claus: Medium Skin Tone", []string{"mrs_claus_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f936\U0001f3fe", "Mrs. Claus: Medium-Dark Skin Tone", []string{"mrs_claus_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f936\U0001f3ff", "Mrs. Claus: Dark Skin Tone", []string{"mrs_claus_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f4aa\U0001f3ff", "flexed biceps: Dark Skin Tone", []string{"muscle_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f4aa\U0001f3fb", "flexed biceps: Light Skin Tone", []string{"muscle_Light_Skin_Tone"}, "12.0", false},
{"\U0001f4aa\U0001f3fc", "flexed biceps: Medium-Light Skin Tone", []string{"muscle_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f4aa\U0001f3fd", "flexed biceps: Medium Skin Tone", []string{"muscle_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f4aa\U0001f3fe", "flexed biceps: Medium-Dark Skin Tone", []string{"muscle_Medium-Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f485\U0001f3ff", "nail polish: Dark Skin Tone", []string{"nail_care_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f4aa\U0001f3ff", "flexed biceps: Dark Skin Tone", []string{"muscle_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f485\U0001f3fb", "nail polish: Light Skin Tone", []string{"nail_care_Light_Skin_Tone"}, "12.0", false},
{"\U0001f485\U0001f3fc", "nail polish: Medium-Light Skin Tone", []string{"nail_care_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f485\U0001f3fd", "nail polish: Medium Skin Tone", []string{"nail_care_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f485\U0001f3fe", "nail polish: Medium-Dark Skin Tone", []string{"nail_care_Medium-Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f485\U0001f3ff", "nail polish: Dark Skin Tone", []string{"nail_care_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f645\U0001f3fb", "person gesturing NO: Light Skin Tone", []string{"no_good_Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f645\U0001f3fc", "person gesturing NO: Medium-Light Skin Tone", []string{"no_good_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f645\U0001f3fd", "person gesturing NO: Medium Skin Tone", []string{"no_good_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f645\U0001f3fe", "person gesturing NO: Medium-Dark Skin Tone", []string{"no_good_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f645\U0001f3ff", "person gesturing NO: Dark Skin Tone", []string{"no_good_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f645\U0001f3fb", "person gesturing NO: Light Skin Tone", []string{"no_good_Light_Skin_Tone"}, "12.0", false},
- {"\U0001f645\U0001f3fc", "person gesturing NO: Medium-Light Skin Tone", []string{"no_good_Medium-Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f645\U0001f3fc\u200d\u2642\ufe0f", "man gesturing NO: Medium-Light Skin Tone", []string{"no_good_man_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f645\U0001f3fd\u200d\u2642\ufe0f", "man gesturing NO: Medium Skin Tone", []string{"no_good_man_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f645\U0001f3fe\u200d\u2642\ufe0f", "man gesturing NO: Medium-Dark Skin Tone", []string{"no_good_man_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f645\U0001f3ff\u200d\u2642\ufe0f", "man gesturing NO: Dark Skin Tone", []string{"no_good_man_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f645\U0001f3fb\u200d\u2642\ufe0f", "man gesturing NO: Light Skin Tone", []string{"no_good_man_Light_Skin_Tone"}, "12.0", false},
- {"\U0001f645\U0001f3fc\u200d\u2642\ufe0f", "man gesturing NO: Medium-Light Skin Tone", []string{"no_good_man_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f645\U0001f3fb\u200d\u2640\ufe0f", "woman gesturing NO: Light Skin Tone", []string{"no_good_woman_Light_Skin_Tone"}, "12.0", false},
{"\U0001f645\U0001f3fc\u200d\u2640\ufe0f", "woman gesturing NO: Medium-Light Skin Tone", []string{"no_good_woman_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f645\U0001f3fd\u200d\u2640\ufe0f", "woman gesturing NO: Medium Skin Tone", []string{"no_good_woman_Medium_Skin_Tone"}, "12.0", false},
@@ -2456,31 +2495,31 @@ var GemojiData = Gemoji{
{"\U0001f443\U0001f3fd", "nose: Medium Skin Tone", []string{"nose_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f443\U0001f3fe", "nose: Medium-Dark Skin Tone", []string{"nose_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f443\U0001f3ff", "nose: Dark Skin Tone", []string{"nose_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f9d1\U0001f3fb\u200d\U0001f4bc", "office worker: Light Skin Tone", []string{"office_worker_Light_Skin_Tone"}, "12.0", false},
- {"\U0001f9d1\U0001f3fc\u200d\U0001f4bc", "office worker: Medium-Light Skin Tone", []string{"office_worker_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3fd\u200d\U0001f4bc", "office worker: Medium Skin Tone", []string{"office_worker_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3fe\u200d\U0001f4bc", "office worker: Medium-Dark Skin Tone", []string{"office_worker_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3ff\u200d\U0001f4bc", "office worker: Dark Skin Tone", []string{"office_worker_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f44c\U0001f3fc", "OK hand: Medium-Light Skin Tone", []string{"ok_hand_Medium-Light_Skin_Tone"}, "12.0", false},
- {"\U0001f44c\U0001f3fd", "OK hand: Medium Skin Tone", []string{"ok_hand_Medium_Skin_Tone"}, "12.0", false},
+ {"\U0001f9d1\U0001f3fb\u200d\U0001f4bc", "office worker: Light Skin Tone", []string{"office_worker_Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f9d1\U0001f3fc\u200d\U0001f4bc", "office worker: Medium-Light Skin Tone", []string{"office_worker_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f44c\U0001f3fe", "OK hand: Medium-Dark Skin Tone", []string{"ok_hand_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f44c\U0001f3ff", "OK hand: Dark Skin Tone", []string{"ok_hand_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f44c\U0001f3fb", "OK hand: Light Skin Tone", []string{"ok_hand_Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f44c\U0001f3fc", "OK hand: Medium-Light Skin Tone", []string{"ok_hand_Medium-Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f44c\U0001f3fd", "OK hand: Medium Skin Tone", []string{"ok_hand_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f646\U0001f3fb\u200d\u2642\ufe0f", "man gesturing OK: Light Skin Tone", []string{"ok_man_Light_Skin_Tone"}, "12.0", false},
{"\U0001f646\U0001f3fc\u200d\u2642\ufe0f", "man gesturing OK: Medium-Light Skin Tone", []string{"ok_man_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f646\U0001f3fd\u200d\u2642\ufe0f", "man gesturing OK: Medium Skin Tone", []string{"ok_man_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f646\U0001f3fe\u200d\u2642\ufe0f", "man gesturing OK: Medium-Dark Skin Tone", []string{"ok_man_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f646\U0001f3ff\u200d\u2642\ufe0f", "man gesturing OK: Dark Skin Tone", []string{"ok_man_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f646\U0001f3ff", "person gesturing OK: Dark Skin Tone", []string{"ok_person_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f646\U0001f3fb", "person gesturing OK: Light Skin Tone", []string{"ok_person_Light_Skin_Tone"}, "12.0", false},
{"\U0001f646\U0001f3fc", "person gesturing OK: Medium-Light Skin Tone", []string{"ok_person_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f646\U0001f3fd", "person gesturing OK: Medium Skin Tone", []string{"ok_person_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f646\U0001f3fe", "person gesturing OK: Medium-Dark Skin Tone", []string{"ok_person_Medium-Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f646\U0001f3ff", "person gesturing OK: Dark Skin Tone", []string{"ok_person_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f646\U0001f3fc\u200d\u2640\ufe0f", "woman gesturing OK: Medium-Light Skin Tone", []string{"ok_woman_Medium-Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f646\U0001f3fd\u200d\u2640\ufe0f", "woman gesturing OK: Medium Skin Tone", []string{"ok_woman_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f646\U0001f3fe\u200d\u2640\ufe0f", "woman gesturing OK: Medium-Dark Skin Tone", []string{"ok_woman_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f646\U0001f3ff\u200d\u2640\ufe0f", "woman gesturing OK: Dark Skin Tone", []string{"ok_woman_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f646\U0001f3fb\u200d\u2640\ufe0f", "woman gesturing OK: Light Skin Tone", []string{"ok_woman_Light_Skin_Tone"}, "12.0", false},
- {"\U0001f646\U0001f3fc\u200d\u2640\ufe0f", "woman gesturing OK: Medium-Light Skin Tone", []string{"ok_woman_Medium-Light_Skin_Tone"}, "12.0", false},
- {"\U0001f646\U0001f3fd\u200d\u2640\ufe0f", "woman gesturing OK: Medium Skin Tone", []string{"ok_woman_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f9d3\U0001f3fb", "older person: Light Skin Tone", []string{"older_adult_Light_Skin_Tone"}, "12.0", false},
{"\U0001f9d3\U0001f3fc", "older person: Medium-Light Skin Tone", []string{"older_adult_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f9d3\U0001f3fd", "older person: Medium Skin Tone", []string{"older_adult_Medium_Skin_Tone"}, "12.0", false},
@@ -2496,206 +2535,206 @@ var GemojiData = Gemoji{
{"\U0001f475\U0001f3fd", "old woman: Medium Skin Tone", []string{"older_woman_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f475\U0001f3fe", "old woman: Medium-Dark Skin Tone", []string{"older_woman_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f475\U0001f3ff", "old woman: Dark Skin Tone", []string{"older_woman_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f450\U0001f3fb", "open hands: Light Skin Tone", []string{"open_hands_Light_Skin_Tone"}, "12.0", false},
{"\U0001f450\U0001f3fc", "open hands: Medium-Light Skin Tone", []string{"open_hands_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f450\U0001f3fd", "open hands: Medium Skin Tone", []string{"open_hands_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f450\U0001f3fe", "open hands: Medium-Dark Skin Tone", []string{"open_hands_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f450\U0001f3ff", "open hands: Dark Skin Tone", []string{"open_hands_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f450\U0001f3fb", "open hands: Light Skin Tone", []string{"open_hands_Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f932\U0001f3fd", "palms up together: Medium Skin Tone", []string{"palms_up_together_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f932\U0001f3fe", "palms up together: Medium-Dark Skin Tone", []string{"palms_up_together_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f932\U0001f3ff", "palms up together: Dark Skin Tone", []string{"palms_up_together_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f932\U0001f3fb", "palms up together: Light Skin Tone", []string{"palms_up_together_Light_Skin_Tone"}, "12.0", false},
{"\U0001f932\U0001f3fc", "palms up together: Medium-Light Skin Tone", []string{"palms_up_together_Medium-Light_Skin_Tone"}, "12.0", false},
- {"\U0001f932\U0001f3fd", "palms up together: Medium Skin Tone", []string{"palms_up_together_Medium_Skin_Tone"}, "12.0", false},
+ {"\U0001f9d1\U0001f3fb\u200d\U0001f91d\u200d\U0001f9d1", "people holding hands: Light Skin Tone", []string{"people_holding_hands_Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f9d1\U0001f3fc\u200d\U0001f91d\u200d\U0001f9d1", "people holding hands: Medium-Light Skin Tone", []string{"people_holding_hands_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3fd\u200d\U0001f91d\u200d\U0001f9d1", "people holding hands: Medium Skin Tone", []string{"people_holding_hands_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3fe\u200d\U0001f91d\u200d\U0001f9d1", "people holding hands: Medium-Dark Skin Tone", []string{"people_holding_hands_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3ff\u200d\U0001f91d\u200d\U0001f9d1", "people holding hands: Dark Skin Tone", []string{"people_holding_hands_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f9d1\U0001f3fb\u200d\U0001f91d\u200d\U0001f9d1", "people holding hands: Light Skin Tone", []string{"people_holding_hands_Light_Skin_Tone"}, "12.0", false},
- {"\U0001f9d1\U0001f3fc\u200d\U0001f91d\u200d\U0001f9d1", "people holding hands: Medium-Light Skin Tone", []string{"people_holding_hands_Medium-Light_Skin_Tone"}, "12.0", false},
- {"\U0001f9d1\U0001f3fd\u200d\U0001f9b2", "person: bald: Medium Skin Tone", []string{"person_bald_Medium_Skin_Tone"}, "12.0", false},
- {"\U0001f9d1\U0001f3fe\u200d\U0001f9b2", "person: bald: Medium-Dark Skin Tone", []string{"person_bald_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3ff\u200d\U0001f9b2", "person: bald: Dark Skin Tone", []string{"person_bald_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3fb\u200d\U0001f9b2", "person: bald: Light Skin Tone", []string{"person_bald_Light_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3fc\u200d\U0001f9b2", "person: bald: Medium-Light Skin Tone", []string{"person_bald_Medium-Light_Skin_Tone"}, "12.0", false},
- {"\U0001f9d1\U0001f3fb\u200d\U0001f9b1", "person: curly hair: Light Skin Tone", []string{"person_curly_hair_Light_Skin_Tone"}, "12.0", false},
- {"\U0001f9d1\U0001f3fc\u200d\U0001f9b1", "person: curly hair: Medium-Light Skin Tone", []string{"person_curly_hair_Medium-Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f9d1\U0001f3fd\u200d\U0001f9b2", "person: bald: Medium Skin Tone", []string{"person_bald_Medium_Skin_Tone"}, "12.0", false},
+ {"\U0001f9d1\U0001f3fe\u200d\U0001f9b2", "person: bald: Medium-Dark Skin Tone", []string{"person_bald_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3fd\u200d\U0001f9b1", "person: curly hair: Medium Skin Tone", []string{"person_curly_hair_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3fe\u200d\U0001f9b1", "person: curly hair: Medium-Dark Skin Tone", []string{"person_curly_hair_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3ff\u200d\U0001f9b1", "person: curly hair: Dark Skin Tone", []string{"person_curly_hair_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f9d1\U0001f3fb\u200d\U0001f9b1", "person: curly hair: Light Skin Tone", []string{"person_curly_hair_Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f9d1\U0001f3fc\u200d\U0001f9b1", "person: curly hair: Medium-Light Skin Tone", []string{"person_curly_hair_Medium-Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f9d1\U0001f3fe\u200d\U0001f9bd", "person in manual wheelchair: Medium-Dark Skin Tone", []string{"person_in_manual_wheelchair_Medium-Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f9d1\U0001f3ff\u200d\U0001f9bd", "person in manual wheelchair: Dark Skin Tone", []string{"person_in_manual_wheelchair_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3fb\u200d\U0001f9bd", "person in manual wheelchair: Light Skin Tone", []string{"person_in_manual_wheelchair_Light_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3fc\u200d\U0001f9bd", "person in manual wheelchair: Medium-Light Skin Tone", []string{"person_in_manual_wheelchair_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3fd\u200d\U0001f9bd", "person in manual wheelchair: Medium Skin Tone", []string{"person_in_manual_wheelchair_Medium_Skin_Tone"}, "12.0", false},
- {"\U0001f9d1\U0001f3fe\u200d\U0001f9bd", "person in manual wheelchair: Medium-Dark Skin Tone", []string{"person_in_manual_wheelchair_Medium-Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f9d1\U0001f3ff\u200d\U0001f9bd", "person in manual wheelchair: Dark Skin Tone", []string{"person_in_manual_wheelchair_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f9d1\U0001f3fe\u200d\U0001f9bc", "person in motorized wheelchair: Medium-Dark Skin Tone", []string{"person_in_motorized_wheelchair_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3ff\u200d\U0001f9bc", "person in motorized wheelchair: Dark Skin Tone", []string{"person_in_motorized_wheelchair_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3fb\u200d\U0001f9bc", "person in motorized wheelchair: Light Skin Tone", []string{"person_in_motorized_wheelchair_Light_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3fc\u200d\U0001f9bc", "person in motorized wheelchair: Medium-Light Skin Tone", []string{"person_in_motorized_wheelchair_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3fd\u200d\U0001f9bc", "person in motorized wheelchair: Medium Skin Tone", []string{"person_in_motorized_wheelchair_Medium_Skin_Tone"}, "12.0", false},
- {"\U0001f9d1\U0001f3fe\u200d\U0001f9bc", "person in motorized wheelchair: Medium-Dark Skin Tone", []string{"person_in_motorized_wheelchair_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f935\U0001f3fb", "person in tuxedo: Light Skin Tone", []string{"person_in_tuxedo_Light_Skin_Tone"}, "12.0", false},
{"\U0001f935\U0001f3fc", "person in tuxedo: Medium-Light Skin Tone", []string{"person_in_tuxedo_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f935\U0001f3fd", "person in tuxedo: Medium Skin Tone", []string{"person_in_tuxedo_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f935\U0001f3fe", "person in tuxedo: Medium-Dark Skin Tone", []string{"person_in_tuxedo_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f935\U0001f3ff", "person in tuxedo: Dark Skin Tone", []string{"person_in_tuxedo_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f9d1\U0001f3ff\u200d\U0001f9b0", "person: red hair: Dark Skin Tone", []string{"person_red_hair_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f9d1\U0001f3fb\u200d\U0001f9b0", "person: red hair: Light Skin Tone", []string{"person_red_hair_Light_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3fc\u200d\U0001f9b0", "person: red hair: Medium-Light Skin Tone", []string{"person_red_hair_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3fd\u200d\U0001f9b0", "person: red hair: Medium Skin Tone", []string{"person_red_hair_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3fe\u200d\U0001f9b0", "person: red hair: Medium-Dark Skin Tone", []string{"person_red_hair_Medium-Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f9d1\U0001f3ff\u200d\U0001f9b0", "person: red hair: Dark Skin Tone", []string{"person_red_hair_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f9d1\U0001f3fb\u200d\U0001f9b0", "person: red hair: Light Skin Tone", []string{"person_red_hair_Light_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3fb\u200d\U0001f9b3", "person: white hair: Light Skin Tone", []string{"person_white_hair_Light_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3fc\u200d\U0001f9b3", "person: white hair: Medium-Light Skin Tone", []string{"person_white_hair_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3fd\u200d\U0001f9b3", "person: white hair: Medium Skin Tone", []string{"person_white_hair_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3fe\u200d\U0001f9b3", "person: white hair: Medium-Dark Skin Tone", []string{"person_white_hair_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3ff\u200d\U0001f9b3", "person: white hair: Dark Skin Tone", []string{"person_white_hair_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f9d1\U0001f3fb\u200d\U0001f9af", "person with white cane: Light Skin Tone", []string{"person_with_probing_cane_Light_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3fc\u200d\U0001f9af", "person with white cane: Medium-Light Skin Tone", []string{"person_with_probing_cane_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3fd\u200d\U0001f9af", "person with white cane: Medium Skin Tone", []string{"person_with_probing_cane_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3fe\u200d\U0001f9af", "person with white cane: Medium-Dark Skin Tone", []string{"person_with_probing_cane_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3ff\u200d\U0001f9af", "person with white cane: Dark Skin Tone", []string{"person_with_probing_cane_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f9d1\U0001f3fb\u200d\U0001f9af", "person with white cane: Light Skin Tone", []string{"person_with_probing_cane_Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f473\U0001f3fe", "person wearing turban: Medium-Dark Skin Tone", []string{"person_with_turban_Medium-Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f473\U0001f3ff", "person wearing turban: Dark Skin Tone", []string{"person_with_turban_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f473\U0001f3fb", "person wearing turban: Light Skin Tone", []string{"person_with_turban_Light_Skin_Tone"}, "12.0", false},
{"\U0001f473\U0001f3fc", "person wearing turban: Medium-Light Skin Tone", []string{"person_with_turban_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f473\U0001f3fd", "person wearing turban: Medium Skin Tone", []string{"person_with_turban_Medium_Skin_Tone"}, "12.0", false},
- {"\U0001f473\U0001f3fe", "person wearing turban: Medium-Dark Skin Tone", []string{"person_with_turban_Medium-Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f473\U0001f3ff", "person wearing turban: Dark Skin Tone", []string{"person_with_turban_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f470\U0001f3fc", "person with veil: Medium-Light Skin Tone", []string{"person_with_veil_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f470\U0001f3fd", "person with veil: Medium Skin Tone", []string{"person_with_veil_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f470\U0001f3fe", "person with veil: Medium-Dark Skin Tone", []string{"person_with_veil_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f470\U0001f3ff", "person with veil: Dark Skin Tone", []string{"person_with_veil_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f470\U0001f3fb", "person with veil: Light Skin Tone", []string{"person_with_veil_Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f470\U0001f3fc", "person with veil: Medium-Light Skin Tone", []string{"person_with_veil_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3fb\u200d\u2708\ufe0f", "pilot: Light Skin Tone", []string{"pilot_Light_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3fc\u200d\u2708\ufe0f", "pilot: Medium-Light Skin Tone", []string{"pilot_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3fd\u200d\u2708\ufe0f", "pilot: Medium Skin Tone", []string{"pilot_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3fe\u200d\u2708\ufe0f", "pilot: Medium-Dark Skin Tone", []string{"pilot_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3ff\u200d\u2708\ufe0f", "pilot: Dark Skin Tone", []string{"pilot_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f90f\U0001f3fd", "pinching hand: Medium Skin Tone", []string{"pinching_hand_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f90f\U0001f3fe", "pinching hand: Medium-Dark Skin Tone", []string{"pinching_hand_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f90f\U0001f3ff", "pinching hand: Dark Skin Tone", []string{"pinching_hand_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f90f\U0001f3fb", "pinching hand: Light Skin Tone", []string{"pinching_hand_Light_Skin_Tone"}, "12.0", false},
{"\U0001f90f\U0001f3fc", "pinching hand: Medium-Light Skin Tone", []string{"pinching_hand_Medium-Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f90f\U0001f3fd", "pinching hand: Medium Skin Tone", []string{"pinching_hand_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f447\U0001f3ff", "backhand index pointing down: Dark Skin Tone", []string{"point_down_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f447\U0001f3fb", "backhand index pointing down: Light Skin Tone", []string{"point_down_Light_Skin_Tone"}, "12.0", false},
{"\U0001f447\U0001f3fc", "backhand index pointing down: Medium-Light Skin Tone", []string{"point_down_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f447\U0001f3fd", "backhand index pointing down: Medium Skin Tone", []string{"point_down_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f447\U0001f3fe", "backhand index pointing down: Medium-Dark Skin Tone", []string{"point_down_Medium-Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f448\U0001f3fe", "backhand index pointing left: Medium-Dark Skin Tone", []string{"point_left_Medium-Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f448\U0001f3ff", "backhand index pointing left: Dark Skin Tone", []string{"point_left_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f448\U0001f3fb", "backhand index pointing left: Light Skin Tone", []string{"point_left_Light_Skin_Tone"}, "12.0", false},
{"\U0001f448\U0001f3fc", "backhand index pointing left: Medium-Light Skin Tone", []string{"point_left_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f448\U0001f3fd", "backhand index pointing left: Medium Skin Tone", []string{"point_left_Medium_Skin_Tone"}, "12.0", false},
- {"\U0001f448\U0001f3fe", "backhand index pointing left: Medium-Dark Skin Tone", []string{"point_left_Medium-Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f448\U0001f3ff", "backhand index pointing left: Dark Skin Tone", []string{"point_left_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f449\U0001f3fb", "backhand index pointing right: Light Skin Tone", []string{"point_right_Light_Skin_Tone"}, "12.0", false},
{"\U0001f449\U0001f3fc", "backhand index pointing right: Medium-Light Skin Tone", []string{"point_right_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f449\U0001f3fd", "backhand index pointing right: Medium Skin Tone", []string{"point_right_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f449\U0001f3fe", "backhand index pointing right: Medium-Dark Skin Tone", []string{"point_right_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f449\U0001f3ff", "backhand index pointing right: Dark Skin Tone", []string{"point_right_Dark_Skin_Tone"}, "12.0", false},
- {"\u261d\U0001f3fb\ufe0f", "index pointing up: Light Skin Tone", []string{"point_up_Light_Skin_Tone"}, "12.0", false},
{"\u261d\U0001f3fc\ufe0f", "index pointing up: Medium-Light Skin Tone", []string{"point_up_Medium-Light_Skin_Tone"}, "12.0", false},
{"\u261d\U0001f3fd\ufe0f", "index pointing up: Medium Skin Tone", []string{"point_up_Medium_Skin_Tone"}, "12.0", false},
{"\u261d\U0001f3fe\ufe0f", "index pointing up: Medium-Dark Skin Tone", []string{"point_up_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\u261d\U0001f3ff\ufe0f", "index pointing up: Dark Skin Tone", []string{"point_up_Dark_Skin_Tone"}, "12.0", false},
+ {"\u261d\U0001f3fb\ufe0f", "index pointing up: Light Skin Tone", []string{"point_up_Light_Skin_Tone"}, "12.0", false},
{"\U0001f446\U0001f3fb", "backhand index pointing up: Light Skin Tone", []string{"point_up_2_Light_Skin_Tone"}, "12.0", false},
{"\U0001f446\U0001f3fc", "backhand index pointing up: Medium-Light Skin Tone", []string{"point_up_2_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f446\U0001f3fd", "backhand index pointing up: Medium Skin Tone", []string{"point_up_2_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f446\U0001f3fe", "backhand index pointing up: Medium-Dark Skin Tone", []string{"point_up_2_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f446\U0001f3ff", "backhand index pointing up: Dark Skin Tone", []string{"point_up_2_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f46e\U0001f3ff", "police officer: Dark Skin Tone", []string{"police_officer_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f46e\U0001f3fb", "police officer: Light Skin Tone", []string{"police_officer_Light_Skin_Tone"}, "12.0", false},
{"\U0001f46e\U0001f3fc", "police officer: Medium-Light Skin Tone", []string{"police_officer_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f46e\U0001f3fd", "police officer: Medium Skin Tone", []string{"police_officer_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f46e\U0001f3fe", "police officer: Medium-Dark Skin Tone", []string{"police_officer_Medium-Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f46e\U0001f3ff", "police officer: Dark Skin Tone", []string{"police_officer_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f46e\U0001f3fb\u200d\u2642\ufe0f", "man police officer: Light Skin Tone", []string{"policeman_Light_Skin_Tone"}, "12.0", false},
- {"\U0001f46e\U0001f3fc\u200d\u2642\ufe0f", "man police officer: Medium-Light Skin Tone", []string{"policeman_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f46e\U0001f3fd\u200d\u2642\ufe0f", "man police officer: Medium Skin Tone", []string{"policeman_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f46e\U0001f3fe\u200d\u2642\ufe0f", "man police officer: Medium-Dark Skin Tone", []string{"policeman_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f46e\U0001f3ff\u200d\u2642\ufe0f", "man police officer: Dark Skin Tone", []string{"policeman_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f46e\U0001f3fb\u200d\u2640\ufe0f", "woman police officer: Light Skin Tone", []string{"policewoman_Light_Skin_Tone"}, "12.0", false},
- {"\U0001f46e\U0001f3fc\u200d\u2640\ufe0f", "woman police officer: Medium-Light Skin Tone", []string{"policewoman_Medium-Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f46e\U0001f3fb\u200d\u2642\ufe0f", "man police officer: Light Skin Tone", []string{"policeman_Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f46e\U0001f3fc\u200d\u2642\ufe0f", "man police officer: Medium-Light Skin Tone", []string{"policeman_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f46e\U0001f3fd\u200d\u2640\ufe0f", "woman police officer: Medium Skin Tone", []string{"policewoman_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f46e\U0001f3fe\u200d\u2640\ufe0f", "woman police officer: Medium-Dark Skin Tone", []string{"policewoman_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f46e\U0001f3ff\u200d\u2640\ufe0f", "woman police officer: Dark Skin Tone", []string{"policewoman_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f46e\U0001f3fb\u200d\u2640\ufe0f", "woman police officer: Light Skin Tone", []string{"policewoman_Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f46e\U0001f3fc\u200d\u2640\ufe0f", "woman police officer: Medium-Light Skin Tone", []string{"policewoman_Medium-Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f64e\U0001f3fb", "person pouting: Light Skin Tone", []string{"pouting_face_Light_Skin_Tone"}, "12.0", false},
{"\U0001f64e\U0001f3fc", "person pouting: Medium-Light Skin Tone", []string{"pouting_face_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f64e\U0001f3fd", "person pouting: Medium Skin Tone", []string{"pouting_face_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f64e\U0001f3fe", "person pouting: Medium-Dark Skin Tone", []string{"pouting_face_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f64e\U0001f3ff", "person pouting: Dark Skin Tone", []string{"pouting_face_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f64e\U0001f3fb", "person pouting: Light Skin Tone", []string{"pouting_face_Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f64e\U0001f3fe\u200d\u2642\ufe0f", "man pouting: Medium-Dark Skin Tone", []string{"pouting_man_Medium-Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f64e\U0001f3ff\u200d\u2642\ufe0f", "man pouting: Dark Skin Tone", []string{"pouting_man_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f64e\U0001f3fb\u200d\u2642\ufe0f", "man pouting: Light Skin Tone", []string{"pouting_man_Light_Skin_Tone"}, "12.0", false},
{"\U0001f64e\U0001f3fc\u200d\u2642\ufe0f", "man pouting: Medium-Light Skin Tone", []string{"pouting_man_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f64e\U0001f3fd\u200d\u2642\ufe0f", "man pouting: Medium Skin Tone", []string{"pouting_man_Medium_Skin_Tone"}, "12.0", false},
- {"\U0001f64e\U0001f3fe\u200d\u2642\ufe0f", "man pouting: Medium-Dark Skin Tone", []string{"pouting_man_Medium-Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f64e\U0001f3ff\u200d\u2642\ufe0f", "man pouting: Dark Skin Tone", []string{"pouting_man_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f64e\U0001f3fb\u200d\u2640\ufe0f", "woman pouting: Light Skin Tone", []string{"pouting_woman_Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f64e\U0001f3fc\u200d\u2640\ufe0f", "woman pouting: Medium-Light Skin Tone", []string{"pouting_woman_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f64e\U0001f3fd\u200d\u2640\ufe0f", "woman pouting: Medium Skin Tone", []string{"pouting_woman_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f64e\U0001f3fe\u200d\u2640\ufe0f", "woman pouting: Medium-Dark Skin Tone", []string{"pouting_woman_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f64e\U0001f3ff\u200d\u2640\ufe0f", "woman pouting: Dark Skin Tone", []string{"pouting_woman_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f64e\U0001f3fb\u200d\u2640\ufe0f", "woman pouting: Light Skin Tone", []string{"pouting_woman_Light_Skin_Tone"}, "12.0", false},
- {"\U0001f64e\U0001f3fc\u200d\u2640\ufe0f", "woman pouting: Medium-Light Skin Tone", []string{"pouting_woman_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f64f\U0001f3fb", "folded hands: Light Skin Tone", []string{"pray_Light_Skin_Tone"}, "12.0", false},
{"\U0001f64f\U0001f3fc", "folded hands: Medium-Light Skin Tone", []string{"pray_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f64f\U0001f3fd", "folded hands: Medium Skin Tone", []string{"pray_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f64f\U0001f3fe", "folded hands: Medium-Dark Skin Tone", []string{"pray_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f64f\U0001f3ff", "folded hands: Dark Skin Tone", []string{"pray_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f930\U0001f3fb", "pregnant woman: Light Skin Tone", []string{"pregnant_woman_Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f930\U0001f3fc", "pregnant woman: Medium-Light Skin Tone", []string{"pregnant_woman_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f930\U0001f3fd", "pregnant woman: Medium Skin Tone", []string{"pregnant_woman_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f930\U0001f3fe", "pregnant woman: Medium-Dark Skin Tone", []string{"pregnant_woman_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f930\U0001f3ff", "pregnant woman: Dark Skin Tone", []string{"pregnant_woman_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f930\U0001f3fb", "pregnant woman: Light Skin Tone", []string{"pregnant_woman_Light_Skin_Tone"}, "12.0", false},
- {"\U0001f930\U0001f3fc", "pregnant woman: Medium-Light Skin Tone", []string{"pregnant_woman_Medium-Light_Skin_Tone"}, "12.0", false},
- {"\U0001f934\U0001f3fe", "prince: Medium-Dark Skin Tone", []string{"prince_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f934\U0001f3ff", "prince: Dark Skin Tone", []string{"prince_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f934\U0001f3fb", "prince: Light Skin Tone", []string{"prince_Light_Skin_Tone"}, "12.0", false},
{"\U0001f934\U0001f3fc", "prince: Medium-Light Skin Tone", []string{"prince_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f934\U0001f3fd", "prince: Medium Skin Tone", []string{"prince_Medium_Skin_Tone"}, "12.0", false},
- {"\U0001f478\U0001f3fb", "princess: Light Skin Tone", []string{"princess_Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f934\U0001f3fe", "prince: Medium-Dark Skin Tone", []string{"prince_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f478\U0001f3fc", "princess: Medium-Light Skin Tone", []string{"princess_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f478\U0001f3fd", "princess: Medium Skin Tone", []string{"princess_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f478\U0001f3fe", "princess: Medium-Dark Skin Tone", []string{"princess_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f478\U0001f3ff", "princess: Dark Skin Tone", []string{"princess_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f478\U0001f3fb", "princess: Light Skin Tone", []string{"princess_Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f91a\U0001f3ff", "raised back of hand: Dark Skin Tone", []string{"raised_back_of_hand_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f91a\U0001f3fb", "raised back of hand: Light Skin Tone", []string{"raised_back_of_hand_Light_Skin_Tone"}, "12.0", false},
{"\U0001f91a\U0001f3fc", "raised back of hand: Medium-Light Skin Tone", []string{"raised_back_of_hand_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f91a\U0001f3fd", "raised back of hand: Medium Skin Tone", []string{"raised_back_of_hand_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f91a\U0001f3fe", "raised back of hand: Medium-Dark Skin Tone", []string{"raised_back_of_hand_Medium-Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f91a\U0001f3ff", "raised back of hand: Dark Skin Tone", []string{"raised_back_of_hand_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f590\U0001f3fd\ufe0f", "hand with fingers splayed: Medium Skin Tone", []string{"raised_hand_with_fingers_splayed_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f590\U0001f3fe\ufe0f", "hand with fingers splayed: Medium-Dark Skin Tone", []string{"raised_hand_with_fingers_splayed_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f590\U0001f3ff\ufe0f", "hand with fingers splayed: Dark Skin Tone", []string{"raised_hand_with_fingers_splayed_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f590\U0001f3fb\ufe0f", "hand with fingers splayed: Light Skin Tone", []string{"raised_hand_with_fingers_splayed_Light_Skin_Tone"}, "12.0", false},
{"\U0001f590\U0001f3fc\ufe0f", "hand with fingers splayed: Medium-Light Skin Tone", []string{"raised_hand_with_fingers_splayed_Medium-Light_Skin_Tone"}, "12.0", false},
- {"\U0001f590\U0001f3fd\ufe0f", "hand with fingers splayed: Medium Skin Tone", []string{"raised_hand_with_fingers_splayed_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f64c\U0001f3fb", "raising hands: Light Skin Tone", []string{"raised_hands_Light_Skin_Tone"}, "12.0", false},
{"\U0001f64c\U0001f3fc", "raising hands: Medium-Light Skin Tone", []string{"raised_hands_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f64c\U0001f3fd", "raising hands: Medium Skin Tone", []string{"raised_hands_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f64c\U0001f3fe", "raising hands: Medium-Dark Skin Tone", []string{"raised_hands_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f64c\U0001f3ff", "raising hands: Dark Skin Tone", []string{"raised_hands_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f64b\U0001f3ff", "person raising hand: Dark Skin Tone", []string{"raising_hand_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f64b\U0001f3fb", "person raising hand: Light Skin Tone", []string{"raising_hand_Light_Skin_Tone"}, "12.0", false},
{"\U0001f64b\U0001f3fc", "person raising hand: Medium-Light Skin Tone", []string{"raising_hand_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f64b\U0001f3fd", "person raising hand: Medium Skin Tone", []string{"raising_hand_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f64b\U0001f3fe", "person raising hand: Medium-Dark Skin Tone", []string{"raising_hand_Medium-Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f64b\U0001f3ff", "person raising hand: Dark Skin Tone", []string{"raising_hand_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f64b\U0001f3fb", "person raising hand: Light Skin Tone", []string{"raising_hand_Light_Skin_Tone"}, "12.0", false},
{"\U0001f64b\U0001f3ff\u200d\u2642\ufe0f", "man raising hand: Dark Skin Tone", []string{"raising_hand_man_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f64b\U0001f3fb\u200d\u2642\ufe0f", "man raising hand: Light Skin Tone", []string{"raising_hand_man_Light_Skin_Tone"}, "12.0", false},
{"\U0001f64b\U0001f3fc\u200d\u2642\ufe0f", "man raising hand: Medium-Light Skin Tone", []string{"raising_hand_man_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f64b\U0001f3fd\u200d\u2642\ufe0f", "man raising hand: Medium Skin Tone", []string{"raising_hand_man_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f64b\U0001f3fe\u200d\u2642\ufe0f", "man raising hand: Medium-Dark Skin Tone", []string{"raising_hand_man_Medium-Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f64b\U0001f3fe\u200d\u2640\ufe0f", "woman raising hand: Medium-Dark Skin Tone", []string{"raising_hand_woman_Medium-Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f64b\U0001f3ff\u200d\u2640\ufe0f", "woman raising hand: Dark Skin Tone", []string{"raising_hand_woman_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f64b\U0001f3fb\u200d\u2640\ufe0f", "woman raising hand: Light Skin Tone", []string{"raising_hand_woman_Light_Skin_Tone"}, "12.0", false},
{"\U0001f64b\U0001f3fc\u200d\u2640\ufe0f", "woman raising hand: Medium-Light Skin Tone", []string{"raising_hand_woman_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f64b\U0001f3fd\u200d\u2640\ufe0f", "woman raising hand: Medium Skin Tone", []string{"raising_hand_woman_Medium_Skin_Tone"}, "12.0", false},
- {"\U0001f468\U0001f3fc\u200d\U0001f9b0", "man: red hair: Medium-Light Skin Tone", []string{"red_haired_man_Medium-Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f64b\U0001f3fe\u200d\u2640\ufe0f", "woman raising hand: Medium-Dark Skin Tone", []string{"raising_hand_woman_Medium-Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f64b\U0001f3ff\u200d\u2640\ufe0f", "woman raising hand: Dark Skin Tone", []string{"raising_hand_woman_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3fd\u200d\U0001f9b0", "man: red hair: Medium Skin Tone", []string{"red_haired_man_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3fe\u200d\U0001f9b0", "man: red hair: Medium-Dark Skin Tone", []string{"red_haired_man_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3ff\u200d\U0001f9b0", "man: red hair: Dark Skin Tone", []string{"red_haired_man_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3fb\u200d\U0001f9b0", "man: red hair: Light Skin Tone", []string{"red_haired_man_Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f468\U0001f3fc\u200d\U0001f9b0", "man: red hair: Medium-Light Skin Tone", []string{"red_haired_man_Medium-Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f469\U0001f3fe\u200d\U0001f9b0", "woman: red hair: Medium-Dark Skin Tone", []string{"red_haired_woman_Medium-Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f469\U0001f3ff\u200d\U0001f9b0", "woman: red hair: Dark Skin Tone", []string{"red_haired_woman_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f469\U0001f3fb\u200d\U0001f9b0", "woman: red hair: Light Skin Tone", []string{"red_haired_woman_Light_Skin_Tone"}, "12.0", false},
{"\U0001f469\U0001f3fc\u200d\U0001f9b0", "woman: red hair: Medium-Light Skin Tone", []string{"red_haired_woman_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f469\U0001f3fd\u200d\U0001f9b0", "woman: red hair: Medium Skin Tone", []string{"red_haired_woman_Medium_Skin_Tone"}, "12.0", false},
- {"\U0001f469\U0001f3fe\u200d\U0001f9b0", "woman: red hair: Medium-Dark Skin Tone", []string{"red_haired_woman_Medium-Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f469\U0001f3ff\u200d\U0001f9b0", "woman: red hair: Dark Skin Tone", []string{"red_haired_woman_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f6a3\U0001f3fb", "person rowing boat: Light Skin Tone", []string{"rowboat_Light_Skin_Tone"}, "12.0", false},
{"\U0001f6a3\U0001f3fc", "person rowing boat: Medium-Light Skin Tone", []string{"rowboat_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f6a3\U0001f3fd", "person rowing boat: Medium Skin Tone", []string{"rowboat_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f6a3\U0001f3fe", "person rowing boat: Medium-Dark Skin Tone", []string{"rowboat_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f6a3\U0001f3ff", "person rowing boat: Dark Skin Tone", []string{"rowboat_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f6a3\U0001f3ff\u200d\u2642\ufe0f", "man rowing boat: Dark Skin Tone", []string{"rowing_man_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f6a3\U0001f3fb\u200d\u2642\ufe0f", "man rowing boat: Light Skin Tone", []string{"rowing_man_Light_Skin_Tone"}, "12.0", false},
{"\U0001f6a3\U0001f3fc\u200d\u2642\ufe0f", "man rowing boat: Medium-Light Skin Tone", []string{"rowing_man_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f6a3\U0001f3fd\u200d\u2642\ufe0f", "man rowing boat: Medium Skin Tone", []string{"rowing_man_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f6a3\U0001f3fe\u200d\u2642\ufe0f", "man rowing boat: Medium-Dark Skin Tone", []string{"rowing_man_Medium-Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f6a3\U0001f3ff\u200d\u2642\ufe0f", "man rowing boat: Dark Skin Tone", []string{"rowing_man_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f6a3\U0001f3fb\u200d\u2640\ufe0f", "woman rowing boat: Light Skin Tone", []string{"rowing_woman_Light_Skin_Tone"}, "12.0", false},
{"\U0001f6a3\U0001f3fc\u200d\u2640\ufe0f", "woman rowing boat: Medium-Light Skin Tone", []string{"rowing_woman_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f6a3\U0001f3fd\u200d\u2640\ufe0f", "woman rowing boat: Medium Skin Tone", []string{"rowing_woman_Medium_Skin_Tone"}, "12.0", false},
@@ -2711,41 +2750,41 @@ var GemojiData = Gemoji{
{"\U0001f3c3\U0001f3fd\u200d\u2642\ufe0f", "man running: Medium Skin Tone", []string{"running_man_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f3c3\U0001f3fe\u200d\u2642\ufe0f", "man running: Medium-Dark Skin Tone", []string{"running_man_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f3c3\U0001f3ff\u200d\u2642\ufe0f", "man running: Dark Skin Tone", []string{"running_man_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f3c3\U0001f3ff\u200d\u2640\ufe0f", "woman running: Dark Skin Tone", []string{"running_woman_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f3c3\U0001f3fb\u200d\u2640\ufe0f", "woman running: Light Skin Tone", []string{"running_woman_Light_Skin_Tone"}, "12.0", false},
{"\U0001f3c3\U0001f3fc\u200d\u2640\ufe0f", "woman running: Medium-Light Skin Tone", []string{"running_woman_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f3c3\U0001f3fd\u200d\u2640\ufe0f", "woman running: Medium Skin Tone", []string{"running_woman_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f3c3\U0001f3fe\u200d\u2640\ufe0f", "woman running: Medium-Dark Skin Tone", []string{"running_woman_Medium-Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f3c3\U0001f3ff\u200d\u2640\ufe0f", "woman running: Dark Skin Tone", []string{"running_woman_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f385\U0001f3fb", "Santa Claus: Light Skin Tone", []string{"santa_Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f385\U0001f3fc", "Santa Claus: Medium-Light Skin Tone", []string{"santa_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f385\U0001f3fd", "Santa Claus: Medium Skin Tone", []string{"santa_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f385\U0001f3fe", "Santa Claus: Medium-Dark Skin Tone", []string{"santa_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f385\U0001f3ff", "Santa Claus: Dark Skin Tone", []string{"santa_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f385\U0001f3fb", "Santa Claus: Light Skin Tone", []string{"santa_Light_Skin_Tone"}, "12.0", false},
- {"\U0001f385\U0001f3fc", "Santa Claus: Medium-Light Skin Tone", []string{"santa_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f9d6\U0001f3fb\u200d\u2642\ufe0f", "man in steamy room: Light Skin Tone", []string{"sauna_man_Light_Skin_Tone"}, "12.0", false},
{"\U0001f9d6\U0001f3fc\u200d\u2642\ufe0f", "man in steamy room: Medium-Light Skin Tone", []string{"sauna_man_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f9d6\U0001f3fd\u200d\u2642\ufe0f", "man in steamy room: Medium Skin Tone", []string{"sauna_man_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f9d6\U0001f3fe\u200d\u2642\ufe0f", "man in steamy room: Medium-Dark Skin Tone", []string{"sauna_man_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9d6\U0001f3ff\u200d\u2642\ufe0f", "man in steamy room: Dark Skin Tone", []string{"sauna_man_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f9d6\U0001f3ff", "person in steamy room: Dark Skin Tone", []string{"sauna_person_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9d6\U0001f3fb", "person in steamy room: Light Skin Tone", []string{"sauna_person_Light_Skin_Tone"}, "12.0", false},
{"\U0001f9d6\U0001f3fc", "person in steamy room: Medium-Light Skin Tone", []string{"sauna_person_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f9d6\U0001f3fd", "person in steamy room: Medium Skin Tone", []string{"sauna_person_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f9d6\U0001f3fe", "person in steamy room: Medium-Dark Skin Tone", []string{"sauna_person_Medium-Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f9d6\U0001f3ff", "person in steamy room: Dark Skin Tone", []string{"sauna_person_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f9d6\U0001f3fb\u200d\u2640\ufe0f", "woman in steamy room: Light Skin Tone", []string{"sauna_woman_Light_Skin_Tone"}, "12.0", false},
{"\U0001f9d6\U0001f3fc\u200d\u2640\ufe0f", "woman in steamy room: Medium-Light Skin Tone", []string{"sauna_woman_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f9d6\U0001f3fd\u200d\u2640\ufe0f", "woman in steamy room: Medium Skin Tone", []string{"sauna_woman_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f9d6\U0001f3fe\u200d\u2640\ufe0f", "woman in steamy room: Medium-Dark Skin Tone", []string{"sauna_woman_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9d6\U0001f3ff\u200d\u2640\ufe0f", "woman in steamy room: Dark Skin Tone", []string{"sauna_woman_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f9d6\U0001f3fb\u200d\u2640\ufe0f", "woman in steamy room: Light Skin Tone", []string{"sauna_woman_Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f9d1\U0001f3fb\u200d\U0001f52c", "scientist: Light Skin Tone", []string{"scientist_Light_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3fc\u200d\U0001f52c", "scientist: Medium-Light Skin Tone", []string{"scientist_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3fd\u200d\U0001f52c", "scientist: Medium Skin Tone", []string{"scientist_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3fe\u200d\U0001f52c", "scientist: Medium-Dark Skin Tone", []string{"scientist_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3ff\u200d\U0001f52c", "scientist: Dark Skin Tone", []string{"scientist_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f9d1\U0001f3fb\u200d\U0001f52c", "scientist: Light Skin Tone", []string{"scientist_Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f933\U0001f3fb", "selfie: Light Skin Tone", []string{"selfie_Light_Skin_Tone"}, "12.0", false},
{"\U0001f933\U0001f3fc", "selfie: Medium-Light Skin Tone", []string{"selfie_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f933\U0001f3fd", "selfie: Medium Skin Tone", []string{"selfie_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f933\U0001f3fe", "selfie: Medium-Dark Skin Tone", []string{"selfie_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f933\U0001f3ff", "selfie: Dark Skin Tone", []string{"selfie_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f933\U0001f3fb", "selfie: Light Skin Tone", []string{"selfie_Light_Skin_Tone"}, "12.0", false},
{"\U0001f937\U0001f3fe", "person shrugging: Medium-Dark Skin Tone", []string{"shrug_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f937\U0001f3ff", "person shrugging: Dark Skin Tone", []string{"shrug_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f937\U0001f3fb", "person shrugging: Light Skin Tone", []string{"shrug_Light_Skin_Tone"}, "12.0", false},
@@ -2756,36 +2795,36 @@ var GemojiData = Gemoji{
{"\U0001f9d1\U0001f3fd\u200d\U0001f3a4", "singer: Medium Skin Tone", []string{"singer_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3fe\u200d\U0001f3a4", "singer: Medium-Dark Skin Tone", []string{"singer_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3ff\u200d\U0001f3a4", "singer: Dark Skin Tone", []string{"singer_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f6cc\U0001f3fc", "person in bed: Medium-Light Skin Tone", []string{"sleeping_bed_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f6cc\U0001f3fd", "person in bed: Medium Skin Tone", []string{"sleeping_bed_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f6cc\U0001f3fe", "person in bed: Medium-Dark Skin Tone", []string{"sleeping_bed_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f6cc\U0001f3ff", "person in bed: Dark Skin Tone", []string{"sleeping_bed_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f6cc\U0001f3fb", "person in bed: Light Skin Tone", []string{"sleeping_bed_Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f6cc\U0001f3fc", "person in bed: Medium-Light Skin Tone", []string{"sleeping_bed_Medium-Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f3c2\U0001f3fe", "snowboarder: Medium-Dark Skin Tone", []string{"snowboarder_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f3c2\U0001f3ff", "snowboarder: Dark Skin Tone", []string{"snowboarder_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f3c2\U0001f3fb", "snowboarder: Light Skin Tone", []string{"snowboarder_Light_Skin_Tone"}, "12.0", false},
{"\U0001f3c2\U0001f3fc", "snowboarder: Medium-Light Skin Tone", []string{"snowboarder_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f3c2\U0001f3fd", "snowboarder: Medium Skin Tone", []string{"snowboarder_Medium_Skin_Tone"}, "12.0", false},
- {"\U0001f3c2\U0001f3fe", "snowboarder: Medium-Dark Skin Tone", []string{"snowboarder_Medium-Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f9cd\U0001f3ff\u200d\u2642\ufe0f", "man standing: Dark Skin Tone", []string{"standing_man_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9cd\U0001f3fb\u200d\u2642\ufe0f", "man standing: Light Skin Tone", []string{"standing_man_Light_Skin_Tone"}, "12.0", false},
{"\U0001f9cd\U0001f3fc\u200d\u2642\ufe0f", "man standing: Medium-Light Skin Tone", []string{"standing_man_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f9cd\U0001f3fd\u200d\u2642\ufe0f", "man standing: Medium Skin Tone", []string{"standing_man_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f9cd\U0001f3fe\u200d\u2642\ufe0f", "man standing: Medium-Dark Skin Tone", []string{"standing_man_Medium-Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f9cd\U0001f3ff\u200d\u2642\ufe0f", "man standing: Dark Skin Tone", []string{"standing_man_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f9cd\U0001f3fb", "person standing: Light Skin Tone", []string{"standing_person_Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f9cd\U0001f3fc", "person standing: Medium-Light Skin Tone", []string{"standing_person_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f9cd\U0001f3fd", "person standing: Medium Skin Tone", []string{"standing_person_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f9cd\U0001f3fe", "person standing: Medium-Dark Skin Tone", []string{"standing_person_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9cd\U0001f3ff", "person standing: Dark Skin Tone", []string{"standing_person_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f9cd\U0001f3fb", "person standing: Light Skin Tone", []string{"standing_person_Light_Skin_Tone"}, "12.0", false},
- {"\U0001f9cd\U0001f3fc", "person standing: Medium-Light Skin Tone", []string{"standing_person_Medium-Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f9cd\U0001f3ff\u200d\u2640\ufe0f", "woman standing: Dark Skin Tone", []string{"standing_woman_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9cd\U0001f3fb\u200d\u2640\ufe0f", "woman standing: Light Skin Tone", []string{"standing_woman_Light_Skin_Tone"}, "12.0", false},
{"\U0001f9cd\U0001f3fc\u200d\u2640\ufe0f", "woman standing: Medium-Light Skin Tone", []string{"standing_woman_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f9cd\U0001f3fd\u200d\u2640\ufe0f", "woman standing: Medium Skin Tone", []string{"standing_woman_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f9cd\U0001f3fe\u200d\u2640\ufe0f", "woman standing: Medium-Dark Skin Tone", []string{"standing_woman_Medium-Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f9cd\U0001f3ff\u200d\u2640\ufe0f", "woman standing: Dark Skin Tone", []string{"standing_woman_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f9d1\U0001f3fb\u200d\U0001f393", "student: Light Skin Tone", []string{"student_Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f9d1\U0001f3fc\u200d\U0001f393", "student: Medium-Light Skin Tone", []string{"student_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3fd\u200d\U0001f393", "student: Medium Skin Tone", []string{"student_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3fe\u200d\U0001f393", "student: Medium-Dark Skin Tone", []string{"student_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3ff\u200d\U0001f393", "student: Dark Skin Tone", []string{"student_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f9d1\U0001f3fb\u200d\U0001f393", "student: Light Skin Tone", []string{"student_Light_Skin_Tone"}, "12.0", false},
- {"\U0001f9d1\U0001f3fc\u200d\U0001f393", "student: Medium-Light Skin Tone", []string{"student_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f9b8\U0001f3fb", "superhero: Light Skin Tone", []string{"superhero_Light_Skin_Tone"}, "12.0", false},
{"\U0001f9b8\U0001f3fc", "superhero: Medium-Light Skin Tone", []string{"superhero_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f9b8\U0001f3fd", "superhero: Medium Skin Tone", []string{"superhero_Medium_Skin_Tone"}, "12.0", false},
@@ -2796,81 +2835,81 @@ var GemojiData = Gemoji{
{"\U0001f9b8\U0001f3fd\u200d\u2642\ufe0f", "man superhero: Medium Skin Tone", []string{"superhero_man_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f9b8\U0001f3fe\u200d\u2642\ufe0f", "man superhero: Medium-Dark Skin Tone", []string{"superhero_man_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9b8\U0001f3ff\u200d\u2642\ufe0f", "man superhero: Dark Skin Tone", []string{"superhero_man_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f9b8\U0001f3ff\u200d\u2640\ufe0f", "woman superhero: Dark Skin Tone", []string{"superhero_woman_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9b8\U0001f3fb\u200d\u2640\ufe0f", "woman superhero: Light Skin Tone", []string{"superhero_woman_Light_Skin_Tone"}, "12.0", false},
{"\U0001f9b8\U0001f3fc\u200d\u2640\ufe0f", "woman superhero: Medium-Light Skin Tone", []string{"superhero_woman_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f9b8\U0001f3fd\u200d\u2640\ufe0f", "woman superhero: Medium Skin Tone", []string{"superhero_woman_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f9b8\U0001f3fe\u200d\u2640\ufe0f", "woman superhero: Medium-Dark Skin Tone", []string{"superhero_woman_Medium-Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f9b8\U0001f3ff\u200d\u2640\ufe0f", "woman superhero: Dark Skin Tone", []string{"superhero_woman_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9b9\U0001f3fe", "supervillain: Medium-Dark Skin Tone", []string{"supervillain_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9b9\U0001f3ff", "supervillain: Dark Skin Tone", []string{"supervillain_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9b9\U0001f3fb", "supervillain: Light Skin Tone", []string{"supervillain_Light_Skin_Tone"}, "12.0", false},
{"\U0001f9b9\U0001f3fc", "supervillain: Medium-Light Skin Tone", []string{"supervillain_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f9b9\U0001f3fd", "supervillain: Medium Skin Tone", []string{"supervillain_Medium_Skin_Tone"}, "12.0", false},
- {"\U0001f9b9\U0001f3ff\u200d\u2642\ufe0f", "man supervillain: Dark Skin Tone", []string{"supervillain_man_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9b9\U0001f3fb\u200d\u2642\ufe0f", "man supervillain: Light Skin Tone", []string{"supervillain_man_Light_Skin_Tone"}, "12.0", false},
{"\U0001f9b9\U0001f3fc\u200d\u2642\ufe0f", "man supervillain: Medium-Light Skin Tone", []string{"supervillain_man_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f9b9\U0001f3fd\u200d\u2642\ufe0f", "man supervillain: Medium Skin Tone", []string{"supervillain_man_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f9b9\U0001f3fe\u200d\u2642\ufe0f", "man supervillain: Medium-Dark Skin Tone", []string{"supervillain_man_Medium-Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f9b9\U0001f3ff\u200d\u2642\ufe0f", "man supervillain: Dark Skin Tone", []string{"supervillain_man_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f9b9\U0001f3fb\u200d\u2640\ufe0f", "woman supervillain: Light Skin Tone", []string{"supervillain_woman_Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f9b9\U0001f3fc\u200d\u2640\ufe0f", "woman supervillain: Medium-Light Skin Tone", []string{"supervillain_woman_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f9b9\U0001f3fd\u200d\u2640\ufe0f", "woman supervillain: Medium Skin Tone", []string{"supervillain_woman_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f9b9\U0001f3fe\u200d\u2640\ufe0f", "woman supervillain: Medium-Dark Skin Tone", []string{"supervillain_woman_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9b9\U0001f3ff\u200d\u2640\ufe0f", "woman supervillain: Dark Skin Tone", []string{"supervillain_woman_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f9b9\U0001f3fb\u200d\u2640\ufe0f", "woman supervillain: Light Skin Tone", []string{"supervillain_woman_Light_Skin_Tone"}, "12.0", false},
- {"\U0001f9b9\U0001f3fc\u200d\u2640\ufe0f", "woman supervillain: Medium-Light Skin Tone", []string{"supervillain_woman_Medium-Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f3c4\U0001f3fb", "person surfing: Light Skin Tone", []string{"surfer_Light_Skin_Tone"}, "12.0", false},
{"\U0001f3c4\U0001f3fc", "person surfing: Medium-Light Skin Tone", []string{"surfer_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f3c4\U0001f3fd", "person surfing: Medium Skin Tone", []string{"surfer_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f3c4\U0001f3fe", "person surfing: Medium-Dark Skin Tone", []string{"surfer_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f3c4\U0001f3ff", "person surfing: Dark Skin Tone", []string{"surfer_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f3c4\U0001f3fb", "person surfing: Light Skin Tone", []string{"surfer_Light_Skin_Tone"}, "12.0", false},
- {"\U0001f3c4\U0001f3fb\u200d\u2642\ufe0f", "man surfing: Light Skin Tone", []string{"surfing_man_Light_Skin_Tone"}, "12.0", false},
{"\U0001f3c4\U0001f3fc\u200d\u2642\ufe0f", "man surfing: Medium-Light Skin Tone", []string{"surfing_man_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f3c4\U0001f3fd\u200d\u2642\ufe0f", "man surfing: Medium Skin Tone", []string{"surfing_man_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f3c4\U0001f3fe\u200d\u2642\ufe0f", "man surfing: Medium-Dark Skin Tone", []string{"surfing_man_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f3c4\U0001f3ff\u200d\u2642\ufe0f", "man surfing: Dark Skin Tone", []string{"surfing_man_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f3c4\U0001f3fb\u200d\u2642\ufe0f", "man surfing: Light Skin Tone", []string{"surfing_man_Light_Skin_Tone"}, "12.0", false},
{"\U0001f3c4\U0001f3fb\u200d\u2640\ufe0f", "woman surfing: Light Skin Tone", []string{"surfing_woman_Light_Skin_Tone"}, "12.0", false},
{"\U0001f3c4\U0001f3fc\u200d\u2640\ufe0f", "woman surfing: Medium-Light Skin Tone", []string{"surfing_woman_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f3c4\U0001f3fd\u200d\u2640\ufe0f", "woman surfing: Medium Skin Tone", []string{"surfing_woman_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f3c4\U0001f3fe\u200d\u2640\ufe0f", "woman surfing: Medium-Dark Skin Tone", []string{"surfing_woman_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f3c4\U0001f3ff\u200d\u2640\ufe0f", "woman surfing: Dark Skin Tone", []string{"surfing_woman_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f3ca\U0001f3ff", "person swimming: Dark Skin Tone", []string{"swimmer_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f3ca\U0001f3fb", "person swimming: Light Skin Tone", []string{"swimmer_Light_Skin_Tone"}, "12.0", false},
{"\U0001f3ca\U0001f3fc", "person swimming: Medium-Light Skin Tone", []string{"swimmer_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f3ca\U0001f3fd", "person swimming: Medium Skin Tone", []string{"swimmer_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f3ca\U0001f3fe", "person swimming: Medium-Dark Skin Tone", []string{"swimmer_Medium-Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f3ca\U0001f3ff", "person swimming: Dark Skin Tone", []string{"swimmer_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f3ca\U0001f3fb\u200d\u2642\ufe0f", "man swimming: Light Skin Tone", []string{"swimming_man_Light_Skin_Tone"}, "12.0", false},
{"\U0001f3ca\U0001f3fc\u200d\u2642\ufe0f", "man swimming: Medium-Light Skin Tone", []string{"swimming_man_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f3ca\U0001f3fd\u200d\u2642\ufe0f", "man swimming: Medium Skin Tone", []string{"swimming_man_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f3ca\U0001f3fe\u200d\u2642\ufe0f", "man swimming: Medium-Dark Skin Tone", []string{"swimming_man_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f3ca\U0001f3ff\u200d\u2642\ufe0f", "man swimming: Dark Skin Tone", []string{"swimming_man_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f3ca\U0001f3fc\u200d\u2640\ufe0f", "woman swimming: Medium-Light Skin Tone", []string{"swimming_woman_Medium-Light_Skin_Tone"}, "12.0", false},
- {"\U0001f3ca\U0001f3fd\u200d\u2640\ufe0f", "woman swimming: Medium Skin Tone", []string{"swimming_woman_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f3ca\U0001f3fe\u200d\u2640\ufe0f", "woman swimming: Medium-Dark Skin Tone", []string{"swimming_woman_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f3ca\U0001f3ff\u200d\u2640\ufe0f", "woman swimming: Dark Skin Tone", []string{"swimming_woman_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f3ca\U0001f3fb\u200d\u2640\ufe0f", "woman swimming: Light Skin Tone", []string{"swimming_woman_Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f3ca\U0001f3fc\u200d\u2640\ufe0f", "woman swimming: Medium-Light Skin Tone", []string{"swimming_woman_Medium-Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f3ca\U0001f3fd\u200d\u2640\ufe0f", "woman swimming: Medium Skin Tone", []string{"swimming_woman_Medium_Skin_Tone"}, "12.0", false},
+ {"\U0001f9d1\U0001f3fb\u200d\U0001f3eb", "teacher: Light Skin Tone", []string{"teacher_Light_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3fc\u200d\U0001f3eb", "teacher: Medium-Light Skin Tone", []string{"teacher_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3fd\u200d\U0001f3eb", "teacher: Medium Skin Tone", []string{"teacher_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3fe\u200d\U0001f3eb", "teacher: Medium-Dark Skin Tone", []string{"teacher_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3ff\u200d\U0001f3eb", "teacher: Dark Skin Tone", []string{"teacher_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f9d1\U0001f3fb\u200d\U0001f3eb", "teacher: Light Skin Tone", []string{"teacher_Light_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3fb\u200d\U0001f4bb", "technologist: Light Skin Tone", []string{"technologist_Light_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3fc\u200d\U0001f4bb", "technologist: Medium-Light Skin Tone", []string{"technologist_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3fd\u200d\U0001f4bb", "technologist: Medium Skin Tone", []string{"technologist_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3fe\u200d\U0001f4bb", "technologist: Medium-Dark Skin Tone", []string{"technologist_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9d1\U0001f3ff\u200d\U0001f4bb", "technologist: Dark Skin Tone", []string{"technologist_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f481\U0001f3ff\u200d\u2642\ufe0f", "man tipping hand: Dark Skin Tone", []string{"tipping_hand_man_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f481\U0001f3fb\u200d\u2642\ufe0f", "man tipping hand: Light Skin Tone", []string{"tipping_hand_man_Light_Skin_Tone"}, "12.0", false},
{"\U0001f481\U0001f3fc\u200d\u2642\ufe0f", "man tipping hand: Medium-Light Skin Tone", []string{"tipping_hand_man_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f481\U0001f3fd\u200d\u2642\ufe0f", "man tipping hand: Medium Skin Tone", []string{"tipping_hand_man_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f481\U0001f3fe\u200d\u2642\ufe0f", "man tipping hand: Medium-Dark Skin Tone", []string{"tipping_hand_man_Medium-Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f481\U0001f3ff\u200d\u2642\ufe0f", "man tipping hand: Dark Skin Tone", []string{"tipping_hand_man_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f481\U0001f3fb", "person tipping hand: Light Skin Tone", []string{"tipping_hand_person_Light_Skin_Tone"}, "12.0", false},
{"\U0001f481\U0001f3fc", "person tipping hand: Medium-Light Skin Tone", []string{"tipping_hand_person_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f481\U0001f3fd", "person tipping hand: Medium Skin Tone", []string{"tipping_hand_person_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f481\U0001f3fe", "person tipping hand: Medium-Dark Skin Tone", []string{"tipping_hand_person_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f481\U0001f3ff", "person tipping hand: Dark Skin Tone", []string{"tipping_hand_person_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f481\U0001f3fb\u200d\u2640\ufe0f", "woman tipping hand: Light Skin Tone", []string{"tipping_hand_woman_Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f481\U0001f3fc\u200d\u2640\ufe0f", "woman tipping hand: Medium-Light Skin Tone", []string{"tipping_hand_woman_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f481\U0001f3fd\u200d\u2640\ufe0f", "woman tipping hand: Medium Skin Tone", []string{"tipping_hand_woman_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f481\U0001f3fe\u200d\u2640\ufe0f", "woman tipping hand: Medium-Dark Skin Tone", []string{"tipping_hand_woman_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f481\U0001f3ff\u200d\u2640\ufe0f", "woman tipping hand: Dark Skin Tone", []string{"tipping_hand_woman_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f481\U0001f3fb\u200d\u2640\ufe0f", "woman tipping hand: Light Skin Tone", []string{"tipping_hand_woman_Light_Skin_Tone"}, "12.0", false},
- {"\U0001f481\U0001f3fc\u200d\u2640\ufe0f", "woman tipping hand: Medium-Light Skin Tone", []string{"tipping_hand_woman_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f46c\U0001f3fb", "men holding hands: Light Skin Tone", []string{"two_men_holding_hands_Light_Skin_Tone"}, "12.0", false},
{"\U0001f46c\U0001f3fc", "men holding hands: Medium-Light Skin Tone", []string{"two_men_holding_hands_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f46c\U0001f3fd", "men holding hands: Medium Skin Tone", []string{"two_men_holding_hands_Medium_Skin_Tone"}, "12.0", false},
@@ -2886,21 +2925,21 @@ var GemojiData = Gemoji{
{"\u270c\U0001f3fd\ufe0f", "victory hand: Medium Skin Tone", []string{"v_Medium_Skin_Tone"}, "12.0", false},
{"\u270c\U0001f3fe\ufe0f", "victory hand: Medium-Dark Skin Tone", []string{"v_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\u270c\U0001f3ff\ufe0f", "victory hand: Dark Skin Tone", []string{"v_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f9db\U0001f3fb", "vampire: Light Skin Tone", []string{"vampire_Light_Skin_Tone"}, "12.0", false},
- {"\U0001f9db\U0001f3fc", "vampire: Medium-Light Skin Tone", []string{"vampire_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f9db\U0001f3fd", "vampire: Medium Skin Tone", []string{"vampire_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f9db\U0001f3fe", "vampire: Medium-Dark Skin Tone", []string{"vampire_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9db\U0001f3ff", "vampire: Dark Skin Tone", []string{"vampire_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f9db\U0001f3fb", "vampire: Light Skin Tone", []string{"vampire_Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f9db\U0001f3fc", "vampire: Medium-Light Skin Tone", []string{"vampire_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f9db\U0001f3fb\u200d\u2642\ufe0f", "man vampire: Light Skin Tone", []string{"vampire_man_Light_Skin_Tone"}, "12.0", false},
{"\U0001f9db\U0001f3fc\u200d\u2642\ufe0f", "man vampire: Medium-Light Skin Tone", []string{"vampire_man_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f9db\U0001f3fd\u200d\u2642\ufe0f", "man vampire: Medium Skin Tone", []string{"vampire_man_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f9db\U0001f3fe\u200d\u2642\ufe0f", "man vampire: Medium-Dark Skin Tone", []string{"vampire_man_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9db\U0001f3ff\u200d\u2642\ufe0f", "man vampire: Dark Skin Tone", []string{"vampire_man_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f9db\U0001f3fb\u200d\u2640\ufe0f", "woman vampire: Light Skin Tone", []string{"vampire_woman_Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f9db\U0001f3fc\u200d\u2640\ufe0f", "woman vampire: Medium-Light Skin Tone", []string{"vampire_woman_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f9db\U0001f3fd\u200d\u2640\ufe0f", "woman vampire: Medium Skin Tone", []string{"vampire_woman_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f9db\U0001f3fe\u200d\u2640\ufe0f", "woman vampire: Medium-Dark Skin Tone", []string{"vampire_woman_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9db\U0001f3ff\u200d\u2640\ufe0f", "woman vampire: Dark Skin Tone", []string{"vampire_woman_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f9db\U0001f3fb\u200d\u2640\ufe0f", "woman vampire: Light Skin Tone", []string{"vampire_woman_Light_Skin_Tone"}, "12.0", false},
- {"\U0001f9db\U0001f3fc\u200d\u2640\ufe0f", "woman vampire: Medium-Light Skin Tone", []string{"vampire_woman_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f596\U0001f3fb", "vulcan salute: Light Skin Tone", []string{"vulcan_salute_Light_Skin_Tone"}, "12.0", false},
{"\U0001f596\U0001f3fc", "vulcan salute: Medium-Light Skin Tone", []string{"vulcan_salute_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f596\U0001f3fd", "vulcan salute: Medium Skin Tone", []string{"vulcan_salute_Medium_Skin_Tone"}, "12.0", false},
@@ -2911,26 +2950,26 @@ var GemojiData = Gemoji{
{"\U0001f6b6\U0001f3fd", "person walking: Medium Skin Tone", []string{"walking_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f6b6\U0001f3fe", "person walking: Medium-Dark Skin Tone", []string{"walking_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f6b6\U0001f3ff", "person walking: Dark Skin Tone", []string{"walking_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f6b6\U0001f3fb\u200d\u2642\ufe0f", "man walking: Light Skin Tone", []string{"walking_man_Light_Skin_Tone"}, "12.0", false},
{"\U0001f6b6\U0001f3fc\u200d\u2642\ufe0f", "man walking: Medium-Light Skin Tone", []string{"walking_man_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f6b6\U0001f3fd\u200d\u2642\ufe0f", "man walking: Medium Skin Tone", []string{"walking_man_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f6b6\U0001f3fe\u200d\u2642\ufe0f", "man walking: Medium-Dark Skin Tone", []string{"walking_man_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f6b6\U0001f3ff\u200d\u2642\ufe0f", "man walking: Dark Skin Tone", []string{"walking_man_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f6b6\U0001f3fb\u200d\u2642\ufe0f", "man walking: Light Skin Tone", []string{"walking_man_Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f6b6\U0001f3fe\u200d\u2640\ufe0f", "woman walking: Medium-Dark Skin Tone", []string{"walking_woman_Medium-Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f6b6\U0001f3ff\u200d\u2640\ufe0f", "woman walking: Dark Skin Tone", []string{"walking_woman_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f6b6\U0001f3fb\u200d\u2640\ufe0f", "woman walking: Light Skin Tone", []string{"walking_woman_Light_Skin_Tone"}, "12.0", false},
{"\U0001f6b6\U0001f3fc\u200d\u2640\ufe0f", "woman walking: Medium-Light Skin Tone", []string{"walking_woman_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f6b6\U0001f3fd\u200d\u2640\ufe0f", "woman walking: Medium Skin Tone", []string{"walking_woman_Medium_Skin_Tone"}, "12.0", false},
- {"\U0001f6b6\U0001f3fe\u200d\u2640\ufe0f", "woman walking: Medium-Dark Skin Tone", []string{"walking_woman_Medium-Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f6b6\U0001f3ff\u200d\u2640\ufe0f", "woman walking: Dark Skin Tone", []string{"walking_woman_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f93d\U0001f3fb", "person playing water polo: Light Skin Tone", []string{"water_polo_Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f93d\U0001f3fc", "person playing water polo: Medium-Light Skin Tone", []string{"water_polo_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f93d\U0001f3fd", "person playing water polo: Medium Skin Tone", []string{"water_polo_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f93d\U0001f3fe", "person playing water polo: Medium-Dark Skin Tone", []string{"water_polo_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f93d\U0001f3ff", "person playing water polo: Dark Skin Tone", []string{"water_polo_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f93d\U0001f3fb", "person playing water polo: Light Skin Tone", []string{"water_polo_Light_Skin_Tone"}, "12.0", false},
- {"\U0001f93d\U0001f3fc", "person playing water polo: Medium-Light Skin Tone", []string{"water_polo_Medium-Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f44b\U0001f3ff", "waving hand: Dark Skin Tone", []string{"wave_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f44b\U0001f3fb", "waving hand: Light Skin Tone", []string{"wave_Light_Skin_Tone"}, "12.0", false},
{"\U0001f44b\U0001f3fc", "waving hand: Medium-Light Skin Tone", []string{"wave_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f44b\U0001f3fd", "waving hand: Medium Skin Tone", []string{"wave_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f44b\U0001f3fe", "waving hand: Medium-Dark Skin Tone", []string{"wave_Medium-Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f44b\U0001f3ff", "waving hand: Dark Skin Tone", []string{"wave_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f44b\U0001f3fb", "waving hand: Light Skin Tone", []string{"wave_Light_Skin_Tone"}, "12.0", false},
{"\U0001f3cb\U0001f3fb\ufe0f", "person lifting weights: Light Skin Tone", []string{"weight_lifting_Light_Skin_Tone"}, "12.0", false},
{"\U0001f3cb\U0001f3fc\ufe0f", "person lifting weights: Medium-Light Skin Tone", []string{"weight_lifting_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f3cb\U0001f3fd\ufe0f", "person lifting weights: Medium Skin Tone", []string{"weight_lifting_Medium_Skin_Tone"}, "12.0", false},
@@ -2941,21 +2980,21 @@ var GemojiData = Gemoji{
{"\U0001f3cb\U0001f3fd\ufe0f\u200d\u2642\ufe0f", "man lifting weights: Medium Skin Tone", []string{"weight_lifting_man_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f3cb\U0001f3fe\ufe0f\u200d\u2642\ufe0f", "man lifting weights: Medium-Dark Skin Tone", []string{"weight_lifting_man_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f3cb\U0001f3ff\ufe0f\u200d\u2642\ufe0f", "man lifting weights: Dark Skin Tone", []string{"weight_lifting_man_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f3cb\U0001f3ff\ufe0f\u200d\u2640\ufe0f", "woman lifting weights: Dark Skin Tone", []string{"weight_lifting_woman_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f3cb\U0001f3fb\ufe0f\u200d\u2640\ufe0f", "woman lifting weights: Light Skin Tone", []string{"weight_lifting_woman_Light_Skin_Tone"}, "12.0", false},
{"\U0001f3cb\U0001f3fc\ufe0f\u200d\u2640\ufe0f", "woman lifting weights: Medium-Light Skin Tone", []string{"weight_lifting_woman_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f3cb\U0001f3fd\ufe0f\u200d\u2640\ufe0f", "woman lifting weights: Medium Skin Tone", []string{"weight_lifting_woman_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f3cb\U0001f3fe\ufe0f\u200d\u2640\ufe0f", "woman lifting weights: Medium-Dark Skin Tone", []string{"weight_lifting_woman_Medium-Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f468\U0001f3fb\u200d\U0001f9b3", "man: white hair: Light Skin Tone", []string{"white_haired_man_Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f3cb\U0001f3ff\ufe0f\u200d\u2640\ufe0f", "woman lifting weights: Dark Skin Tone", []string{"weight_lifting_woman_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3fc\u200d\U0001f9b3", "man: white hair: Medium-Light Skin Tone", []string{"white_haired_man_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3fd\u200d\U0001f9b3", "man: white hair: Medium Skin Tone", []string{"white_haired_man_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3fe\u200d\U0001f9b3", "man: white hair: Medium-Dark Skin Tone", []string{"white_haired_man_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f468\U0001f3ff\u200d\U0001f9b3", "man: white hair: Dark Skin Tone", []string{"white_haired_man_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f469\U0001f3fe\u200d\U0001f9b3", "woman: white hair: Medium-Dark Skin Tone", []string{"white_haired_woman_Medium-Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f468\U0001f3fb\u200d\U0001f9b3", "man: white hair: Light Skin Tone", []string{"white_haired_man_Light_Skin_Tone"}, "12.0", false},
{"\U0001f469\U0001f3ff\u200d\U0001f9b3", "woman: white hair: Dark Skin Tone", []string{"white_haired_woman_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f469\U0001f3fb\u200d\U0001f9b3", "woman: white hair: Light Skin Tone", []string{"white_haired_woman_Light_Skin_Tone"}, "12.0", false},
{"\U0001f469\U0001f3fc\u200d\U0001f9b3", "woman: white hair: Medium-Light Skin Tone", []string{"white_haired_woman_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f469\U0001f3fd\u200d\U0001f9b3", "woman: white hair: Medium Skin Tone", []string{"white_haired_woman_Medium_Skin_Tone"}, "12.0", false},
+ {"\U0001f469\U0001f3fe\u200d\U0001f9b3", "woman: white hair: Medium-Dark Skin Tone", []string{"white_haired_woman_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f469\U0001f3fb", "woman: Light Skin Tone", []string{"woman_Light_Skin_Tone"}, "12.0", false},
{"\U0001f469\U0001f3fc", "woman: Medium-Light Skin Tone", []string{"woman_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f469\U0001f3fd", "woman: Medium Skin Tone", []string{"woman_Medium_Skin_Tone"}, "12.0", false},
@@ -2966,26 +3005,26 @@ var GemojiData = Gemoji{
{"\U0001f469\U0001f3fd\u200d\U0001f3a8", "woman artist: Medium Skin Tone", []string{"woman_artist_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f469\U0001f3fe\u200d\U0001f3a8", "woman artist: Medium-Dark Skin Tone", []string{"woman_artist_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f469\U0001f3ff\u200d\U0001f3a8", "woman artist: Dark Skin Tone", []string{"woman_artist_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f469\U0001f3ff\u200d\U0001f680", "woman astronaut: Dark Skin Tone", []string{"woman_astronaut_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f469\U0001f3fb\u200d\U0001f680", "woman astronaut: Light Skin Tone", []string{"woman_astronaut_Light_Skin_Tone"}, "12.0", false},
{"\U0001f469\U0001f3fc\u200d\U0001f680", "woman astronaut: Medium-Light Skin Tone", []string{"woman_astronaut_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f469\U0001f3fd\u200d\U0001f680", "woman astronaut: Medium Skin Tone", []string{"woman_astronaut_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f469\U0001f3fe\u200d\U0001f680", "woman astronaut: Medium-Dark Skin Tone", []string{"woman_astronaut_Medium-Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f469\U0001f3ff\u200d\U0001f680", "woman astronaut: Dark Skin Tone", []string{"woman_astronaut_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f938\U0001f3fb\u200d\u2640\ufe0f", "woman cartwheeling: Light Skin Tone", []string{"woman_cartwheeling_Light_Skin_Tone"}, "12.0", false},
{"\U0001f938\U0001f3fc\u200d\u2640\ufe0f", "woman cartwheeling: Medium-Light Skin Tone", []string{"woman_cartwheeling_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f938\U0001f3fd\u200d\u2640\ufe0f", "woman cartwheeling: Medium Skin Tone", []string{"woman_cartwheeling_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f938\U0001f3fe\u200d\u2640\ufe0f", "woman cartwheeling: Medium-Dark Skin Tone", []string{"woman_cartwheeling_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f938\U0001f3ff\u200d\u2640\ufe0f", "woman cartwheeling: Dark Skin Tone", []string{"woman_cartwheeling_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f469\U0001f3fb\u200d\U0001f373", "woman cook: Light Skin Tone", []string{"woman_cook_Light_Skin_Tone"}, "12.0", false},
{"\U0001f469\U0001f3fc\u200d\U0001f373", "woman cook: Medium-Light Skin Tone", []string{"woman_cook_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f469\U0001f3fd\u200d\U0001f373", "woman cook: Medium Skin Tone", []string{"woman_cook_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f469\U0001f3fe\u200d\U0001f373", "woman cook: Medium-Dark Skin Tone", []string{"woman_cook_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f469\U0001f3ff\u200d\U0001f373", "woman cook: Dark Skin Tone", []string{"woman_cook_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f469\U0001f3fb\u200d\U0001f373", "woman cook: Light Skin Tone", []string{"woman_cook_Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f483\U0001f3fb", "woman dancing: Light Skin Tone", []string{"woman_dancing_Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f483\U0001f3fc", "woman dancing: Medium-Light Skin Tone", []string{"woman_dancing_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f483\U0001f3fd", "woman dancing: Medium Skin Tone", []string{"woman_dancing_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f483\U0001f3fe", "woman dancing: Medium-Dark Skin Tone", []string{"woman_dancing_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f483\U0001f3ff", "woman dancing: Dark Skin Tone", []string{"woman_dancing_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f483\U0001f3fb", "woman dancing: Light Skin Tone", []string{"woman_dancing_Light_Skin_Tone"}, "12.0", false},
- {"\U0001f483\U0001f3fc", "woman dancing: Medium-Light Skin Tone", []string{"woman_dancing_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f926\U0001f3fb\u200d\u2640\ufe0f", "woman facepalming: Light Skin Tone", []string{"woman_facepalming_Light_Skin_Tone"}, "12.0", false},
{"\U0001f926\U0001f3fc\u200d\u2640\ufe0f", "woman facepalming: Medium-Light Skin Tone", []string{"woman_facepalming_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f926\U0001f3fd\u200d\u2640\ufe0f", "woman facepalming: Medium Skin Tone", []string{"woman_facepalming_Medium_Skin_Tone"}, "12.0", false},
@@ -3011,21 +3050,21 @@ var GemojiData = Gemoji{
{"\U0001f469\U0001f3fd\u200d\u2695\ufe0f", "woman health worker: Medium Skin Tone", []string{"woman_health_worker_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f469\U0001f3fe\u200d\u2695\ufe0f", "woman health worker: Medium-Dark Skin Tone", []string{"woman_health_worker_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f469\U0001f3ff\u200d\u2695\ufe0f", "woman health worker: Dark Skin Tone", []string{"woman_health_worker_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f469\U0001f3fe\u200d\U0001f9bd", "woman in manual wheelchair: Medium-Dark Skin Tone", []string{"woman_in_manual_wheelchair_Medium-Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f469\U0001f3ff\u200d\U0001f9bd", "woman in manual wheelchair: Dark Skin Tone", []string{"woman_in_manual_wheelchair_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f469\U0001f3fb\u200d\U0001f9bd", "woman in manual wheelchair: Light Skin Tone", []string{"woman_in_manual_wheelchair_Light_Skin_Tone"}, "12.0", false},
{"\U0001f469\U0001f3fc\u200d\U0001f9bd", "woman in manual wheelchair: Medium-Light Skin Tone", []string{"woman_in_manual_wheelchair_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f469\U0001f3fd\u200d\U0001f9bd", "woman in manual wheelchair: Medium Skin Tone", []string{"woman_in_manual_wheelchair_Medium_Skin_Tone"}, "12.0", false},
- {"\U0001f469\U0001f3fe\u200d\U0001f9bd", "woman in manual wheelchair: Medium-Dark Skin Tone", []string{"woman_in_manual_wheelchair_Medium-Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f469\U0001f3ff\u200d\U0001f9bd", "woman in manual wheelchair: Dark Skin Tone", []string{"woman_in_manual_wheelchair_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f469\U0001f3fe\u200d\U0001f9bc", "woman in motorized wheelchair: Medium-Dark Skin Tone", []string{"woman_in_motorized_wheelchair_Medium-Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f469\U0001f3ff\u200d\U0001f9bc", "woman in motorized wheelchair: Dark Skin Tone", []string{"woman_in_motorized_wheelchair_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f469\U0001f3fb\u200d\U0001f9bc", "woman in motorized wheelchair: Light Skin Tone", []string{"woman_in_motorized_wheelchair_Light_Skin_Tone"}, "12.0", false},
{"\U0001f469\U0001f3fc\u200d\U0001f9bc", "woman in motorized wheelchair: Medium-Light Skin Tone", []string{"woman_in_motorized_wheelchair_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f469\U0001f3fd\u200d\U0001f9bc", "woman in motorized wheelchair: Medium Skin Tone", []string{"woman_in_motorized_wheelchair_Medium_Skin_Tone"}, "12.0", false},
- {"\U0001f469\U0001f3fb\u200d\u2696\ufe0f", "woman judge: Light Skin Tone", []string{"woman_judge_Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f469\U0001f3fe\u200d\U0001f9bc", "woman in motorized wheelchair: Medium-Dark Skin Tone", []string{"woman_in_motorized_wheelchair_Medium-Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f469\U0001f3ff\u200d\U0001f9bc", "woman in motorized wheelchair: Dark Skin Tone", []string{"woman_in_motorized_wheelchair_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f469\U0001f3fc\u200d\u2696\ufe0f", "woman judge: Medium-Light Skin Tone", []string{"woman_judge_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f469\U0001f3fd\u200d\u2696\ufe0f", "woman judge: Medium Skin Tone", []string{"woman_judge_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f469\U0001f3fe\u200d\u2696\ufe0f", "woman judge: Medium-Dark Skin Tone", []string{"woman_judge_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f469\U0001f3ff\u200d\u2696\ufe0f", "woman judge: Dark Skin Tone", []string{"woman_judge_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f469\U0001f3fb\u200d\u2696\ufe0f", "woman judge: Light Skin Tone", []string{"woman_judge_Light_Skin_Tone"}, "12.0", false},
{"\U0001f939\U0001f3fb\u200d\u2640\ufe0f", "woman juggling: Light Skin Tone", []string{"woman_juggling_Light_Skin_Tone"}, "12.0", false},
{"\U0001f939\U0001f3fc\u200d\u2640\ufe0f", "woman juggling: Medium-Light Skin Tone", []string{"woman_juggling_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f939\U0001f3fd\u200d\u2640\ufe0f", "woman juggling: Medium Skin Tone", []string{"woman_juggling_Medium_Skin_Tone"}, "12.0", false},
@@ -3041,66 +3080,66 @@ var GemojiData = Gemoji{
{"\U0001f469\U0001f3fd\u200d\U0001f4bc", "woman office worker: Medium Skin Tone", []string{"woman_office_worker_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f469\U0001f3fe\u200d\U0001f4bc", "woman office worker: Medium-Dark Skin Tone", []string{"woman_office_worker_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f469\U0001f3ff\u200d\U0001f4bc", "woman office worker: Dark Skin Tone", []string{"woman_office_worker_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f469\U0001f3fd\u200d\u2708\ufe0f", "woman pilot: Medium Skin Tone", []string{"woman_pilot_Medium_Skin_Tone"}, "12.0", false},
+ {"\U0001f469\U0001f3fe\u200d\u2708\ufe0f", "woman pilot: Medium-Dark Skin Tone", []string{"woman_pilot_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f469\U0001f3ff\u200d\u2708\ufe0f", "woman pilot: Dark Skin Tone", []string{"woman_pilot_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f469\U0001f3fb\u200d\u2708\ufe0f", "woman pilot: Light Skin Tone", []string{"woman_pilot_Light_Skin_Tone"}, "12.0", false},
{"\U0001f469\U0001f3fc\u200d\u2708\ufe0f", "woman pilot: Medium-Light Skin Tone", []string{"woman_pilot_Medium-Light_Skin_Tone"}, "12.0", false},
- {"\U0001f469\U0001f3fd\u200d\u2708\ufe0f", "woman pilot: Medium Skin Tone", []string{"woman_pilot_Medium_Skin_Tone"}, "12.0", false},
- {"\U0001f469\U0001f3fe\u200d\u2708\ufe0f", "woman pilot: Medium-Dark Skin Tone", []string{"woman_pilot_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f93e\U0001f3fb\u200d\u2640\ufe0f", "woman playing handball: Light Skin Tone", []string{"woman_playing_handball_Light_Skin_Tone"}, "12.0", false},
{"\U0001f93e\U0001f3fc\u200d\u2640\ufe0f", "woman playing handball: Medium-Light Skin Tone", []string{"woman_playing_handball_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f93e\U0001f3fd\u200d\u2640\ufe0f", "woman playing handball: Medium Skin Tone", []string{"woman_playing_handball_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f93e\U0001f3fe\u200d\u2640\ufe0f", "woman playing handball: Medium-Dark Skin Tone", []string{"woman_playing_handball_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f93e\U0001f3ff\u200d\u2640\ufe0f", "woman playing handball: Dark Skin Tone", []string{"woman_playing_handball_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f93d\U0001f3fb\u200d\u2640\ufe0f", "woman playing water polo: Light Skin Tone", []string{"woman_playing_water_polo_Light_Skin_Tone"}, "12.0", false},
{"\U0001f93d\U0001f3fc\u200d\u2640\ufe0f", "woman playing water polo: Medium-Light Skin Tone", []string{"woman_playing_water_polo_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f93d\U0001f3fd\u200d\u2640\ufe0f", "woman playing water polo: Medium Skin Tone", []string{"woman_playing_water_polo_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f93d\U0001f3fe\u200d\u2640\ufe0f", "woman playing water polo: Medium-Dark Skin Tone", []string{"woman_playing_water_polo_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f93d\U0001f3ff\u200d\u2640\ufe0f", "woman playing water polo: Dark Skin Tone", []string{"woman_playing_water_polo_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f93d\U0001f3fb\u200d\u2640\ufe0f", "woman playing water polo: Light Skin Tone", []string{"woman_playing_water_polo_Light_Skin_Tone"}, "12.0", false},
{"\U0001f469\U0001f3fb\u200d\U0001f52c", "woman scientist: Light Skin Tone", []string{"woman_scientist_Light_Skin_Tone"}, "12.0", false},
{"\U0001f469\U0001f3fc\u200d\U0001f52c", "woman scientist: Medium-Light Skin Tone", []string{"woman_scientist_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f469\U0001f3fd\u200d\U0001f52c", "woman scientist: Medium Skin Tone", []string{"woman_scientist_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f469\U0001f3fe\u200d\U0001f52c", "woman scientist: Medium-Dark Skin Tone", []string{"woman_scientist_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f469\U0001f3ff\u200d\U0001f52c", "woman scientist: Dark Skin Tone", []string{"woman_scientist_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f937\U0001f3ff\u200d\u2640\ufe0f", "woman shrugging: Dark Skin Tone", []string{"woman_shrugging_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f937\U0001f3fb\u200d\u2640\ufe0f", "woman shrugging: Light Skin Tone", []string{"woman_shrugging_Light_Skin_Tone"}, "12.0", false},
{"\U0001f937\U0001f3fc\u200d\u2640\ufe0f", "woman shrugging: Medium-Light Skin Tone", []string{"woman_shrugging_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f937\U0001f3fd\u200d\u2640\ufe0f", "woman shrugging: Medium Skin Tone", []string{"woman_shrugging_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f937\U0001f3fe\u200d\u2640\ufe0f", "woman shrugging: Medium-Dark Skin Tone", []string{"woman_shrugging_Medium-Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f937\U0001f3ff\u200d\u2640\ufe0f", "woman shrugging: Dark Skin Tone", []string{"woman_shrugging_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f469\U0001f3fb\u200d\U0001f3a4", "woman singer: Light Skin Tone", []string{"woman_singer_Light_Skin_Tone"}, "12.0", false},
{"\U0001f469\U0001f3fc\u200d\U0001f3a4", "woman singer: Medium-Light Skin Tone", []string{"woman_singer_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f469\U0001f3fd\u200d\U0001f3a4", "woman singer: Medium Skin Tone", []string{"woman_singer_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f469\U0001f3fe\u200d\U0001f3a4", "woman singer: Medium-Dark Skin Tone", []string{"woman_singer_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f469\U0001f3ff\u200d\U0001f3a4", "woman singer: Dark Skin Tone", []string{"woman_singer_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f469\U0001f3fb\u200d\U0001f3a4", "woman singer: Light Skin Tone", []string{"woman_singer_Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f469\U0001f3fb\u200d\U0001f393", "woman student: Light Skin Tone", []string{"woman_student_Light_Skin_Tone"}, "12.0", false},
{"\U0001f469\U0001f3fc\u200d\U0001f393", "woman student: Medium-Light Skin Tone", []string{"woman_student_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f469\U0001f3fd\u200d\U0001f393", "woman student: Medium Skin Tone", []string{"woman_student_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f469\U0001f3fe\u200d\U0001f393", "woman student: Medium-Dark Skin Tone", []string{"woman_student_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f469\U0001f3ff\u200d\U0001f393", "woman student: Dark Skin Tone", []string{"woman_student_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f469\U0001f3fb\u200d\U0001f393", "woman student: Light Skin Tone", []string{"woman_student_Light_Skin_Tone"}, "12.0", false},
- {"\U0001f469\U0001f3ff\u200d\U0001f3eb", "woman teacher: Dark Skin Tone", []string{"woman_teacher_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f469\U0001f3fb\u200d\U0001f3eb", "woman teacher: Light Skin Tone", []string{"woman_teacher_Light_Skin_Tone"}, "12.0", false},
{"\U0001f469\U0001f3fc\u200d\U0001f3eb", "woman teacher: Medium-Light Skin Tone", []string{"woman_teacher_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f469\U0001f3fd\u200d\U0001f3eb", "woman teacher: Medium Skin Tone", []string{"woman_teacher_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f469\U0001f3fe\u200d\U0001f3eb", "woman teacher: Medium-Dark Skin Tone", []string{"woman_teacher_Medium-Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f469\U0001f3ff\u200d\U0001f3eb", "woman teacher: Dark Skin Tone", []string{"woman_teacher_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f469\U0001f3fb\u200d\U0001f3eb", "woman teacher: Light Skin Tone", []string{"woman_teacher_Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f469\U0001f3fe\u200d\U0001f4bb", "woman technologist: Medium-Dark Skin Tone", []string{"woman_technologist_Medium-Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f469\U0001f3ff\u200d\U0001f4bb", "woman technologist: Dark Skin Tone", []string{"woman_technologist_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f469\U0001f3fb\u200d\U0001f4bb", "woman technologist: Light Skin Tone", []string{"woman_technologist_Light_Skin_Tone"}, "12.0", false},
{"\U0001f469\U0001f3fc\u200d\U0001f4bb", "woman technologist: Medium-Light Skin Tone", []string{"woman_technologist_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f469\U0001f3fd\u200d\U0001f4bb", "woman technologist: Medium Skin Tone", []string{"woman_technologist_Medium_Skin_Tone"}, "12.0", false},
- {"\U0001f469\U0001f3fe\u200d\U0001f4bb", "woman technologist: Medium-Dark Skin Tone", []string{"woman_technologist_Medium-Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f469\U0001f3ff\u200d\U0001f4bb", "woman technologist: Dark Skin Tone", []string{"woman_technologist_Dark_Skin_Tone"}, "12.0", false},
- {"\U0001f9d5\U0001f3fc", "woman with headscarf: Medium-Light Skin Tone", []string{"woman_with_headscarf_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f9d5\U0001f3fd", "woman with headscarf: Medium Skin Tone", []string{"woman_with_headscarf_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f9d5\U0001f3fe", "woman with headscarf: Medium-Dark Skin Tone", []string{"woman_with_headscarf_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9d5\U0001f3ff", "woman with headscarf: Dark Skin Tone", []string{"woman_with_headscarf_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f9d5\U0001f3fb", "woman with headscarf: Light Skin Tone", []string{"woman_with_headscarf_Light_Skin_Tone"}, "12.0", false},
- {"\U0001f469\U0001f3fc\u200d\U0001f9af", "woman with white cane: Medium-Light Skin Tone", []string{"woman_with_probing_cane_Medium-Light_Skin_Tone"}, "12.0", false},
- {"\U0001f469\U0001f3fd\u200d\U0001f9af", "woman with white cane: Medium Skin Tone", []string{"woman_with_probing_cane_Medium_Skin_Tone"}, "12.0", false},
+ {"\U0001f9d5\U0001f3fc", "woman with headscarf: Medium-Light Skin Tone", []string{"woman_with_headscarf_Medium-Light_Skin_Tone"}, "12.0", false},
{"\U0001f469\U0001f3fe\u200d\U0001f9af", "woman with white cane: Medium-Dark Skin Tone", []string{"woman_with_probing_cane_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f469\U0001f3ff\u200d\U0001f9af", "woman with white cane: Dark Skin Tone", []string{"woman_with_probing_cane_Dark_Skin_Tone"}, "12.0", false},
{"\U0001f469\U0001f3fb\u200d\U0001f9af", "woman with white cane: Light Skin Tone", []string{"woman_with_probing_cane_Light_Skin_Tone"}, "12.0", false},
- {"\U0001f473\U0001f3fb\u200d\u2640\ufe0f", "woman wearing turban: Light Skin Tone", []string{"woman_with_turban_Light_Skin_Tone"}, "12.0", false},
- {"\U0001f473\U0001f3fc\u200d\u2640\ufe0f", "woman wearing turban: Medium-Light Skin Tone", []string{"woman_with_turban_Medium-Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f469\U0001f3fc\u200d\U0001f9af", "woman with white cane: Medium-Light Skin Tone", []string{"woman_with_probing_cane_Medium-Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f469\U0001f3fd\u200d\U0001f9af", "woman with white cane: Medium Skin Tone", []string{"woman_with_probing_cane_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f473\U0001f3fd\u200d\u2640\ufe0f", "woman wearing turban: Medium Skin Tone", []string{"woman_with_turban_Medium_Skin_Tone"}, "12.0", false},
{"\U0001f473\U0001f3fe\u200d\u2640\ufe0f", "woman wearing turban: Medium-Dark Skin Tone", []string{"woman_with_turban_Medium-Dark_Skin_Tone"}, "12.0", false},
{"\U0001f473\U0001f3ff\u200d\u2640\ufe0f", "woman wearing turban: Dark Skin Tone", []string{"woman_with_turban_Dark_Skin_Tone"}, "12.0", false},
+ {"\U0001f473\U0001f3fb\u200d\u2640\ufe0f", "woman wearing turban: Light Skin Tone", []string{"woman_with_turban_Light_Skin_Tone"}, "12.0", false},
+ {"\U0001f473\U0001f3fc\u200d\u2640\ufe0f", "woman wearing turban: Medium-Light Skin Tone", []string{"woman_with_turban_Medium-Light_Skin_Tone"}, "12.0", false},
{"\u270d\U0001f3fb\ufe0f", "writing hand: Light Skin Tone", []string{"writing_hand_Light_Skin_Tone"}, "12.0", false},
{"\u270d\U0001f3fc\ufe0f", "writing hand: Medium-Light Skin Tone", []string{"writing_hand_Medium-Light_Skin_Tone"}, "12.0", false},
{"\u270d\U0001f3fd\ufe0f", "writing hand: Medium Skin Tone", []string{"writing_hand_Medium_Skin_Tone"}, "12.0", false},
diff --git a/modules/eventsource/manager_run.go b/modules/eventsource/manager_run.go
index 06d48454fd..44e878fd4e 100644
--- a/modules/eventsource/manager_run.go
+++ b/modules/eventsource/manager_run.go
@@ -8,7 +8,7 @@ import (
"context"
"time"
- "code.gitea.io/gitea/models"
+ activities_model "code.gitea.io/gitea/models/activities"
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/modules/convert"
"code.gitea.io/gitea/modules/graceful"
@@ -72,7 +72,7 @@ loop:
now := timeutil.TimeStampNow().Add(-2)
- uidCounts, err := models.GetUIDsAndNotificationCounts(then, now)
+ uidCounts, err := activities_model.GetUIDsAndNotificationCounts(then, now)
if err != nil {
log.Error("Unable to get UIDcounts: %v", err)
}
diff --git a/modules/git/command.go b/modules/git/command.go
index a1bacbb707..ed06dd9b08 100644
--- a/modules/git/command.go
+++ b/modules/git/command.go
@@ -40,6 +40,7 @@ type Command struct {
parentContext context.Context
desc string
globalArgsLength int
+ brokenArgs []string
}
func (c *Command) String() string {
@@ -50,6 +51,7 @@ func (c *Command) String() string {
}
// NewCommand creates and returns a new Git Command based on given command and arguments.
+// Each argument should be safe to be trusted. User-provided arguments should be passed to AddDynamicArguments instead.
func NewCommand(ctx context.Context, args ...string) *Command {
// Make an explicit copy of globalCommandArgs, otherwise append might overwrite it
cargs := make([]string, len(globalCommandArgs))
@@ -63,11 +65,13 @@ func NewCommand(ctx context.Context, args ...string) *Command {
}
// NewCommandNoGlobals creates and returns a new Git Command based on given command and arguments only with the specify args and don't care global command args
+// Each argument should be safe to be trusted. User-provided arguments should be passed to AddDynamicArguments instead.
func NewCommandNoGlobals(args ...string) *Command {
return NewCommandContextNoGlobals(DefaultContext, args...)
}
// NewCommandContextNoGlobals creates and returns a new Git Command based on given command and arguments only with the specify args and don't care global command args
+// Each argument should be safe to be trusted. User-provided arguments should be passed to AddDynamicArguments instead.
func NewCommandContextNoGlobals(ctx context.Context, args ...string) *Command {
return &Command{
name: GitExecutable,
@@ -89,20 +93,37 @@ func (c *Command) SetDescription(desc string) *Command {
return c
}
-// AddArguments adds new argument(s) to the command.
+// AddArguments adds new argument(s) to the command. Each argument must be safe to be trusted.
+// User-provided arguments should be passed to AddDynamicArguments instead.
func (c *Command) AddArguments(args ...string) *Command {
c.args = append(c.args, args...)
return c
}
-// RunOpts represents parameters to run the command
+// AddDynamicArguments adds new dynamic argument(s) to the command.
+// The arguments may come from user input and can not be trusted, so no leading '-' is allowed to avoid passing options
+func (c *Command) AddDynamicArguments(args ...string) *Command {
+ for _, arg := range args {
+ if arg != "" && arg[0] == '-' {
+ c.brokenArgs = append(c.brokenArgs, arg)
+ }
+ }
+ if len(c.brokenArgs) != 0 {
+ return c
+ }
+ c.args = append(c.args, args...)
+ return c
+}
+
+// RunOpts represents parameters to run the command. If UseContextTimeout is specified, then Timeout is ignored.
type RunOpts struct {
- Env []string
- Timeout time.Duration
- Dir string
- Stdout, Stderr io.Writer
- Stdin io.Reader
- PipelineFunc func(context.Context, context.CancelFunc) error
+ Env []string
+ Timeout time.Duration
+ UseContextTimeout bool
+ Dir string
+ Stdout, Stderr io.Writer
+ Stdin io.Reader
+ PipelineFunc func(context.Context, context.CancelFunc) error
}
func commonBaseEnvs() []string {
@@ -137,8 +158,14 @@ func CommonCmdServEnvs() []string {
return commonBaseEnvs()
}
+var ErrBrokenCommand = errors.New("git command is broken")
+
// Run runs the command with the RunOpts
func (c *Command) Run(opts *RunOpts) error {
+ if len(c.brokenArgs) != 0 {
+ log.Error("git command is broken: %s, broken args: %s", c.String(), strings.Join(c.brokenArgs, " "))
+ return ErrBrokenCommand
+ }
if opts == nil {
opts = &RunOpts{}
}
@@ -171,7 +198,15 @@ func (c *Command) Run(opts *RunOpts) error {
desc = fmt.Sprintf("%s %s [repo_path: %s]", c.name, strings.Join(args, " "), opts.Dir)
}
- ctx, cancel, finished := process.GetManager().AddContextTimeout(c.parentContext, opts.Timeout, desc)
+ var ctx context.Context
+ var cancel context.CancelFunc
+ var finished context.CancelFunc
+
+ if opts.UseContextTimeout {
+ ctx, cancel, finished = process.GetManager().AddContext(c.parentContext, desc)
+ } else {
+ ctx, cancel, finished = process.GetManager().AddContextTimeout(c.parentContext, opts.Timeout, desc)
+ }
defer finished()
cmd := exec.CommandContext(ctx, c.name, c.args...)
diff --git a/modules/git/command_test.go b/modules/git/command_test.go
index 67d4ca388e..52d25c9c74 100644
--- a/modules/git/command_test.go
+++ b/modules/git/command_test.go
@@ -26,4 +26,19 @@ func TestRunWithContextStd(t *testing.T) {
assert.Contains(t, err.Error(), "exit status 129 - unknown option:")
assert.Empty(t, stdout)
}
+
+ cmd = NewCommand(context.Background())
+ cmd.AddDynamicArguments("-test")
+ assert.ErrorIs(t, cmd.Run(&RunOpts{}), ErrBrokenCommand)
+
+ cmd = NewCommand(context.Background())
+ cmd.AddDynamicArguments("--test")
+ assert.ErrorIs(t, cmd.Run(&RunOpts{}), ErrBrokenCommand)
+
+ subCmd := "version"
+ cmd = NewCommand(context.Background()).AddDynamicArguments(subCmd) // for test purpose only, the sub-command should never be dynamic for production
+ stdout, stderr, err = cmd.RunStdString(&RunOpts{})
+ assert.NoError(t, err)
+ assert.Empty(t, stderr)
+ assert.Contains(t, stdout, "git version")
}
diff --git a/modules/git/commit.go b/modules/git/commit.go
index 82712dd1ef..8fac9921b1 100644
--- a/modules/git/commit.go
+++ b/modules/git/commit.go
@@ -80,6 +80,9 @@ func (c *Commit) ParentCount() int {
// GetCommitByPath return the commit of relative path object.
func (c *Commit) GetCommitByPath(relpath string) (*Commit, error) {
+ if c.repo.LastCommitCache != nil {
+ return c.repo.LastCommitCache.GetCommitByPath(c.ID.String(), relpath)
+ }
return c.repo.getCommitByPathWithID(c.ID, relpath)
}
@@ -163,7 +166,7 @@ func AllCommitsCount(ctx context.Context, repoPath string, hidePRRefs bool, file
// CommitsCountFiles returns number of total commits of until given revision.
func CommitsCountFiles(ctx context.Context, repoPath string, revision, relpath []string) (int64, error) {
cmd := NewCommand(ctx, "rev-list", "--count")
- cmd.AddArguments(revision...)
+ cmd.AddDynamicArguments(revision...)
if len(relpath) > 0 {
cmd.AddArguments("--")
cmd.AddArguments(relpath...)
diff --git a/modules/git/commit_info_gogit.go b/modules/git/commit_info_gogit.go
index 91a1804db5..341698ab34 100644
--- a/modules/git/commit_info_gogit.go
+++ b/modules/git/commit_info_gogit.go
@@ -17,7 +17,7 @@ import (
)
// GetCommitsInfo gets information of all commits that are corresponding to these entries
-func (tes Entries) GetCommitsInfo(ctx context.Context, commit *Commit, treePath string, cache *LastCommitCache) ([]CommitInfo, *Commit, error) {
+func (tes Entries) GetCommitsInfo(ctx context.Context, commit *Commit, treePath string) ([]CommitInfo, *Commit, error) {
entryPaths := make([]string, len(tes)+1)
// Get the commit for the treePath itself
entryPaths[0] = ""
@@ -35,15 +35,15 @@ func (tes Entries) GetCommitsInfo(ctx context.Context, commit *Commit, treePath
return nil, nil, err
}
- var revs map[string]*object.Commit
- if cache != nil {
+ var revs map[string]*Commit
+ if commit.repo.LastCommitCache != nil {
var unHitPaths []string
- revs, unHitPaths, err = getLastCommitForPathsByCache(commit.ID.String(), treePath, entryPaths, cache)
+ revs, unHitPaths, err = getLastCommitForPathsByCache(commit.ID.String(), treePath, entryPaths, commit.repo.LastCommitCache)
if err != nil {
return nil, nil, err
}
if len(unHitPaths) > 0 {
- revs2, err := GetLastCommitForPaths(ctx, cache, c, treePath, unHitPaths)
+ revs2, err := GetLastCommitForPaths(ctx, commit.repo.LastCommitCache, c, treePath, unHitPaths)
if err != nil {
return nil, nil, err
}
@@ -68,8 +68,7 @@ func (tes Entries) GetCommitsInfo(ctx context.Context, commit *Commit, treePath
}
// Check if we have found a commit for this entry in time
- if rev, ok := revs[entry.Name()]; ok {
- entryCommit := convertCommit(rev)
+ if entryCommit, ok := revs[entry.Name()]; ok {
commitsInfo[i].Commit = entryCommit
}
@@ -96,10 +95,10 @@ func (tes Entries) GetCommitsInfo(ctx context.Context, commit *Commit, treePath
// get it for free during the tree traversal and it's used for listing
// pages to display information about newest commit for a given path.
var treeCommit *Commit
+ var ok bool
if treePath == "" {
treeCommit = commit
- } else if rev, ok := revs[""]; ok {
- treeCommit = convertCommit(rev)
+ } else if treeCommit, ok = revs[""]; ok {
treeCommit.repo = commit.repo
}
return commitsInfo, treeCommit, nil
@@ -155,16 +154,16 @@ func getFileHashes(c cgobject.CommitNode, treePath string, paths []string) (map[
return hashes, nil
}
-func getLastCommitForPathsByCache(commitID, treePath string, paths []string, cache *LastCommitCache) (map[string]*object.Commit, []string, error) {
+func getLastCommitForPathsByCache(commitID, treePath string, paths []string, cache *LastCommitCache) (map[string]*Commit, []string, error) {
var unHitEntryPaths []string
- results := make(map[string]*object.Commit)
+ results := make(map[string]*Commit)
for _, p := range paths {
lastCommit, err := cache.Get(commitID, path.Join(treePath, p))
if err != nil {
return nil, nil, err
}
if lastCommit != nil {
- results[p] = lastCommit.(*object.Commit)
+ results[p] = lastCommit
continue
}
@@ -175,7 +174,7 @@ func getLastCommitForPathsByCache(commitID, treePath string, paths []string, cac
}
// GetLastCommitForPaths returns last commit information
-func GetLastCommitForPaths(ctx context.Context, cache *LastCommitCache, c cgobject.CommitNode, treePath string, paths []string) (map[string]*object.Commit, error) {
+func GetLastCommitForPaths(ctx context.Context, cache *LastCommitCache, c cgobject.CommitNode, treePath string, paths []string) (map[string]*Commit, error) {
refSha := c.ID().String()
// We do a tree traversal with nodes sorted by commit time
@@ -293,13 +292,13 @@ heaploop:
}
// Post-processing
- result := make(map[string]*object.Commit)
+ result := make(map[string]*Commit)
for path, commitNode := range resultNodes {
- var err error
- result[path], err = commitNode.Commit()
+ commit, err := commitNode.Commit()
if err != nil {
return nil, err
}
+ result[path] = convertCommit(commit)
}
return result, nil
diff --git a/modules/git/commit_info_nogogit.go b/modules/git/commit_info_nogogit.go
index ceab11adbb..d7bca3b948 100644
--- a/modules/git/commit_info_nogogit.go
+++ b/modules/git/commit_info_nogogit.go
@@ -17,7 +17,7 @@ import (
)
// GetCommitsInfo gets information of all commits that are corresponding to these entries
-func (tes Entries) GetCommitsInfo(ctx context.Context, commit *Commit, treePath string, cache *LastCommitCache) ([]CommitInfo, *Commit, error) {
+func (tes Entries) GetCommitsInfo(ctx context.Context, commit *Commit, treePath string) ([]CommitInfo, *Commit, error) {
entryPaths := make([]string, len(tes)+1)
// Get the commit for the treePath itself
entryPaths[0] = ""
@@ -28,15 +28,15 @@ func (tes Entries) GetCommitsInfo(ctx context.Context, commit *Commit, treePath
var err error
var revs map[string]*Commit
- if cache != nil {
+ if commit.repo.LastCommitCache != nil {
var unHitPaths []string
- revs, unHitPaths, err = getLastCommitForPathsByCache(ctx, commit.ID.String(), treePath, entryPaths, cache)
+ revs, unHitPaths, err = getLastCommitForPathsByCache(ctx, commit.ID.String(), treePath, entryPaths, commit.repo.LastCommitCache)
if err != nil {
return nil, nil, err
}
if len(unHitPaths) > 0 {
sort.Strings(unHitPaths)
- commits, err := GetLastCommitForPaths(ctx, cache, commit, treePath, unHitPaths)
+ commits, err := GetLastCommitForPaths(ctx, commit, treePath, unHitPaths)
if err != nil {
return nil, nil, err
}
@@ -47,7 +47,7 @@ func (tes Entries) GetCommitsInfo(ctx context.Context, commit *Commit, treePath
}
} else {
sort.Strings(entryPaths)
- revs, err = GetLastCommitForPaths(ctx, nil, commit, treePath, entryPaths)
+ revs, err = GetLastCommitForPaths(ctx, commit, treePath, entryPaths)
}
if err != nil {
return nil, nil, err
@@ -99,18 +99,15 @@ func (tes Entries) GetCommitsInfo(ctx context.Context, commit *Commit, treePath
}
func getLastCommitForPathsByCache(ctx context.Context, commitID, treePath string, paths []string, cache *LastCommitCache) (map[string]*Commit, []string, error) {
- wr, rd, cancel := cache.repo.CatFileBatch(ctx)
- defer cancel()
-
var unHitEntryPaths []string
results := make(map[string]*Commit)
for _, p := range paths {
- lastCommit, err := cache.Get(commitID, path.Join(treePath, p), wr, rd)
+ lastCommit, err := cache.Get(commitID, path.Join(treePath, p))
if err != nil {
return nil, nil, err
}
if lastCommit != nil {
- results[p] = lastCommit.(*Commit)
+ results[p] = lastCommit
continue
}
@@ -121,9 +118,9 @@ func getLastCommitForPathsByCache(ctx context.Context, commitID, treePath string
}
// GetLastCommitForPaths returns last commit information
-func GetLastCommitForPaths(ctx context.Context, cache *LastCommitCache, commit *Commit, treePath string, paths []string) (map[string]*Commit, error) {
+func GetLastCommitForPaths(ctx context.Context, commit *Commit, treePath string, paths []string) (map[string]*Commit, error) {
// We read backwards from the commit to obtain all of the commits
- revs, err := WalkGitLog(ctx, cache, commit.repo, commit, treePath, paths...)
+ revs, err := WalkGitLog(ctx, commit.repo, commit, treePath, paths...)
if err != nil {
return nil, err
}
diff --git a/modules/git/commit_info_test.go b/modules/git/commit_info_test.go
index 49845522a9..4bc3596896 100644
--- a/modules/git/commit_info_test.go
+++ b/modules/git/commit_info_test.go
@@ -6,13 +6,10 @@ package git
import (
"context"
- "os"
"path/filepath"
"testing"
"time"
- "code.gitea.io/gitea/modules/util"
-
"github.com/stretchr/testify/assert"
)
@@ -20,18 +17,14 @@ const (
testReposDir = "tests/repos/"
)
-func cloneRepo(url, name string) (string, error) {
- repoDir, err := os.MkdirTemp("", name)
- if err != nil {
- return "", err
- }
+func cloneRepo(tb testing.TB, url string) (string, error) {
+ repoDir := tb.TempDir()
if err := Clone(DefaultContext, url, repoDir, CloneRepoOptions{
Mirror: false,
Bare: false,
Quiet: true,
Timeout: 5 * time.Minute,
}); err != nil {
- _ = util.RemoveAll(repoDir)
return "", err
}
return repoDir, nil
@@ -91,7 +84,7 @@ func testGetCommitsInfo(t *testing.T, repo1 *Repository) {
}
// FIXME: Context.TODO() - if graceful has started we should use its Shutdown context otherwise use install signals in TestMain.
- commitsInfo, treeCommit, err := entries.GetCommitsInfo(context.TODO(), commit, testCase.Path, nil)
+ commitsInfo, treeCommit, err := entries.GetCommitsInfo(context.TODO(), commit, testCase.Path)
assert.NoError(t, err, "Unable to get commit information for entries of subtree: %s in commit: %s from testcase due to error: %v", testCase.Path, testCase.CommitID, err)
if err != nil {
t.FailNow()
@@ -118,11 +111,10 @@ func TestEntries_GetCommitsInfo(t *testing.T) {
testGetCommitsInfo(t, bareRepo1)
- clonedPath, err := cloneRepo(bareRepo1Path, "repo1_TestEntries_GetCommitsInfo")
+ clonedPath, err := cloneRepo(t, bareRepo1Path)
if err != nil {
assert.NoError(t, err)
}
- defer util.RemoveAll(clonedPath)
clonedRepo1, err := openRepositoryWithDefaultContext(clonedPath)
if err != nil {
assert.NoError(t, err)
@@ -150,11 +142,10 @@ func BenchmarkEntries_GetCommitsInfo(b *testing.B) {
var commit *Commit
var entries Entries
var repo *Repository
- repoPath, err := cloneRepo(benchmark.url, benchmark.name)
+ repoPath, err := cloneRepo(b, benchmark.url)
if err != nil {
b.Fatal(err)
}
- defer util.RemoveAll(repoPath)
if repo, err = openRepositoryWithDefaultContext(repoPath); err != nil {
b.Fatal(err)
@@ -170,7 +161,7 @@ func BenchmarkEntries_GetCommitsInfo(b *testing.B) {
b.ResetTimer()
b.Run(benchmark.name, func(b *testing.B) {
for i := 0; i < b.N; i++ {
- _, _, err := entries.GetCommitsInfo(context.Background(), commit, "", nil)
+ _, _, err := entries.GetCommitsInfo(context.Background(), commit, "")
if err != nil {
b.Fatal(err)
}
diff --git a/modules/git/error.go b/modules/git/error.go
index 387dd724e5..40c4106414 100644
--- a/modules/git/error.go
+++ b/modules/git/error.go
@@ -8,6 +8,8 @@ import (
"fmt"
"strings"
"time"
+
+ "code.gitea.io/gitea/modules/util"
)
// ErrExecTimeout error when exec timed out
@@ -41,6 +43,10 @@ func (err ErrNotExist) Error() string {
return fmt.Sprintf("object does not exist [id: %s, rel_path: %s]", err.ID, err.RelPath)
}
+func (err ErrNotExist) Unwrap() error {
+ return util.ErrNotExist
+}
+
// ErrBadLink entry.FollowLink error
type ErrBadLink struct {
Name string
@@ -87,6 +93,10 @@ func (err ErrBranchNotExist) Error() string {
return fmt.Sprintf("branch does not exist [name: %s]", err.Name)
}
+func (err ErrBranchNotExist) Unwrap() error {
+ return util.ErrNotExist
+}
+
// ErrPushOutOfDate represents an error if merging fails due to unrelated histories
type ErrPushOutOfDate struct {
StdOut string
diff --git a/modules/git/foreachref/parser.go b/modules/git/foreachref/parser.go
index eb8b77d903..bf83a10ed5 100644
--- a/modules/git/foreachref/parser.go
+++ b/modules/git/foreachref/parser.go
@@ -68,8 +68,7 @@ func NewParser(r io.Reader, format Format) *Parser {
//
// It could, for example return something like:
//
-// { "objecttype": "tag", "refname:short": "v1.16.4", "object": "f460b7543ed500e49c133c2cd85c8c55ee9dbe27" }
-//
+// { "objecttype": "tag", "refname:short": "v1.16.4", "object": "f460b7543ed500e49c133c2cd85c8c55ee9dbe27" }
func (p *Parser) Next() map[string]string {
if !p.scanner.Scan() {
return nil
@@ -89,8 +88,7 @@ func (p *Parser) Err() error {
// parseRef parses out all key-value pairs from a single reference block, such as
//
-// "objecttype tag\0refname:short v1.16.4\0object f460b7543ed500e49c133c2cd85c8c55ee9dbe27"
-//
+// "objecttype tag\0refname:short v1.16.4\0object f460b7543ed500e49c133c2cd85c8c55ee9dbe27"
func (p *Parser) parseRef(refBlock string) (map[string]string, error) {
if refBlock == "" {
// must be at EOF
diff --git a/modules/git/git.go b/modules/git/git.go
index b8317396c0..28899222e7 100644
--- a/modules/git/git.go
+++ b/modules/git/git.go
@@ -15,7 +15,6 @@ import (
"regexp"
"runtime"
"strings"
- "sync"
"time"
"code.gitea.io/gitea/modules/log"
@@ -24,8 +23,8 @@ import (
"github.com/hashicorp/go-version"
)
-// GitVersionRequired is the minimum Git version required
-const GitVersionRequired = "2.0.0"
+// RequiredVersion is the minimum Git version required
+const RequiredVersion = "2.0.0"
var (
// GitExecutable is the command name of git
@@ -43,7 +42,7 @@ var (
// loadGitVersion returns current Git version from shell. Internal usage only.
func loadGitVersion() (*version.Version, error) {
- // doesn't need RWMutex because its exec by Init()
+ // doesn't need RWMutex because it's executed by Init()
if gitVersion != nil {
return gitVersion, nil
}
@@ -90,7 +89,7 @@ func SetExecutablePath(path string) error {
return fmt.Errorf("unable to load git version: %w", err)
}
- versionRequired, err := version.NewVersion(GitVersionRequired)
+ versionRequired, err := version.NewVersion(RequiredVersion)
if err != nil {
return err
}
@@ -104,7 +103,7 @@ func SetExecutablePath(path string) error {
moreHint = "get git: https://git-scm.com/download/linux and https://ius.io"
}
}
- return fmt.Errorf("installed git version %q is not supported, Gitea requires git version >= %q, %s", gitVersion.Original(), GitVersionRequired, moreHint)
+ return fmt.Errorf("installed git version %q is not supported, Gitea requires git version >= %q, %s", gitVersion.Original(), RequiredVersion, moreHint)
}
return nil
@@ -131,7 +130,7 @@ func checkInit() error {
return errors.New("unable to init Git's HomeDir, incorrect initialization of the setting and git modules")
}
if DefaultContext != nil {
- log.Warn("git module has been initialized already, duplicate init should be fixed")
+ log.Warn("git module has been initialized already, duplicate init may work but it's better to fix it")
}
return nil
}
@@ -140,7 +139,7 @@ func checkInit() error {
func HomeDir() string {
if setting.Git.HomePath == "" {
// strict check, make sure the git module is initialized correctly.
- // attention: when the git module is called in gitea sub-command (serv/hook), the log module is not able to show messages to users.
+ // attention: when the git module is called in gitea sub-command (serv/hook), the log module might not obviously show messages to users/developers.
// for example: if there is gitea git hook code calling git.NewCommand before git.InitXxx, the integration test won't show the real failure reasons.
log.Fatal("Unable to init Git's HomeDir, incorrect initialization of the setting and git modules")
return ""
@@ -149,14 +148,14 @@ func HomeDir() string {
}
// InitSimple initializes git module with a very simple step, no config changes, no global command arguments.
-// This method doesn't change anything to filesystem. At the moment, it is only used by "git serv" sub-command, no data-race
-// However, in integration test, the sub-command function may be called in the current process, so the InitSimple would be called multiple times, too
+// This method doesn't change anything to filesystem. At the moment, it is only used by some Gitea sub-commands.
func InitSimple(ctx context.Context) error {
if err := checkInit(); err != nil {
return err
}
DefaultContext = ctx
+ globalCommandArgs = nil
if setting.Git.Timeout.Default > 0 {
defaultCommandExecutionTimeout = time.Duration(setting.Git.Timeout.Default) * time.Second
@@ -165,46 +164,41 @@ func InitSimple(ctx context.Context) error {
return SetExecutablePath(setting.Git.Path)
}
-var initOnce sync.Once
-
-// InitOnceWithSync initializes git module with version check and change global variables, sync gitconfig.
-// This method will update the global variables ONLY ONCE (just like git.CheckLFSVersion -- which is not ideal too),
-// otherwise there will be data-race problem at the moment.
-func InitOnceWithSync(ctx context.Context) (err error) {
+// InitFull initializes git module with version check and change global variables, sync gitconfig.
+// It should only be called once at the beginning of the program initialization (TestMain/GlobalInitInstalled) as this code makes unsynchronized changes to variables.
+func InitFull(ctx context.Context) (err error) {
if err = checkInit(); err != nil {
return err
}
- initOnce.Do(func() {
- if err = InitSimple(ctx); err != nil {
- return
- }
-
- // when git works with gnupg (commit signing), there should be a stable home for gnupg commands
- if _, ok := os.LookupEnv("GNUPGHOME"); !ok {
- _ = os.Setenv("GNUPGHOME", filepath.Join(HomeDir(), ".gnupg"))
- }
-
- // Since git wire protocol has been released from git v2.18
- if setting.Git.EnableAutoGitWireProtocol && CheckGitVersionAtLeast("2.18") == nil {
- globalCommandArgs = append(globalCommandArgs, "-c", "protocol.version=2")
- }
-
- // By default partial clones are disabled, enable them from git v2.22
- if !setting.Git.DisablePartialClone && CheckGitVersionAtLeast("2.22") == nil {
- globalCommandArgs = append(globalCommandArgs, "-c", "uploadpack.allowfilter=true", "-c", "uploadpack.allowAnySHA1InWant=true")
- }
-
- // Explicitly disable credential helper, otherwise Git credentials might leak
- if CheckGitVersionAtLeast("2.9") == nil {
- globalCommandArgs = append(globalCommandArgs, "-c", "credential.helper=")
- }
-
- SupportProcReceive = CheckGitVersionAtLeast("2.29") == nil
- })
- if err != nil {
- return err
+ if err = InitSimple(ctx); err != nil {
+ return
}
+
+ // when git works with gnupg (commit signing), there should be a stable home for gnupg commands
+ if _, ok := os.LookupEnv("GNUPGHOME"); !ok {
+ _ = os.Setenv("GNUPGHOME", filepath.Join(HomeDir(), ".gnupg"))
+ }
+
+ // Since git wire protocol has been released from git v2.18
+ if setting.Git.EnableAutoGitWireProtocol && CheckGitVersionAtLeast("2.18") == nil {
+ globalCommandArgs = append(globalCommandArgs, "-c", "protocol.version=2")
+ }
+
+ // Explicitly disable credential helper, otherwise Git credentials might leak
+ if CheckGitVersionAtLeast("2.9") == nil {
+ globalCommandArgs = append(globalCommandArgs, "-c", "credential.helper=")
+ }
+
+ SupportProcReceive = CheckGitVersionAtLeast("2.29") == nil
+
+ if setting.LFS.StartServer {
+ if CheckGitVersionAtLeast("2.1.2") != nil {
+ return errors.New("LFS server support requires Git >= 2.1.2")
+ }
+ globalCommandArgs = append(globalCommandArgs, "-c", "filter.lfs.required=", "-c", "filter.lfs.smudge=", "-c", "filter.lfs.clean=")
+ }
+
return syncGitConfig()
}
@@ -287,7 +281,20 @@ func syncGitConfig() (err error) {
}
}
- return nil
+ // By default partial clones are disabled, enable them from git v2.22
+ if !setting.Git.DisablePartialClone && CheckGitVersionAtLeast("2.22") == nil {
+ if err = configSet("uploadpack.allowfilter", "true"); err != nil {
+ return err
+ }
+ err = configSet("uploadpack.allowAnySHA1InWant", "true")
+ } else {
+ if err = configUnsetAll("uploadpack.allowfilter", "true"); err != nil {
+ return err
+ }
+ err = configUnsetAll("uploadpack.allowAnySHA1InWant", "true")
+ }
+
+ return err
}
// CheckGitVersionAtLeast check git version is at least the constraint version
diff --git a/modules/git/git_test.go b/modules/git/git_test.go
index c5a63de064..0915737878 100644
--- a/modules/git/git_test.go
+++ b/modules/git/git_test.go
@@ -28,7 +28,7 @@ func testRun(m *testing.M) error {
defer util.RemoveAll(gitHomePath)
setting.Git.HomePath = gitHomePath
- if err = InitOnceWithSync(context.Background()); err != nil {
+ if err = InitFull(context.Background()); err != nil {
return fmt.Errorf("failed to call Init: %w", err)
}
diff --git a/modules/git/last_commit_cache.go b/modules/git/last_commit_cache.go
index d4ec517b51..2b51d59720 100644
--- a/modules/git/last_commit_cache.go
+++ b/modules/git/last_commit_cache.go
@@ -9,6 +9,7 @@ import (
"fmt"
"code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/setting"
)
// Cache represents a caching interface
@@ -19,16 +20,96 @@ type Cache interface {
Get(key string) interface{}
}
-func (c *LastCommitCache) getCacheKey(repoPath, ref, entryPath string) string {
- hashBytes := sha256.Sum256([]byte(fmt.Sprintf("%s:%s:%s", repoPath, ref, entryPath)))
+func getCacheKey(repoPath, commitID, entryPath string) string {
+ hashBytes := sha256.Sum256([]byte(fmt.Sprintf("%s:%s:%s", repoPath, commitID, entryPath)))
return fmt.Sprintf("last_commit:%x", hashBytes)
}
+// LastCommitCache represents a cache to store last commit
+type LastCommitCache struct {
+ repoPath string
+ ttl func() int64
+ repo *Repository
+ commitCache map[string]*Commit
+ cache Cache
+}
+
+// NewLastCommitCache creates a new last commit cache for repo
+func NewLastCommitCache(count int64, repoPath string, gitRepo *Repository, cache Cache) *LastCommitCache {
+ if cache == nil {
+ return nil
+ }
+ if !setting.CacheService.LastCommit.Enabled || count < setting.CacheService.LastCommit.CommitsCount {
+ return nil
+ }
+
+ return &LastCommitCache{
+ repoPath: repoPath,
+ repo: gitRepo,
+ ttl: setting.LastCommitCacheTTLSeconds,
+ cache: cache,
+ }
+}
+
// Put put the last commit id with commit and entry path
func (c *LastCommitCache) Put(ref, entryPath, commitID string) error {
if c == nil || c.cache == nil {
return nil
}
log.Debug("LastCommitCache save: [%s:%s:%s]", ref, entryPath, commitID)
- return c.cache.Put(c.getCacheKey(c.repoPath, ref, entryPath), commitID, c.ttl())
+ return c.cache.Put(getCacheKey(c.repoPath, ref, entryPath), commitID, c.ttl())
+}
+
+// Get gets the last commit information by commit id and entry path
+func (c *LastCommitCache) Get(ref, entryPath string) (*Commit, error) {
+ if c == nil || c.cache == nil {
+ return nil, nil
+ }
+
+ commitID, ok := c.cache.Get(getCacheKey(c.repoPath, ref, entryPath)).(string)
+ if !ok || commitID == "" {
+ return nil, nil
+ }
+
+ log.Debug("LastCommitCache hit level 1: [%s:%s:%s]", ref, entryPath, commitID)
+ if c.commitCache != nil {
+ if commit, ok := c.commitCache[commitID]; ok {
+ log.Debug("LastCommitCache hit level 2: [%s:%s:%s]", ref, entryPath, commitID)
+ return commit, nil
+ }
+ }
+
+ commit, err := c.repo.GetCommit(commitID)
+ if err != nil {
+ return nil, err
+ }
+ if c.commitCache == nil {
+ c.commitCache = make(map[string]*Commit)
+ }
+ c.commitCache[commitID] = commit
+ return commit, nil
+}
+
+// GetCommitByPath gets the last commit for the entry in the provided commit
+func (c *LastCommitCache) GetCommitByPath(commitID, entryPath string) (*Commit, error) {
+ sha1, err := NewIDFromString(commitID)
+ if err != nil {
+ return nil, err
+ }
+
+ lastCommit, err := c.Get(sha1.String(), entryPath)
+ if err != nil || lastCommit != nil {
+ return lastCommit, err
+ }
+
+ lastCommit, err = c.repo.getCommitByPathWithID(sha1, entryPath)
+ if err != nil {
+ return nil, err
+ }
+
+ if err := c.Put(commitID, entryPath, lastCommit.ID.String()); err != nil {
+ log.Error("Unable to cache %s as the last commit for %q in %s %s. Error %v", lastCommit.ID.String(), entryPath, commitID, c.repoPath, err)
+ }
+
+ return lastCommit, nil
}
diff --git a/modules/git/last_commit_cache_gogit.go b/modules/git/last_commit_cache_gogit.go
index 8897000350..82c76bad20 100644
--- a/modules/git/last_commit_cache_gogit.go
+++ b/modules/git/last_commit_cache_gogit.go
@@ -9,71 +9,25 @@ package git
import (
"context"
- "code.gitea.io/gitea/modules/log"
-
- "github.com/go-git/go-git/v5/plumbing/object"
cgobject "github.com/go-git/go-git/v5/plumbing/object/commitgraph"
)
-// LastCommitCache represents a cache to store last commit
-type LastCommitCache struct {
- repoPath string
- ttl func() int64
- repo *Repository
- commitCache map[string]*object.Commit
- cache Cache
-}
-
-// NewLastCommitCache creates a new last commit cache for repo
-func NewLastCommitCache(repoPath string, gitRepo *Repository, ttl func() int64, cache Cache) *LastCommitCache {
- if cache == nil {
+// CacheCommit will cache the commit from the gitRepository
+func (c *Commit) CacheCommit(ctx context.Context) error {
+ if c.repo.LastCommitCache == nil {
return nil
}
- return &LastCommitCache{
- repoPath: repoPath,
- repo: gitRepo,
- commitCache: make(map[string]*object.Commit),
- ttl: ttl,
- cache: cache,
- }
-}
+ commitNodeIndex, _ := c.repo.CommitNodeIndex()
-// Get get the last commit information by commit id and entry path
-func (c *LastCommitCache) Get(ref, entryPath string) (interface{}, error) {
- v := c.cache.Get(c.getCacheKey(c.repoPath, ref, entryPath))
- if vs, ok := v.(string); ok {
- log.Debug("LastCommitCache hit level 1: [%s:%s:%s]", ref, entryPath, vs)
- if commit, ok := c.commitCache[vs]; ok {
- log.Debug("LastCommitCache hit level 2: [%s:%s:%s]", ref, entryPath, vs)
- return commit, nil
- }
- id, err := c.repo.ConvertToSHA1(vs)
- if err != nil {
- return nil, err
- }
- commit, err := c.repo.GoGitRepo().CommitObject(id)
- if err != nil {
- return nil, err
- }
- c.commitCache[vs] = commit
- return commit, nil
- }
- return nil, nil
-}
-
-// CacheCommit will cache the commit from the gitRepository
-func (c *LastCommitCache) CacheCommit(ctx context.Context, commit *Commit) error {
- commitNodeIndex, _ := commit.repo.CommitNodeIndex()
-
- index, err := commitNodeIndex.Get(commit.ID)
+ index, err := commitNodeIndex.Get(c.ID)
if err != nil {
return err
}
- return c.recursiveCache(ctx, index, &commit.Tree, "", 1)
+ return c.recursiveCache(ctx, index, &c.Tree, "", 1)
}
-func (c *LastCommitCache) recursiveCache(ctx context.Context, index cgobject.CommitNode, tree *Tree, treePath string, level int) error {
+func (c *Commit) recursiveCache(ctx context.Context, index cgobject.CommitNode, tree *Tree, treePath string, level int) error {
if level == 0 {
return nil
}
@@ -90,7 +44,7 @@ func (c *LastCommitCache) recursiveCache(ctx context.Context, index cgobject.Com
entryMap[entry.Name()] = entry
}
- commits, err := GetLastCommitForPaths(ctx, c, index, treePath, entryPaths)
+ commits, err := GetLastCommitForPaths(ctx, c.repo.LastCommitCache, index, treePath, entryPaths)
if err != nil {
return err
}
diff --git a/modules/git/last_commit_cache_nogogit.go b/modules/git/last_commit_cache_nogogit.go
index 030d5486b6..1f4d693a26 100644
--- a/modules/git/last_commit_cache_nogogit.go
+++ b/modules/git/last_commit_cache_nogogit.go
@@ -7,67 +7,18 @@
package git
import (
- "bufio"
"context"
-
- "code.gitea.io/gitea/modules/log"
)
-// LastCommitCache represents a cache to store last commit
-type LastCommitCache struct {
- repoPath string
- ttl func() int64
- repo *Repository
- commitCache map[string]*Commit
- cache Cache
-}
-
-// NewLastCommitCache creates a new last commit cache for repo
-func NewLastCommitCache(repoPath string, gitRepo *Repository, ttl func() int64, cache Cache) *LastCommitCache {
- if cache == nil {
+// CacheCommit will cache the commit from the gitRepository
+func (c *Commit) CacheCommit(ctx context.Context) error {
+ if c.repo.LastCommitCache == nil {
return nil
}
- return &LastCommitCache{
- repoPath: repoPath,
- repo: gitRepo,
- commitCache: make(map[string]*Commit),
- ttl: ttl,
- cache: cache,
- }
+ return c.recursiveCache(ctx, &c.Tree, "", 1)
}
-// Get get the last commit information by commit id and entry path
-func (c *LastCommitCache) Get(ref, entryPath string, wr WriteCloserError, rd *bufio.Reader) (interface{}, error) {
- v := c.cache.Get(c.getCacheKey(c.repoPath, ref, entryPath))
- if vs, ok := v.(string); ok {
- log.Debug("LastCommitCache hit level 1: [%s:%s:%s]", ref, entryPath, vs)
- if commit, ok := c.commitCache[vs]; ok {
- log.Debug("LastCommitCache hit level 2: [%s:%s:%s]", ref, entryPath, vs)
- return commit, nil
- }
- id, err := c.repo.ConvertToSHA1(vs)
- if err != nil {
- return nil, err
- }
- if _, err := wr.Write([]byte(vs + "\n")); err != nil {
- return nil, err
- }
- commit, err := c.repo.getCommitFromBatchReader(rd, id)
- if err != nil {
- return nil, err
- }
- c.commitCache[vs] = commit
- return commit, nil
- }
- return nil, nil
-}
-
-// CacheCommit will cache the commit from the gitRepository
-func (c *LastCommitCache) CacheCommit(ctx context.Context, commit *Commit) error {
- return c.recursiveCache(ctx, commit, &commit.Tree, "", 1)
-}
-
-func (c *LastCommitCache) recursiveCache(ctx context.Context, commit *Commit, tree *Tree, treePath string, level int) error {
+func (c *Commit) recursiveCache(ctx context.Context, tree *Tree, treePath string, level int) error {
if level == 0 {
return nil
}
@@ -82,7 +33,7 @@ func (c *LastCommitCache) recursiveCache(ctx context.Context, commit *Commit, tr
entryPaths[i] = entry.Name()
}
- _, err = WalkGitLog(ctx, c, commit.repo, commit, treePath, entryPaths...)
+ _, err = WalkGitLog(ctx, c.repo, c, treePath, entryPaths...)
if err != nil {
return err
}
@@ -94,7 +45,7 @@ func (c *LastCommitCache) recursiveCache(ctx context.Context, commit *Commit, tr
if err != nil {
return err
}
- if err := c.recursiveCache(ctx, commit, subTree, treeEntry.Name(), level-1); err != nil {
+ if err := c.recursiveCache(ctx, subTree, treeEntry.Name(), level-1); err != nil {
return err
}
}
diff --git a/modules/git/lfs.go b/modules/git/lfs.go
deleted file mode 100644
index c5d8354b6d..0000000000
--- a/modules/git/lfs.go
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 2021 The Gitea Authors. All rights reserved.
-// Use of this source code is governed by a MIT-style
-// license that can be found in the LICENSE file.
-
-package git
-
-import (
- "sync"
-
- logger "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
-)
-
-var once sync.Once
-
-// CheckLFSVersion will check lfs version, if not satisfied, then disable it.
-func CheckLFSVersion() {
- if setting.LFS.StartServer {
- // Disable LFS client hooks if installed for the current OS user
- // Needs at least git v2.1.2
- if CheckGitVersionAtLeast("2.1.2") != nil {
- setting.LFS.StartServer = false
- logger.Error("LFS server support needs at least Git v2.1.2")
- } else {
- once.Do(func() {
- globalCommandArgs = append(globalCommandArgs, "-c", "filter.lfs.required=",
- "-c", "filter.lfs.smudge=", "-c", "filter.lfs.clean=")
- })
- }
- }
-}
diff --git a/modules/git/log_name_status.go b/modules/git/log_name_status.go
index e1e117ff4b..469932525f 100644
--- a/modules/git/log_name_status.go
+++ b/modules/git/log_name_status.go
@@ -14,6 +14,8 @@ import (
"sort"
"strings"
+ "code.gitea.io/gitea/modules/container"
+
"github.com/djherbis/buffer"
"github.com/djherbis/nio/v3"
)
@@ -281,7 +283,7 @@ func (g *LogNameStatusRepoParser) Close() {
}
// WalkGitLog walks the git log --name-status for the head commit in the provided treepath and files
-func WalkGitLog(ctx context.Context, cache *LastCommitCache, repo *Repository, head *Commit, treepath string, paths ...string) (map[string]string, error) {
+func WalkGitLog(ctx context.Context, repo *Repository, head *Commit, treepath string, paths ...string) (map[string]string, error) {
headRef := head.ID.String()
tree, err := head.SubTree(treepath)
@@ -339,7 +341,7 @@ func WalkGitLog(ctx context.Context, cache *LastCommitCache, repo *Repository, h
lastEmptyParent := head.ID.String()
commitSinceLastEmptyParent := uint64(0)
commitSinceNextRestart := uint64(0)
- parentRemaining := map[string]bool{}
+ parentRemaining := make(container.Set[string])
changed := make([]bool, len(paths))
@@ -365,7 +367,7 @@ heaploop:
if current == nil {
break heaploop
}
- delete(parentRemaining, current.CommitID)
+ parentRemaining.Remove(current.CommitID)
if current.Paths != nil {
for i, found := range current.Paths {
if !found {
@@ -374,14 +376,14 @@ heaploop:
changed[i] = false
if results[i] == "" {
results[i] = current.CommitID
- if err := cache.Put(headRef, path.Join(treepath, paths[i]), current.CommitID); err != nil {
+ if err := repo.LastCommitCache.Put(headRef, path.Join(treepath, paths[i]), current.CommitID); err != nil {
return nil, err
}
delete(path2idx, paths[i])
remaining--
if results[0] == "" {
results[0] = current.CommitID
- if err := cache.Put(headRef, treepath, current.CommitID); err != nil {
+ if err := repo.LastCommitCache.Put(headRef, treepath, current.CommitID); err != nil {
return nil, err
}
delete(path2idx, "")
@@ -410,14 +412,12 @@ heaploop:
}
}
g = NewLogNameStatusRepoParser(ctx, repo.Path, lastEmptyParent, treepath, remainingPaths...)
- parentRemaining = map[string]bool{}
+ parentRemaining = make(container.Set[string])
nextRestart = (remaining * 3) / 4
continue heaploop
}
}
- for _, parent := range current.ParentIDs {
- parentRemaining[parent] = true
- }
+ parentRemaining.AddMultiple(current.ParentIDs...)
}
g.Close()
diff --git a/modules/git/notes_gogit.go b/modules/git/notes_gogit.go
index 76bc828957..fe6d1f1e58 100644
--- a/modules/git/notes_gogit.go
+++ b/modules/git/notes_gogit.go
@@ -83,7 +83,7 @@ func GetNote(ctx context.Context, repo *Repository, commitID string, note *Note)
log.Error("Unable to get the commit for the path %q. Error: %v", path, err)
return err
}
- note.Commit = convertCommit(lastCommits[path])
+ note.Commit = lastCommits[path]
return nil
}
diff --git a/modules/git/notes_nogogit.go b/modules/git/notes_nogogit.go
index 1476805dcd..ba216ce3e4 100644
--- a/modules/git/notes_nogogit.go
+++ b/modules/git/notes_nogogit.go
@@ -81,7 +81,7 @@ func GetNote(ctx context.Context, repo *Repository, commitID string, note *Note)
path = path[idx+1:]
}
- lastCommits, err := GetLastCommitForPaths(ctx, nil, notes, treePath, []string{path})
+ lastCommits, err := GetLastCommitForPaths(ctx, notes, treePath, []string{path})
if err != nil {
log.Error("Unable to get the commit for the path %q. Error: %v", treePath, err)
return err
diff --git a/modules/git/parse_nogogit.go b/modules/git/parse_nogogit.go
index 6dc4900992..fb5b63def9 100644
--- a/modules/git/parse_nogogit.go
+++ b/modules/git/parse_nogogit.go
@@ -22,70 +22,72 @@ func ParseTreeEntries(data []byte) ([]*TreeEntry, error) {
return parseTreeEntries(data, nil)
}
+var sepSpace = []byte{' '}
+
func parseTreeEntries(data []byte, ptree *Tree) ([]*TreeEntry, error) {
- entries := make([]*TreeEntry, 0, 10)
+ var err error
+ entries := make([]*TreeEntry, 0, bytes.Count(data, []byte{'\n'})+1)
for pos := 0; pos < len(data); {
- // expect line to be of the form " \t"
+ // expect line to be of the form:
+ // \t
+ // \t
+ posEnd := bytes.IndexByte(data[pos:], '\n')
+ if posEnd == -1 {
+ posEnd = len(data)
+ } else {
+ posEnd += pos
+ }
+ line := data[pos:posEnd]
+ posTab := bytes.IndexByte(line, '\t')
+ if posTab == -1 {
+ return nil, fmt.Errorf("invalid ls-tree output (no tab): %q", line)
+ }
+
entry := new(TreeEntry)
entry.ptree = ptree
- if pos+6 > len(data) {
- return nil, fmt.Errorf("Invalid ls-tree output: %s", string(data))
+
+ entryAttrs := line[:posTab]
+ entryName := line[posTab+1:]
+
+ entryMode, entryAttrs, _ := bytes.Cut(entryAttrs, sepSpace)
+ _ /* entryType */, entryAttrs, _ = bytes.Cut(entryAttrs, sepSpace) // the type is not used, the mode is enough to determine the type
+ entryObjectID, entryAttrs, _ := bytes.Cut(entryAttrs, sepSpace)
+ if len(entryAttrs) > 0 {
+ entrySize := entryAttrs // the last field is the space-padded-size
+ entry.size, _ = strconv.ParseInt(strings.TrimSpace(string(entrySize)), 10, 64)
+ entry.sized = true
}
- switch string(data[pos : pos+6]) {
+
+ switch string(entryMode) {
case "100644":
entry.entryMode = EntryModeBlob
- pos += 12 // skip over "100644 blob "
case "100755":
entry.entryMode = EntryModeExec
- pos += 12 // skip over "100755 blob "
case "120000":
entry.entryMode = EntryModeSymlink
- pos += 12 // skip over "120000 blob "
case "160000":
entry.entryMode = EntryModeCommit
- pos += 14 // skip over "160000 object "
- case "040000":
+ case "040000", "040755": // git uses 040000 for tree object, but some users may get 040755 for unknown reasons
entry.entryMode = EntryModeTree
- pos += 12 // skip over "040000 tree "
default:
- return nil, fmt.Errorf("unknown type: %v", string(data[pos:pos+6]))
+ return nil, fmt.Errorf("unknown type: %v", string(entryMode))
}
- if pos+40 > len(data) {
- return nil, fmt.Errorf("Invalid ls-tree output: %s", string(data))
- }
- id, err := NewIDFromString(string(data[pos : pos+40]))
+ entry.ID, err = NewIDFromString(string(entryObjectID))
if err != nil {
- return nil, fmt.Errorf("Invalid ls-tree output: %v", err)
- }
- entry.ID = id
- pos += 41 // skip over sha and trailing space
-
- end := pos + bytes.IndexByte(data[pos:], '\t')
- if end < pos {
- return nil, fmt.Errorf("Invalid ls-tree -l output: %s", string(data))
- }
- entry.size, _ = strconv.ParseInt(strings.TrimSpace(string(data[pos:end])), 10, 64)
- entry.sized = true
-
- pos = end + 1
-
- end = pos + bytes.IndexByte(data[pos:], '\n')
- if end < pos {
- return nil, fmt.Errorf("Invalid ls-tree output: %s", string(data))
+ return nil, fmt.Errorf("invalid ls-tree output (invalid object id): %q, err: %w", line, err)
}
- // In case entry name is surrounded by double quotes(it happens only in git-shell).
- if data[pos] == '"' {
- entry.name, err = strconv.Unquote(string(data[pos:end]))
+ if len(entryName) > 0 && entryName[0] == '"' {
+ entry.name, err = strconv.Unquote(string(entryName))
if err != nil {
- return nil, fmt.Errorf("Invalid ls-tree output: %v", err)
+ return nil, fmt.Errorf("invalid ls-tree output (invalid name): %q, err: %w", line, err)
}
} else {
- entry.name = string(data[pos:end])
+ entry.name = string(entryName)
}
- pos = end + 1
+ pos = posEnd + 1
entries = append(entries, entry)
}
return entries, nil
@@ -119,7 +121,7 @@ loop:
entry.entryMode = EntryModeSymlink
case "160000":
entry.entryMode = EntryModeCommit
- case "40000":
+ case "40000", "40755": // git uses 40000 for tree object, but some users may get 40755 for unknown reasons
entry.entryMode = EntryModeTree
default:
log.Debug("Unknown mode: %v", string(mode))
diff --git a/modules/git/parse_nogogit_test.go b/modules/git/parse_nogogit_test.go
index 483f96e9a7..cecd3960da 100644
--- a/modules/git/parse_nogogit_test.go
+++ b/modules/git/parse_nogogit_test.go
@@ -12,7 +12,7 @@ import (
"github.com/stretchr/testify/assert"
)
-func TestParseTreeEntries(t *testing.T) {
+func TestParseTreeEntriesLong(t *testing.T) {
testCases := []struct {
Input string
Expected []*TreeEntry
@@ -59,11 +59,47 @@ func TestParseTreeEntries(t *testing.T) {
assert.NoError(t, err)
assert.Len(t, entries, len(testCase.Expected))
for i, entry := range entries {
- assert.EqualValues(t, testCase.Expected[i].ID, entry.ID)
- assert.EqualValues(t, testCase.Expected[i].name, entry.name)
- assert.EqualValues(t, testCase.Expected[i].entryMode, entry.entryMode)
- assert.EqualValues(t, testCase.Expected[i].sized, entry.sized)
- assert.EqualValues(t, testCase.Expected[i].size, entry.size)
+ assert.EqualValues(t, testCase.Expected[i], entry)
}
}
}
+
+func TestParseTreeEntriesShort(t *testing.T) {
+ testCases := []struct {
+ Input string
+ Expected []*TreeEntry
+ }{
+ {
+ Input: `100644 blob ea0d83c9081af9500ac9f804101b3fd0a5c293af README.md
+040000 tree 84b90550547016f73c5dd3f50dea662389e67b6d assets
+`,
+ Expected: []*TreeEntry{
+ {
+ ID: MustIDFromString("ea0d83c9081af9500ac9f804101b3fd0a5c293af"),
+ name: "README.md",
+ entryMode: EntryModeBlob,
+ },
+ {
+ ID: MustIDFromString("84b90550547016f73c5dd3f50dea662389e67b6d"),
+ name: "assets",
+ entryMode: EntryModeTree,
+ },
+ },
+ },
+ }
+ for _, testCase := range testCases {
+ entries, err := ParseTreeEntries([]byte(testCase.Input))
+ assert.NoError(t, err)
+ assert.Len(t, entries, len(testCase.Expected))
+ for i, entry := range entries {
+ assert.EqualValues(t, testCase.Expected[i], entry)
+ }
+ }
+}
+
+func TestParseTreeEntriesInvalid(t *testing.T) {
+ // there was a panic: "runtime error: slice bounds out of range" when the input was invalid: #20315
+ entries, err := ParseTreeEntries([]byte("100644 blob ea0d83c9081af9500ac9f804101b3fd0a5c293af"))
+ assert.Error(t, err)
+ assert.Len(t, entries, 0)
+}
diff --git a/modules/git/ref.go b/modules/git/ref.go
index 9fd071ce58..2f459148a2 100644
--- a/modules/git/ref.go
+++ b/modules/git/ref.go
@@ -4,7 +4,10 @@
package git
-import "strings"
+import (
+ "regexp"
+ "strings"
+)
const (
// RemotePrefix is the base directory of the remotes information of git.
@@ -15,6 +18,29 @@ const (
pullLen = len(PullPrefix)
)
+// refNamePatternInvalid is regular expression with unallowed characters in git reference name
+// They cannot have ASCII control characters (i.e. bytes whose values are lower than \040, or \177 DEL), space, tilde ~, caret ^, or colon : anywhere.
+// They cannot have question-mark ?, asterisk *, or open bracket [ anywhere
+var refNamePatternInvalid = regexp.MustCompile(
+ `[\000-\037\177 \\~^:?*[]|` + // No absolutely invalid characters
+ `(?:^[/.])|` + // Not HasPrefix("/") or "."
+ `(?:/\.)|` + // no "/."
+ `(?:\.lock$)|(?:\.lock/)|` + // No ".lock/"" or ".lock" at the end
+ `(?:\.\.)|` + // no ".." anywhere
+ `(?://)|` + // no "//" anywhere
+ `(?:@{)|` + // no "@{"
+ `(?:[/.]$)|` + // no terminal '/' or '.'
+ `(?:^@$)`) // Not "@"
+
+// IsValidRefPattern ensures that the provided string could be a valid reference
+func IsValidRefPattern(name string) bool {
+ return !refNamePatternInvalid.MatchString(name)
+}
+
+func SanitizeRefPattern(name string) string {
+ return refNamePatternInvalid.ReplaceAllString(name, "_")
+}
+
// Reference represents a Git ref.
type Reference struct {
Name string
diff --git a/modules/git/repo_attribute.go b/modules/git/repo_attribute.go
index 1305e6f224..21ff93e498 100644
--- a/modules/git/repo_attribute.go
+++ b/modules/git/repo_attribute.go
@@ -191,8 +191,8 @@ func (c *CheckAttributeReader) Run() error {
// CheckPath check attr for given path
func (c *CheckAttributeReader) CheckPath(path string) (rs map[string]string, err error) {
defer func() {
- if err != nil {
- log.Error("CheckPath returns error: %v", err)
+ if err != nil && err != c.ctx.Err() {
+ log.Error("Unexpected error when checking path %s in %s. Error: %v", path, c.Repo.Path, err)
}
}()
diff --git a/modules/git/repo_base_gogit.go b/modules/git/repo_base_gogit.go
index cd2ca25dfb..8fe9c404c3 100644
--- a/modules/git/repo_base_gogit.go
+++ b/modules/git/repo_base_gogit.go
@@ -31,7 +31,8 @@ type Repository struct {
gogitStorage *filesystem.Storage
gpgSettings *GPGSettings
- Ctx context.Context
+ Ctx context.Context
+ LastCommitCache *LastCommitCache
}
// openRepositoryWithDefaultContext opens the repository at the given path with DefaultContext.
@@ -79,6 +80,8 @@ func (repo *Repository) Close() (err error) {
if err := repo.gogitStorage.Close(); err != nil {
gitealog.Error("Error closing storage: %v", err)
}
+ repo.LastCommitCache = nil
+ repo.tagCache = nil
return
}
diff --git a/modules/git/repo_base_nogogit.go b/modules/git/repo_base_nogogit.go
index 63c278c261..56af2c640f 100644
--- a/modules/git/repo_base_nogogit.go
+++ b/modules/git/repo_base_nogogit.go
@@ -32,7 +32,8 @@ type Repository struct {
checkReader *bufio.Reader
checkWriter WriteCloserError
- Ctx context.Context
+ Ctx context.Context
+ LastCommitCache *LastCommitCache
}
// openRepositoryWithDefaultContext opens the repository at the given path with DefaultContext.
@@ -101,5 +102,7 @@ func (repo *Repository) Close() (err error) {
repo.checkReader = nil
repo.checkWriter = nil
}
+ repo.LastCommitCache = nil
+ repo.tagCache = nil
return err
}
diff --git a/modules/git/repo_branch_nogogit.go b/modules/git/repo_branch_nogogit.go
index 2983a35ca5..91112d0190 100644
--- a/modules/git/repo_branch_nogogit.go
+++ b/modules/git/repo_branch_nogogit.go
@@ -63,34 +63,32 @@ func (repo *Repository) IsBranchExist(name string) bool {
// GetBranchNames returns branches from the repository, skipping skip initial branches and
// returning at most limit branches, or all branches if limit is 0.
func (repo *Repository) GetBranchNames(skip, limit int) ([]string, int, error) {
- return callShowRef(repo.Ctx, repo.Path, BranchPrefix, "--heads", skip, limit)
+ return callShowRef(repo.Ctx, repo.Path, BranchPrefix, []string{BranchPrefix, "--sort=-committerdate"}, skip, limit)
}
// WalkReferences walks all the references from the repository
func WalkReferences(ctx context.Context, repoPath string, walkfn func(sha1, refname string) error) (int, error) {
- return walkShowRef(ctx, repoPath, "", 0, 0, walkfn)
+ return walkShowRef(ctx, repoPath, nil, 0, 0, walkfn)
}
// WalkReferences walks all the references from the repository
// refType should be empty, ObjectTag or ObjectBranch. All other values are equivalent to empty.
func (repo *Repository) WalkReferences(refType ObjectType, skip, limit int, walkfn func(sha1, refname string) error) (int, error) {
- var arg string
+ var args []string
switch refType {
case ObjectTag:
- arg = "--tags"
+ args = []string{TagPrefix, "--sort=-taggerdate"}
case ObjectBranch:
- arg = "--heads"
- default:
- arg = ""
+ args = []string{BranchPrefix, "--sort=-committerdate"}
}
- return walkShowRef(repo.Ctx, repo.Path, arg, skip, limit, walkfn)
+ return walkShowRef(repo.Ctx, repo.Path, args, skip, limit, walkfn)
}
// callShowRef return refs, if limit = 0 it will not limit
-func callShowRef(ctx context.Context, repoPath, prefix, arg string, skip, limit int) (branchNames []string, countAll int, err error) {
- countAll, err = walkShowRef(ctx, repoPath, arg, skip, limit, func(_, branchName string) error {
- branchName = strings.TrimPrefix(branchName, prefix)
+func callShowRef(ctx context.Context, repoPath, trimPrefix string, extraArgs []string, skip, limit int) (branchNames []string, countAll int, err error) {
+ countAll, err = walkShowRef(ctx, repoPath, extraArgs, skip, limit, func(_, branchName string) error {
+ branchName = strings.TrimPrefix(branchName, trimPrefix)
branchNames = append(branchNames, branchName)
return nil
@@ -98,7 +96,7 @@ func callShowRef(ctx context.Context, repoPath, prefix, arg string, skip, limit
return branchNames, countAll, err
}
-func walkShowRef(ctx context.Context, repoPath, arg string, skip, limit int, walkfn func(sha1, refname string) error) (countAll int, err error) {
+func walkShowRef(ctx context.Context, repoPath string, extraArgs []string, skip, limit int, walkfn func(sha1, refname string) error) (countAll int, err error) {
stdoutReader, stdoutWriter := io.Pipe()
defer func() {
_ = stdoutReader.Close()
@@ -107,10 +105,8 @@ func walkShowRef(ctx context.Context, repoPath, arg string, skip, limit int, wal
go func() {
stderrBuilder := &strings.Builder{}
- args := []string{"show-ref"}
- if arg != "" {
- args = append(args, arg)
- }
+ args := []string{"for-each-ref", "--format=%(objectname) %(refname)"}
+ args = append(args, extraArgs...)
err := NewCommand(ctx, args...).Run(&RunOpts{
Dir: repoPath,
Stdout: stdoutWriter,
@@ -194,7 +190,7 @@ func walkShowRef(ctx context.Context, repoPath, arg string, skip, limit int, wal
// GetRefsBySha returns all references filtered with prefix that belong to a sha commit hash
func (repo *Repository) GetRefsBySha(sha, prefix string) ([]string, error) {
var revList []string
- _, err := walkShowRef(repo.Ctx, repo.Path, "", 0, 0, func(walkSha, refname string) error {
+ _, err := walkShowRef(repo.Ctx, repo.Path, nil, 0, 0, func(walkSha, refname string) error {
if walkSha == sha && strings.HasPrefix(refname, prefix) {
revList = append(revList, refname)
}
diff --git a/modules/git/repo_branch_test.go b/modules/git/repo_branch_test.go
index 56f7387097..58a738e28b 100644
--- a/modules/git/repo_branch_test.go
+++ b/modules/git/repo_branch_test.go
@@ -22,14 +22,14 @@ func TestRepository_GetBranches(t *testing.T) {
assert.NoError(t, err)
assert.Len(t, branches, 2)
assert.EqualValues(t, 3, countAll)
- assert.ElementsMatch(t, []string{"branch1", "branch2"}, branches)
+ assert.ElementsMatch(t, []string{"master", "branch2"}, branches)
branches, countAll, err = bareRepo1.GetBranchNames(0, 0)
assert.NoError(t, err)
assert.Len(t, branches, 3)
assert.EqualValues(t, 3, countAll)
- assert.ElementsMatch(t, []string{"branch1", "branch2", "master"}, branches)
+ assert.ElementsMatch(t, []string{"master", "branch2", "branch1"}, branches)
branches, countAll, err = bareRepo1.GetBranchNames(5, 1)
diff --git a/modules/git/repo_commit.go b/modules/git/repo_commit.go
index e6fec4d1a3..ec72593b80 100644
--- a/modules/git/repo_commit.go
+++ b/modules/git/repo_commit.go
@@ -7,10 +7,13 @@ package git
import (
"bytes"
+ "encoding/hex"
+ "fmt"
"io"
"strconv"
"strings"
+ "code.gitea.io/gitea/modules/cache"
"code.gitea.io/gitea/modules/setting"
)
@@ -151,14 +154,14 @@ func (repo *Repository) searchCommits(id SHA1, opts SearchCommitsOptions) ([]*Co
// then let's iterate over them
if len(opts.Keywords) > 0 {
for _, v := range opts.Keywords {
- // ignore anything below 4 characters as too unspecific
- if len(v) >= 4 {
+ // ignore anything not matching a valid sha pattern
+ if IsValidSHAPattern(v) {
// create new git log command with 1 commit limit
hashCmd := NewCommand(repo.Ctx, "log", "-1", prettyLogFormat)
// add previous arguments except for --grep and --all
hashCmd.AddArguments(args...)
// add keyword as
- hashCmd.AddArguments(v)
+ hashCmd.AddDynamicArguments(v)
// search with given constraints for commit matching sha hash of v
hashMatching, _, err := hashCmd.RunStdBytes(&RunOpts{Dir: repo.Path})
@@ -208,14 +211,17 @@ func (repo *Repository) CommitsByFileAndRange(revision, file string, page int) (
}()
go func() {
stderr := strings.Builder{}
- err := NewCommand(repo.Ctx, "log", revision, "--follow",
+ gitCmd := NewCommand(repo.Ctx, "rev-list",
"--max-count="+strconv.Itoa(setting.Git.CommitsRangeSize*page),
- prettyLogFormat, "--", file).
- Run(&RunOpts{
- Dir: repo.Path,
- Stdout: stdoutWriter,
- Stderr: &stderr,
- })
+ "--skip="+strconv.Itoa(skip),
+ )
+ gitCmd.AddDynamicArguments(revision)
+ gitCmd.AddArguments("--", file)
+ err := gitCmd.Run(&RunOpts{
+ Dir: repo.Path,
+ Stdout: stdoutWriter,
+ Stderr: &stderr,
+ })
if err != nil {
_ = stdoutWriter.CloseWithError(ConcatenateError(err, (&stderr).String()))
} else {
@@ -223,32 +229,30 @@ func (repo *Repository) CommitsByFileAndRange(revision, file string, page int) (
}
}()
- if skip > 0 {
- _, err := io.CopyN(io.Discard, stdoutReader, int64(skip*41))
- if err != nil {
+ commits := []*Commit{}
+ shaline := [41]byte{}
+ var sha1 SHA1
+ for {
+ n, err := io.ReadFull(stdoutReader, shaline[:])
+ if err != nil || n < 40 {
if err == io.EOF {
- return []*Commit{}, nil
+ err = nil
}
- _ = stdoutReader.CloseWithError(err)
+ return commits, err
+ }
+ n, err = hex.Decode(sha1[:], shaline[0:40])
+ if n != 20 {
+ err = fmt.Errorf("invalid sha %q", string(shaline[:40]))
+ }
+ if err != nil {
return nil, err
}
+ commit, err := repo.getCommit(sha1)
+ if err != nil {
+ return nil, err
+ }
+ commits = append(commits, commit)
}
-
- stdout, err := io.ReadAll(stdoutReader)
- if err != nil {
- return nil, err
- }
- return repo.parsePrettyFormatLogToList(stdout)
-}
-
-// CommitsByFileAndRangeNoFollow return the commits according revision file and the page
-func (repo *Repository) CommitsByFileAndRangeNoFollow(revision, file string, page int) ([]*Commit, error) {
- stdout, _, err := NewCommand(repo.Ctx, "log", revision, "--skip="+strconv.Itoa((page-1)*50),
- "--max-count="+strconv.Itoa(setting.Git.CommitsRangeSize), prettyLogFormat, "--", file).RunStdBytes(&RunOpts{Dir: repo.Path})
- if err != nil {
- return nil, err
- }
- return repo.parsePrettyFormatLogToList(stdout)
}
// FilesCountBetween return the number of files changed between two commits
@@ -434,3 +438,20 @@ func (repo *Repository) IsCommitInBranch(commitID, branch string) (r bool, err e
}
return len(stdout) > 0, err
}
+
+func (repo *Repository) AddLastCommitCache(cacheKey, fullName, sha string) error {
+ if repo.LastCommitCache == nil {
+ commitsCount, err := cache.GetInt64(cacheKey, func() (int64, error) {
+ commit, err := repo.GetCommit(sha)
+ if err != nil {
+ return 0, err
+ }
+ return commit.CommitsCount()
+ })
+ if err != nil {
+ return err
+ }
+ repo.LastCommitCache = NewLastCommitCache(commitsCount, fullName, repo, cache.GetCache())
+ }
+ return nil
+}
diff --git a/modules/git/repo_commit_nogogit.go b/modules/git/repo_commit_nogogit.go
index e528af0ffb..efea307b37 100644
--- a/modules/git/repo_commit_nogogit.go
+++ b/modules/git/repo_commit_nogogit.go
@@ -138,7 +138,7 @@ func (repo *Repository) getCommitFromBatchReader(rd *bufio.Reader, id SHA1) (*Co
// ConvertToSHA1 returns a Hash object from a potential ID string
func (repo *Repository) ConvertToSHA1(commitID string) (SHA1, error) {
- if len(commitID) == 40 && SHAPattern.MatchString(commitID) {
+ if len(commitID) == 40 && IsValidSHAPattern(commitID) {
sha1, err := NewIDFromString(commitID)
if err == nil {
return sha1, nil
diff --git a/modules/git/repo_compare.go b/modules/git/repo_compare.go
index 577c9f475e..589a7eae00 100644
--- a/modules/git/repo_compare.go
+++ b/modules/git/repo_compare.go
@@ -40,7 +40,7 @@ func (repo *Repository) GetMergeBase(tmpRemote, base, head string) (string, stri
if tmpRemote != "origin" {
tmpBaseName := RemotePrefix + tmpRemote + "/tmp_" + base
// Fetch commit into a temporary branch in order to be able to handle commits and tags
- _, _, err := NewCommand(repo.Ctx, "fetch", tmpRemote, base+":"+tmpBaseName).RunStdString(&RunOpts{Dir: repo.Path})
+ _, _, err := NewCommand(repo.Ctx, "fetch", "--no-tags", tmpRemote, "--", base+":"+tmpBaseName).RunStdString(&RunOpts{Dir: repo.Path})
if err == nil {
base = tmpBaseName
}
diff --git a/modules/git/repo_compare_test.go b/modules/git/repo_compare_test.go
index 245920c2bd..63f7254dea 100644
--- a/modules/git/repo_compare_test.go
+++ b/modules/git/repo_compare_test.go
@@ -10,19 +10,16 @@ import (
"path/filepath"
"testing"
- "code.gitea.io/gitea/modules/util"
-
"github.com/stretchr/testify/assert"
)
func TestGetFormatPatch(t *testing.T) {
bareRepo1Path := filepath.Join(testReposDir, "repo1_bare")
- clonedPath, err := cloneRepo(bareRepo1Path, "repo1_TestGetFormatPatch")
+ clonedPath, err := cloneRepo(t, bareRepo1Path)
if err != nil {
assert.NoError(t, err)
return
}
- defer util.RemoveAll(clonedPath)
repo, err := openRepositoryWithDefaultContext(clonedPath)
if err != nil {
@@ -84,12 +81,11 @@ func TestReadWritePullHead(t *testing.T) {
bareRepo1Path := filepath.Join(testReposDir, "repo1_bare")
// As we are writing we should clone the repository first
- clonedPath, err := cloneRepo(bareRepo1Path, "TestReadWritePullHead")
+ clonedPath, err := cloneRepo(t, bareRepo1Path)
if err != nil {
assert.NoError(t, err)
return
}
- defer util.RemoveAll(clonedPath)
repo, err := openRepositoryWithDefaultContext(clonedPath)
if err != nil {
diff --git a/modules/git/repo_language_stats_nogogit.go b/modules/git/repo_language_stats_nogogit.go
index d237924f92..7388ef403b 100644
--- a/modules/git/repo_language_stats_nogogit.go
+++ b/modules/git/repo_language_stats_nogogit.go
@@ -57,7 +57,7 @@ func (repo *Repository) GetLanguageStats(commitID string) (map[string]int64, err
tree := commit.Tree
- entries, err := tree.ListEntriesRecursive()
+ entries, err := tree.ListEntriesRecursiveWithSize()
if err != nil {
return nil, err
}
diff --git a/modules/git/repo_stats.go b/modules/git/repo_stats.go
index c0c91c6fc6..7eccf9a190 100644
--- a/modules/git/repo_stats.go
+++ b/modules/git/repo_stats.go
@@ -13,6 +13,8 @@ import (
"strconv"
"strings"
"time"
+
+ "code.gitea.io/gitea/modules/container"
)
// CodeActivityStats represents git statistics data
@@ -59,15 +61,15 @@ func (repo *Repository) GetCodeActivityStats(fromTime time.Time, branch string)
_ = stdoutWriter.Close()
}()
- args := []string{"log", "--numstat", "--no-merges", "--pretty=format:---%n%h%n%aN%n%aE%n", "--date=iso", fmt.Sprintf("--since='%s'", since)}
+ gitCmd := NewCommand(repo.Ctx, "log", "--numstat", "--no-merges", "--pretty=format:---%n%h%n%aN%n%aE%n", "--date=iso", fmt.Sprintf("--since='%s'", since))
if len(branch) == 0 {
- args = append(args, "--branches=*")
+ gitCmd.AddArguments("--branches=*")
} else {
- args = append(args, "--first-parent", branch)
+ gitCmd.AddArguments("--first-parent").AddDynamicArguments(branch)
}
stderr := new(strings.Builder)
- err = NewCommand(repo.Ctx, args...).Run(&RunOpts{
+ err = gitCmd.Run(&RunOpts{
Env: []string{},
Dir: repo.Path,
Stdout: stdoutWriter,
@@ -80,7 +82,7 @@ func (repo *Repository) GetCodeActivityStats(fromTime time.Time, branch string)
stats.Additions = 0
stats.Deletions = 0
authors := make(map[string]*CodeActivityAuthor)
- files := make(map[string]bool)
+ files := make(container.Set[string])
var author string
p := 0
for scanner.Scan() {
@@ -119,9 +121,7 @@ func (repo *Repository) GetCodeActivityStats(fromTime time.Time, branch string)
stats.Deletions += c
}
}
- if _, ok := files[parts[2]]; !ok {
- files[parts[2]] = true
- }
+ files.Add(parts[2])
}
}
}
diff --git a/modules/git/repo_tag_nogogit.go b/modules/git/repo_tag_nogogit.go
index 9a574666f8..72c1c979dc 100644
--- a/modules/git/repo_tag_nogogit.go
+++ b/modules/git/repo_tag_nogogit.go
@@ -26,7 +26,7 @@ func (repo *Repository) IsTagExist(name string) bool {
// GetTags returns all tags of the repository.
// returning at most limit tags, or all if limit is 0.
func (repo *Repository) GetTags(skip, limit int) (tags []string, err error) {
- tags, _, err = callShowRef(repo.Ctx, repo.Path, TagPrefix, "--tags", skip, limit)
+ tags, _, err = callShowRef(repo.Ctx, repo.Path, TagPrefix, []string{TagPrefix, "--sort=-taggerdate"}, skip, limit)
return tags, err
}
diff --git a/modules/git/repo_tag_test.go b/modules/git/repo_tag_test.go
index 9d84672862..6a00473bb7 100644
--- a/modules/git/repo_tag_test.go
+++ b/modules/git/repo_tag_test.go
@@ -8,8 +8,6 @@ import (
"path/filepath"
"testing"
- "code.gitea.io/gitea/modules/util"
-
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@@ -38,12 +36,11 @@ func TestRepository_GetTags(t *testing.T) {
func TestRepository_GetTag(t *testing.T) {
bareRepo1Path := filepath.Join(testReposDir, "repo1_bare")
- clonedPath, err := cloneRepo(bareRepo1Path, "TestRepository_GetTag")
+ clonedPath, err := cloneRepo(t, bareRepo1Path)
if err != nil {
assert.NoError(t, err)
return
}
- defer util.RemoveAll(clonedPath)
bareRepo1, err := openRepositoryWithDefaultContext(clonedPath)
if err != nil {
@@ -143,12 +140,11 @@ func TestRepository_GetTag(t *testing.T) {
func TestRepository_GetAnnotatedTag(t *testing.T) {
bareRepo1Path := filepath.Join(testReposDir, "repo1_bare")
- clonedPath, err := cloneRepo(bareRepo1Path, "TestRepository_GetAnnotatedTag")
+ clonedPath, err := cloneRepo(t, bareRepo1Path)
if err != nil {
assert.NoError(t, err)
return
}
- defer util.RemoveAll(clonedPath)
bareRepo1, err := openRepositoryWithDefaultContext(clonedPath)
if err != nil {
diff --git a/modules/git/sha1.go b/modules/git/sha1.go
index 2da74733df..15f282c6e4 100644
--- a/modules/git/sha1.go
+++ b/modules/git/sha1.go
@@ -19,7 +19,12 @@ const EmptySHA = "0000000000000000000000000000000000000000"
const EmptyTreeSHA = "4b825dc642cb6eb9a060e54bf8d69288fbee4904"
// SHAPattern can be used to determine if a string is an valid sha
-var SHAPattern = regexp.MustCompile(`^[0-9a-f]{4,40}$`)
+var shaPattern = regexp.MustCompile(`^[0-9a-f]{4,40}$`)
+
+// IsValidSHAPattern will check if the provided string matches the SHA Pattern
+func IsValidSHAPattern(sha string) bool {
+ return shaPattern.MatchString(sha)
+}
// MustID always creates a new SHA1 from a [20]byte array with no validation of input.
func MustID(b []byte) SHA1 {
diff --git a/modules/git/sha1_test.go b/modules/git/sha1_test.go
new file mode 100644
index 0000000000..c5c00f5445
--- /dev/null
+++ b/modules/git/sha1_test.go
@@ -0,0 +1,21 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package git
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestIsValidSHAPattern(t *testing.T) {
+ assert.True(t, IsValidSHAPattern("fee1"))
+ assert.True(t, IsValidSHAPattern("abc000"))
+ assert.True(t, IsValidSHAPattern("9023902390239023902390239023902390239023"))
+ assert.False(t, IsValidSHAPattern("90239023902390239023902390239023902390239023"))
+ assert.False(t, IsValidSHAPattern("abc"))
+ assert.False(t, IsValidSHAPattern("123g"))
+ assert.False(t, IsValidSHAPattern("some random text"))
+}
diff --git a/modules/git/signature_gogit.go b/modules/git/signature_gogit.go
index fe81cd97df..6f1c98420d 100644
--- a/modules/git/signature_gogit.go
+++ b/modules/git/signature_gogit.go
@@ -19,8 +19,10 @@ import (
type Signature = object.Signature
// Helper to get a signature from the commit line, which looks like these:
-// author Patrick Gundlach 1378823654 +0200
-// author Patrick Gundlach Thu, 07 Apr 2005 22:13:13 +0200
+//
+// author Patrick Gundlach 1378823654 +0200
+// author Patrick Gundlach Thu, 07 Apr 2005 22:13:13 +0200
+//
// but without the "author " at the beginning (this method should)
// be used for author and committer.
//
diff --git a/modules/git/signature_nogogit.go b/modules/git/signature_nogogit.go
index 2fc8dde04d..07a3b79f1e 100644
--- a/modules/git/signature_nogogit.go
+++ b/modules/git/signature_nogogit.go
@@ -37,8 +37,10 @@ func (s *Signature) Decode(b []byte) {
}
// Helper to get a signature from the commit line, which looks like these:
-// author Patrick Gundlach 1378823654 +0200
-// author Patrick Gundlach Thu, 07 Apr 2005 22:13:13 +0200
+//
+// author Patrick Gundlach 1378823654 +0200
+// author Patrick Gundlach Thu, 07 Apr 2005 22:13:13 +0200
+//
// but without the "author " at the beginning (this method should)
// be used for author and committer.
func newSignatureFromCommitline(line []byte) (sig *Signature, err error) {
diff --git a/modules/git/tree_gogit.go b/modules/git/tree_gogit.go
index 54f8e140fb..480c5e44da 100644
--- a/modules/git/tree_gogit.go
+++ b/modules/git/tree_gogit.go
@@ -57,8 +57,8 @@ func (t *Tree) ListEntries() (Entries, error) {
return entries, nil
}
-// ListEntriesRecursive returns all entries of current tree recursively including all subtrees
-func (t *Tree) ListEntriesRecursive() (Entries, error) {
+// ListEntriesRecursiveWithSize returns all entries of current tree recursively including all subtrees
+func (t *Tree) ListEntriesRecursiveWithSize() (Entries, error) {
if t.gogitTree == nil {
err := t.loadTreeObject()
if err != nil {
@@ -92,3 +92,8 @@ func (t *Tree) ListEntriesRecursive() (Entries, error) {
return entries, nil
}
+
+// ListEntriesRecursiveFast is the alias of ListEntriesRecursiveWithSize for the gogit version
+func (t *Tree) ListEntriesRecursiveFast() (Entries, error) {
+ return t.ListEntriesRecursiveWithSize()
+}
diff --git a/modules/git/tree_nogogit.go b/modules/git/tree_nogogit.go
index 7defb064a4..d61d19e4c2 100644
--- a/modules/git/tree_nogogit.go
+++ b/modules/git/tree_nogogit.go
@@ -99,13 +99,16 @@ func (t *Tree) ListEntries() (Entries, error) {
return t.entries, err
}
-// ListEntriesRecursive returns all entries of current tree recursively including all subtrees
-func (t *Tree) ListEntriesRecursive() (Entries, error) {
+// listEntriesRecursive returns all entries of current tree recursively including all subtrees
+// extraArgs could be "-l" to get the size, which is slower
+func (t *Tree) listEntriesRecursive(extraArgs ...string) (Entries, error) {
if t.entriesRecursiveParsed {
return t.entriesRecursive, nil
}
- stdout, _, runErr := NewCommand(t.repo.Ctx, "ls-tree", "-t", "-l", "-r", t.ID.String()).RunStdBytes(&RunOpts{Dir: t.repo.Path})
+ args := append([]string{"ls-tree", "-t", "-r"}, extraArgs...)
+ args = append(args, t.ID.String())
+ stdout, _, runErr := NewCommand(t.repo.Ctx, args...).RunStdBytes(&RunOpts{Dir: t.repo.Path})
if runErr != nil {
return nil, runErr
}
@@ -118,3 +121,13 @@ func (t *Tree) ListEntriesRecursive() (Entries, error) {
return t.entriesRecursive, err
}
+
+// ListEntriesRecursiveFast returns all entries of current tree recursively including all subtrees, no size
+func (t *Tree) ListEntriesRecursiveFast() (Entries, error) {
+ return t.listEntriesRecursive()
+}
+
+// ListEntriesRecursiveWithSize returns all entries of current tree recursively including all subtrees, with size
+func (t *Tree) ListEntriesRecursiveWithSize() (Entries, error) {
+ return t.listEntriesRecursive("--long")
+}
diff --git a/modules/git/utils.go b/modules/git/utils.go
index 1a7041433d..a439dabae1 100644
--- a/modules/git/utils.go
+++ b/modules/git/utils.go
@@ -100,7 +100,7 @@ func RefURL(repoURL, ref string) string {
return repoURL + "/src/branch/" + refName
case strings.HasPrefix(ref, TagPrefix):
return repoURL + "/src/tag/" + refName
- case !SHAPattern.MatchString(ref):
+ case !IsValidSHAPattern(ref):
// assume they mean a branch
return repoURL + "/src/branch/" + refName
default:
diff --git a/modules/gitgraph/graph.go b/modules/gitgraph/graph.go
index 271382525a..0f3c021344 100644
--- a/modules/gitgraph/graph.go
+++ b/modules/gitgraph/graph.go
@@ -24,19 +24,17 @@ func GetCommitGraph(r *git.Repository, page, maxAllowedColors int, hidePRRefs bo
page = 1
}
- args := make([]string, 0, 12+len(branches)+len(files))
-
- args = append(args, "--graph", "--date-order", "--decorate=full")
+ graphCmd := git.NewCommand(r.Ctx, "log", "--graph", "--date-order", "--decorate=full")
if hidePRRefs {
- args = append(args, "--exclude="+git.PullPrefix+"*")
+ graphCmd.AddArguments("--exclude=" + git.PullPrefix + "*")
}
if len(branches) == 0 {
- args = append(args, "--all")
+ graphCmd.AddArguments("--all")
}
- args = append(args,
+ graphCmd.AddArguments(
"-C",
"-M",
fmt.Sprintf("-n %d", setting.UI.GraphMaxCommitNum*page),
@@ -44,15 +42,12 @@ func GetCommitGraph(r *git.Repository, page, maxAllowedColors int, hidePRRefs bo
fmt.Sprintf("--pretty=format:%s", format))
if len(branches) > 0 {
- args = append(args, branches...)
+ graphCmd.AddDynamicArguments(branches...)
}
- args = append(args, "--")
if len(files) > 0 {
- args = append(args, files...)
+ graphCmd.AddArguments("--")
+ graphCmd.AddArguments(files...)
}
-
- graphCmd := git.NewCommand(r.Ctx, "log")
- graphCmd.AddArguments(args...)
graph := NewGraph()
stderr := new(strings.Builder)
diff --git a/modules/graceful/manager.go b/modules/graceful/manager.go
index 8766cfca0e..21f019fb56 100644
--- a/modules/graceful/manager.go
+++ b/modules/graceful/manager.go
@@ -24,11 +24,12 @@ const (
stateTerminate
)
-// There are three places that could inherit sockets:
+// There are some places that could inherit sockets:
//
// * HTTP or HTTPS main listener
+// * HTTP or HTTPS install listener
// * HTTP redirection fallback
-// * SSH
+// * Builtin SSH listener
//
// If you add an additional place you must increment this number
// and add a function to call manager.InformCleanup if it's not going to be used
@@ -305,8 +306,9 @@ func (g *Manager) setState(st state) {
g.state = st
}
-// InformCleanup tells the cleanup wait group that we have either taken a listener
-// or will not be taking a listener
+// InformCleanup tells the cleanup wait group that we have either taken a listener or will not be taking a listener.
+// At the moment the total number of servers (numberOfServersToCreate) are pre-defined as a const before global init,
+// so this function MUST be called if a server is not used.
func (g *Manager) InformCleanup() {
g.createServerWaitGroup.Done()
}
diff --git a/modules/graceful/manager_windows.go b/modules/graceful/manager_windows.go
index e7e619f53f..10c1d67b97 100644
--- a/modules/graceful/manager_windows.go
+++ b/modules/graceful/manager_windows.go
@@ -114,9 +114,9 @@ func (g *Manager) start() {
// Execute makes Manager implement svc.Handler
func (g *Manager) Execute(args []string, changes <-chan svc.ChangeRequest, status chan<- svc.Status) (svcSpecificEC bool, exitCode uint32) {
if setting.StartupTimeout > 0 {
- status <- svc.Status{State: svc.StartPending}
- } else {
status <- svc.Status{State: svc.StartPending, WaitHint: uint32(setting.StartupTimeout / time.Millisecond)}
+ } else {
+ status <- svc.Status{State: svc.StartPending}
}
log.Trace("Awaiting server start-up")
diff --git a/modules/graceful/net_unix.go b/modules/graceful/net_unix.go
index 680ff529af..c7524a79db 100644
--- a/modules/graceful/net_unix.go
+++ b/modules/graceful/net_unix.go
@@ -23,6 +23,7 @@ import (
const (
listenFDs = "LISTEN_FDS"
startFD = 3
+ unlinkFDs = "GITEA_UNLINK_FDS"
)
// In order to keep the working directory the same as when we started we record
@@ -33,8 +34,10 @@ var (
once = sync.Once{}
mutex = sync.Mutex{}
- providedListeners = []net.Listener{}
- activeListeners = []net.Listener{}
+ providedListenersToUnlink = []bool{}
+ activeListenersToUnlink = []bool{}
+ providedListeners = []net.Listener{}
+ activeListeners = []net.Listener{}
)
func getProvidedFDs() (savedErr error) {
@@ -53,6 +56,16 @@ func getProvidedFDs() (savedErr error) {
return
}
+ fdsToUnlinkStr := strings.Split(os.Getenv(unlinkFDs), ",")
+ providedListenersToUnlink = make([]bool, n)
+ for _, fdStr := range fdsToUnlinkStr {
+ i, err := strconv.Atoi(fdStr)
+ if err != nil || i < 0 || i >= n {
+ continue
+ }
+ providedListenersToUnlink[i] = true
+ }
+
for i := startFD; i < n+startFD; i++ {
file := os.NewFile(uintptr(i), fmt.Sprintf("listener_FD%d", i))
@@ -136,8 +149,11 @@ func GetListenerTCP(network string, address *net.TCPAddr) (*net.TCPListener, err
for i, l := range providedListeners {
if isSameAddr(l.Addr(), address) {
providedListeners = append(providedListeners[:i], providedListeners[i+1:]...)
+ needsUnlink := providedListenersToUnlink[i]
+ providedListenersToUnlink = append(providedListenersToUnlink[:i], providedListenersToUnlink[i+1:]...)
activeListeners = append(activeListeners, l)
+ activeListenersToUnlink = append(activeListenersToUnlink, needsUnlink)
return l.(*net.TCPListener), nil
}
}
@@ -148,6 +164,7 @@ func GetListenerTCP(network string, address *net.TCPAddr) (*net.TCPListener, err
return nil, err
}
activeListeners = append(activeListeners, l)
+ activeListenersToUnlink = append(activeListenersToUnlink, false)
return l, nil
}
@@ -166,9 +183,15 @@ func GetListenerUnix(network string, address *net.UnixAddr) (*net.UnixListener,
for i, l := range providedListeners {
if isSameAddr(l.Addr(), address) {
providedListeners = append(providedListeners[:i], providedListeners[i+1:]...)
+ needsUnlink := providedListenersToUnlink[i]
+ providedListenersToUnlink = append(providedListenersToUnlink[:i], providedListenersToUnlink[i+1:]...)
+
+ activeListenersToUnlink = append(activeListenersToUnlink, needsUnlink)
activeListeners = append(activeListeners, l)
unixListener := l.(*net.UnixListener)
- unixListener.SetUnlinkOnClose(true)
+ if needsUnlink {
+ unixListener.SetUnlinkOnClose(true)
+ }
return unixListener, nil
}
}
@@ -189,6 +212,7 @@ func GetListenerUnix(network string, address *net.UnixAddr) (*net.UnixListener,
}
activeListeners = append(activeListeners, l)
+ activeListenersToUnlink = append(activeListenersToUnlink, true)
return l, nil
}
@@ -223,3 +247,11 @@ func getActiveListeners() []net.Listener {
copy(listeners, activeListeners)
return listeners
}
+
+func getActiveListenersToUnlink() []bool {
+ mutex.Lock()
+ defer mutex.Unlock()
+ listenersToUnlink := make([]bool, len(activeListenersToUnlink))
+ copy(listenersToUnlink, activeListenersToUnlink)
+ return listenersToUnlink
+}
diff --git a/modules/graceful/restart_unix.go b/modules/graceful/restart_unix.go
index 2654ddfb94..1d0d1059e9 100644
--- a/modules/graceful/restart_unix.go
+++ b/modules/graceful/restart_unix.go
@@ -12,6 +12,7 @@ import (
"net"
"os"
"os/exec"
+ "strconv"
"strings"
"sync"
"syscall"
@@ -75,6 +76,20 @@ func RestartProcess() (int, error) {
}
env = append(env, fmt.Sprintf("%s=%d", listenFDs, len(listeners)))
+ sb := &strings.Builder{}
+ for i, unlink := range getActiveListenersToUnlink() {
+ if !unlink {
+ continue
+ }
+ _, _ = sb.WriteString(strconv.Itoa(i))
+ _, _ = sb.WriteString(",")
+ }
+ unlinkStr := sb.String()
+ if len(unlinkStr) > 0 {
+ unlinkStr = unlinkStr[:len(unlinkStr)-1]
+ env = append(env, fmt.Sprintf("%s=%s", unlinkFDs, unlinkStr))
+ }
+
allFiles := append([]*os.File{os.Stdin, os.Stdout, os.Stderr}, files...)
process, err := os.StartProcess(argv0, os.Args, &os.ProcAttr{
Dir: originalWD,
diff --git a/modules/graceful/server.go b/modules/graceful/server.go
index 159a9879df..30a460a943 100644
--- a/modules/graceful/server.go
+++ b/modules/graceful/server.go
@@ -16,6 +16,7 @@ import (
"time"
"code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/proxyprotocol"
"code.gitea.io/gitea/modules/setting"
)
@@ -79,16 +80,27 @@ func NewServer(network, address, name string) *Server {
// ListenAndServe listens on the provided network address and then calls Serve
// to handle requests on incoming connections.
-func (srv *Server) ListenAndServe(serve ServeFunction) error {
+func (srv *Server) ListenAndServe(serve ServeFunction, useProxyProtocol bool) error {
go srv.awaitShutdown()
- l, err := GetListener(srv.network, srv.address)
+ listener, err := GetListener(srv.network, srv.address)
if err != nil {
log.Error("Unable to GetListener: %v", err)
return err
}
- srv.listener = newWrappedListener(l, srv)
+ // we need to wrap the listener to take account of our lifecycle
+ listener = newWrappedListener(listener, srv)
+
+ // Now we need to take account of ProxyProtocol settings...
+ if useProxyProtocol {
+ listener = &proxyprotocol.Listener{
+ Listener: listener,
+ ProxyHeaderTimeout: setting.ProxyProtocolHeaderTimeout,
+ AcceptUnknown: setting.ProxyProtocolAcceptUnknown,
+ }
+ }
+ srv.listener = listener
srv.BeforeBegin(srv.network, srv.address)
@@ -97,22 +109,44 @@ func (srv *Server) ListenAndServe(serve ServeFunction) error {
// ListenAndServeTLSConfig listens on the provided network address and then calls
// Serve to handle requests on incoming TLS connections.
-func (srv *Server) ListenAndServeTLSConfig(tlsConfig *tls.Config, serve ServeFunction) error {
+func (srv *Server) ListenAndServeTLSConfig(tlsConfig *tls.Config, serve ServeFunction, useProxyProtocol, proxyProtocolTLSBridging bool) error {
go srv.awaitShutdown()
if tlsConfig.MinVersion == 0 {
tlsConfig.MinVersion = tls.VersionTLS12
}
- l, err := GetListener(srv.network, srv.address)
+ listener, err := GetListener(srv.network, srv.address)
if err != nil {
log.Error("Unable to get Listener: %v", err)
return err
}
- wl := newWrappedListener(l, srv)
- srv.listener = tls.NewListener(wl, tlsConfig)
+ // we need to wrap the listener to take account of our lifecycle
+ listener = newWrappedListener(listener, srv)
+ // Now we need to take account of ProxyProtocol settings... If we're not bridging then we expect that the proxy will forward the connection to us
+ if useProxyProtocol && !proxyProtocolTLSBridging {
+ listener = &proxyprotocol.Listener{
+ Listener: listener,
+ ProxyHeaderTimeout: setting.ProxyProtocolHeaderTimeout,
+ AcceptUnknown: setting.ProxyProtocolAcceptUnknown,
+ }
+ }
+
+ // Now handle the tls protocol
+ listener = tls.NewListener(listener, tlsConfig)
+
+ // Now if we're bridging then we need the proxy to tell us who we're bridging for...
+ if useProxyProtocol && proxyProtocolTLSBridging {
+ listener = &proxyprotocol.Listener{
+ Listener: listener,
+ ProxyHeaderTimeout: setting.ProxyProtocolHeaderTimeout,
+ AcceptUnknown: setting.ProxyProtocolAcceptUnknown,
+ }
+ }
+
+ srv.listener = listener
srv.BeforeBegin(srv.network, srv.address)
return srv.Serve(serve)
diff --git a/modules/graceful/server_http.go b/modules/graceful/server_http.go
index f7b22ceb5e..8ab2bdf41f 100644
--- a/modules/graceful/server_http.go
+++ b/modules/graceful/server_http.go
@@ -28,14 +28,14 @@ func newHTTPServer(network, address, name string, handler http.Handler) (*Server
// HTTPListenAndServe listens on the provided network address and then calls Serve
// to handle requests on incoming connections.
-func HTTPListenAndServe(network, address, name string, handler http.Handler) error {
+func HTTPListenAndServe(network, address, name string, handler http.Handler, useProxyProtocol bool) error {
server, lHandler := newHTTPServer(network, address, name, handler)
- return server.ListenAndServe(lHandler)
+ return server.ListenAndServe(lHandler, useProxyProtocol)
}
// HTTPListenAndServeTLSConfig listens on the provided network address and then calls Serve
// to handle requests on incoming connections.
-func HTTPListenAndServeTLSConfig(network, address, name string, tlsConfig *tls.Config, handler http.Handler) error {
+func HTTPListenAndServeTLSConfig(network, address, name string, tlsConfig *tls.Config, handler http.Handler, useProxyProtocol, proxyProtocolTLSBridging bool) error {
server, lHandler := newHTTPServer(network, address, name, handler)
- return server.ListenAndServeTLSConfig(tlsConfig, lHandler)
+ return server.ListenAndServeTLSConfig(tlsConfig, lHandler, useProxyProtocol, proxyProtocolTLSBridging)
}
diff --git a/modules/highlight/highlight.go b/modules/highlight/highlight.go
index acd3bebb9f..65ed74b019 100644
--- a/modules/highlight/highlight.go
+++ b/modules/highlight/highlight.go
@@ -10,6 +10,7 @@ import (
"bytes"
"fmt"
gohtml "html"
+ "io"
"path/filepath"
"strings"
"sync"
@@ -18,15 +19,15 @@ import (
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
- "github.com/alecthomas/chroma"
- "github.com/alecthomas/chroma/formatters/html"
- "github.com/alecthomas/chroma/lexers"
- "github.com/alecthomas/chroma/styles"
+ "github.com/alecthomas/chroma/v2"
+ "github.com/alecthomas/chroma/v2/formatters/html"
+ "github.com/alecthomas/chroma/v2/lexers"
+ "github.com/alecthomas/chroma/v2/styles"
lru "github.com/hashicorp/golang-lru"
)
// don't index files larger than this many bytes for performance purposes
-const sizeLimit = 1000000
+const sizeLimit = 1024 * 1024
var (
// For custom user mapping
@@ -40,11 +41,12 @@ var (
// NewContext loads custom highlight map from local config
func NewContext() {
once.Do(func() {
- keys := setting.Cfg.Section("highlight.mapping").Keys()
- for i := range keys {
- highlightMapping[keys[i].Name()] = keys[i].Value()
+ if setting.Cfg != nil {
+ keys := setting.Cfg.Section("highlight.mapping").Keys()
+ for i := range keys {
+ highlightMapping[keys[i].Name()] = keys[i].Value()
+ }
}
-
// The size 512 is simply a conservative rule of thumb
c, err := lru.New2Q(512)
if err != nil {
@@ -58,7 +60,7 @@ func NewContext() {
func Code(fileName, language, code string) string {
NewContext()
- // diff view newline will be passed as empty, change to literal \n so it can be copied
+ // diff view newline will be passed as empty, change to literal '\n' so it can be copied
// preserve literal newline in blame view
if code == "" || code == "\n" {
return "\n"
@@ -126,36 +128,29 @@ func CodeFromLexer(lexer chroma.Lexer, code string) string {
return code
}
- htmlw.Flush()
+ _ = htmlw.Flush()
// Chroma will add newlines for certain lexers in order to highlight them properly
- // Once highlighted, strip them here so they don't cause copy/paste trouble in HTML output
+ // Once highlighted, strip them here, so they don't cause copy/paste trouble in HTML output
return strings.TrimSuffix(htmlbuf.String(), "\n")
}
-// File returns a slice of chroma syntax highlighted lines of code
-func File(numLines int, fileName, language string, code []byte) []string {
+// File returns a slice of chroma syntax highlighted HTML lines of code
+func File(fileName, language string, code []byte) ([]string, error) {
NewContext()
if len(code) > sizeLimit {
- return plainText(string(code), numLines)
+ return PlainText(code), nil
}
+
formatter := html.New(html.WithClasses(true),
html.WithLineNumbers(false),
html.PreventSurroundingPre(true),
)
- if formatter == nil {
- log.Error("Couldn't create chroma formatter")
- return plainText(string(code), numLines)
- }
-
- htmlbuf := bytes.Buffer{}
- htmlw := bufio.NewWriter(&htmlbuf)
-
var lexer chroma.Lexer
// provided language overrides everything
- if len(language) > 0 {
+ if language != "" {
lexer = lexers.Get(language)
}
@@ -166,9 +161,9 @@ func File(numLines int, fileName, language string, code []byte) []string {
}
if lexer == nil {
- language := analyze.GetCodeLanguage(fileName, code)
+ guessLanguage := analyze.GetCodeLanguage(fileName, code)
- lexer = lexers.Get(language)
+ lexer = lexers.Get(guessLanguage)
if lexer == nil {
lexer = lexers.Match(fileName)
if lexer == nil {
@@ -179,54 +174,41 @@ func File(numLines int, fileName, language string, code []byte) []string {
iterator, err := lexer.Tokenise(nil, string(code))
if err != nil {
- log.Error("Can't tokenize code: %v", err)
- return plainText(string(code), numLines)
+ return nil, fmt.Errorf("can't tokenize code: %w", err)
}
- err = formatter.Format(htmlw, styles.GitHub, iterator)
- if err != nil {
- log.Error("Can't format code: %v", err)
- return plainText(string(code), numLines)
- }
+ tokensLines := chroma.SplitTokensIntoLines(iterator.Tokens())
+ htmlBuf := &bytes.Buffer{}
- htmlw.Flush()
- finalNewLine := false
- if len(code) > 0 {
- finalNewLine = code[len(code)-1] == '\n'
- }
-
- m := make([]string, 0, numLines)
- for _, v := range strings.SplitN(htmlbuf.String(), "\n", numLines) {
- content := v
- // need to keep lines that are only \n so copy/paste works properly in browser
- if content == "" {
- content = "\n"
- } else if content == `` {
- content += "\n"
- } else if content == `` {
- content += "\n"
+ lines := make([]string, 0, len(tokensLines))
+ for _, tokens := range tokensLines {
+ iterator = chroma.Literator(tokens...)
+ err = formatter.Format(htmlBuf, styles.GitHub, iterator)
+ if err != nil {
+ return nil, fmt.Errorf("can't format code: %w", err)
}
- content = strings.TrimSuffix(content, ``)
- content = strings.TrimPrefix(content, ``)
- m = append(m, content)
- }
- if finalNewLine {
- m = append(m, "\n")
+ lines = append(lines, htmlBuf.String())
+ htmlBuf.Reset()
}
- return m
+ return lines, nil
}
-// return unhiglighted map
-func plainText(code string, numLines int) []string {
- m := make([]string, 0, numLines)
- for _, v := range strings.SplitN(code, "\n", numLines) {
- content := v
- // need to keep lines that are only \n so copy/paste works properly in browser
- if content == "" {
- content = "\n"
+// PlainText returns non-highlighted HTML for code
+func PlainText(code []byte) []string {
+ r := bufio.NewReader(bytes.NewReader(code))
+ m := make([]string, 0, bytes.Count(code, []byte{'\n'})+1)
+ for {
+ content, err := r.ReadString('\n')
+ if err != nil && err != io.EOF {
+ log.Error("failed to read string from buffer: %v", err)
+ break
}
- m = append(m, gohtml.EscapeString(content))
+ if content == "" && err == io.EOF {
+ break
+ }
+ s := gohtml.EscapeString(content)
+ m = append(m, s)
}
return m
}
diff --git a/modules/highlight/highlight_test.go b/modules/highlight/highlight_test.go
index e5dfedd2b3..8f83f4a2f6 100644
--- a/modules/highlight/highlight_test.go
+++ b/modules/highlight/highlight_test.go
@@ -8,97 +8,146 @@ import (
"strings"
"testing"
- "code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/util"
-
"github.com/stretchr/testify/assert"
- "gopkg.in/ini.v1"
)
+func lines(s string) []string {
+ return strings.Split(strings.ReplaceAll(strings.TrimSpace(s), `\n`, "\n"), "\n")
+}
+
func TestFile(t *testing.T) {
- setting.Cfg = ini.Empty()
tests := []struct {
- name string
- numLines int
- fileName string
- code string
- want string
+ name string
+ code string
+ want []string
}{
{
- name: ".drone.yml",
- numLines: 12,
- fileName: ".drone.yml",
- code: util.Dedent(`
- kind: pipeline
- name: default
-
- steps:
- - name: test
- image: golang:1.13
- environment:
- GOPROXY: https://goproxy.cn
- commands:
- - go get -u
- - go build -v
- - go test -v -race -coverprofile=coverage.txt -covermode=atomic
- `),
- want: util.Dedent(`
- kind: pipeline
- name: default
-
- steps:
- - name: test
- image: golang:1.13
- environment:
- GOPROXY: https://goproxy.cn
- commands:
- - go get -u
- - go build -v
- - go test -v -race -coverprofile=coverage.txt -covermode=atomic
- `),
+ name: "empty.py",
+ code: "",
+ want: lines(""),
},
{
- name: ".drone.yml - trailing space",
- numLines: 13,
- fileName: ".drone.yml",
- code: strings.Replace(util.Dedent(`
- kind: pipeline
- name: default
+ name: "tags.txt",
+ code: "<>",
+ want: lines("<>"),
+ },
+ {
+ name: "tags.py",
+ code: "<>",
+ want: lines(`<>`),
+ },
+ {
+ name: "eol-no.py",
+ code: "a=1",
+ want: lines(`a=1`),
+ },
+ {
+ name: "eol-newline1.py",
+ code: "a=1\n",
+ want: lines(`a=1\n`),
+ },
+ {
+ name: "eol-newline2.py",
+ code: "a=1\n\n",
+ want: lines(`
+a=1\n
+\n
+ `,
+ ),
+ },
+ {
+ name: "empty-line-with-space.py",
+ code: strings.ReplaceAll(strings.TrimSpace(`
+def:
+ a=1
- steps:
- - name: test
- image: golang:1.13
- environment:
- GOPROXY: https://goproxy.cn
- commands:
- - go get -u
- - go build -v
- - go test -v -race -coverprofile=coverage.txt -covermode=atomic
- `)+"\n", "name: default", "name: default ", 1),
- want: util.Dedent(`
- kind: pipeline
- name: default
-
- steps:
- - name: test
- image: golang:1.13
- environment:
- GOPROXY: https://goproxy.cn
- commands:
- - go get -u
- - go build -v
- - go test -v -race -coverprofile=coverage.txt -covermode=atomic
-
-
-
- `),
+b=''
+{space}
+c=2
+ `), "{space}", " "),
+ want: lines(`
+def:\n
+ a=1\n
+\n
+b=''\n
+ \n
+c=2`,
+ ),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
- got := strings.Join(File(tt.numLines, tt.fileName, "", []byte(tt.code)), "\n")
- assert.Equal(t, tt.want, got)
+ out, err := File(tt.name, "", []byte(tt.code))
+ assert.NoError(t, err)
+ expected := strings.Join(tt.want, "\n")
+ actual := strings.Join(out, "\n")
+ assert.Equal(t, strings.Count(actual, ""))
+ assert.EqualValues(t, expected, actual)
+ })
+ }
+}
+
+func TestPlainText(t *testing.T) {
+ tests := []struct {
+ name string
+ code string
+ want []string
+ }{
+ {
+ name: "empty.py",
+ code: "",
+ want: lines(""),
+ },
+ {
+ name: "tags.py",
+ code: "<>",
+ want: lines("<>"),
+ },
+ {
+ name: "eol-no.py",
+ code: "a=1",
+ want: lines(`a=1`),
+ },
+ {
+ name: "eol-newline1.py",
+ code: "a=1\n",
+ want: lines(`a=1\n`),
+ },
+ {
+ name: "eol-newline2.py",
+ code: "a=1\n\n",
+ want: lines(`
+a=1\n
+\n
+ `),
+ },
+ {
+ name: "empty-line-with-space.py",
+ code: strings.ReplaceAll(strings.TrimSpace(`
+def:
+ a=1
+
+b=''
+{space}
+c=2
+ `), "{space}", " "),
+ want: lines(`
+def:\n
+ a=1\n
+\n
+b=''\n
+ \n
+c=2`),
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ out := PlainText([]byte(tt.code))
+ expected := strings.Join(tt.want, "\n")
+ actual := strings.Join(out, "\n")
+ assert.EqualValues(t, expected, actual)
})
}
}
diff --git a/modules/hostmatcher/hostmatcher.go b/modules/hostmatcher/hostmatcher.go
index 81c4202fcd..a092e07f41 100644
--- a/modules/hostmatcher/hostmatcher.go
+++ b/modules/hostmatcher/hostmatcher.go
@@ -78,6 +78,11 @@ func (hl *HostMatchList) AppendBuiltin(builtin string) {
hl.builtins = append(hl.builtins, builtin)
}
+// AppendPattern appends more pattern to match
+func (hl *HostMatchList) AppendPattern(pattern string) {
+ hl.patterns = append(hl.patterns, pattern)
+}
+
// IsEmpty checks if the checklist is empty
func (hl *HostMatchList) IsEmpty() bool {
return hl == nil || (len(hl.builtins) == 0 && len(hl.patterns) == 0 && len(hl.ipNets) == 0)
diff --git a/modules/indexer/code/bleve_test.go b/modules/indexer/code/bleve_test.go
index 37ed2eb9f0..a34d54bc0e 100644
--- a/modules/indexer/code/bleve_test.go
+++ b/modules/indexer/code/bleve_test.go
@@ -5,11 +5,9 @@
package code
import (
- "os"
"testing"
"code.gitea.io/gitea/models/unittest"
- "code.gitea.io/gitea/modules/util"
"github.com/stretchr/testify/assert"
)
@@ -17,13 +15,7 @@ import (
func TestBleveIndexAndSearch(t *testing.T) {
unittest.PrepareTestEnv(t)
- dir, err := os.MkdirTemp("", "bleve.index")
- assert.NoError(t, err)
- if err != nil {
- assert.Fail(t, "Unable to create temporary directory")
- return
- }
- defer util.RemoveAll(dir)
+ dir := t.TempDir()
idx, _, err := NewBleveIndexer(dir)
if err != nil {
diff --git a/modules/indexer/issues/bleve_test.go b/modules/indexer/issues/bleve_test.go
index 68a7831a3f..42d22f06a2 100644
--- a/modules/indexer/issues/bleve_test.go
+++ b/modules/indexer/issues/bleve_test.go
@@ -6,22 +6,13 @@ package issues
import (
"context"
- "os"
"testing"
- "code.gitea.io/gitea/modules/util"
-
"github.com/stretchr/testify/assert"
)
func TestBleveIndexAndSearch(t *testing.T) {
- dir, err := os.MkdirTemp("", "bleve.index")
- assert.NoError(t, err)
- if err != nil {
- assert.Fail(t, "Unable to create temporary directory")
- return
- }
- defer util.RemoveAll(dir)
+ dir := t.TempDir()
indexer := NewBleveIndexer(dir)
defer indexer.Close()
@@ -30,7 +21,7 @@ func TestBleveIndexAndSearch(t *testing.T) {
return
}
- err = indexer.Index([]*IndexerData{
+ err := indexer.Index([]*IndexerData{
{
ID: 1,
RepoID: 2,
diff --git a/modules/indexer/issues/indexer_test.go b/modules/indexer/issues/indexer_test.go
index 6bafcbdf24..e8b9ff8370 100644
--- a/modules/indexer/issues/indexer_test.go
+++ b/modules/indexer/issues/indexer_test.go
@@ -6,7 +6,6 @@ package issues
import (
"context"
- "os"
"path"
"path/filepath"
"testing"
@@ -14,7 +13,6 @@ import (
"code.gitea.io/gitea/models/unittest"
"code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/util"
_ "code.gitea.io/gitea/models"
@@ -32,11 +30,7 @@ func TestBleveSearchIssues(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
setting.Cfg = ini.Empty()
- tmpIndexerDir, err := os.MkdirTemp("", "issues-indexer")
- if err != nil {
- assert.Fail(t, "Unable to create temporary directory: %v", err)
- return
- }
+ tmpIndexerDir := t.TempDir()
setting.Cfg.Section("queue.issue_indexer").Key("DATADIR").MustString(path.Join(tmpIndexerDir, "issues.queue"))
@@ -44,7 +38,6 @@ func TestBleveSearchIssues(t *testing.T) {
setting.Indexer.IssuePath = path.Join(tmpIndexerDir, "issues.queue")
defer func() {
setting.Indexer.IssuePath = oldIssuePath
- util.RemoveAll(tmpIndexerDir)
}()
setting.Indexer.IssueType = "bleve"
diff --git a/modules/issue/template/template.go b/modules/issue/template/template.go
new file mode 100644
index 0000000000..3b33852cb5
--- /dev/null
+++ b/modules/issue/template/template.go
@@ -0,0 +1,392 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package template
+
+import (
+ "fmt"
+ "net/url"
+ "regexp"
+ "strconv"
+ "strings"
+
+ "code.gitea.io/gitea/modules/container"
+ api "code.gitea.io/gitea/modules/structs"
+
+ "gitea.com/go-chi/binding"
+)
+
+// Validate checks whether an IssueTemplate is considered valid, and returns the first error
+func Validate(template *api.IssueTemplate) error {
+ if err := validateMetadata(template); err != nil {
+ return err
+ }
+ if template.Type() == api.IssueTemplateTypeYaml {
+ if err := validateYaml(template); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func validateMetadata(template *api.IssueTemplate) error {
+ if strings.TrimSpace(template.Name) == "" {
+ return fmt.Errorf("'name' is required")
+ }
+ if strings.TrimSpace(template.About) == "" {
+ return fmt.Errorf("'about' is required")
+ }
+ return nil
+}
+
+func validateYaml(template *api.IssueTemplate) error {
+ if len(template.Fields) == 0 {
+ return fmt.Errorf("'body' is required")
+ }
+ ids := make(container.Set[string])
+ for idx, field := range template.Fields {
+ if err := validateID(field, idx, ids); err != nil {
+ return err
+ }
+ if err := validateLabel(field, idx); err != nil {
+ return err
+ }
+
+ position := newErrorPosition(idx, field.Type)
+ switch field.Type {
+ case api.IssueFormFieldTypeMarkdown:
+ if err := validateStringItem(position, field.Attributes, true, "value"); err != nil {
+ return err
+ }
+ case api.IssueFormFieldTypeTextarea:
+ if err := validateStringItem(position, field.Attributes, false,
+ "description",
+ "placeholder",
+ "value",
+ "render",
+ ); err != nil {
+ return err
+ }
+ case api.IssueFormFieldTypeInput:
+ if err := validateStringItem(position, field.Attributes, false,
+ "description",
+ "placeholder",
+ "value",
+ ); err != nil {
+ return err
+ }
+ if err := validateBoolItem(position, field.Validations, "is_number"); err != nil {
+ return err
+ }
+ if err := validateStringItem(position, field.Validations, false, "regex"); err != nil {
+ return err
+ }
+ case api.IssueFormFieldTypeDropdown:
+ if err := validateStringItem(position, field.Attributes, false, "description"); err != nil {
+ return err
+ }
+ if err := validateBoolItem(position, field.Attributes, "multiple"); err != nil {
+ return err
+ }
+ if err := validateOptions(field, idx); err != nil {
+ return err
+ }
+ case api.IssueFormFieldTypeCheckboxes:
+ if err := validateStringItem(position, field.Attributes, false, "description"); err != nil {
+ return err
+ }
+ if err := validateOptions(field, idx); err != nil {
+ return err
+ }
+ default:
+ return position.Errorf("unknown type")
+ }
+
+ if err := validateRequired(field, idx); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func validateLabel(field *api.IssueFormField, idx int) error {
+ if field.Type == api.IssueFormFieldTypeMarkdown {
+ // The label is not required for a markdown field
+ return nil
+ }
+ return validateStringItem(newErrorPosition(idx, field.Type), field.Attributes, true, "label")
+}
+
+func validateRequired(field *api.IssueFormField, idx int) error {
+ if field.Type == api.IssueFormFieldTypeMarkdown || field.Type == api.IssueFormFieldTypeCheckboxes {
+ // The label is not required for a markdown or checkboxes field
+ return nil
+ }
+ return validateBoolItem(newErrorPosition(idx, field.Type), field.Validations, "required")
+}
+
+func validateID(field *api.IssueFormField, idx int, ids container.Set[string]) error {
+ if field.Type == api.IssueFormFieldTypeMarkdown {
+ // The ID is not required for a markdown field
+ return nil
+ }
+
+ position := newErrorPosition(idx, field.Type)
+ if field.ID == "" {
+ // If the ID is empty in yaml, template.Unmarshal will auto autofill it, so it cannot be empty
+ return position.Errorf("'id' is required")
+ }
+ if binding.AlphaDashPattern.MatchString(field.ID) {
+ return position.Errorf("'id' should contain only alphanumeric, '-' and '_'")
+ }
+ if !ids.Add(field.ID) {
+ return position.Errorf("'id' should be unique")
+ }
+ return nil
+}
+
+func validateOptions(field *api.IssueFormField, idx int) error {
+ if field.Type != api.IssueFormFieldTypeDropdown && field.Type != api.IssueFormFieldTypeCheckboxes {
+ return nil
+ }
+ position := newErrorPosition(idx, field.Type)
+
+ options, ok := field.Attributes["options"].([]interface{})
+ if !ok || len(options) == 0 {
+ return position.Errorf("'options' is required and should be a array")
+ }
+
+ for optIdx, option := range options {
+ position := newErrorPosition(idx, field.Type, optIdx)
+ switch field.Type {
+ case api.IssueFormFieldTypeDropdown:
+ if _, ok := option.(string); !ok {
+ return position.Errorf("should be a string")
+ }
+ case api.IssueFormFieldTypeCheckboxes:
+ opt, ok := option.(map[interface{}]interface{})
+ if !ok {
+ return position.Errorf("should be a dictionary")
+ }
+ if label, ok := opt["label"].(string); !ok || label == "" {
+ return position.Errorf("'label' is required and should be a string")
+ }
+
+ if required, ok := opt["required"]; ok {
+ if _, ok := required.(bool); !ok {
+ return position.Errorf("'required' should be a bool")
+ }
+ }
+ }
+ }
+ return nil
+}
+
+func validateStringItem(position errorPosition, m map[string]interface{}, required bool, names ...string) error {
+ for _, name := range names {
+ v, ok := m[name]
+ if !ok {
+ if required {
+ return position.Errorf("'%s' is required", name)
+ }
+ return nil
+ }
+ attr, ok := v.(string)
+ if !ok {
+ return position.Errorf("'%s' should be a string", name)
+ }
+ if strings.TrimSpace(attr) == "" && required {
+ return position.Errorf("'%s' is required", name)
+ }
+ }
+ return nil
+}
+
+func validateBoolItem(position errorPosition, m map[string]interface{}, names ...string) error {
+ for _, name := range names {
+ v, ok := m[name]
+ if !ok {
+ return nil
+ }
+ if _, ok := v.(bool); !ok {
+ return position.Errorf("'%s' should be a bool", name)
+ }
+ }
+ return nil
+}
+
+type errorPosition string
+
+func (p errorPosition) Errorf(format string, a ...interface{}) error {
+ return fmt.Errorf(string(p)+": "+format, a...)
+}
+
+func newErrorPosition(fieldIdx int, fieldType api.IssueFormFieldType, optionIndex ...int) errorPosition {
+ ret := fmt.Sprintf("body[%d](%s)", fieldIdx, fieldType)
+ if len(optionIndex) > 0 {
+ ret += fmt.Sprintf(", option[%d]", optionIndex[0])
+ }
+ return errorPosition(ret)
+}
+
+// RenderToMarkdown renders template to markdown with specified values
+func RenderToMarkdown(template *api.IssueTemplate, values url.Values) string {
+ builder := &strings.Builder{}
+
+ for _, field := range template.Fields {
+ f := &valuedField{
+ IssueFormField: field,
+ Values: values,
+ }
+ if f.ID == "" {
+ continue
+ }
+ f.WriteTo(builder)
+ }
+
+ return builder.String()
+}
+
+type valuedField struct {
+ *api.IssueFormField
+ url.Values
+}
+
+func (f *valuedField) WriteTo(builder *strings.Builder) {
+ if f.Type == api.IssueFormFieldTypeMarkdown {
+ // markdown blocks do not appear in output
+ return
+ }
+
+ // write label
+ _, _ = fmt.Fprintf(builder, "### %s\n\n", f.Label())
+
+ blankPlaceholder := "_No response_\n"
+
+ // write body
+ switch f.Type {
+ case api.IssueFormFieldTypeCheckboxes:
+ for _, option := range f.Options() {
+ checked := " "
+ if option.IsChecked() {
+ checked = "x"
+ }
+ _, _ = fmt.Fprintf(builder, "- [%s] %s\n", checked, option.Label())
+ }
+ case api.IssueFormFieldTypeDropdown:
+ var checkeds []string
+ for _, option := range f.Options() {
+ if option.IsChecked() {
+ checkeds = append(checkeds, option.Label())
+ }
+ }
+ if len(checkeds) > 0 {
+ _, _ = fmt.Fprintf(builder, "%s\n", strings.Join(checkeds, ", "))
+ } else {
+ _, _ = fmt.Fprint(builder, blankPlaceholder)
+ }
+ case api.IssueFormFieldTypeInput:
+ if value := f.Value(); value == "" {
+ _, _ = fmt.Fprint(builder, blankPlaceholder)
+ } else {
+ _, _ = fmt.Fprintf(builder, "%s\n", value)
+ }
+ case api.IssueFormFieldTypeTextarea:
+ if value := f.Value(); value == "" {
+ _, _ = fmt.Fprint(builder, blankPlaceholder)
+ } else if render := f.Render(); render != "" {
+ quotes := minQuotes(value)
+ _, _ = fmt.Fprintf(builder, "%s%s\n%s\n%s\n", quotes, f.Render(), value, quotes)
+ } else {
+ _, _ = fmt.Fprintf(builder, "%s\n", value)
+ }
+ }
+ _, _ = fmt.Fprintln(builder)
+}
+
+func (f *valuedField) Label() string {
+ if label, ok := f.Attributes["label"].(string); ok {
+ return label
+ }
+ return ""
+}
+
+func (f *valuedField) Render() string {
+ if render, ok := f.Attributes["render"].(string); ok {
+ return render
+ }
+ return ""
+}
+
+func (f *valuedField) Value() string {
+ return strings.TrimSpace(f.Get(fmt.Sprintf("form-field-" + f.ID)))
+}
+
+func (f *valuedField) Options() []*valuedOption {
+ if options, ok := f.Attributes["options"].([]interface{}); ok {
+ ret := make([]*valuedOption, 0, len(options))
+ for i, option := range options {
+ ret = append(ret, &valuedOption{
+ index: i,
+ data: option,
+ field: f,
+ })
+ }
+ return ret
+ }
+ return nil
+}
+
+type valuedOption struct {
+ index int
+ data interface{}
+ field *valuedField
+}
+
+func (o *valuedOption) Label() string {
+ switch o.field.Type {
+ case api.IssueFormFieldTypeDropdown:
+ if label, ok := o.data.(string); ok {
+ return label
+ }
+ case api.IssueFormFieldTypeCheckboxes:
+ if vs, ok := o.data.(map[interface{}]interface{}); ok {
+ if v, ok := vs["label"].(string); ok {
+ return v
+ }
+ }
+ }
+ return ""
+}
+
+func (o *valuedOption) IsChecked() bool {
+ switch o.field.Type {
+ case api.IssueFormFieldTypeDropdown:
+ checks := strings.Split(o.field.Get(fmt.Sprintf("form-field-%s", o.field.ID)), ",")
+ idx := strconv.Itoa(o.index)
+ for _, v := range checks {
+ if v == idx {
+ return true
+ }
+ }
+ return false
+ case api.IssueFormFieldTypeCheckboxes:
+ return o.field.Get(fmt.Sprintf("form-field-%s-%d", o.field.ID, o.index)) == "on"
+ }
+ return false
+}
+
+var minQuotesRegex = regexp.MustCompilePOSIX("^`{3,}")
+
+// minQuotes return 3 or more back-quotes.
+// If n back-quotes exists, use n+1 back-quotes to quote.
+func minQuotes(value string) string {
+ ret := "```"
+ for _, v := range minQuotesRegex.FindAllString(value, -1) {
+ if len(v) >= len(ret) {
+ ret = v + "`"
+ }
+ }
+ return ret
+}
diff --git a/modules/issue/template/template_test.go b/modules/issue/template/template_test.go
new file mode 100644
index 0000000000..883e1e0780
--- /dev/null
+++ b/modules/issue/template/template_test.go
@@ -0,0 +1,645 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package template
+
+import (
+ "net/url"
+ "reflect"
+ "testing"
+
+ "code.gitea.io/gitea/modules/json"
+ api "code.gitea.io/gitea/modules/structs"
+)
+
+func TestValidate(t *testing.T) {
+ tests := []struct {
+ name string
+ content string
+ wantErr string
+ }{
+ {
+ name: "miss name",
+ content: ``,
+ wantErr: "'name' is required",
+ },
+ {
+ name: "miss about",
+ content: `
+name: "test"
+`,
+ wantErr: "'about' is required",
+ },
+ {
+ name: "miss body",
+ content: `
+name: "test"
+about: "this is about"
+`,
+ wantErr: "'body' is required",
+ },
+ {
+ name: "markdown miss value",
+ content: `
+name: "test"
+about: "this is about"
+body:
+ - type: "markdown"
+`,
+ wantErr: "body[0](markdown): 'value' is required",
+ },
+ {
+ name: "markdown invalid value",
+ content: `
+name: "test"
+about: "this is about"
+body:
+ - type: "markdown"
+ attributes:
+ value: true
+`,
+ wantErr: "body[0](markdown): 'value' should be a string",
+ },
+ {
+ name: "markdown empty value",
+ content: `
+name: "test"
+about: "this is about"
+body:
+ - type: "markdown"
+ attributes:
+ value: ""
+`,
+ wantErr: "body[0](markdown): 'value' is required",
+ },
+ {
+ name: "textarea invalid id",
+ content: `
+name: "test"
+about: "this is about"
+body:
+ - type: "textarea"
+ id: "?"
+`,
+ wantErr: "body[0](textarea): 'id' should contain only alphanumeric, '-' and '_'",
+ },
+ {
+ name: "textarea miss label",
+ content: `
+name: "test"
+about: "this is about"
+body:
+ - type: "textarea"
+ id: "1"
+`,
+ wantErr: "body[0](textarea): 'label' is required",
+ },
+ {
+ name: "textarea conflict id",
+ content: `
+name: "test"
+about: "this is about"
+body:
+ - type: "textarea"
+ id: "1"
+ attributes:
+ label: "a"
+ - type: "textarea"
+ id: "1"
+ attributes:
+ label: "b"
+`,
+ wantErr: "body[1](textarea): 'id' should be unique",
+ },
+ {
+ name: "textarea invalid description",
+ content: `
+name: "test"
+about: "this is about"
+body:
+ - type: "textarea"
+ id: "1"
+ attributes:
+ label: "a"
+ description: true
+`,
+ wantErr: "body[0](textarea): 'description' should be a string",
+ },
+ {
+ name: "textarea invalid required",
+ content: `
+name: "test"
+about: "this is about"
+body:
+ - type: "textarea"
+ id: "1"
+ attributes:
+ label: "a"
+ validations:
+ required: "on"
+`,
+ wantErr: "body[0](textarea): 'required' should be a bool",
+ },
+ {
+ name: "input invalid description",
+ content: `
+name: "test"
+about: "this is about"
+body:
+ - type: "input"
+ id: "1"
+ attributes:
+ label: "a"
+ description: true
+`,
+ wantErr: "body[0](input): 'description' should be a string",
+ },
+ {
+ name: "input invalid is_number",
+ content: `
+name: "test"
+about: "this is about"
+body:
+ - type: "input"
+ id: "1"
+ attributes:
+ label: "a"
+ validations:
+ is_number: "yes"
+`,
+ wantErr: "body[0](input): 'is_number' should be a bool",
+ },
+ {
+ name: "input invalid regex",
+ content: `
+name: "test"
+about: "this is about"
+body:
+ - type: "input"
+ id: "1"
+ attributes:
+ label: "a"
+ validations:
+ regex: true
+`,
+ wantErr: "body[0](input): 'regex' should be a string",
+ },
+ {
+ name: "dropdown invalid description",
+ content: `
+name: "test"
+about: "this is about"
+body:
+ - type: "dropdown"
+ id: "1"
+ attributes:
+ label: "a"
+ description: true
+`,
+ wantErr: "body[0](dropdown): 'description' should be a string",
+ },
+ {
+ name: "dropdown invalid multiple",
+ content: `
+name: "test"
+about: "this is about"
+body:
+ - type: "dropdown"
+ id: "1"
+ attributes:
+ label: "a"
+ multiple: "on"
+`,
+ wantErr: "body[0](dropdown): 'multiple' should be a bool",
+ },
+ {
+ name: "checkboxes invalid description",
+ content: `
+name: "test"
+about: "this is about"
+body:
+ - type: "checkboxes"
+ id: "1"
+ attributes:
+ label: "a"
+ description: true
+`,
+ wantErr: "body[0](checkboxes): 'description' should be a string",
+ },
+ {
+ name: "invalid type",
+ content: `
+name: "test"
+about: "this is about"
+body:
+ - type: "video"
+ id: "1"
+ attributes:
+ label: "a"
+`,
+ wantErr: "body[0](video): unknown type",
+ },
+ {
+ name: "dropdown miss options",
+ content: `
+name: "test"
+about: "this is about"
+body:
+ - type: "dropdown"
+ id: "1"
+ attributes:
+ label: "a"
+`,
+ wantErr: "body[0](dropdown): 'options' is required and should be a array",
+ },
+ {
+ name: "dropdown invalid options",
+ content: `
+name: "test"
+about: "this is about"
+body:
+ - type: "dropdown"
+ id: "1"
+ attributes:
+ label: "a"
+ options:
+ - "a"
+ - true
+`,
+ wantErr: "body[0](dropdown), option[1]: should be a string",
+ },
+ {
+ name: "checkboxes invalid options",
+ content: `
+name: "test"
+about: "this is about"
+body:
+ - type: "checkboxes"
+ id: "1"
+ attributes:
+ label: "a"
+ options:
+ - "a"
+ - true
+`,
+ wantErr: "body[0](checkboxes), option[0]: should be a dictionary",
+ },
+ {
+ name: "checkboxes option miss label",
+ content: `
+name: "test"
+about: "this is about"
+body:
+ - type: "checkboxes"
+ id: "1"
+ attributes:
+ label: "a"
+ options:
+ - required: true
+`,
+ wantErr: "body[0](checkboxes), option[0]: 'label' is required and should be a string",
+ },
+ {
+ name: "checkboxes option invalid required",
+ content: `
+name: "test"
+about: "this is about"
+body:
+ - type: "checkboxes"
+ id: "1"
+ attributes:
+ label: "a"
+ options:
+ - label: "a"
+ required: "on"
+`,
+ wantErr: "body[0](checkboxes), option[0]: 'required' should be a bool",
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ tmpl, err := unmarshal("test.yaml", []byte(tt.content))
+ if err != nil {
+ t.Fatal(err)
+ }
+ if err := Validate(tmpl); (err == nil) != (tt.wantErr == "") || err != nil && err.Error() != tt.wantErr {
+ t.Errorf("Validate() error = %v, wantErr %q", err, tt.wantErr)
+ }
+ })
+ }
+
+ t.Run("valid", func(t *testing.T) {
+ content := `
+name: Name
+title: Title
+about: About
+labels: ["label1", "label2"]
+ref: Ref
+body:
+ - type: markdown
+ id: id1
+ attributes:
+ value: Value of the markdown
+ - type: textarea
+ id: id2
+ attributes:
+ label: Label of textarea
+ description: Description of textarea
+ placeholder: Placeholder of textarea
+ value: Value of textarea
+ render: bash
+ validations:
+ required: true
+ - type: input
+ id: id3
+ attributes:
+ label: Label of input
+ description: Description of input
+ placeholder: Placeholder of input
+ value: Value of input
+ validations:
+ required: true
+ is_number: true
+ regex: "[a-zA-Z0-9]+"
+ - type: dropdown
+ id: id4
+ attributes:
+ label: Label of dropdown
+ description: Description of dropdown
+ multiple: true
+ options:
+ - Option 1 of dropdown
+ - Option 2 of dropdown
+ - Option 3 of dropdown
+ validations:
+ required: true
+ - type: checkboxes
+ id: id5
+ attributes:
+ label: Label of checkboxes
+ description: Description of checkboxes
+ options:
+ - label: Option 1 of checkboxes
+ required: true
+ - label: Option 2 of checkboxes
+ required: false
+ - label: Option 3 of checkboxes
+ required: true
+`
+ want := &api.IssueTemplate{
+ Name: "Name",
+ Title: "Title",
+ About: "About",
+ Labels: []string{"label1", "label2"},
+ Ref: "Ref",
+ Fields: []*api.IssueFormField{
+ {
+ Type: "markdown",
+ ID: "id1",
+ Attributes: map[string]interface{}{
+ "value": "Value of the markdown",
+ },
+ },
+ {
+ Type: "textarea",
+ ID: "id2",
+ Attributes: map[string]interface{}{
+ "label": "Label of textarea",
+ "description": "Description of textarea",
+ "placeholder": "Placeholder of textarea",
+ "value": "Value of textarea",
+ "render": "bash",
+ },
+ Validations: map[string]interface{}{
+ "required": true,
+ },
+ },
+ {
+ Type: "input",
+ ID: "id3",
+ Attributes: map[string]interface{}{
+ "label": "Label of input",
+ "description": "Description of input",
+ "placeholder": "Placeholder of input",
+ "value": "Value of input",
+ },
+ Validations: map[string]interface{}{
+ "required": true,
+ "is_number": true,
+ "regex": "[a-zA-Z0-9]+",
+ },
+ },
+ {
+ Type: "dropdown",
+ ID: "id4",
+ Attributes: map[string]interface{}{
+ "label": "Label of dropdown",
+ "description": "Description of dropdown",
+ "multiple": true,
+ "options": []interface{}{
+ "Option 1 of dropdown",
+ "Option 2 of dropdown",
+ "Option 3 of dropdown",
+ },
+ },
+ Validations: map[string]interface{}{
+ "required": true,
+ },
+ },
+ {
+ Type: "checkboxes",
+ ID: "id5",
+ Attributes: map[string]interface{}{
+ "label": "Label of checkboxes",
+ "description": "Description of checkboxes",
+ "options": []interface{}{
+ map[interface{}]interface{}{"label": "Option 1 of checkboxes", "required": true},
+ map[interface{}]interface{}{"label": "Option 2 of checkboxes", "required": false},
+ map[interface{}]interface{}{"label": "Option 3 of checkboxes", "required": true},
+ },
+ },
+ },
+ },
+ FileName: "test.yaml",
+ }
+ got, err := unmarshal("test.yaml", []byte(content))
+ if err != nil {
+ t.Fatal(err)
+ }
+ if err := Validate(got); err != nil {
+ t.Errorf("Validate() error = %v", err)
+ }
+ if !reflect.DeepEqual(want, got) {
+ jsonWant, _ := json.Marshal(want)
+ jsonGot, _ := json.Marshal(got)
+ t.Errorf("want:\n%s\ngot:\n%s", jsonWant, jsonGot)
+ }
+ })
+}
+
+func TestRenderToMarkdown(t *testing.T) {
+ type args struct {
+ template string
+ values url.Values
+ }
+ tests := []struct {
+ name string
+ args args
+ want string
+ }{
+ {
+ name: "normal",
+ args: args{
+ template: `
+name: Name
+title: Title
+about: About
+labels: ["label1", "label2"]
+ref: Ref
+body:
+ - type: markdown
+ id: id1
+ attributes:
+ value: Value of the markdown
+ - type: textarea
+ id: id2
+ attributes:
+ label: Label of textarea
+ description: Description of textarea
+ placeholder: Placeholder of textarea
+ value: Value of textarea
+ render: bash
+ validations:
+ required: true
+ - type: input
+ id: id3
+ attributes:
+ label: Label of input
+ description: Description of input
+ placeholder: Placeholder of input
+ value: Value of input
+ validations:
+ required: true
+ is_number: true
+ regex: "[a-zA-Z0-9]+"
+ - type: dropdown
+ id: id4
+ attributes:
+ label: Label of dropdown
+ description: Description of dropdown
+ multiple: true
+ options:
+ - Option 1 of dropdown
+ - Option 2 of dropdown
+ - Option 3 of dropdown
+ validations:
+ required: true
+ - type: checkboxes
+ id: id5
+ attributes:
+ label: Label of checkboxes
+ description: Description of checkboxes
+ options:
+ - label: Option 1 of checkboxes
+ required: true
+ - label: Option 2 of checkboxes
+ required: false
+ - label: Option 3 of checkboxes
+ required: true
+`,
+ values: map[string][]string{
+ "form-field-id2": {"Value of id2"},
+ "form-field-id3": {"Value of id3"},
+ "form-field-id4": {"0,1"},
+ "form-field-id5-0": {"on"},
+ "form-field-id5-2": {"on"},
+ },
+ },
+ want: `### Label of textarea
+
+` + "```bash\nValue of id2\n```" + `
+
+### Label of input
+
+Value of id3
+
+### Label of dropdown
+
+Option 1 of dropdown, Option 2 of dropdown
+
+### Label of checkboxes
+
+- [x] Option 1 of checkboxes
+- [ ] Option 2 of checkboxes
+- [x] Option 3 of checkboxes
+
+`,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ template, err := Unmarshal("test.yaml", []byte(tt.args.template))
+ if err != nil {
+ t.Fatal(err)
+ }
+ if got := RenderToMarkdown(template, tt.args.values); got != tt.want {
+ t.Errorf("RenderToMarkdown() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
+
+func Test_minQuotes(t *testing.T) {
+ type args struct {
+ value string
+ }
+ tests := []struct {
+ name string
+ args args
+ want string
+ }{
+ {
+ name: "without quote",
+ args: args{
+ value: "Hello\nWorld",
+ },
+ want: "```",
+ },
+ {
+ name: "with 1 quote",
+ args: args{
+ value: "Hello\nWorld\n`text`\n",
+ },
+ want: "```",
+ },
+ {
+ name: "with 3 quotes",
+ args: args{
+ value: "Hello\nWorld\n`text`\n```go\ntext\n```\n",
+ },
+ want: "````",
+ },
+ {
+ name: "with more quotes",
+ args: args{
+ value: "Hello\nWorld\n`text`\n```go\ntext\n```\n``````````bash\ntext\n``````````\n",
+ },
+ want: "```````````",
+ },
+ {
+ name: "not leading quotes",
+ args: args{
+ value: "Hello\nWorld`text````go\ntext`````````````bash\ntext``````````\n",
+ },
+ want: "```",
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ if got := minQuotes(tt.args.value); got != tt.want {
+ t.Errorf("minQuotes() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
diff --git a/modules/issue/template/unmarshal.go b/modules/issue/template/unmarshal.go
new file mode 100644
index 0000000000..e695d1e1cc
--- /dev/null
+++ b/modules/issue/template/unmarshal.go
@@ -0,0 +1,125 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package template
+
+import (
+ "fmt"
+ "io"
+ "path/filepath"
+ "strconv"
+
+ "code.gitea.io/gitea/modules/git"
+ "code.gitea.io/gitea/modules/markup/markdown"
+ "code.gitea.io/gitea/modules/setting"
+ api "code.gitea.io/gitea/modules/structs"
+
+ "gopkg.in/yaml.v2"
+)
+
+// CouldBe indicates a file with the filename could be a template,
+// it is a low cost check before further processing.
+func CouldBe(filename string) bool {
+ it := &api.IssueTemplate{
+ FileName: filename,
+ }
+ return it.Type() != ""
+}
+
+// Unmarshal parses out a valid template from the content
+func Unmarshal(filename string, content []byte) (*api.IssueTemplate, error) {
+ it, err := unmarshal(filename, content)
+ if err != nil {
+ return nil, err
+ }
+
+ if err := Validate(it); err != nil {
+ return nil, err
+ }
+
+ return it, nil
+}
+
+// UnmarshalFromEntry parses out a valid template from the blob in entry
+func UnmarshalFromEntry(entry *git.TreeEntry, dir string) (*api.IssueTemplate, error) {
+ return unmarshalFromEntry(entry, filepath.Join(dir, entry.Name()))
+}
+
+// UnmarshalFromCommit parses out a valid template from the commit
+func UnmarshalFromCommit(commit *git.Commit, filename string) (*api.IssueTemplate, error) {
+ entry, err := commit.GetTreeEntryByPath(filename)
+ if err != nil {
+ return nil, fmt.Errorf("get entry for %q: %w", filename, err)
+ }
+ return unmarshalFromEntry(entry, filename)
+}
+
+// UnmarshalFromRepo parses out a valid template from the head commit of the branch
+func UnmarshalFromRepo(repo *git.Repository, branch, filename string) (*api.IssueTemplate, error) {
+ commit, err := repo.GetBranchCommit(branch)
+ if err != nil {
+ return nil, fmt.Errorf("get commit on branch %q: %w", branch, err)
+ }
+
+ return UnmarshalFromCommit(commit, filename)
+}
+
+func unmarshalFromEntry(entry *git.TreeEntry, filename string) (*api.IssueTemplate, error) {
+ if size := entry.Blob().Size(); size > setting.UI.MaxDisplayFileSize {
+ return nil, fmt.Errorf("too large: %v > MaxDisplayFileSize", size)
+ }
+
+ r, err := entry.Blob().DataAsync()
+ if err != nil {
+ return nil, fmt.Errorf("data async: %w", err)
+ }
+ defer r.Close()
+
+ content, err := io.ReadAll(r)
+ if err != nil {
+ return nil, fmt.Errorf("read all: %w", err)
+ }
+
+ return Unmarshal(filename, content)
+}
+
+func unmarshal(filename string, content []byte) (*api.IssueTemplate, error) {
+ it := &api.IssueTemplate{
+ FileName: filename,
+ }
+
+ // Compatible with treating description as about
+ compatibleTemplate := &struct {
+ About string `yaml:"description"`
+ }{}
+
+ if typ := it.Type(); typ == api.IssueTemplateTypeMarkdown {
+ templateBody, err := markdown.ExtractMetadata(string(content), it)
+ if err != nil {
+ return nil, err
+ }
+ it.Content = templateBody
+ if it.About == "" {
+ if _, err := markdown.ExtractMetadata(string(content), compatibleTemplate); err == nil && compatibleTemplate.About != "" {
+ it.About = compatibleTemplate.About
+ }
+ }
+ } else if typ == api.IssueTemplateTypeYaml {
+ if err := yaml.Unmarshal(content, it); err != nil {
+ return nil, fmt.Errorf("yaml unmarshal: %w", err)
+ }
+ if it.About == "" {
+ if err := yaml.Unmarshal(content, compatibleTemplate); err == nil && compatibleTemplate.About != "" {
+ it.About = compatibleTemplate.About
+ }
+ }
+ for i, v := range it.Fields {
+ if v.ID == "" {
+ v.ID = strconv.Itoa(i)
+ }
+ }
+ }
+
+ return it, nil
+}
diff --git a/modules/lfs/content_store.go b/modules/lfs/content_store.go
index c794a1fecc..0eedf4de17 100644
--- a/modules/lfs/content_store.go
+++ b/modules/lfs/content_store.go
@@ -8,7 +8,6 @@ import (
"crypto/sha256"
"encoding/hex"
"errors"
- "fmt"
"hash"
"io"
"os"
@@ -24,21 +23,6 @@ var (
ErrSizeMismatch = errors.New("Content size does not match")
)
-// ErrRangeNotSatisfiable represents an error which request range is not satisfiable.
-type ErrRangeNotSatisfiable struct {
- FromByte int64
-}
-
-// IsErrRangeNotSatisfiable returns true if the error is an ErrRangeNotSatisfiable
-func IsErrRangeNotSatisfiable(err error) bool {
- _, ok := err.(ErrRangeNotSatisfiable)
- return ok
-}
-
-func (err ErrRangeNotSatisfiable) Error() string {
- return fmt.Sprintf("Requested range %d is not satisfiable", err.FromByte)
-}
-
// ContentStore provides a simple file system based storage.
type ContentStore struct {
storage.ObjectStorage
diff --git a/modules/log/file.go b/modules/log/file.go
index d9a529e67f..8110a9587e 100644
--- a/modules/log/file.go
+++ b/modules/log/file.go
@@ -93,6 +93,7 @@ func NewFileLogger() LoggerProvider {
// Init file logger with json config.
// config like:
+//
// {
// "filename":"log/gogs.log",
// "maxsize":1<<30,
diff --git a/modules/log/file_test.go b/modules/log/file_test.go
index c3074b69df..cc2b9fe077 100644
--- a/modules/log/file_test.go
+++ b/modules/log/file_test.go
@@ -14,15 +14,11 @@ import (
"testing"
"time"
- "code.gitea.io/gitea/modules/util"
-
"github.com/stretchr/testify/assert"
)
func TestFileLoggerFails(t *testing.T) {
- tmpDir, err := os.MkdirTemp("", "TestFileLogger")
- assert.NoError(t, err)
- defer util.RemoveAll(tmpDir)
+ tmpDir := t.TempDir()
prefix := "TestPrefix "
level := INFO
@@ -34,7 +30,7 @@ func TestFileLoggerFails(t *testing.T) {
// assert.True(t, ok)
// Fail if there is bad json
- err = fileLogger.Init("{")
+ err := fileLogger.Init("{")
assert.Error(t, err)
// Fail if there is no filename
@@ -47,9 +43,7 @@ func TestFileLoggerFails(t *testing.T) {
}
func TestFileLogger(t *testing.T) {
- tmpDir, err := os.MkdirTemp("", "TestFileLogger")
- assert.NoError(t, err)
- defer util.RemoveAll(tmpDir)
+ tmpDir := t.TempDir()
prefix := "TestPrefix "
level := INFO
@@ -150,9 +144,7 @@ func TestFileLogger(t *testing.T) {
}
func TestCompressFileLogger(t *testing.T) {
- tmpDir, err := os.MkdirTemp("", "TestFileLogger")
- assert.NoError(t, err)
- defer util.RemoveAll(tmpDir)
+ tmpDir := t.TempDir()
prefix := "TestPrefix "
level := INFO
@@ -210,9 +202,7 @@ func TestCompressFileLogger(t *testing.T) {
}
func TestCompressOldFile(t *testing.T) {
- tmpDir, err := os.MkdirTemp("", "TestFileLogger")
- assert.NoError(t, err)
- defer util.RemoveAll(tmpDir)
+ tmpDir := t.TempDir()
fname := filepath.Join(tmpDir, "test")
nonGzip := filepath.Join(tmpDir, "test-nonGzip")
diff --git a/modules/log/multichannel.go b/modules/log/multichannel.go
index 273df81df1..519abf663d 100644
--- a/modules/log/multichannel.go
+++ b/modules/log/multichannel.go
@@ -33,7 +33,7 @@ func newLogger(name string, buffer int64) *MultiChannelledLogger {
func (l *MultiChannelledLogger) SetLogger(name, provider, config string) error {
eventLogger, err := NewChannelledLog(l.ctx, name, provider, config, l.bufferLength)
if err != nil {
- return fmt.Errorf("Failed to create sublogger (%s): %v", name, err)
+ return fmt.Errorf("failed to create sublogger (%s): %w", name, err)
}
l.MultiChannelledLog.DelLogger(name)
@@ -41,9 +41,9 @@ func (l *MultiChannelledLogger) SetLogger(name, provider, config string) error {
err = l.MultiChannelledLog.AddLogger(eventLogger)
if err != nil {
if IsErrDuplicateName(err) {
- return fmt.Errorf("Duplicate named sublogger %s %v", name, l.MultiChannelledLog.GetEventLoggerNames())
+ return fmt.Errorf("%w other names: %v", err, l.MultiChannelledLog.GetEventLoggerNames())
}
- return fmt.Errorf("Failed to add sublogger (%s): %v", name, err)
+ return fmt.Errorf("failed to add sublogger (%s): %w", name, err)
}
return nil
diff --git a/modules/log/smtp.go b/modules/log/smtp.go
index c5163292e6..1706517d6a 100644
--- a/modules/log/smtp.go
+++ b/modules/log/smtp.go
@@ -48,6 +48,7 @@ func NewSMTPLogger() LoggerProvider {
// Init smtp writer with json config.
// config like:
+//
// {
// "Username":"example@gmail.com",
// "password:"password",
diff --git a/modules/markup/html_test.go b/modules/markup/html_test.go
index a7cf81250b..e57187a677 100644
--- a/modules/markup/html_test.go
+++ b/modules/markup/html_test.go
@@ -7,6 +7,7 @@ package markup_test
import (
"context"
"io"
+ "os"
"strings"
"testing"
@@ -24,7 +25,7 @@ import (
var localMetas = map[string]string{
"user": "gogits",
"repo": "gogs",
- "repoPath": "../../integrations/gitea-repositories-meta/user13/repo11.git/",
+ "repoPath": "../../tests/gitea-repositories-meta/user13/repo11.git/",
}
func TestMain(m *testing.M) {
@@ -32,6 +33,7 @@ func TestMain(m *testing.M) {
if err := git.InitSimple(context.Background()); err != nil {
log.Fatal("git init failed, err: %v", err)
}
+ os.Exit(m.Run())
}
func TestRender_Commits(t *testing.T) {
@@ -336,7 +338,7 @@ func TestRender_emoji(t *testing.T) {
`]Some text with 😄😄 2 emoji next to each other
`)
test(
"😎🤪🔐🤑❓",
- `😎🤪🔐🤑❓
`)
+ `😎🤪🔐🤑❓
`)
// should match nothing
test(
diff --git a/modules/markup/markdown/convertyaml.go b/modules/markup/markdown/convertyaml.go
new file mode 100644
index 0000000000..3f5ebec908
--- /dev/null
+++ b/modules/markup/markdown/convertyaml.go
@@ -0,0 +1,84 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package markdown
+
+import (
+ "github.com/yuin/goldmark/ast"
+ east "github.com/yuin/goldmark/extension/ast"
+ "gopkg.in/yaml.v3"
+)
+
+func nodeToTable(meta *yaml.Node) ast.Node {
+ for {
+ if meta == nil {
+ return nil
+ }
+ switch meta.Kind {
+ case yaml.DocumentNode:
+ meta = meta.Content[0]
+ continue
+ default:
+ }
+ break
+ }
+ switch meta.Kind {
+ case yaml.MappingNode:
+ return mappingNodeToTable(meta)
+ case yaml.SequenceNode:
+ return sequenceNodeToTable(meta)
+ default:
+ return ast.NewString([]byte(meta.Value))
+ }
+}
+
+func mappingNodeToTable(meta *yaml.Node) ast.Node {
+ table := east.NewTable()
+ alignments := []east.Alignment{}
+ for i := 0; i < len(meta.Content); i += 2 {
+ alignments = append(alignments, east.AlignNone)
+ }
+
+ headerRow := east.NewTableRow(alignments)
+ valueRow := east.NewTableRow(alignments)
+ for i := 0; i < len(meta.Content); i += 2 {
+ cell := east.NewTableCell()
+
+ cell.AppendChild(cell, nodeToTable(meta.Content[i]))
+ headerRow.AppendChild(headerRow, cell)
+
+ if i+1 < len(meta.Content) {
+ cell = east.NewTableCell()
+ cell.AppendChild(cell, nodeToTable(meta.Content[i+1]))
+ valueRow.AppendChild(valueRow, cell)
+ }
+ }
+
+ table.AppendChild(table, east.NewTableHeader(headerRow))
+ table.AppendChild(table, valueRow)
+ return table
+}
+
+func sequenceNodeToTable(meta *yaml.Node) ast.Node {
+ table := east.NewTable()
+ alignments := []east.Alignment{east.AlignNone}
+ for _, item := range meta.Content {
+ row := east.NewTableRow(alignments)
+ cell := east.NewTableCell()
+ cell.AppendChild(cell, nodeToTable(item))
+ row.AppendChild(row, cell)
+ table.AppendChild(table, row)
+ }
+ return table
+}
+
+func nodeToDetails(meta *yaml.Node, icon string) ast.Node {
+ details := NewDetails()
+ summary := NewSummary()
+ summary.AppendChild(summary, NewIcon(icon))
+ details.AppendChild(details, summary)
+ details.AppendChild(details, nodeToTable(meta))
+
+ return details
+}
diff --git a/modules/markup/markdown/goldmark.go b/modules/markup/markdown/goldmark.go
index 1750128dec..8417019ddb 100644
--- a/modules/markup/markdown/goldmark.go
+++ b/modules/markup/markdown/goldmark.go
@@ -10,12 +10,12 @@ import (
"regexp"
"strings"
+ "code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/markup"
"code.gitea.io/gitea/modules/markup/common"
"code.gitea.io/gitea/modules/setting"
giteautil "code.gitea.io/gitea/modules/util"
- meta "github.com/yuin/goldmark-meta"
"github.com/yuin/goldmark/ast"
east "github.com/yuin/goldmark/extension/ast"
"github.com/yuin/goldmark/parser"
@@ -32,20 +32,12 @@ type ASTTransformer struct{}
// Transform transforms the given AST tree.
func (g *ASTTransformer) Transform(node *ast.Document, reader text.Reader, pc parser.Context) {
- metaData := meta.GetItems(pc)
firstChild := node.FirstChild()
createTOC := false
ctx := pc.Get(renderContextKey).(*markup.RenderContext)
- rc := &RenderConfig{
- Meta: "table",
- Icon: "table",
- Lang: "",
- }
-
- if metaData != nil {
- rc.ToRenderConfig(metaData)
-
- metaNode := rc.toMetaNode(metaData)
+ rc := pc.Get(renderConfigKey).(*RenderConfig)
+ if rc.yamlNode != nil {
+ metaNode := rc.toMetaNode()
if metaNode != nil {
node.InsertBefore(node, firstChild, metaNode)
}
@@ -207,7 +199,7 @@ func (g *ASTTransformer) Transform(node *ast.Document, reader text.Reader, pc pa
}
type prefixedIDs struct {
- values map[string]bool
+ values container.Set[string]
}
// Generate generates a new element id.
@@ -228,14 +220,12 @@ func (p *prefixedIDs) GenerateWithDefault(value, dft []byte) []byte {
if !bytes.HasPrefix(result, []byte("user-content-")) {
result = append([]byte("user-content-"), result...)
}
- if _, ok := p.values[util.BytesToReadOnlyString(result)]; !ok {
- p.values[util.BytesToReadOnlyString(result)] = true
+ if p.values.Add(util.BytesToReadOnlyString(result)) {
return result
}
for i := 1; ; i++ {
newResult := fmt.Sprintf("%s-%d", result, i)
- if _, ok := p.values[newResult]; !ok {
- p.values[newResult] = true
+ if p.values.Add(newResult) {
return []byte(newResult)
}
}
@@ -243,12 +233,12 @@ func (p *prefixedIDs) GenerateWithDefault(value, dft []byte) []byte {
// Put puts a given element id to the used ids table.
func (p *prefixedIDs) Put(value []byte) {
- p.values[util.BytesToReadOnlyString(value)] = true
+ p.values.Add(util.BytesToReadOnlyString(value))
}
func newPrefixedIDs() *prefixedIDs {
return &prefixedIDs{
- values: map[string]bool{},
+ values: make(container.Set[string]),
}
}
diff --git a/modules/markup/markdown/markdown.go b/modules/markup/markdown/markdown.go
index 4ce85dfc31..fa289986cc 100644
--- a/modules/markup/markdown/markdown.go
+++ b/modules/markup/markdown/markdown.go
@@ -14,12 +14,13 @@ import (
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/markup"
"code.gitea.io/gitea/modules/markup/common"
+ "code.gitea.io/gitea/modules/markup/markdown/math"
"code.gitea.io/gitea/modules/setting"
giteautil "code.gitea.io/gitea/modules/util"
- chromahtml "github.com/alecthomas/chroma/formatters/html"
+ chromahtml "github.com/alecthomas/chroma/v2/formatters/html"
"github.com/yuin/goldmark"
- highlighting "github.com/yuin/goldmark-highlighting"
+ highlighting "github.com/yuin/goldmark-highlighting/v2"
meta "github.com/yuin/goldmark-meta"
"github.com/yuin/goldmark/extension"
"github.com/yuin/goldmark/parser"
@@ -38,6 +39,7 @@ var (
isWikiKey = parser.NewContextKey()
renderMetasKey = parser.NewContextKey()
renderContextKey = parser.NewContextKey()
+ renderConfigKey = parser.NewContextKey()
)
type limitWriter struct {
@@ -98,7 +100,7 @@ func actualRender(ctx *markup.RenderContext, input io.Reader, output io.Writer)
languageStr := string(language)
preClasses := []string{"code-block"}
- if languageStr == "mermaid" {
+ if languageStr == "mermaid" || languageStr == "math" {
preClasses = append(preClasses, "is-loading")
}
@@ -120,6 +122,9 @@ func actualRender(ctx *markup.RenderContext, input io.Reader, output io.Writer)
}
}),
),
+ math.NewExtension(
+ math.Enabled(setting.Markdown.EnableMath),
+ ),
meta.Meta,
),
goldmark.WithParserOptions(
@@ -167,7 +172,18 @@ func actualRender(ctx *markup.RenderContext, input io.Reader, output io.Writer)
log.Error("Unable to ReadAll: %v", err)
return err
}
- if err := converter.Convert(giteautil.NormalizeEOL(buf), lw, parser.WithContext(pc)); err != nil {
+ buf = giteautil.NormalizeEOL(buf)
+
+ rc := &RenderConfig{
+ Meta: "table",
+ Icon: "table",
+ Lang: "",
+ }
+ buf, _ = ExtractMetadataBytes(buf, rc)
+
+ pc.Set(renderConfigKey, rc)
+
+ if err := converter.Convert(buf, lw, parser.WithContext(pc)); err != nil {
log.Error("Unable to render: %v", err)
return err
}
diff --git a/modules/markup/markdown/markdown_test.go b/modules/markup/markdown/markdown_test.go
index 732fe1a6be..49ed3d75d6 100644
--- a/modules/markup/markdown/markdown_test.go
+++ b/modules/markup/markdown/markdown_test.go
@@ -6,6 +6,7 @@ package markdown_test
import (
"context"
+ "os"
"strings"
"testing"
@@ -29,7 +30,7 @@ const (
var localMetas = map[string]string{
"user": "gogits",
"repo": "gogs",
- "repoPath": "../../../integrations/gitea-repositories-meta/user13/repo11.git/",
+ "repoPath": "../../../tests/gitea-repositories-meta/user13/repo11.git/",
}
func TestMain(m *testing.M) {
@@ -37,6 +38,7 @@ func TestMain(m *testing.M) {
if err := git.InitSimple(context.Background()); err != nil {
log.Fatal("git init failed, err: %v", err)
}
+ os.Exit(m.Run())
}
func TestRender_StandardLinks(t *testing.T) {
@@ -426,3 +428,51 @@ func TestRenderEmojiInLinks_Issue12331(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, expected, res)
}
+
+func TestMathBlock(t *testing.T) {
+ const nl = "\n"
+ testcases := []struct {
+ testcase string
+ expected string
+ }{
+ {
+ "$a$",
+ `a
` + nl,
+ },
+ {
+ "$ a $",
+ `a
` + nl,
+ },
+ {
+ "$a$ $b$",
+ `a
b
` + nl,
+ },
+ {
+ `\(a\) \(b\)`,
+ `a
b
` + nl,
+ },
+ {
+ `$a a$b b$`,
+ `a a$b b
` + nl,
+ },
+ {
+ `a a$b b`,
+ `a a$b b
` + nl,
+ },
+ {
+ `a$b $a a$b b$`,
+ `a$b a a$b b
` + nl,
+ },
+ {
+ "$$a$$",
+ `a
` + nl,
+ },
+ }
+
+ for _, test := range testcases {
+ res, err := RenderString(&markup.RenderContext{}, test.testcase)
+ assert.NoError(t, err, "Unexpected error in testcase: %q", test.testcase)
+ assert.Equal(t, test.expected, res, "Unexpected result in testcase %q", test.testcase)
+
+ }
+}
diff --git a/modules/markup/markdown/math/block_node.go b/modules/markup/markdown/math/block_node.go
new file mode 100644
index 0000000000..bd8449babf
--- /dev/null
+++ b/modules/markup/markdown/math/block_node.go
@@ -0,0 +1,42 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package math
+
+import "github.com/yuin/goldmark/ast"
+
+// Block represents a display math block e.g. $$...$$ or \[...\]
+type Block struct {
+ ast.BaseBlock
+ Dollars bool
+ Indent int
+ Closed bool
+}
+
+// KindBlock is the node kind for math blocks
+var KindBlock = ast.NewNodeKind("MathBlock")
+
+// NewBlock creates a new math Block
+func NewBlock(dollars bool, indent int) *Block {
+ return &Block{
+ Dollars: dollars,
+ Indent: indent,
+ }
+}
+
+// Dump dumps the block to a string
+func (n *Block) Dump(source []byte, level int) {
+ m := map[string]string{}
+ ast.DumpHelper(n, source, level, m, nil)
+}
+
+// Kind returns KindBlock for math Blocks
+func (n *Block) Kind() ast.NodeKind {
+ return KindBlock
+}
+
+// IsRaw returns true as this block should not be processed further
+func (n *Block) IsRaw() bool {
+ return true
+}
diff --git a/modules/markup/markdown/math/block_parser.go b/modules/markup/markdown/math/block_parser.go
new file mode 100644
index 0000000000..f865122886
--- /dev/null
+++ b/modules/markup/markdown/math/block_parser.go
@@ -0,0 +1,123 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package math
+
+import (
+ "bytes"
+
+ "github.com/yuin/goldmark/ast"
+ "github.com/yuin/goldmark/parser"
+ "github.com/yuin/goldmark/text"
+ "github.com/yuin/goldmark/util"
+)
+
+type blockParser struct {
+ parseDollars bool
+}
+
+// NewBlockParser creates a new math BlockParser
+func NewBlockParser(parseDollarBlocks bool) parser.BlockParser {
+ return &blockParser{
+ parseDollars: parseDollarBlocks,
+ }
+}
+
+// Open parses the current line and returns a result of parsing.
+func (b *blockParser) Open(parent ast.Node, reader text.Reader, pc parser.Context) (ast.Node, parser.State) {
+ line, segment := reader.PeekLine()
+ pos := pc.BlockOffset()
+ if pos == -1 || len(line[pos:]) < 2 {
+ return nil, parser.NoChildren
+ }
+
+ dollars := false
+ if b.parseDollars && line[pos] == '$' && line[pos+1] == '$' {
+ dollars = true
+ } else if line[pos] != '\\' || line[pos+1] != '[' {
+ return nil, parser.NoChildren
+ }
+
+ node := NewBlock(dollars, pos)
+
+ // Now we need to check if the ending block is on the segment...
+ endBytes := []byte{'\\', ']'}
+ if dollars {
+ endBytes = []byte{'$', '$'}
+ }
+ idx := bytes.Index(line[pos+2:], endBytes)
+ if idx >= 0 {
+ segment.Stop = segment.Start + idx + 2
+ reader.Advance(segment.Len() - 1)
+ segment.Start += 2
+ node.Lines().Append(segment)
+ node.Closed = true
+ return node, parser.Close | parser.NoChildren
+ }
+
+ reader.Advance(segment.Len() - 1)
+ segment.Start += 2
+ node.Lines().Append(segment)
+ return node, parser.NoChildren
+}
+
+// Continue parses the current line and returns a result of parsing.
+func (b *blockParser) Continue(node ast.Node, reader text.Reader, pc parser.Context) parser.State {
+ block := node.(*Block)
+ if block.Closed {
+ return parser.Close
+ }
+
+ line, segment := reader.PeekLine()
+ w, pos := util.IndentWidth(line, 0)
+ if w < 4 {
+ if block.Dollars {
+ i := pos
+ for ; i < len(line) && line[i] == '$'; i++ {
+ }
+ length := i - pos
+ if length >= 2 && util.IsBlank(line[i:]) {
+ reader.Advance(segment.Stop - segment.Start - segment.Padding)
+ block.Closed = true
+ return parser.Close
+ }
+ } else if len(line[pos:]) > 1 && line[pos] == '\\' && line[pos+1] == ']' && util.IsBlank(line[pos+2:]) {
+ reader.Advance(segment.Stop - segment.Start - segment.Padding)
+ block.Closed = true
+ return parser.Close
+ }
+ }
+
+ pos, padding := util.IndentPosition(line, 0, block.Indent)
+ seg := text.NewSegmentPadding(segment.Start+pos, segment.Stop, padding)
+ node.Lines().Append(seg)
+ reader.AdvanceAndSetPadding(segment.Stop-segment.Start-pos-1, padding)
+ return parser.Continue | parser.NoChildren
+}
+
+// Close will be called when the parser returns Close.
+func (b *blockParser) Close(node ast.Node, reader text.Reader, pc parser.Context) {
+ // noop
+}
+
+// CanInterruptParagraph returns true if the parser can interrupt paragraphs,
+// otherwise false.
+func (b *blockParser) CanInterruptParagraph() bool {
+ return true
+}
+
+// CanAcceptIndentedLine returns true if the parser can open new node when
+// the given line is being indented more than 3 spaces.
+func (b *blockParser) CanAcceptIndentedLine() bool {
+ return false
+}
+
+// Trigger returns a list of characters that triggers Parse method of
+// this parser.
+// If Trigger returns a nil, Open will be called with any lines.
+//
+// We leave this as nil as our parse method is quick enough
+func (b *blockParser) Trigger() []byte {
+ return nil
+}
diff --git a/modules/markup/markdown/math/block_renderer.go b/modules/markup/markdown/math/block_renderer.go
new file mode 100644
index 0000000000..d502065259
--- /dev/null
+++ b/modules/markup/markdown/math/block_renderer.go
@@ -0,0 +1,43 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package math
+
+import (
+ gast "github.com/yuin/goldmark/ast"
+ "github.com/yuin/goldmark/renderer"
+ "github.com/yuin/goldmark/util"
+)
+
+// BlockRenderer represents a renderer for math Blocks
+type BlockRenderer struct{}
+
+// NewBlockRenderer creates a new renderer for math Blocks
+func NewBlockRenderer() renderer.NodeRenderer {
+ return &BlockRenderer{}
+}
+
+// RegisterFuncs registers the renderer for math Blocks
+func (r *BlockRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) {
+ reg.Register(KindBlock, r.renderBlock)
+}
+
+func (r *BlockRenderer) writeLines(w util.BufWriter, source []byte, n gast.Node) {
+ l := n.Lines().Len()
+ for i := 0; i < l; i++ {
+ line := n.Lines().At(i)
+ _, _ = w.Write(util.EscapeHTML(line.Value(source)))
+ }
+}
+
+func (r *BlockRenderer) renderBlock(w util.BufWriter, source []byte, node gast.Node, entering bool) (gast.WalkStatus, error) {
+ n := node.(*Block)
+ if entering {
+ _, _ = w.WriteString(``)
+ r.writeLines(w, source, n)
+ } else {
+ _, _ = w.WriteString(`
` + "\n")
+ }
+ return gast.WalkContinue, nil
+}
diff --git a/modules/markup/markdown/math/inline_node.go b/modules/markup/markdown/math/inline_node.go
new file mode 100644
index 0000000000..245ff8dab0
--- /dev/null
+++ b/modules/markup/markdown/math/inline_node.go
@@ -0,0 +1,49 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package math
+
+import (
+ "github.com/yuin/goldmark/ast"
+ "github.com/yuin/goldmark/util"
+)
+
+// Inline represents inline math e.g. $...$ or \(...\)
+type Inline struct {
+ ast.BaseInline
+}
+
+// Inline implements Inline.Inline.
+func (n *Inline) Inline() {}
+
+// IsBlank returns if this inline node is empty
+func (n *Inline) IsBlank(source []byte) bool {
+ for c := n.FirstChild(); c != nil; c = c.NextSibling() {
+ text := c.(*ast.Text).Segment
+ if !util.IsBlank(text.Value(source)) {
+ return false
+ }
+ }
+ return true
+}
+
+// Dump renders this inline math as debug
+func (n *Inline) Dump(source []byte, level int) {
+ ast.DumpHelper(n, source, level, nil, nil)
+}
+
+// KindInline is the kind for math inline
+var KindInline = ast.NewNodeKind("MathInline")
+
+// Kind returns KindInline
+func (n *Inline) Kind() ast.NodeKind {
+ return KindInline
+}
+
+// NewInline creates a new ast math inline node
+func NewInline() *Inline {
+ return &Inline{
+ BaseInline: ast.BaseInline{},
+ }
+}
diff --git a/modules/markup/markdown/math/inline_parser.go b/modules/markup/markdown/math/inline_parser.go
new file mode 100644
index 0000000000..8dc88eb858
--- /dev/null
+++ b/modules/markup/markdown/math/inline_parser.go
@@ -0,0 +1,120 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package math
+
+import (
+ "bytes"
+
+ "github.com/yuin/goldmark/ast"
+ "github.com/yuin/goldmark/parser"
+ "github.com/yuin/goldmark/text"
+)
+
+type inlineParser struct {
+ start []byte
+ end []byte
+}
+
+var defaultInlineDollarParser = &inlineParser{
+ start: []byte{'$'},
+ end: []byte{'$'},
+}
+
+// NewInlineDollarParser returns a new inline parser
+func NewInlineDollarParser() parser.InlineParser {
+ return defaultInlineDollarParser
+}
+
+var defaultInlineBracketParser = &inlineParser{
+ start: []byte{'\\', '('},
+ end: []byte{'\\', ')'},
+}
+
+// NewInlineDollarParser returns a new inline parser
+func NewInlineBracketParser() parser.InlineParser {
+ return defaultInlineBracketParser
+}
+
+// Trigger triggers this parser on $ or \
+func (parser *inlineParser) Trigger() []byte {
+ return parser.start[0:1]
+}
+
+func isAlphanumeric(b byte) bool {
+ // Github only cares about 0-9A-Za-z
+ return (b >= '0' && b <= '9') || (b >= 'A' && b <= 'Z') || (b >= 'a' && b <= 'z')
+}
+
+// Parse parses the current line and returns a result of parsing.
+func (parser *inlineParser) Parse(parent ast.Node, block text.Reader, pc parser.Context) ast.Node {
+ line, _ := block.PeekLine()
+
+ if !bytes.HasPrefix(line, parser.start) {
+ // We'll catch this one on the next time round
+ return nil
+ }
+
+ precedingCharacter := block.PrecendingCharacter()
+ if precedingCharacter < 256 && isAlphanumeric(byte(precedingCharacter)) {
+ // need to exclude things like `a$` from being considered a start
+ return nil
+ }
+
+ // move the opener marker point at the start of the text
+ opener := len(parser.start)
+
+ // Now look for an ending line
+ ender := opener
+ for {
+ pos := bytes.Index(line[ender:], parser.end)
+ if pos < 0 {
+ return nil
+ }
+
+ ender += pos
+
+ // Now we want to check the character at the end of our parser section
+ // that is ender + len(parser.end)
+ pos = ender + len(parser.end)
+ if len(line) <= pos {
+ break
+ }
+ if !isAlphanumeric(line[pos]) {
+ break
+ }
+ // move the pointer onwards
+ ender += len(parser.end)
+ }
+
+ block.Advance(opener)
+ _, pos := block.Position()
+ node := NewInline()
+ segment := pos.WithStop(pos.Start + ender - opener)
+ node.AppendChild(node, ast.NewRawTextSegment(segment))
+ block.Advance(ender - opener + len(parser.end))
+
+ trimBlock(node, block)
+ return node
+}
+
+func trimBlock(node *Inline, block text.Reader) {
+ if node.IsBlank(block.Source()) {
+ return
+ }
+
+ // trim first space and last space
+ first := node.FirstChild().(*ast.Text)
+ if !(!first.Segment.IsEmpty() && block.Source()[first.Segment.Start] == ' ') {
+ return
+ }
+
+ last := node.LastChild().(*ast.Text)
+ if !(!last.Segment.IsEmpty() && block.Source()[last.Segment.Stop-1] == ' ') {
+ return
+ }
+
+ first.Segment = first.Segment.WithStart(first.Segment.Start + 1)
+ last.Segment = last.Segment.WithStop(last.Segment.Stop - 1)
+}
diff --git a/modules/markup/markdown/math/inline_renderer.go b/modules/markup/markdown/math/inline_renderer.go
new file mode 100644
index 0000000000..e4c0f3761d
--- /dev/null
+++ b/modules/markup/markdown/math/inline_renderer.go
@@ -0,0 +1,47 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package math
+
+import (
+ "bytes"
+
+ "github.com/yuin/goldmark/ast"
+ "github.com/yuin/goldmark/renderer"
+ "github.com/yuin/goldmark/util"
+)
+
+// InlineRenderer is an inline renderer
+type InlineRenderer struct{}
+
+// NewInlineRenderer returns a new renderer for inline math
+func NewInlineRenderer() renderer.NodeRenderer {
+ return &InlineRenderer{}
+}
+
+func (r *InlineRenderer) renderInline(w util.BufWriter, source []byte, n ast.Node, entering bool) (ast.WalkStatus, error) {
+ if entering {
+ _, _ = w.WriteString(``)
+ for c := n.FirstChild(); c != nil; c = c.NextSibling() {
+ segment := c.(*ast.Text).Segment
+ value := util.EscapeHTML(segment.Value(source))
+ if bytes.HasSuffix(value, []byte("\n")) {
+ _, _ = w.Write(value[:len(value)-1])
+ if c != n.LastChild() {
+ _, _ = w.Write([]byte(" "))
+ }
+ } else {
+ _, _ = w.Write(value)
+ }
+ }
+ return ast.WalkSkipChildren, nil
+ }
+ _, _ = w.WriteString(`
`)
+ return ast.WalkContinue, nil
+}
+
+// RegisterFuncs registers the renderer for inline math nodes
+func (r *InlineRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) {
+ reg.Register(KindInline, r.renderInline)
+}
diff --git a/modules/markup/markdown/math/math.go b/modules/markup/markdown/math/math.go
new file mode 100644
index 0000000000..7854ac84db
--- /dev/null
+++ b/modules/markup/markdown/math/math.go
@@ -0,0 +1,108 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package math
+
+import (
+ "github.com/yuin/goldmark"
+ "github.com/yuin/goldmark/parser"
+ "github.com/yuin/goldmark/renderer"
+ "github.com/yuin/goldmark/util"
+)
+
+// Extension is a math extension
+type Extension struct {
+ enabled bool
+ parseDollarInline bool
+ parseDollarBlock bool
+}
+
+// Option is the interface Options should implement
+type Option interface {
+ SetOption(e *Extension)
+}
+
+type extensionFunc func(e *Extension)
+
+func (fn extensionFunc) SetOption(e *Extension) {
+ fn(e)
+}
+
+// Enabled enables or disables this extension
+func Enabled(enable ...bool) Option {
+ value := true
+ if len(enable) > 0 {
+ value = enable[0]
+ }
+ return extensionFunc(func(e *Extension) {
+ e.enabled = value
+ })
+}
+
+// WithInlineDollarParser enables or disables the parsing of $...$
+func WithInlineDollarParser(enable ...bool) Option {
+ value := true
+ if len(enable) > 0 {
+ value = enable[0]
+ }
+ return extensionFunc(func(e *Extension) {
+ e.parseDollarInline = value
+ })
+}
+
+// WithBlockDollarParser enables or disables the parsing of $$...$$
+func WithBlockDollarParser(enable ...bool) Option {
+ value := true
+ if len(enable) > 0 {
+ value = enable[0]
+ }
+ return extensionFunc(func(e *Extension) {
+ e.parseDollarBlock = value
+ })
+}
+
+// Math represents a math extension with default rendered delimiters
+var Math = &Extension{
+ enabled: true,
+ parseDollarBlock: true,
+ parseDollarInline: true,
+}
+
+// NewExtension creates a new math extension with the provided options
+func NewExtension(opts ...Option) *Extension {
+ r := &Extension{
+ enabled: true,
+ parseDollarBlock: true,
+ parseDollarInline: true,
+ }
+
+ for _, o := range opts {
+ o.SetOption(r)
+ }
+ return r
+}
+
+// Extend extends goldmark with our parsers and renderers
+func (e *Extension) Extend(m goldmark.Markdown) {
+ if !e.enabled {
+ return
+ }
+
+ m.Parser().AddOptions(parser.WithBlockParsers(
+ util.Prioritized(NewBlockParser(e.parseDollarBlock), 701),
+ ))
+
+ inlines := []util.PrioritizedValue{
+ util.Prioritized(NewInlineBracketParser(), 501),
+ }
+ if e.parseDollarInline {
+ inlines = append(inlines, util.Prioritized(NewInlineDollarParser(), 501))
+ }
+ m.Parser().AddOptions(parser.WithInlineParsers(inlines...))
+
+ m.Renderer().AddOptions(renderer.WithNodeRenderers(
+ util.Prioritized(NewBlockRenderer(), 501),
+ util.Prioritized(NewInlineRenderer(), 502),
+ ))
+}
diff --git a/modules/markup/markdown/meta.go b/modules/markup/markdown/meta.go
index faf92ae2c6..45d79d537a 100644
--- a/modules/markup/markdown/meta.go
+++ b/modules/markup/markdown/meta.go
@@ -5,47 +5,104 @@
package markdown
import (
+ "bytes"
"errors"
- "strings"
+ "unicode"
+ "unicode/utf8"
- "gopkg.in/yaml.v2"
+ "code.gitea.io/gitea/modules/log"
+
+ "gopkg.in/yaml.v3"
)
-func isYAMLSeparator(line string) bool {
- line = strings.TrimSpace(line)
- for i := 0; i < len(line); i++ {
- if line[i] != '-' {
+func isYAMLSeparator(line []byte) bool {
+ idx := 0
+ for ; idx < len(line); idx++ {
+ if line[idx] >= utf8.RuneSelf {
+ r, sz := utf8.DecodeRune(line[idx:])
+ if !unicode.IsSpace(r) {
+ return false
+ }
+ idx += sz
+ continue
+ }
+ if line[idx] != ' ' {
+ break
+ }
+ }
+ dashCount := 0
+ for ; idx < len(line); idx++ {
+ if line[idx] != '-' {
+ break
+ }
+ dashCount++
+ }
+ if dashCount < 3 {
+ return false
+ }
+ for ; idx < len(line); idx++ {
+ if line[idx] >= utf8.RuneSelf {
+ r, sz := utf8.DecodeRune(line[idx:])
+ if !unicode.IsSpace(r) {
+ return false
+ }
+ idx += sz
+ continue
+ }
+ if line[idx] != ' ' {
return false
}
}
- return len(line) > 2
+ return true
}
// ExtractMetadata consumes a markdown file, parses YAML frontmatter,
// and returns the frontmatter metadata separated from the markdown content
func ExtractMetadata(contents string, out interface{}) (string, error) {
- var front, body []string
- lines := strings.Split(contents, "\n")
- for idx, line := range lines {
- if idx == 0 {
- // First line has to be a separator
- if !isYAMLSeparator(line) {
- return "", errors.New("frontmatter must start with a separator line")
- }
- continue
+ body, err := ExtractMetadataBytes([]byte(contents), out)
+ return string(body), err
+}
+
+// ExtractMetadata consumes a markdown file, parses YAML frontmatter,
+// and returns the frontmatter metadata separated from the markdown content
+func ExtractMetadataBytes(contents []byte, out interface{}) ([]byte, error) {
+ var front, body []byte
+
+ start, end := 0, len(contents)
+ idx := bytes.IndexByte(contents[start:], '\n')
+ if idx >= 0 {
+ end = start + idx
+ }
+ line := contents[start:end]
+
+ if !isYAMLSeparator(line) {
+ return contents, errors.New("frontmatter must start with a separator line")
+ }
+ frontMatterStart := end + 1
+ for start = frontMatterStart; start < len(contents); start = end + 1 {
+ end = len(contents)
+ idx := bytes.IndexByte(contents[start:], '\n')
+ if idx >= 0 {
+ end = start + idx
}
+ line := contents[start:end]
if isYAMLSeparator(line) {
- front, body = lines[1:idx], lines[idx+1:]
+ front = contents[frontMatterStart:start]
+ if end+1 < len(contents) {
+ body = contents[end+1:]
+ }
break
}
}
if len(front) == 0 {
- return "", errors.New("could not determine metadata")
+ return contents, errors.New("could not determine metadata")
}
- if err := yaml.Unmarshal([]byte(strings.Join(front, "\n")), out); err != nil {
- return "", err
+ log.Info("%s", string(front))
+
+ if err := yaml.Unmarshal(front, out); err != nil {
+ return contents, err
}
- return strings.Join(body, "\n"), nil
+ return body, nil
}
diff --git a/modules/markup/markdown/meta_test.go b/modules/markup/markdown/meta_test.go
index f525777a54..720d0066f4 100644
--- a/modules/markup/markdown/meta_test.go
+++ b/modules/markup/markdown/meta_test.go
@@ -6,6 +6,7 @@ package markdown
import (
"fmt"
+ "strings"
"testing"
"code.gitea.io/gitea/modules/structs"
@@ -13,6 +14,16 @@ import (
"github.com/stretchr/testify/assert"
)
+func validateMetadata(it structs.IssueTemplate) bool {
+ /*
+ A legacy to keep the unit tests working.
+ Copied from the method "func (it IssueTemplate) Valid() bool", the original method has been removed.
+ Because it becomes quite complicated to validate an issue template which is support yaml form now.
+ The new way to validate an issue template is to call the Validate in modules/issue/template,
+ */
+ return strings.TrimSpace(it.Name) != "" && strings.TrimSpace(it.About) != ""
+}
+
func TestExtractMetadata(t *testing.T) {
t.Run("ValidFrontAndBody", func(t *testing.T) {
var meta structs.IssueTemplate
@@ -20,7 +31,7 @@ func TestExtractMetadata(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, bodyTest, body)
assert.Equal(t, metaTest, meta)
- assert.True(t, meta.Valid())
+ assert.True(t, validateMetadata(meta))
})
t.Run("NoFirstSeparator", func(t *testing.T) {
@@ -41,7 +52,39 @@ func TestExtractMetadata(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, "", body)
assert.Equal(t, metaTest, meta)
- assert.True(t, meta.Valid())
+ assert.True(t, validateMetadata(meta))
+ })
+}
+
+func TestExtractMetadataBytes(t *testing.T) {
+ t.Run("ValidFrontAndBody", func(t *testing.T) {
+ var meta structs.IssueTemplate
+ body, err := ExtractMetadataBytes([]byte(fmt.Sprintf("%s\n%s\n%s\n%s", sepTest, frontTest, sepTest, bodyTest)), &meta)
+ assert.NoError(t, err)
+ assert.Equal(t, bodyTest, string(body))
+ assert.Equal(t, metaTest, meta)
+ assert.True(t, validateMetadata(meta))
+ })
+
+ t.Run("NoFirstSeparator", func(t *testing.T) {
+ var meta structs.IssueTemplate
+ _, err := ExtractMetadataBytes([]byte(fmt.Sprintf("%s\n%s\n%s", frontTest, sepTest, bodyTest)), &meta)
+ assert.Error(t, err)
+ })
+
+ t.Run("NoLastSeparator", func(t *testing.T) {
+ var meta structs.IssueTemplate
+ _, err := ExtractMetadataBytes([]byte(fmt.Sprintf("%s\n%s\n%s", sepTest, frontTest, bodyTest)), &meta)
+ assert.Error(t, err)
+ })
+
+ t.Run("NoBody", func(t *testing.T) {
+ var meta structs.IssueTemplate
+ body, err := ExtractMetadataBytes([]byte(fmt.Sprintf("%s\n%s\n%s", sepTest, frontTest, sepTest)), &meta)
+ assert.NoError(t, err)
+ assert.Equal(t, "", string(body))
+ assert.Equal(t, metaTest, meta)
+ assert.True(t, validateMetadata(meta))
})
}
diff --git a/modules/markup/markdown/renderconfig.go b/modules/markup/markdown/renderconfig.go
index bef67e9e59..1ba75dbb68 100644
--- a/modules/markup/markdown/renderconfig.go
+++ b/modules/markup/markdown/renderconfig.go
@@ -9,58 +9,53 @@ import (
"strings"
"github.com/yuin/goldmark/ast"
- east "github.com/yuin/goldmark/extension/ast"
- "gopkg.in/yaml.v2"
+ "gopkg.in/yaml.v3"
)
// RenderConfig represents rendering configuration for this file
type RenderConfig struct {
- Meta string
- Icon string
- TOC bool
- Lang string
+ Meta string
+ Icon string
+ TOC bool
+ Lang string
+ yamlNode *yaml.Node
}
-// ToRenderConfig converts a yaml.MapSlice to a RenderConfig
-func (rc *RenderConfig) ToRenderConfig(meta yaml.MapSlice) {
- if meta == nil {
- return
+// UnmarshalYAML implement yaml.v3 UnmarshalYAML
+func (rc *RenderConfig) UnmarshalYAML(value *yaml.Node) error {
+ if rc == nil {
+ rc = &RenderConfig{
+ Meta: "table",
+ Icon: "table",
+ Lang: "",
+ }
}
- found := false
- var giteaMetaControl yaml.MapItem
- for _, item := range meta {
- strKey, ok := item.Key.(string)
- if !ok {
- continue
- }
- strKey = strings.TrimSpace(strings.ToLower(strKey))
- switch strKey {
- case "gitea":
- giteaMetaControl = item
- found = true
- case "include_toc":
- val, ok := item.Value.(bool)
- if !ok {
- continue
- }
- rc.TOC = val
- case "lang":
- val, ok := item.Value.(string)
- if !ok {
- continue
- }
- val = strings.TrimSpace(val)
- if len(val) == 0 {
- continue
- }
- rc.Lang = val
- }
+ rc.yamlNode = value
+
+ type commonRenderConfig struct {
+ TOC bool `yaml:"include_toc"`
+ Lang string `yaml:"lang"`
+ }
+ var basic commonRenderConfig
+ if err := value.Decode(&basic); err != nil {
+ return fmt.Errorf("unable to decode into commonRenderConfig %w", err)
}
- if found {
- switch v := giteaMetaControl.Value.(type) {
- case string:
- switch v {
+ if basic.Lang != "" {
+ rc.Lang = basic.Lang
+ }
+
+ rc.TOC = basic.TOC
+
+ type controlStringRenderConfig struct {
+ Gitea string `yaml:"gitea"`
+ }
+
+ var stringBasic controlStringRenderConfig
+
+ if err := value.Decode(&stringBasic); err == nil {
+ if stringBasic.Gitea != "" {
+ switch strings.TrimSpace(strings.ToLower(stringBasic.Gitea)) {
case "none":
rc.Meta = "none"
case "table":
@@ -68,96 +63,65 @@ func (rc *RenderConfig) ToRenderConfig(meta yaml.MapSlice) {
default: // "details"
rc.Meta = "details"
}
- case yaml.MapSlice:
- for _, item := range v {
- strKey, ok := item.Key.(string)
- if !ok {
- continue
- }
- strKey = strings.TrimSpace(strings.ToLower(strKey))
- switch strKey {
- case "meta":
- val, ok := item.Value.(string)
- if !ok {
- continue
- }
- switch strings.TrimSpace(strings.ToLower(val)) {
- case "none":
- rc.Meta = "none"
- case "table":
- rc.Meta = "table"
- default: // "details"
- rc.Meta = "details"
- }
- case "details_icon":
- val, ok := item.Value.(string)
- if !ok {
- continue
- }
- rc.Icon = strings.TrimSpace(strings.ToLower(val))
- case "include_toc":
- val, ok := item.Value.(bool)
- if !ok {
- continue
- }
- rc.TOC = val
- case "lang":
- val, ok := item.Value.(string)
- if !ok {
- continue
- }
- val = strings.TrimSpace(val)
- if len(val) == 0 {
- continue
- }
- rc.Lang = val
- }
- }
+ }
+ return nil
+ }
+
+ type giteaControl struct {
+ Meta *string `yaml:"meta"`
+ Icon *string `yaml:"details_icon"`
+ TOC *bool `yaml:"include_toc"`
+ Lang *string `yaml:"lang"`
+ }
+
+ type complexGiteaConfig struct {
+ Gitea *giteaControl `yaml:"gitea"`
+ }
+ var complex complexGiteaConfig
+ if err := value.Decode(&complex); err != nil {
+ return fmt.Errorf("unable to decode into complexRenderConfig %w", err)
+ }
+
+ if complex.Gitea == nil {
+ return nil
+ }
+
+ if complex.Gitea.Meta != nil {
+ switch strings.TrimSpace(strings.ToLower(*complex.Gitea.Meta)) {
+ case "none":
+ rc.Meta = "none"
+ case "table":
+ rc.Meta = "table"
+ default: // "details"
+ rc.Meta = "details"
}
}
+
+ if complex.Gitea.Icon != nil {
+ rc.Icon = strings.TrimSpace(strings.ToLower(*complex.Gitea.Icon))
+ }
+
+ if complex.Gitea.Lang != nil && *complex.Gitea.Lang != "" {
+ rc.Lang = *complex.Gitea.Lang
+ }
+
+ if complex.Gitea.TOC != nil {
+ rc.TOC = *complex.Gitea.TOC
+ }
+
+ return nil
}
-func (rc *RenderConfig) toMetaNode(meta yaml.MapSlice) ast.Node {
+func (rc *RenderConfig) toMetaNode() ast.Node {
+ if rc.yamlNode == nil {
+ return nil
+ }
switch rc.Meta {
case "table":
- return metaToTable(meta)
+ return nodeToTable(rc.yamlNode)
case "details":
- return metaToDetails(meta, rc.Icon)
+ return nodeToDetails(rc.yamlNode, rc.Icon)
default:
return nil
}
}
-
-func metaToTable(meta yaml.MapSlice) ast.Node {
- table := east.NewTable()
- alignments := []east.Alignment{}
- for range meta {
- alignments = append(alignments, east.AlignNone)
- }
- row := east.NewTableRow(alignments)
- for _, item := range meta {
- cell := east.NewTableCell()
- cell.AppendChild(cell, ast.NewString([]byte(fmt.Sprintf("%v", item.Key))))
- row.AppendChild(row, cell)
- }
- table.AppendChild(table, east.NewTableHeader(row))
-
- row = east.NewTableRow(alignments)
- for _, item := range meta {
- cell := east.NewTableCell()
- cell.AppendChild(cell, ast.NewString([]byte(fmt.Sprintf("%v", item.Value))))
- row.AppendChild(row, cell)
- }
- table.AppendChild(table, row)
- return table
-}
-
-func metaToDetails(meta yaml.MapSlice, icon string) ast.Node {
- details := NewDetails()
- summary := NewSummary()
- summary.AppendChild(summary, NewIcon(icon))
- details.AppendChild(details, summary)
- details.AppendChild(details, metaToTable(meta))
-
- return details
-}
diff --git a/modules/markup/markdown/renderconfig_test.go b/modules/markup/markdown/renderconfig_test.go
new file mode 100644
index 0000000000..672edbf46d
--- /dev/null
+++ b/modules/markup/markdown/renderconfig_test.go
@@ -0,0 +1,163 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package markdown
+
+import (
+ "strings"
+ "testing"
+
+ "gopkg.in/yaml.v3"
+)
+
+func TestRenderConfig_UnmarshalYAML(t *testing.T) {
+ tests := []struct {
+ name string
+ expected *RenderConfig
+ args string
+ }{
+ {
+ "empty", &RenderConfig{
+ Meta: "table",
+ Icon: "table",
+ Lang: "",
+ }, "",
+ },
+ {
+ "lang", &RenderConfig{
+ Meta: "table",
+ Icon: "table",
+ Lang: "test",
+ }, "lang: test",
+ },
+ {
+ "metatable", &RenderConfig{
+ Meta: "table",
+ Icon: "table",
+ Lang: "",
+ }, "gitea: table",
+ },
+ {
+ "metanone", &RenderConfig{
+ Meta: "none",
+ Icon: "table",
+ Lang: "",
+ }, "gitea: none",
+ },
+ {
+ "metadetails", &RenderConfig{
+ Meta: "details",
+ Icon: "table",
+ Lang: "",
+ }, "gitea: details",
+ },
+ {
+ "metawrong", &RenderConfig{
+ Meta: "details",
+ Icon: "table",
+ Lang: "",
+ }, "gitea: wrong",
+ },
+ {
+ "toc", &RenderConfig{
+ TOC: true,
+ Meta: "table",
+ Icon: "table",
+ Lang: "",
+ }, "include_toc: true",
+ },
+ {
+ "tocfalse", &RenderConfig{
+ TOC: false,
+ Meta: "table",
+ Icon: "table",
+ Lang: "",
+ }, "include_toc: false",
+ },
+ {
+ "toclang", &RenderConfig{
+ Meta: "table",
+ Icon: "table",
+ TOC: true,
+ Lang: "testlang",
+ }, `
+ include_toc: true
+ lang: testlang
+ `,
+ },
+ {
+ "complexlang", &RenderConfig{
+ Meta: "table",
+ Icon: "table",
+ Lang: "testlang",
+ }, `
+ gitea:
+ lang: testlang
+ `,
+ },
+ {
+ "complexlang2", &RenderConfig{
+ Meta: "table",
+ Icon: "table",
+ Lang: "testlang",
+ }, `
+ lang: notright
+ gitea:
+ lang: testlang
+`,
+ },
+ {
+ "complexlang", &RenderConfig{
+ Meta: "table",
+ Icon: "table",
+ Lang: "testlang",
+ }, `
+ gitea:
+ lang: testlang
+`,
+ },
+ {
+ "complex2", &RenderConfig{
+ Lang: "two",
+ Meta: "table",
+ TOC: true,
+ Icon: "smiley",
+ }, `
+ lang: one
+ include_toc: true
+ gitea:
+ details_icon: smiley
+ meta: table
+ include_toc: true
+ lang: two
+`,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ got := &RenderConfig{
+ Meta: "table",
+ Icon: "table",
+ Lang: "",
+ }
+ if err := yaml.Unmarshal([]byte(strings.ReplaceAll(tt.args, "\t", " ")), got); err != nil {
+ t.Errorf("RenderConfig.UnmarshalYAML() error = %v\n%q", err, tt.args)
+ return
+ }
+
+ if got.Meta != tt.expected.Meta {
+ t.Errorf("Meta Expected %s Got %s", tt.expected.Meta, got.Meta)
+ }
+ if got.Icon != tt.expected.Icon {
+ t.Errorf("Icon Expected %s Got %s", tt.expected.Icon, got.Icon)
+ }
+ if got.Lang != tt.expected.Lang {
+ t.Errorf("Lang Expected %s Got %s", tt.expected.Lang, got.Lang)
+ }
+ if got.TOC != tt.expected.TOC {
+ t.Errorf("TOC Expected %t Got %t", tt.expected.TOC, got.TOC)
+ }
+ })
+ }
+}
diff --git a/modules/markup/mdstripper/mdstripper.go b/modules/markup/mdstripper/mdstripper.go
index 64079194ff..c7f8ee69f1 100644
--- a/modules/markup/mdstripper/mdstripper.go
+++ b/modules/markup/mdstripper/mdstripper.go
@@ -141,7 +141,7 @@ func (r *stripRenderer) AddOptions(...renderer.Option) {
}
// StripMarkdown parses markdown content by removing all markup and code blocks
-// in order to extract links and other references
+// in order to extract links and other references
func StripMarkdown(rawBytes []byte) (string, []string) {
buf, links := StripMarkdownBytes(rawBytes)
return string(buf), links
@@ -153,7 +153,7 @@ var (
)
// StripMarkdownBytes parses markdown content by removing all markup and code blocks
-// in order to extract links and other references
+// in order to extract links and other references
func StripMarkdownBytes(rawBytes []byte) ([]byte, []string) {
once.Do(func() {
gdMarkdown := goldmark.New(
diff --git a/modules/markup/orgmode/orgmode.go b/modules/markup/orgmode/orgmode.go
index a78531720d..1c02f274ba 100644
--- a/modules/markup/orgmode/orgmode.go
+++ b/modules/markup/orgmode/orgmode.go
@@ -17,8 +17,8 @@ import (
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
- "github.com/alecthomas/chroma"
- "github.com/alecthomas/chroma/lexers"
+ "github.com/alecthomas/chroma/v2"
+ "github.com/alecthomas/chroma/v2/lexers"
"github.com/niklasfasching/go-org/org"
)
diff --git a/modules/markup/orgmode/orgmode_test.go b/modules/markup/orgmode/orgmode_test.go
index 4fc0a20db2..cd12cd8c17 100644
--- a/modules/markup/orgmode/orgmode_test.go
+++ b/modules/markup/orgmode/orgmode_test.go
@@ -79,9 +79,9 @@ func HelloWorld() {
}
#+end_src
`, `
-
// HelloWorld prints "Hello World"
-func HelloWorld() {
- fmt.Println("Hello World")
-}
+
// HelloWorld prints "Hello World"
+func HelloWorld() {
+ fmt.Println("Hello World")
+}
`)
}
diff --git a/modules/markup/renderer.go b/modules/markup/renderer.go
index e88fa31187..5f69dc7235 100644
--- a/modules/markup/renderer.go
+++ b/modules/markup/renderer.go
@@ -310,14 +310,9 @@ func IsMarkupFile(name, markup string) bool {
}
// IsReadmeFile reports whether name looks like a README file
-// based on its name. If an extension is provided, it will strictly
-// match that extension.
-// Note that the '.' should be provided in ext, e.g ".md"
-func IsReadmeFile(name string, ext ...string) bool {
+// based on its name.
+func IsReadmeFile(name string) bool {
name = strings.ToLower(name)
- if len(ext) > 0 {
- return name == "readme"+ext[0]
- }
if len(name) < 6 {
return false
} else if len(name) == 6 {
@@ -325,3 +320,29 @@ func IsReadmeFile(name string, ext ...string) bool {
}
return name[:7] == "readme."
}
+
+// IsReadmeFileExtension reports whether name looks like a README file
+// based on its name. It will look through the provided extensions and check if the file matches
+// one of the extensions and provide the index in the extension list.
+// If the filename is `readme.` with an unmatched extension it will match with the index equaling
+// the length of the provided extension list.
+// Note that the '.' should be provided in ext, e.g ".md"
+func IsReadmeFileExtension(name string, ext ...string) (int, bool) {
+ name = strings.ToLower(name)
+ if len(name) < 6 || name[:6] != "readme" {
+ return 0, false
+ }
+
+ for i, extension := range ext {
+ extension = strings.ToLower(extension)
+ if name[6:] == extension {
+ return i, true
+ }
+ }
+
+ if name[6] == '.' {
+ return len(ext), true
+ }
+
+ return 0, false
+}
diff --git a/modules/markup/renderer_test.go b/modules/markup/renderer_test.go
index 4cfa022463..950ee15b91 100644
--- a/modules/markup/renderer_test.go
+++ b/modules/markup/renderer_test.go
@@ -40,24 +40,57 @@ func TestMisc_IsReadmeFile(t *testing.T) {
assert.False(t, IsReadmeFile(testCase))
}
- trueTestCasesStrict := [][]string{
- {"readme", ""},
- {"readme.md", ".md"},
- {"readme.txt", ".txt"},
- }
- falseTestCasesStrict := [][]string{
- {"readme", ".md"},
- {"readme.md", ""},
- {"readme.md", ".txt"},
- {"readme.md", "md"},
- {"readmee.md", ".md"},
- {"readme.i18n.md", ".md"},
+ type extensionTestcase struct {
+ name string
+ expected bool
+ idx int
}
- for _, testCase := range trueTestCasesStrict {
- assert.True(t, IsReadmeFile(testCase[0], testCase[1]))
+ exts := []string{".md", ".txt", ""}
+ testCasesExtensions := []extensionTestcase{
+ {
+ name: "readme",
+ expected: true,
+ idx: 2,
+ },
+ {
+ name: "readme.md",
+ expected: true,
+ idx: 0,
+ },
+ {
+ name: "README.md",
+ expected: true,
+ idx: 0,
+ },
+ {
+ name: "ReAdMe.Md",
+ expected: true,
+ idx: 0,
+ },
+ {
+ name: "readme.txt",
+ expected: true,
+ idx: 1,
+ },
+ {
+ name: "readme.doc",
+ expected: true,
+ idx: 3,
+ },
+ {
+ name: "readmee.md",
+ },
+ {
+ name: "readme..",
+ expected: true,
+ idx: 3,
+ },
}
- for _, testCase := range falseTestCasesStrict {
- assert.False(t, IsReadmeFile(testCase[0], testCase[1]))
+
+ for _, testCase := range testCasesExtensions {
+ idx, ok := IsReadmeFileExtension(testCase.name, exts...)
+ assert.Equal(t, testCase.expected, ok)
+ assert.Equal(t, testCase.idx, idx)
}
}
diff --git a/modules/markup/sanitizer.go b/modules/markup/sanitizer.go
index 57e88fdabc..807a8a7892 100644
--- a/modules/markup/sanitizer.go
+++ b/modules/markup/sanitizer.go
@@ -56,7 +56,7 @@ func createDefaultPolicy() *bluemonday.Policy {
policy.AllowAttrs("class").Matching(regexp.MustCompile(`^code-block( is-loading)?$`)).OnElements("pre")
// For Chroma markdown plugin
- policy.AllowAttrs("class").Matching(regexp.MustCompile(`^(chroma )?language-[\w-]+$`)).OnElements("code")
+ policy.AllowAttrs("class").Matching(regexp.MustCompile(`^(chroma )?language-[\w-]+( display)?( is-loading)?$`)).OnElements("code")
// Checkboxes
policy.AllowAttrs("type").Matching(regexp.MustCompile(`^checkbox$`)).OnElements("input")
@@ -83,7 +83,7 @@ func createDefaultPolicy() *bluemonday.Policy {
policy.AllowAttrs("class").Matching(regexp.MustCompile(`emoji`)).OnElements("img")
// Allow icons, emojis, chroma syntax and keyword markup on span
- policy.AllowAttrs("class").Matching(regexp.MustCompile(`^((icon(\s+[\p{L}\p{N}_-]+)+)|(emoji))$|^([a-z][a-z0-9]{0,2})$|^` + keywordClass + `$`)).OnElements("span")
+ policy.AllowAttrs("class").Matching(regexp.MustCompile(`^((icon(\s+[\p{L}\p{N}_-]+)+)|(emoji)|(language-math display)|(language-math inline))$|^([a-z][a-z0-9]{0,2})$|^` + keywordClass + `$`)).OnElements("span")
// Allow 'style' attribute on text elements.
policy.AllowAttrs("style").OnElements("span", "p")
diff --git a/modules/mcaptcha/mcaptcha.go b/modules/mcaptcha/mcaptcha.go
new file mode 100644
index 0000000000..b889cf423b
--- /dev/null
+++ b/modules/mcaptcha/mcaptcha.go
@@ -0,0 +1,27 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package mcaptcha
+
+import (
+ "context"
+ "fmt"
+
+ "code.gitea.io/gitea/modules/setting"
+
+ "codeberg.org/gusted/mcaptcha"
+)
+
+func Verify(ctx context.Context, token string) (bool, error) {
+ valid, err := mcaptcha.Verify(ctx, &mcaptcha.VerifyOpts{
+ InstanceURL: setting.Service.McaptchaURL,
+ Sitekey: setting.Service.McaptchaSitekey,
+ Secret: setting.Service.McaptchaSecret,
+ Token: token,
+ })
+ if err != nil {
+ return false, fmt.Errorf("wasn't able to verify mCaptcha: %v", err)
+ }
+ return valid, nil
+}
diff --git a/modules/metrics/collector.go b/modules/metrics/collector.go
index 069633a565..dcd80b05a9 100755
--- a/modules/metrics/collector.go
+++ b/modules/metrics/collector.go
@@ -5,7 +5,7 @@
package metrics
import (
- "code.gitea.io/gitea/models"
+ activities_model "code.gitea.io/gitea/models/activities"
"github.com/prometheus/client_golang/prometheus"
)
@@ -225,7 +225,7 @@ func (c Collector) Describe(ch chan<- *prometheus.Desc) {
// Collect returns the metrics with values
func (c Collector) Collect(ch chan<- prometheus.Metric) {
- stats := models.GetStatistic()
+ stats := activities_model.GetStatistic()
ch <- prometheus.MustNewConstMetric(
c.Accesses,
diff --git a/modules/migration/pullrequest.go b/modules/migration/pullrequest.go
index eaa0dd45e2..dd520cd63f 100644
--- a/modules/migration/pullrequest.go
+++ b/modules/migration/pullrequest.go
@@ -26,7 +26,7 @@ type PullRequest struct {
Updated time.Time
Closed *time.Time
Labels []*Label
- PatchURL string `yaml:"patch_url"`
+ PatchURL string `yaml:"patch_url"` // SECURITY: This must be safe to download directly from
Merged bool
MergedTime *time.Time `yaml:"merged_time"`
MergeCommitSHA string `yaml:"merge_commit_sha"`
@@ -37,6 +37,7 @@ type PullRequest struct {
Reactions []*Reaction
ForeignIndex int64
Context DownloaderContext `yaml:"-"`
+ EnsuredSafe bool `yaml:"ensured_safe"`
}
func (p *PullRequest) GetLocalIndex() int64 { return p.Number }
@@ -55,9 +56,9 @@ func (p PullRequest) GetGitRefName() string {
// PullRequestBranch represents a pull request branch
type PullRequestBranch struct {
- CloneURL string `yaml:"clone_url"`
- Ref string
- SHA string
+ CloneURL string `yaml:"clone_url"` // SECURITY: This must be safe to download from
+ Ref string // SECURITY: this must be a git.IsValidRefPattern
+ SHA string // SECURITY: this must be a git.IsValidSHAPattern
RepoName string `yaml:"repo_name"`
OwnerName string `yaml:"owner_name"`
}
diff --git a/modules/migration/release.go b/modules/migration/release.go
index cbdf01a3ed..923b3817b0 100644
--- a/modules/migration/release.go
+++ b/modules/migration/release.go
@@ -18,15 +18,16 @@ type ReleaseAsset struct {
DownloadCount *int `yaml:"download_count"`
Created time.Time
Updated time.Time
- DownloadURL *string `yaml:"download_url"`
+
+ DownloadURL *string `yaml:"download_url"` // SECURITY: It is the responsibility of downloader to make sure this is safe
// if DownloadURL is nil, the function should be invoked
- DownloadFunc func() (io.ReadCloser, error) `yaml:"-"`
+ DownloadFunc func() (io.ReadCloser, error) `yaml:"-"` // SECURITY: It is the responsibility of downloader to make sure this is safe
}
// Release represents a release
type Release struct {
- TagName string `yaml:"tag_name"`
- TargetCommitish string `yaml:"target_commitish"`
+ TagName string `yaml:"tag_name"` // SECURITY: This must pass git.IsValidRefPattern
+ TargetCommitish string `yaml:"target_commitish"` // SECURITY: This must pass git.IsValidRefPattern
Name string
Body string
Draft bool
diff --git a/modules/migration/repo.go b/modules/migration/repo.go
index d0d62de8da..75622595d9 100644
--- a/modules/migration/repo.go
+++ b/modules/migration/repo.go
@@ -12,7 +12,7 @@ type Repository struct {
IsPrivate bool `yaml:"is_private"`
IsMirror bool `yaml:"is_mirror"`
Description string
- CloneURL string `yaml:"clone_url"`
+ CloneURL string `yaml:"clone_url"` // SECURITY: This must be checked to ensure that is safe to be used
OriginalURL string `yaml:"original_url"`
DefaultBranch string
}
diff --git a/modules/nosql/manager_redis.go b/modules/nosql/manager_redis.go
index 3b2ad75b41..f7d5a72ed2 100644
--- a/modules/nosql/manager_redis.go
+++ b/modules/nosql/manager_redis.go
@@ -245,7 +245,7 @@ func getRedisTLSOptions(uri *url.URL) *tls.Config {
if len(skipverify) > 0 {
skipverify, err := strconv.ParseBool(skipverify)
- if err != nil {
+ if err == nil {
tlsConfig.InsecureSkipVerify = skipverify
}
}
@@ -254,7 +254,7 @@ func getRedisTLSOptions(uri *url.URL) *tls.Config {
if len(insecureskipverify) > 0 {
insecureskipverify, err := strconv.ParseBool(insecureskipverify)
- if err != nil {
+ if err == nil {
tlsConfig.InsecureSkipVerify = insecureskipverify
}
}
diff --git a/modules/nosql/manager_redis_test.go b/modules/nosql/manager_redis_test.go
index 3d94532135..99a8856f1e 100644
--- a/modules/nosql/manager_redis_test.go
+++ b/modules/nosql/manager_redis_test.go
@@ -27,6 +27,24 @@ func TestRedisPasswordOpt(t *testing.T) {
}
}
+func TestSkipVerifyOpt(t *testing.T) {
+ uri, _ := url.Parse("rediss://myredis/0?skipverify=true")
+ tlsConfig := getRedisTLSOptions(uri)
+
+ if !tlsConfig.InsecureSkipVerify {
+ t.Fail()
+ }
+}
+
+func TestInsecureSkipVerifyOpt(t *testing.T) {
+ uri, _ := url.Parse("rediss://myredis/0?insecureskipverify=true")
+ tlsConfig := getRedisTLSOptions(uri)
+
+ if !tlsConfig.InsecureSkipVerify {
+ t.Fail()
+ }
+}
+
func TestRedisSentinelUsernameOpt(t *testing.T) {
uri, _ := url.Parse("redis+sentinel://redis:password@myredis/0?sentinelusername=suser&sentinelpassword=spass")
opts := getRedisOptions(uri).Failover()
diff --git a/modules/notification/action/action.go b/modules/notification/action/action.go
index e438f41485..d3ff8b156e 100644
--- a/modules/notification/action/action.go
+++ b/modules/notification/action/action.go
@@ -9,7 +9,7 @@ import (
"path"
"strings"
- "code.gitea.io/gitea/models"
+ activities_model "code.gitea.io/gitea/models/activities"
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
repo_model "code.gitea.io/gitea/models/repo"
@@ -45,10 +45,10 @@ func (a *actionNotifier) NotifyNewIssue(issue *issues_model.Issue, mentions []*u
}
repo := issue.Repo
- if err := models.NotifyWatchers(&models.Action{
+ if err := activities_model.NotifyWatchers(&activities_model.Action{
ActUserID: issue.Poster.ID,
ActUser: issue.Poster,
- OpType: models.ActionCreateIssue,
+ OpType: activities_model.ActionCreateIssue,
Content: fmt.Sprintf("%d|%s", issue.Index, issue.Title),
RepoID: repo.ID,
Repo: repo,
@@ -62,7 +62,7 @@ func (a *actionNotifier) NotifyNewIssue(issue *issues_model.Issue, mentions []*u
func (a *actionNotifier) NotifyIssueChangeStatus(doer *user_model.User, issue *issues_model.Issue, actionComment *issues_model.Comment, closeOrReopen bool) {
// Compose comment action, could be plain comment, close or reopen issue/pull request.
// This object will be used to notify watchers in the end of function.
- act := &models.Action{
+ act := &activities_model.Action{
ActUserID: doer.ID,
ActUser: doer,
Content: fmt.Sprintf("%d|%s", issue.Index, ""),
@@ -74,19 +74,19 @@ func (a *actionNotifier) NotifyIssueChangeStatus(doer *user_model.User, issue *i
}
// Check comment type.
if closeOrReopen {
- act.OpType = models.ActionCloseIssue
+ act.OpType = activities_model.ActionCloseIssue
if issue.IsPull {
- act.OpType = models.ActionClosePullRequest
+ act.OpType = activities_model.ActionClosePullRequest
}
} else {
- act.OpType = models.ActionReopenIssue
+ act.OpType = activities_model.ActionReopenIssue
if issue.IsPull {
- act.OpType = models.ActionReopenPullRequest
+ act.OpType = activities_model.ActionReopenPullRequest
}
}
// Notify watchers for whatever action comes in, ignore if no action type.
- if err := models.NotifyWatchers(act); err != nil {
+ if err := activities_model.NotifyWatchers(act); err != nil {
log.Error("NotifyWatchers: %v", err)
}
}
@@ -95,7 +95,7 @@ func (a *actionNotifier) NotifyIssueChangeStatus(doer *user_model.User, issue *i
func (a *actionNotifier) NotifyCreateIssueComment(doer *user_model.User, repo *repo_model.Repository,
issue *issues_model.Issue, comment *issues_model.Comment, mentions []*user_model.User,
) {
- act := &models.Action{
+ act := &activities_model.Action{
ActUserID: doer.ID,
ActUser: doer,
RepoID: issue.Repo.ID,
@@ -116,13 +116,13 @@ func (a *actionNotifier) NotifyCreateIssueComment(doer *user_model.User, repo *r
act.Content = fmt.Sprintf("%d|%s", issue.Index, truncatedContent)
if issue.IsPull {
- act.OpType = models.ActionCommentPull
+ act.OpType = activities_model.ActionCommentPull
} else {
- act.OpType = models.ActionCommentIssue
+ act.OpType = activities_model.ActionCommentIssue
}
// Notify watchers for whatever action comes in, ignore if no action type.
- if err := models.NotifyWatchers(act); err != nil {
+ if err := activities_model.NotifyWatchers(act); err != nil {
log.Error("NotifyWatchers: %v", err)
}
}
@@ -141,10 +141,10 @@ func (a *actionNotifier) NotifyNewPullRequest(pull *issues_model.PullRequest, me
return
}
- if err := models.NotifyWatchers(&models.Action{
+ if err := activities_model.NotifyWatchers(&activities_model.Action{
ActUserID: pull.Issue.Poster.ID,
ActUser: pull.Issue.Poster,
- OpType: models.ActionCreatePullRequest,
+ OpType: activities_model.ActionCreatePullRequest,
Content: fmt.Sprintf("%d|%s", pull.Issue.Index, pull.Issue.Title),
RepoID: pull.Issue.Repo.ID,
Repo: pull.Issue.Repo,
@@ -155,10 +155,10 @@ func (a *actionNotifier) NotifyNewPullRequest(pull *issues_model.PullRequest, me
}
func (a *actionNotifier) NotifyRenameRepository(doer *user_model.User, repo *repo_model.Repository, oldRepoName string) {
- if err := models.NotifyWatchers(&models.Action{
+ if err := activities_model.NotifyWatchers(&activities_model.Action{
ActUserID: doer.ID,
ActUser: doer,
- OpType: models.ActionRenameRepo,
+ OpType: activities_model.ActionRenameRepo,
RepoID: repo.ID,
Repo: repo,
IsPrivate: repo.IsPrivate,
@@ -169,10 +169,10 @@ func (a *actionNotifier) NotifyRenameRepository(doer *user_model.User, repo *rep
}
func (a *actionNotifier) NotifyTransferRepository(doer *user_model.User, repo *repo_model.Repository, oldOwnerName string) {
- if err := models.NotifyWatchers(&models.Action{
+ if err := activities_model.NotifyWatchers(&activities_model.Action{
ActUserID: doer.ID,
ActUser: doer,
- OpType: models.ActionTransferRepo,
+ OpType: activities_model.ActionTransferRepo,
RepoID: repo.ID,
Repo: repo,
IsPrivate: repo.IsPrivate,
@@ -183,10 +183,10 @@ func (a *actionNotifier) NotifyTransferRepository(doer *user_model.User, repo *r
}
func (a *actionNotifier) NotifyCreateRepository(doer, u *user_model.User, repo *repo_model.Repository) {
- if err := models.NotifyWatchers(&models.Action{
+ if err := activities_model.NotifyWatchers(&activities_model.Action{
ActUserID: doer.ID,
ActUser: doer,
- OpType: models.ActionCreateRepo,
+ OpType: activities_model.ActionCreateRepo,
RepoID: repo.ID,
Repo: repo,
IsPrivate: repo.IsPrivate,
@@ -196,10 +196,10 @@ func (a *actionNotifier) NotifyCreateRepository(doer, u *user_model.User, repo *
}
func (a *actionNotifier) NotifyForkRepository(doer *user_model.User, oldRepo, repo *repo_model.Repository) {
- if err := models.NotifyWatchers(&models.Action{
+ if err := activities_model.NotifyWatchers(&activities_model.Action{
ActUserID: doer.ID,
ActUser: doer,
- OpType: models.ActionCreateRepo,
+ OpType: activities_model.ActionCreateRepo,
RepoID: repo.ID,
Repo: repo,
IsPrivate: repo.IsPrivate,
@@ -221,15 +221,15 @@ func (a *actionNotifier) NotifyPullRequestReview(pr *issues_model.PullRequest, r
return
}
- actions := make([]*models.Action, 0, 10)
+ actions := make([]*activities_model.Action, 0, 10)
for _, lines := range review.CodeComments {
for _, comments := range lines {
for _, comm := range comments {
- actions = append(actions, &models.Action{
+ actions = append(actions, &activities_model.Action{
ActUserID: review.Reviewer.ID,
ActUser: review.Reviewer,
Content: fmt.Sprintf("%d|%s", review.Issue.Index, strings.Split(comm.Content, "\n")[0]),
- OpType: models.ActionCommentPull,
+ OpType: activities_model.ActionCommentPull,
RepoID: review.Issue.RepoID,
Repo: review.Issue.Repo,
IsPrivate: review.Issue.Repo.IsPrivate,
@@ -241,7 +241,7 @@ func (a *actionNotifier) NotifyPullRequestReview(pr *issues_model.PullRequest, r
}
if review.Type != issues_model.ReviewTypeComment || strings.TrimSpace(comment.Content) != "" {
- action := &models.Action{
+ action := &activities_model.Action{
ActUserID: review.Reviewer.ID,
ActUser: review.Reviewer,
Content: fmt.Sprintf("%d|%s", review.Issue.Index, strings.Split(comment.Content, "\n")[0]),
@@ -254,26 +254,26 @@ func (a *actionNotifier) NotifyPullRequestReview(pr *issues_model.PullRequest, r
switch review.Type {
case issues_model.ReviewTypeApprove:
- action.OpType = models.ActionApprovePullRequest
+ action.OpType = activities_model.ActionApprovePullRequest
case issues_model.ReviewTypeReject:
- action.OpType = models.ActionRejectPullRequest
+ action.OpType = activities_model.ActionRejectPullRequest
default:
- action.OpType = models.ActionCommentPull
+ action.OpType = activities_model.ActionCommentPull
}
actions = append(actions, action)
}
- if err := models.NotifyWatchersActions(actions); err != nil {
+ if err := activities_model.NotifyWatchersActions(actions); err != nil {
log.Error("notify watchers '%d/%d': %v", review.Reviewer.ID, review.Issue.RepoID, err)
}
}
func (*actionNotifier) NotifyMergePullRequest(pr *issues_model.PullRequest, doer *user_model.User) {
- if err := models.NotifyWatchers(&models.Action{
+ if err := activities_model.NotifyWatchers(&activities_model.Action{
ActUserID: doer.ID,
ActUser: doer,
- OpType: models.ActionMergePullRequest,
+ OpType: activities_model.ActionMergePullRequest,
Content: fmt.Sprintf("%d|%s", pr.Issue.Index, pr.Issue.Title),
RepoID: pr.Issue.Repo.ID,
Repo: pr.Issue.Repo,
@@ -288,10 +288,10 @@ func (*actionNotifier) NotifyPullRevieweDismiss(doer *user_model.User, review *i
if len(review.OriginalAuthor) > 0 {
reviewerName = review.OriginalAuthor
}
- if err := models.NotifyWatchers(&models.Action{
+ if err := activities_model.NotifyWatchers(&activities_model.Action{
ActUserID: doer.ID,
ActUser: doer,
- OpType: models.ActionPullReviewDismissed,
+ OpType: activities_model.ActionPullReviewDismissed,
Content: fmt.Sprintf("%d|%s|%s", review.Issue.Index, reviewerName, comment.Content),
RepoID: review.Issue.Repo.ID,
Repo: review.Issue.Repo,
@@ -310,19 +310,19 @@ func (a *actionNotifier) NotifyPushCommits(pusher *user_model.User, repo *repo_m
return
}
- opType := models.ActionCommitRepo
+ opType := activities_model.ActionCommitRepo
// Check it's tag push or branch.
if opts.IsTag() {
- opType = models.ActionPushTag
+ opType = activities_model.ActionPushTag
if opts.IsDelRef() {
- opType = models.ActionDeleteTag
+ opType = activities_model.ActionDeleteTag
}
} else if opts.IsDelRef() {
- opType = models.ActionDeleteBranch
+ opType = activities_model.ActionDeleteBranch
}
- if err = models.NotifyWatchers(&models.Action{
+ if err = activities_model.NotifyWatchers(&activities_model.Action{
ActUserID: pusher.ID,
ActUser: pusher,
OpType: opType,
@@ -337,12 +337,12 @@ func (a *actionNotifier) NotifyPushCommits(pusher *user_model.User, repo *repo_m
}
func (a *actionNotifier) NotifyCreateRef(doer *user_model.User, repo *repo_model.Repository, refType, refFullName, refID string) {
- opType := models.ActionCommitRepo
+ opType := activities_model.ActionCommitRepo
if refType == "tag" {
// has sent same action in `NotifyPushCommits`, so skip it.
return
}
- if err := models.NotifyWatchers(&models.Action{
+ if err := activities_model.NotifyWatchers(&activities_model.Action{
ActUserID: doer.ID,
ActUser: doer,
OpType: opType,
@@ -356,12 +356,12 @@ func (a *actionNotifier) NotifyCreateRef(doer *user_model.User, repo *repo_model
}
func (a *actionNotifier) NotifyDeleteRef(doer *user_model.User, repo *repo_model.Repository, refType, refFullName string) {
- opType := models.ActionDeleteBranch
+ opType := activities_model.ActionDeleteBranch
if refType == "tag" {
// has sent same action in `NotifyPushCommits`, so skip it.
return
}
- if err := models.NotifyWatchers(&models.Action{
+ if err := activities_model.NotifyWatchers(&activities_model.Action{
ActUserID: doer.ID,
ActUser: doer,
OpType: opType,
@@ -381,10 +381,10 @@ func (a *actionNotifier) NotifySyncPushCommits(pusher *user_model.User, repo *re
return
}
- if err := models.NotifyWatchers(&models.Action{
+ if err := activities_model.NotifyWatchers(&activities_model.Action{
ActUserID: repo.OwnerID,
ActUser: repo.MustOwner(),
- OpType: models.ActionMirrorSyncPush,
+ OpType: activities_model.ActionMirrorSyncPush,
RepoID: repo.ID,
Repo: repo,
IsPrivate: repo.IsPrivate,
@@ -396,10 +396,10 @@ func (a *actionNotifier) NotifySyncPushCommits(pusher *user_model.User, repo *re
}
func (a *actionNotifier) NotifySyncCreateRef(doer *user_model.User, repo *repo_model.Repository, refType, refFullName, refID string) {
- if err := models.NotifyWatchers(&models.Action{
+ if err := activities_model.NotifyWatchers(&activities_model.Action{
ActUserID: repo.OwnerID,
ActUser: repo.MustOwner(),
- OpType: models.ActionMirrorSyncCreate,
+ OpType: activities_model.ActionMirrorSyncCreate,
RepoID: repo.ID,
Repo: repo,
IsPrivate: repo.IsPrivate,
@@ -410,10 +410,10 @@ func (a *actionNotifier) NotifySyncCreateRef(doer *user_model.User, repo *repo_m
}
func (a *actionNotifier) NotifySyncDeleteRef(doer *user_model.User, repo *repo_model.Repository, refType, refFullName string) {
- if err := models.NotifyWatchers(&models.Action{
+ if err := activities_model.NotifyWatchers(&activities_model.Action{
ActUserID: repo.OwnerID,
ActUser: repo.MustOwner(),
- OpType: models.ActionMirrorSyncDelete,
+ OpType: activities_model.ActionMirrorSyncDelete,
RepoID: repo.ID,
Repo: repo,
IsPrivate: repo.IsPrivate,
@@ -423,15 +423,15 @@ func (a *actionNotifier) NotifySyncDeleteRef(doer *user_model.User, repo *repo_m
}
}
-func (a *actionNotifier) NotifyNewRelease(rel *models.Release) {
+func (a *actionNotifier) NotifyNewRelease(rel *repo_model.Release) {
if err := rel.LoadAttributes(); err != nil {
log.Error("NotifyNewRelease: %v", err)
return
}
- if err := models.NotifyWatchers(&models.Action{
+ if err := activities_model.NotifyWatchers(&activities_model.Action{
ActUserID: rel.PublisherID,
ActUser: rel.Publisher,
- OpType: models.ActionPublishRelease,
+ OpType: activities_model.ActionPublishRelease,
RepoID: rel.RepoID,
Repo: rel.Repo,
IsPrivate: rel.Repo.IsPrivate,
diff --git a/modules/notification/action/action_test.go b/modules/notification/action/action_test.go
index 2898c8ec3d..79a938d6cd 100644
--- a/modules/notification/action/action_test.go
+++ b/modules/notification/action/action_test.go
@@ -9,7 +9,7 @@ import (
"strings"
"testing"
- "code.gitea.io/gitea/models"
+ activities_model "code.gitea.io/gitea/models/activities"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
@@ -26,8 +26,8 @@ func TestMain(m *testing.M) {
func TestRenameRepoAction(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerID: user.ID}).(*repo_model.Repository)
+ user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerID: user.ID})
repo.Owner = user
oldRepoName := repo.Name
@@ -35,8 +35,8 @@ func TestRenameRepoAction(t *testing.T) {
repo.Name = newRepoName
repo.LowerName = strings.ToLower(newRepoName)
- actionBean := &models.Action{
- OpType: models.ActionRenameRepo,
+ actionBean := &activities_model.Action{
+ OpType: activities_model.ActionRenameRepo,
ActUserID: user.ID,
ActUser: user,
RepoID: repo.ID,
@@ -49,5 +49,5 @@ func TestRenameRepoAction(t *testing.T) {
NewNotifier().NotifyRenameRepository(user, repo, oldRepoName)
unittest.AssertExistsAndLoadBean(t, actionBean)
- unittest.CheckConsistencyFor(t, &models.Action{})
+ unittest.CheckConsistencyFor(t, &activities_model.Action{})
}
diff --git a/modules/notification/base/notifier.go b/modules/notification/base/notifier.go
index 31fa8f5f18..d6cec92e19 100644
--- a/modules/notification/base/notifier.go
+++ b/modules/notification/base/notifier.go
@@ -5,7 +5,6 @@
package base
import (
- "code.gitea.io/gitea/models"
issues_model "code.gitea.io/gitea/models/issues"
packages_model "code.gitea.io/gitea/models/packages"
repo_model "code.gitea.io/gitea/models/repo"
@@ -46,9 +45,12 @@ type Notifier interface {
issue *issues_model.Issue, comment *issues_model.Comment, mentions []*user_model.User)
NotifyUpdateComment(*user_model.User, *issues_model.Comment, string)
NotifyDeleteComment(*user_model.User, *issues_model.Comment)
- NotifyNewRelease(rel *models.Release)
- NotifyUpdateRelease(doer *user_model.User, rel *models.Release)
- NotifyDeleteRelease(doer *user_model.User, rel *models.Release)
+ NotifyNewWikiPage(doer *user_model.User, repo *repo_model.Repository, page, comment string)
+ NotifyEditWikiPage(doer *user_model.User, repo *repo_model.Repository, page, comment string)
+ NotifyDeleteWikiPage(doer *user_model.User, repo *repo_model.Repository, page string)
+ NotifyNewRelease(rel *repo_model.Release)
+ NotifyUpdateRelease(doer *user_model.User, rel *repo_model.Release)
+ NotifyDeleteRelease(doer *user_model.User, rel *repo_model.Release)
NotifyPushCommits(pusher *user_model.User, repo *repo_model.Repository, opts *repository.PushUpdateOptions, commits *repository.PushCommits)
NotifyCreateRef(doer *user_model.User, repo *repo_model.Repository, refType, refFullName, refID string)
NotifyDeleteRef(doer *user_model.User, repo *repo_model.Repository, refType, refFullName string)
diff --git a/modules/notification/base/null.go b/modules/notification/base/null.go
index d336f09301..b113ae79e1 100644
--- a/modules/notification/base/null.go
+++ b/modules/notification/base/null.go
@@ -5,7 +5,6 @@
package base
import (
- "code.gitea.io/gitea/models"
issues_model "code.gitea.io/gitea/models/issues"
packages_model "code.gitea.io/gitea/models/packages"
repo_model "code.gitea.io/gitea/models/repo"
@@ -79,16 +78,28 @@ func (*NullNotifier) NotifyUpdateComment(doer *user_model.User, c *issues_model.
func (*NullNotifier) NotifyDeleteComment(doer *user_model.User, c *issues_model.Comment) {
}
+// NotifyNewWikiPage places a place holder function
+func (*NullNotifier) NotifyNewWikiPage(doer *user_model.User, repo *repo_model.Repository, page, comment string) {
+}
+
+// NotifyEditWikiPage places a place holder function
+func (*NullNotifier) NotifyEditWikiPage(doer *user_model.User, repo *repo_model.Repository, page, comment string) {
+}
+
+// NotifyDeleteWikiPage places a place holder function
+func (*NullNotifier) NotifyDeleteWikiPage(doer *user_model.User, repo *repo_model.Repository, page string) {
+}
+
// NotifyNewRelease places a place holder function
-func (*NullNotifier) NotifyNewRelease(rel *models.Release) {
+func (*NullNotifier) NotifyNewRelease(rel *repo_model.Release) {
}
// NotifyUpdateRelease places a place holder function
-func (*NullNotifier) NotifyUpdateRelease(doer *user_model.User, rel *models.Release) {
+func (*NullNotifier) NotifyUpdateRelease(doer *user_model.User, rel *repo_model.Release) {
}
// NotifyDeleteRelease places a place holder function
-func (*NullNotifier) NotifyDeleteRelease(doer *user_model.User, rel *models.Release) {
+func (*NullNotifier) NotifyDeleteRelease(doer *user_model.User, rel *repo_model.Release) {
}
// NotifyIssueChangeMilestone places a place holder function
diff --git a/modules/notification/mail/mail.go b/modules/notification/mail/mail.go
index 1f217304b0..100b4eb36f 100644
--- a/modules/notification/mail/mail.go
+++ b/modules/notification/mail/mail.go
@@ -7,7 +7,7 @@ package mail
import (
"fmt"
- "code.gitea.io/gitea/models"
+ activities_model "code.gitea.io/gitea/models/activities"
issues_model "code.gitea.io/gitea/models/issues"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
@@ -35,15 +35,15 @@ func (m *mailNotifier) NotifyCreateIssueComment(doer *user_model.User, repo *rep
ctx, _, finished := process.GetManager().AddContext(graceful.GetManager().HammerContext(), fmt.Sprintf("mailNotifier.NotifyCreateIssueComment Issue[%d] #%d in [%d]", issue.ID, issue.Index, issue.RepoID))
defer finished()
- var act models.ActionType
+ var act activities_model.ActionType
if comment.Type == issues_model.CommentTypeClose {
- act = models.ActionCloseIssue
+ act = activities_model.ActionCloseIssue
} else if comment.Type == issues_model.CommentTypeReopen {
- act = models.ActionReopenIssue
+ act = activities_model.ActionReopenIssue
} else if comment.Type == issues_model.CommentTypeComment {
- act = models.ActionCommentIssue
+ act = activities_model.ActionCommentIssue
} else if comment.Type == issues_model.CommentTypeCode {
- act = models.ActionCommentIssue
+ act = activities_model.ActionCommentIssue
} else if comment.Type == issues_model.CommentTypePullRequestPush {
act = 0
}
@@ -54,24 +54,24 @@ func (m *mailNotifier) NotifyCreateIssueComment(doer *user_model.User, repo *rep
}
func (m *mailNotifier) NotifyNewIssue(issue *issues_model.Issue, mentions []*user_model.User) {
- if err := mailer.MailParticipants(issue, issue.Poster, models.ActionCreateIssue, mentions); err != nil {
+ if err := mailer.MailParticipants(issue, issue.Poster, activities_model.ActionCreateIssue, mentions); err != nil {
log.Error("MailParticipants: %v", err)
}
}
func (m *mailNotifier) NotifyIssueChangeStatus(doer *user_model.User, issue *issues_model.Issue, actionComment *issues_model.Comment, isClosed bool) {
- var actionType models.ActionType
+ var actionType activities_model.ActionType
if issue.IsPull {
if isClosed {
- actionType = models.ActionClosePullRequest
+ actionType = activities_model.ActionClosePullRequest
} else {
- actionType = models.ActionReopenPullRequest
+ actionType = activities_model.ActionReopenPullRequest
}
} else {
if isClosed {
- actionType = models.ActionCloseIssue
+ actionType = activities_model.ActionCloseIssue
} else {
- actionType = models.ActionReopenIssue
+ actionType = activities_model.ActionReopenIssue
}
}
@@ -86,14 +86,14 @@ func (m *mailNotifier) NotifyIssueChangeTitle(doer *user_model.User, issue *issu
return
}
if issue.IsPull && issues_model.HasWorkInProgressPrefix(oldTitle) && !issue.PullRequest.IsWorkInProgress() {
- if err := mailer.MailParticipants(issue, doer, models.ActionPullRequestReadyForReview, nil); err != nil {
+ if err := mailer.MailParticipants(issue, doer, activities_model.ActionPullRequestReadyForReview, nil); err != nil {
log.Error("MailParticipants: %v", err)
}
}
}
func (m *mailNotifier) NotifyNewPullRequest(pr *issues_model.PullRequest, mentions []*user_model.User) {
- if err := mailer.MailParticipants(pr.Issue, pr.Issue.Poster, models.ActionCreatePullRequest, mentions); err != nil {
+ if err := mailer.MailParticipants(pr.Issue, pr.Issue.Poster, activities_model.ActionCreatePullRequest, mentions); err != nil {
log.Error("MailParticipants: %v", err)
}
}
@@ -102,13 +102,13 @@ func (m *mailNotifier) NotifyPullRequestReview(pr *issues_model.PullRequest, r *
ctx, _, finished := process.GetManager().AddContext(graceful.GetManager().HammerContext(), fmt.Sprintf("mailNotifier.NotifyPullRequestReview Pull[%d] #%d in [%d]", pr.ID, pr.Index, pr.BaseRepoID))
defer finished()
- var act models.ActionType
+ var act activities_model.ActionType
if comment.Type == issues_model.CommentTypeClose {
- act = models.ActionCloseIssue
+ act = activities_model.ActionCloseIssue
} else if comment.Type == issues_model.CommentTypeReopen {
- act = models.ActionReopenIssue
+ act = activities_model.ActionReopenIssue
} else if comment.Type == issues_model.CommentTypeComment {
- act = models.ActionCommentPull
+ act = activities_model.ActionCommentPull
}
if err := mailer.MailParticipantsComment(ctx, comment, act, pr.Issue, mentions); err != nil {
log.Error("MailParticipantsComment: %v", err)
@@ -126,7 +126,7 @@ func (m *mailNotifier) NotifyPullRequestCodeComment(pr *issues_model.PullRequest
func (m *mailNotifier) NotifyIssueChangeAssignee(doer *user_model.User, issue *issues_model.Issue, assignee *user_model.User, removed bool, comment *issues_model.Comment) {
// mail only sent to added assignees and not self-assignee
- if !removed && doer.ID != assignee.ID && (assignee.EmailNotifications() == user_model.EmailNotificationsEnabled || assignee.EmailNotifications() == user_model.EmailNotificationsOnMention) {
+ if !removed && doer.ID != assignee.ID && assignee.EmailNotifications() != user_model.EmailNotificationsDisabled {
ct := fmt.Sprintf("Assigned #%d.", issue.Index)
if err := mailer.SendIssueAssignedMail(issue, doer, ct, comment, []*user_model.User{assignee}); err != nil {
log.Error("Error in SendIssueAssignedMail for issue[%d] to assignee[%d]: %v", issue.ID, assignee.ID, err)
@@ -135,7 +135,7 @@ func (m *mailNotifier) NotifyIssueChangeAssignee(doer *user_model.User, issue *i
}
func (m *mailNotifier) NotifyPullReviewRequest(doer *user_model.User, issue *issues_model.Issue, reviewer *user_model.User, isRequest bool, comment *issues_model.Comment) {
- if isRequest && doer.ID != reviewer.ID && (reviewer.EmailNotifications() == user_model.EmailNotificationsEnabled || reviewer.EmailNotifications() == user_model.EmailNotificationsOnMention) {
+ if isRequest && doer.ID != reviewer.ID && reviewer.EmailNotifications() != user_model.EmailNotificationsDisabled {
ct := fmt.Sprintf("Requested to review %s.", issue.HTMLURL())
if err := mailer.SendIssueAssignedMail(issue, doer, ct, comment, []*user_model.User{reviewer}); err != nil {
log.Error("Error in SendIssueAssignedMail for issue[%d] to reviewer[%d]: %v", issue.ID, reviewer.ID, err)
@@ -148,7 +148,7 @@ func (m *mailNotifier) NotifyMergePullRequest(pr *issues_model.PullRequest, doer
log.Error("pr.LoadIssue: %v", err)
return
}
- if err := mailer.MailParticipants(pr.Issue, doer, models.ActionMergePullRequest, nil); err != nil {
+ if err := mailer.MailParticipants(pr.Issue, doer, activities_model.ActionMergePullRequest, nil); err != nil {
log.Error("MailParticipants: %v", err)
}
}
@@ -184,12 +184,12 @@ func (m *mailNotifier) NotifyPullRevieweDismiss(doer *user_model.User, review *i
ctx, _, finished := process.GetManager().AddContext(graceful.GetManager().HammerContext(), fmt.Sprintf("mailNotifier.NotifyPullRevieweDismiss Review[%d] in Issue[%d]", review.ID, review.IssueID))
defer finished()
- if err := mailer.MailParticipantsComment(ctx, comment, models.ActionPullReviewDismissed, review.Issue, nil); err != nil {
+ if err := mailer.MailParticipantsComment(ctx, comment, activities_model.ActionPullReviewDismissed, review.Issue, nil); err != nil {
log.Error("MailParticipantsComment: %v", err)
}
}
-func (m *mailNotifier) NotifyNewRelease(rel *models.Release) {
+func (m *mailNotifier) NotifyNewRelease(rel *repo_model.Release) {
ctx, _, finished := process.GetManager().AddContext(graceful.GetManager().HammerContext(), fmt.Sprintf("mailNotifier.NotifyNewRelease rel[%d]%s in [%d]", rel.ID, rel.Title, rel.RepoID))
defer finished()
diff --git a/modules/notification/notification.go b/modules/notification/notification.go
index bdfed90b78..693c7f5c22 100644
--- a/modules/notification/notification.go
+++ b/modules/notification/notification.go
@@ -5,7 +5,6 @@
package notification
import (
- "code.gitea.io/gitea/models"
issues_model "code.gitea.io/gitea/models/issues"
packages_model "code.gitea.io/gitea/models/packages"
repo_model "code.gitea.io/gitea/models/repo"
@@ -41,6 +40,27 @@ func NewContext() {
RegisterNotifier(mirror.NewNotifier())
}
+// NotifyNewWikiPage notifies creating new wiki pages to notifiers
+func NotifyNewWikiPage(doer *user_model.User, repo *repo_model.Repository, page, comment string) {
+ for _, notifier := range notifiers {
+ notifier.NotifyNewWikiPage(doer, repo, page, comment)
+ }
+}
+
+// NotifyEditWikiPage notifies editing or renaming wiki pages to notifiers
+func NotifyEditWikiPage(doer *user_model.User, repo *repo_model.Repository, page, comment string) {
+ for _, notifier := range notifiers {
+ notifier.NotifyEditWikiPage(doer, repo, page, comment)
+ }
+}
+
+// NotifyDeleteWikiPage notifies deleting wiki pages to notifiers
+func NotifyDeleteWikiPage(doer *user_model.User, repo *repo_model.Repository, page string) {
+ for _, notifier := range notifiers {
+ notifier.NotifyDeleteWikiPage(doer, repo, page)
+ }
+}
+
// NotifyCreateIssueComment notifies issue comment related message to notifiers
func NotifyCreateIssueComment(doer *user_model.User, repo *repo_model.Repository,
issue *issues_model.Issue, comment *issues_model.Comment, mentions []*user_model.User,
@@ -142,21 +162,21 @@ func NotifyDeleteComment(doer *user_model.User, c *issues_model.Comment) {
}
// NotifyNewRelease notifies new release to notifiers
-func NotifyNewRelease(rel *models.Release) {
+func NotifyNewRelease(rel *repo_model.Release) {
for _, notifier := range notifiers {
notifier.NotifyNewRelease(rel)
}
}
// NotifyUpdateRelease notifies update release to notifiers
-func NotifyUpdateRelease(doer *user_model.User, rel *models.Release) {
+func NotifyUpdateRelease(doer *user_model.User, rel *repo_model.Release) {
for _, notifier := range notifiers {
notifier.NotifyUpdateRelease(doer, rel)
}
}
// NotifyDeleteRelease notifies delete release to notifiers
-func NotifyDeleteRelease(doer *user_model.User, rel *models.Release) {
+func NotifyDeleteRelease(doer *user_model.User, rel *repo_model.Release) {
for _, notifier := range notifiers {
notifier.NotifyDeleteRelease(doer, rel)
}
diff --git a/modules/notification/ui/ui.go b/modules/notification/ui/ui.go
index 74866a3363..4d96a6b0ed 100644
--- a/modules/notification/ui/ui.go
+++ b/modules/notification/ui/ui.go
@@ -5,11 +5,12 @@
package ui
import (
- "code.gitea.io/gitea/models"
+ activities_model "code.gitea.io/gitea/models/activities"
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/graceful"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/notification/base"
@@ -42,7 +43,7 @@ func NewNotifier() base.Notifier {
func (ns *notificationService) handle(data ...queue.Data) []queue.Data {
for _, datum := range data {
opts := datum.(issueNotificationOpts)
- if err := models.CreateOrUpdateIssueNotifications(opts.IssueID, opts.CommentID, opts.NotificationAuthorID, opts.ReceiverID); err != nil {
+ if err := activities_model.CreateOrUpdateIssueNotifications(opts.IssueID, opts.CommentID, opts.NotificationAuthorID, opts.ReceiverID); err != nil {
log.Error("Was unable to create issue notification: %v", err)
}
}
@@ -123,14 +124,14 @@ func (ns *notificationService) NotifyNewPullRequest(pr *issues_model.PullRequest
log.Error("Unable to load issue: %d for pr: %d: Error: %v", pr.IssueID, pr.ID, err)
return
}
- toNotify := make(map[int64]struct{}, 32)
+ toNotify := make(container.Set[int64], 32)
repoWatchers, err := repo_model.GetRepoWatchersIDs(db.DefaultContext, pr.Issue.RepoID)
if err != nil {
log.Error("GetRepoWatchersIDs: %v", err)
return
}
for _, id := range repoWatchers {
- toNotify[id] = struct{}{}
+ toNotify.Add(id)
}
issueParticipants, err := issues_model.GetParticipantsIDsByIssueID(pr.IssueID)
if err != nil {
@@ -138,11 +139,11 @@ func (ns *notificationService) NotifyNewPullRequest(pr *issues_model.PullRequest
return
}
for _, id := range issueParticipants {
- toNotify[id] = struct{}{}
+ toNotify.Add(id)
}
delete(toNotify, pr.Issue.PosterID)
for _, mention := range mentions {
- toNotify[mention.ID] = struct{}{}
+ toNotify.Add(mention.ID)
}
for receiverID := range toNotify {
_ = ns.issueQueue.Push(issueNotificationOpts{
@@ -237,7 +238,7 @@ func (ns *notificationService) NotifyPullReviewRequest(doer *user_model.User, is
}
func (ns *notificationService) NotifyRepoPendingTransfer(doer, newOwner *user_model.User, repo *repo_model.Repository) {
- if err := models.CreateRepoTransferNotification(doer, newOwner, repo); err != nil {
+ if err := activities_model.CreateRepoTransferNotification(doer, newOwner, repo); err != nil {
log.Error("NotifyRepoPendingTransfer: %v", err)
}
}
diff --git a/modules/notification/webhook/webhook.go b/modules/notification/webhook/webhook.go
index be71d18fda..b93e90368a 100644
--- a/modules/notification/webhook/webhook.go
+++ b/modules/notification/webhook/webhook.go
@@ -7,7 +7,6 @@ package webhook
import (
"fmt"
- "code.gitea.io/gitea/models"
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
packages_model "code.gitea.io/gitea/models/packages"
@@ -502,6 +501,44 @@ func (m *webhookNotifier) NotifyDeleteComment(doer *user_model.User, comment *is
}
}
+func (m *webhookNotifier) NotifyNewWikiPage(doer *user_model.User, repo *repo_model.Repository, page, comment string) {
+ // Add to hook queue for created wiki page.
+ if err := webhook_services.PrepareWebhooks(repo, webhook.HookEventWiki, &api.WikiPayload{
+ Action: api.HookWikiCreated,
+ Repository: convert.ToRepo(repo, perm.AccessModeOwner),
+ Sender: convert.ToUser(doer, nil),
+ Page: page,
+ Comment: comment,
+ }); err != nil {
+ log.Error("PrepareWebhooks [repo_id: %d]: %v", repo.ID, err)
+ }
+}
+
+func (m *webhookNotifier) NotifyEditWikiPage(doer *user_model.User, repo *repo_model.Repository, page, comment string) {
+ // Add to hook queue for edit wiki page.
+ if err := webhook_services.PrepareWebhooks(repo, webhook.HookEventWiki, &api.WikiPayload{
+ Action: api.HookWikiEdited,
+ Repository: convert.ToRepo(repo, perm.AccessModeOwner),
+ Sender: convert.ToUser(doer, nil),
+ Page: page,
+ Comment: comment,
+ }); err != nil {
+ log.Error("PrepareWebhooks [repo_id: %d]: %v", repo.ID, err)
+ }
+}
+
+func (m *webhookNotifier) NotifyDeleteWikiPage(doer *user_model.User, repo *repo_model.Repository, page string) {
+ // Add to hook queue for edit wiki page.
+ if err := webhook_services.PrepareWebhooks(repo, webhook.HookEventWiki, &api.WikiPayload{
+ Action: api.HookWikiDeleted,
+ Repository: convert.ToRepo(repo, perm.AccessModeOwner),
+ Sender: convert.ToUser(doer, nil),
+ Page: page,
+ }); err != nil {
+ log.Error("PrepareWebhooks [repo_id: %d]: %v", repo.ID, err)
+ }
+}
+
func (m *webhookNotifier) NotifyIssueChangeLabels(doer *user_model.User, issue *issues_model.Issue,
addedLabels, removedLabels []*issues_model.Label,
) {
@@ -608,15 +645,16 @@ func (m *webhookNotifier) NotifyPushCommits(pusher *user_model.User, repo *repo_
}
if err := webhook_services.PrepareWebhooks(repo, webhook.HookEventPush, &api.PushPayload{
- Ref: opts.RefFullName,
- Before: opts.OldCommitID,
- After: opts.NewCommitID,
- CompareURL: setting.AppURL + commits.CompareURL,
- Commits: apiCommits,
- HeadCommit: apiHeadCommit,
- Repo: convert.ToRepo(repo, perm.AccessModeOwner),
- Pusher: apiPusher,
- Sender: apiPusher,
+ Ref: opts.RefFullName,
+ Before: opts.OldCommitID,
+ After: opts.NewCommitID,
+ CompareURL: setting.AppURL + commits.CompareURL,
+ Commits: apiCommits,
+ TotalCommits: commits.Len,
+ HeadCommit: apiHeadCommit,
+ Repo: convert.ToRepo(repo, perm.AccessModeOwner),
+ Pusher: apiPusher,
+ Sender: apiPusher,
}); err != nil {
log.Error("PrepareWebhooks: %v", err)
}
@@ -797,7 +835,7 @@ func (m *webhookNotifier) NotifyDeleteRef(pusher *user_model.User, repo *repo_mo
}
}
-func sendReleaseHook(doer *user_model.User, rel *models.Release, action api.HookReleaseAction) {
+func sendReleaseHook(doer *user_model.User, rel *repo_model.Release, action api.HookReleaseAction) {
if err := rel.LoadAttributes(); err != nil {
log.Error("LoadAttributes: %v", err)
return
@@ -814,15 +852,15 @@ func sendReleaseHook(doer *user_model.User, rel *models.Release, action api.Hook
}
}
-func (m *webhookNotifier) NotifyNewRelease(rel *models.Release) {
+func (m *webhookNotifier) NotifyNewRelease(rel *repo_model.Release) {
sendReleaseHook(rel.Publisher, rel, api.HookReleasePublished)
}
-func (m *webhookNotifier) NotifyUpdateRelease(doer *user_model.User, rel *models.Release) {
+func (m *webhookNotifier) NotifyUpdateRelease(doer *user_model.User, rel *repo_model.Release) {
sendReleaseHook(doer, rel, api.HookReleaseUpdated)
}
-func (m *webhookNotifier) NotifyDeleteRelease(doer *user_model.User, rel *models.Release) {
+func (m *webhookNotifier) NotifyDeleteRelease(doer *user_model.User, rel *repo_model.Release) {
sendReleaseHook(doer, rel, api.HookReleaseDeleted)
}
@@ -838,15 +876,16 @@ func (m *webhookNotifier) NotifySyncPushCommits(pusher *user_model.User, repo *r
}
if err := webhook_services.PrepareWebhooks(repo, webhook.HookEventPush, &api.PushPayload{
- Ref: opts.RefFullName,
- Before: opts.OldCommitID,
- After: opts.NewCommitID,
- CompareURL: setting.AppURL + commits.CompareURL,
- Commits: apiCommits,
- HeadCommit: apiHeadCommit,
- Repo: convert.ToRepo(repo, perm.AccessModeOwner),
- Pusher: apiPusher,
- Sender: apiPusher,
+ Ref: opts.RefFullName,
+ Before: opts.OldCommitID,
+ After: opts.NewCommitID,
+ CompareURL: setting.AppURL + commits.CompareURL,
+ Commits: apiCommits,
+ TotalCommits: commits.Len,
+ HeadCommit: apiHeadCommit,
+ Repo: convert.ToRepo(repo, perm.AccessModeOwner),
+ Pusher: apiPusher,
+ Sender: apiPusher,
}); err != nil {
log.Error("PrepareWebhooks: %v", err)
}
diff --git a/modules/options/base.go b/modules/options/base.go
new file mode 100644
index 0000000000..e1d6efa7f0
--- /dev/null
+++ b/modules/options/base.go
@@ -0,0 +1,40 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package options
+
+import (
+ "fmt"
+ "io/fs"
+ "os"
+ "path/filepath"
+
+ "code.gitea.io/gitea/modules/util"
+)
+
+func walkAssetDir(root string, callback func(path, name string, d fs.DirEntry, err error) error) error {
+ if err := filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error {
+ // name is the path relative to the root
+ name := path[len(root):]
+ if len(name) > 0 && name[0] == '/' {
+ name = name[1:]
+ }
+ if err != nil {
+ if os.IsNotExist(err) {
+ return callback(path, name, d, err)
+ }
+ return err
+ }
+ if util.CommonSkip(d.Name()) {
+ if d.IsDir() {
+ return fs.SkipDir
+ }
+ return nil
+ }
+ return callback(path, name, d, err)
+ }); err != nil && !os.IsNotExist(err) {
+ return fmt.Errorf("unable to get files for assets in %s: %w", root, err)
+ }
+ return nil
+}
diff --git a/modules/options/dynamic.go b/modules/options/dynamic.go
index 5fea337e42..eeef11e8da 100644
--- a/modules/options/dynamic.go
+++ b/modules/options/dynamic.go
@@ -8,8 +8,10 @@ package options
import (
"fmt"
+ "io/fs"
"os"
"path"
+ "path/filepath"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
@@ -45,7 +47,7 @@ func Dir(name string) ([]string, error) {
isDir, err = util.IsDir(staticDir)
if err != nil {
- return []string{}, fmt.Errorf("Unabe to check if static directory %s is a directory. %v", staticDir, err)
+ return []string{}, fmt.Errorf("unable to check if static directory %s is a directory. %v", staticDir, err)
}
if isDir {
files, err := util.StatDir(staticDir, true)
@@ -64,6 +66,18 @@ func Locale(name string) ([]byte, error) {
return fileFromDir(path.Join("locale", name))
}
+// WalkLocales reads the content of a specific locale from static or custom path.
+func WalkLocales(callback func(path, name string, d fs.DirEntry, err error) error) error {
+ if err := walkAssetDir(filepath.Join(setting.StaticRootPath, "options", "locale"), callback); err != nil && !os.IsNotExist(err) {
+ return fmt.Errorf("failed to walk locales. Error: %w", err)
+ }
+
+ if err := walkAssetDir(filepath.Join(setting.CustomPath, "options", "locale"), callback); err != nil && !os.IsNotExist(err) {
+ return fmt.Errorf("failed to walk locales. Error: %w", err)
+ }
+ return nil
+}
+
// Readme reads the content of a specific readme from static or custom path.
func Readme(name string) ([]byte, error) {
return fileFromDir(path.Join("readme", name))
diff --git a/modules/options/static.go b/modules/options/static.go
index 6cad88cb61..4d60879be3 100644
--- a/modules/options/static.go
+++ b/modules/options/static.go
@@ -1,4 +1,4 @@
-// Copyright 2016 The Gitea Authors. All rights reserved.
+// Copyright 2022 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
@@ -9,8 +9,10 @@ package options
import (
"fmt"
"io"
+ "io/fs"
"os"
"path"
+ "path/filepath"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
@@ -30,12 +32,12 @@ func Dir(name string) ([]string, error) {
customDir := path.Join(setting.CustomPath, "options", name)
isDir, err := util.IsDir(customDir)
if err != nil {
- return []string{}, fmt.Errorf("Failed to check if custom directory %s is a directory. %v", err)
+ return []string{}, fmt.Errorf("unable to check if custom directory %q is a directory. %w", customDir, err)
}
if isDir {
files, err := util.StatDir(customDir, true)
if err != nil {
- return []string{}, fmt.Errorf("Failed to read custom directory. %v", err)
+ return []string{}, fmt.Errorf("unable to read custom directory %q. %w", customDir, err)
}
result = append(result, files...)
@@ -43,11 +45,10 @@ func Dir(name string) ([]string, error) {
files, err := AssetDir(name)
if err != nil {
- return []string{}, fmt.Errorf("Failed to read embedded directory. %v", err)
+ return []string{}, fmt.Errorf("unable to read embedded directory %q. %w", name, err)
}
result = append(result, files...)
-
return directories.AddAndGet(name, result), nil
}
@@ -74,6 +75,14 @@ func Locale(name string) ([]byte, error) {
return fileFromDir(path.Join("locale", name))
}
+// WalkLocales reads the content of a specific locale from static or custom path.
+func WalkLocales(callback func(path, name string, d fs.DirEntry, err error) error) error {
+ if err := walkAssetDir(filepath.Join(setting.CustomPath, "options", "locale"), callback); err != nil && !os.IsNotExist(err) {
+ return fmt.Errorf("failed to walk locales. Error: %w", err)
+ }
+ return nil
+}
+
// Readme reads the content of a specific readme from bindata or custom path.
func Readme(name string) ([]byte, error) {
return fileFromDir(path.Join("readme", name))
diff --git a/modules/packages/container/metadata.go b/modules/packages/container/metadata.go
index 087d38e5bd..fd38e67859 100644
--- a/modules/packages/container/metadata.go
+++ b/modules/packages/container/metadata.go
@@ -16,6 +16,7 @@ import (
)
const (
+ PropertyRepository = "container.repository"
PropertyDigest = "container.digest"
PropertyMediaType = "container.mediatype"
PropertyManifestTagged = "container.manifest.tagged"
@@ -94,7 +95,9 @@ func parseOCIImageConfig(r io.Reader) (*Metadata, error) {
if i := strings.Index(cmd, "#(nop) "); i != -1 {
cmd = strings.TrimSpace(cmd[i+7:])
}
- imageLayers = append(imageLayers, cmd)
+ if cmd != "" {
+ imageLayers = append(imageLayers, cmd)
+ }
}
metadata := &Metadata{
diff --git a/modules/packages/content_store.go b/modules/packages/content_store.go
index 64c3eedc23..a3a5d1a666 100644
--- a/modules/packages/content_store.go
+++ b/modules/packages/content_store.go
@@ -27,21 +27,21 @@ func NewContentStore() *ContentStore {
// Get gets a package blob
func (s *ContentStore) Get(key BlobHash256Key) (storage.Object, error) {
- return s.store.Open(keyToRelativePath(key))
+ return s.store.Open(KeyToRelativePath(key))
}
// Save stores a package blob
func (s *ContentStore) Save(key BlobHash256Key, r io.Reader, size int64) error {
- _, err := s.store.Save(keyToRelativePath(key), r, size)
+ _, err := s.store.Save(KeyToRelativePath(key), r, size)
return err
}
// Delete deletes a package blob
func (s *ContentStore) Delete(key BlobHash256Key) error {
- return s.store.Delete(keyToRelativePath(key))
+ return s.store.Delete(KeyToRelativePath(key))
}
-// keyToRelativePath converts the sha256 key aabb000000... to aa/bb/aabb000000...
-func keyToRelativePath(key BlobHash256Key) string {
+// KeyToRelativePath converts the sha256 key aabb000000... to aa/bb/aabb000000...
+func KeyToRelativePath(key BlobHash256Key) string {
return path.Join(string(key)[0:2], string(key)[2:4], string(key))
}
diff --git a/modules/packages/hashed_buffer_test.go b/modules/packages/hashed_buffer_test.go
new file mode 100644
index 0000000000..e21ec67e1f
--- /dev/null
+++ b/modules/packages/hashed_buffer_test.go
@@ -0,0 +1,47 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package packages
+
+import (
+ "fmt"
+ "io"
+ "strings"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestHashedBuffer(t *testing.T) {
+ cases := []struct {
+ MaxMemorySize int
+ Data string
+ HashMD5 string
+ HashSHA1 string
+ HashSHA256 string
+ HashSHA512 string
+ }{
+ {5, "test", "098f6bcd4621d373cade4e832627b4f6", "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3", "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08", "ee26b0dd4af7e749aa1a8ee3c10ae9923f618980772e473f8819a5d4940e0db27ac185f8a0e1d5f84f88bc887fd67b143732c304cc5fa9ad8e6f57f50028a8ff"},
+ {5, "testtest", "05a671c66aefea124cc08b76ea6d30bb", "51abb9636078defbf888d8457a7c76f85c8f114c", "37268335dd6931045bdcdf92623ff819a64244b53d0e746d438797349d4da578", "125d6d03b32c84d492747f79cf0bf6e179d287f341384eb5d6d3197525ad6be8e6df0116032935698f99a09e265073d1d6c32c274591bf1d0a20ad67cba921bc"},
+ }
+
+ for _, c := range cases {
+ buf, err := CreateHashedBufferFromReader(strings.NewReader(c.Data), c.MaxMemorySize)
+ assert.NoError(t, err)
+
+ assert.EqualValues(t, len(c.Data), buf.Size())
+
+ data, err := io.ReadAll(buf)
+ assert.NoError(t, err)
+ assert.Equal(t, c.Data, string(data))
+
+ hashMD5, hashSHA1, hashSHA256, hashSHA512 := buf.Sums()
+ assert.Equal(t, c.HashMD5, fmt.Sprintf("%x", hashMD5))
+ assert.Equal(t, c.HashSHA1, fmt.Sprintf("%x", hashSHA1))
+ assert.Equal(t, c.HashSHA256, fmt.Sprintf("%x", hashSHA256))
+ assert.Equal(t, c.HashSHA512, fmt.Sprintf("%x", hashSHA512))
+
+ assert.NoError(t, buf.Close())
+ }
+}
diff --git a/modules/packages/npm/creator.go b/modules/packages/npm/creator.go
index 88ce55ecdb..16cadefb89 100644
--- a/modules/packages/npm/creator.go
+++ b/modules/packages/npm/creator.go
@@ -66,7 +66,8 @@ type PackageMetadata struct {
License string `json:"license,omitempty"`
}
-// PackageMetadataVersion https://github.com/npm/registry/blob/master/docs/REGISTRY-API.md#version
+// PackageMetadataVersion documentation: https://github.com/npm/registry/blob/master/docs/REGISTRY-API.md#version
+// PackageMetadataVersion response: https://github.com/npm/registry/blob/master/docs/responses/package-metadata.md#abbreviated-version-object
type PackageMetadataVersion struct {
ID string `json:"_id"`
Name string `json:"name"`
@@ -80,6 +81,7 @@ type PackageMetadataVersion struct {
Dependencies map[string]string `json:"dependencies,omitempty"`
DevDependencies map[string]string `json:"devDependencies,omitempty"`
PeerDependencies map[string]string `json:"peerDependencies,omitempty"`
+ Bin map[string]string `json:"bin,omitempty"`
OptionalDependencies map[string]string `json:"optionalDependencies,omitempty"`
Readme string `json:"readme,omitempty"`
Dist PackageDistribution `json:"dist"`
@@ -96,6 +98,34 @@ type PackageDistribution struct {
NpmSignature string `json:"npm-signature,omitempty"`
}
+type PackageSearch struct {
+ Objects []*PackageSearchObject `json:"objects"`
+ Total int64 `json:"total"`
+}
+
+type PackageSearchObject struct {
+ Package *PackageSearchPackage `json:"package"`
+}
+
+type PackageSearchPackage struct {
+ Scope string `json:"scope"`
+ Name string `json:"name"`
+ Version string `json:"version"`
+ Date time.Time `json:"date"`
+ Description string `json:"description"`
+ Author User `json:"author"`
+ Publisher User `json:"publisher"`
+ Maintainers []User `json:"maintainers"`
+ Keywords []string `json:"keywords,omitempty"`
+ Links *PackageSearchPackageLinks `json:"links"`
+}
+
+type PackageSearchPackageLinks struct {
+ Registry string `json:"npm"`
+ Homepage string `json:"homepage,omitempty"`
+ Repository string `json:"repository,omitempty"`
+}
+
// User https://github.com/npm/registry/blob/master/docs/REGISTRY-API.md#package
type User struct {
Username string `json:"username,omitempty"`
@@ -192,6 +222,7 @@ func ParsePackage(r io.Reader) (*Package, error) {
DevelopmentDependencies: meta.DevDependencies,
PeerDependencies: meta.PeerDependencies,
OptionalDependencies: meta.OptionalDependencies,
+ Bin: meta.Bin,
Readme: meta.Readme,
},
}
diff --git a/modules/packages/npm/creator_test.go b/modules/packages/npm/creator_test.go
index 64ae6238f3..2b844f4b0e 100644
--- a/modules/packages/npm/creator_test.go
+++ b/modules/packages/npm/creator_test.go
@@ -23,6 +23,7 @@ func TestParsePackage(t *testing.T) {
packageVersion := "1.0.1-pre"
packageTag := "latest"
packageAuthor := "KN4CK3R"
+ packageBin := "gitea"
packageDescription := "Test Description"
data := "H4sIAAAAAAAA/ytITM5OTE/VL4DQelnF+XkMVAYGBgZmJiYK2MRBwNDcSIHB2NTMwNDQzMwAqA7IMDUxA9LUdgg2UFpcklgEdAql5kD8ogCnhwio5lJQUMpLzE1VslJQcihOzi9I1S9JLS7RhSYIJR2QgrLUouLM/DyQGkM9Az1D3YIiqExKanFyUWZBCVQ2BKhVwQVJDKwosbQkI78IJO/tZ+LsbRykxFXLNdA+HwWjYBSMgpENACgAbtAACAAA"
integrity := "sha512-yA4FJsVhetynGfOC1jFf79BuS+jrHbm0fhh+aHzCQkOaOBXKf9oBnC4a6DnLLnEsHQDRLYd00cwj8sCXpC+wIg=="
@@ -236,6 +237,9 @@ func TestParsePackage(t *testing.T) {
Dependencies: map[string]string{
"package": "1.2.0",
},
+ Bin: map[string]string{
+ "bin": packageBin,
+ },
Dist: PackageDistribution{
Integrity: integrity,
},
@@ -264,6 +268,7 @@ func TestParsePackage(t *testing.T) {
assert.Equal(t, packageDescription, p.Metadata.Description)
assert.Equal(t, packageDescription, p.Metadata.Readme)
assert.Equal(t, packageAuthor, p.Metadata.Author)
+ assert.Equal(t, packageBin, p.Metadata.Bin["bin"])
assert.Equal(t, "MIT", p.Metadata.License)
assert.Equal(t, "https://gitea.io/", p.Metadata.ProjectURL)
assert.Contains(t, p.Metadata.Dependencies, "package")
diff --git a/modules/packages/npm/metadata.go b/modules/packages/npm/metadata.go
index 643a4d344b..44714cd6ea 100644
--- a/modules/packages/npm/metadata.go
+++ b/modules/packages/npm/metadata.go
@@ -20,5 +20,6 @@ type Metadata struct {
DevelopmentDependencies map[string]string `json:"development_dependencies,omitempty"`
PeerDependencies map[string]string `json:"peer_dependencies,omitempty"`
OptionalDependencies map[string]string `json:"optional_dependencies,omitempty"`
+ Bin map[string]string `json:"bin,omitempty"`
Readme string `json:"readme,omitempty"`
}
diff --git a/modules/packages/nuget/metadata.go b/modules/packages/nuget/metadata.go
index 797bff45ac..2b555e47e9 100644
--- a/modules/packages/nuget/metadata.go
+++ b/modules/packages/nuget/metadata.go
@@ -55,12 +55,13 @@ type Package struct {
// Metadata represents the metadata of a Nuget package
type Metadata struct {
- Description string `json:"description,omitempty"`
- ReleaseNotes string `json:"release_notes,omitempty"`
- Authors string `json:"authors,omitempty"`
- ProjectURL string `json:"project_url,omitempty"`
- RepositoryURL string `json:"repository_url,omitempty"`
- Dependencies map[string][]Dependency `json:"dependencies,omitempty"`
+ Description string `json:"description,omitempty"`
+ ReleaseNotes string `json:"release_notes,omitempty"`
+ Authors string `json:"authors,omitempty"`
+ ProjectURL string `json:"project_url,omitempty"`
+ RepositoryURL string `json:"repository_url,omitempty"`
+ RequireLicenseAcceptance bool `json:"require_license_acceptance"`
+ Dependencies map[string][]Dependency `json:"dependencies,omitempty"`
}
// Dependency represents a dependency of a Nuget package
@@ -155,12 +156,13 @@ func ParseNuspecMetaData(r io.Reader) (*Package, error) {
}
m := &Metadata{
- Description: p.Metadata.Description,
- ReleaseNotes: p.Metadata.ReleaseNotes,
- Authors: p.Metadata.Authors,
- ProjectURL: p.Metadata.ProjectURL,
- RepositoryURL: p.Metadata.Repository.URL,
- Dependencies: make(map[string][]Dependency),
+ Description: p.Metadata.Description,
+ ReleaseNotes: p.Metadata.ReleaseNotes,
+ Authors: p.Metadata.Authors,
+ ProjectURL: p.Metadata.ProjectURL,
+ RepositoryURL: p.Metadata.Repository.URL,
+ RequireLicenseAcceptance: p.Metadata.RequireLicenseAcceptance,
+ Dependencies: make(map[string][]Dependency),
}
for _, group := range p.Metadata.Dependencies.Group {
diff --git a/modules/packages/pub/metadata.go b/modules/packages/pub/metadata.go
new file mode 100644
index 0000000000..1fc4908b91
--- /dev/null
+++ b/modules/packages/pub/metadata.go
@@ -0,0 +1,154 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package pub
+
+import (
+ "archive/tar"
+ "compress/gzip"
+ "errors"
+ "io"
+ "regexp"
+ "strings"
+
+ "code.gitea.io/gitea/modules/validation"
+
+ "github.com/hashicorp/go-version"
+ "gopkg.in/yaml.v2"
+)
+
+var (
+ ErrMissingPubspecFile = errors.New("Pubspec file is missing")
+ ErrPubspecFileTooLarge = errors.New("Pubspec file is too large")
+ ErrInvalidName = errors.New("Package name is invalid")
+ ErrInvalidVersion = errors.New("Package version is invalid")
+)
+
+var namePattern = regexp.MustCompile(`\A[a-zA-Z_][a-zA-Z0-9_]*\z`)
+
+// https://github.com/dart-lang/pub-dev/blob/4d582302a8d10152a5cd6129f65bf4f4dbca239d/pkg/pub_package_reader/lib/pub_package_reader.dart#L143
+const maxPubspecFileSize = 128 * 1024
+
+// Package represents a Pub package
+type Package struct {
+ Name string
+ Version string
+ Metadata *Metadata
+}
+
+// Metadata represents the metadata of a Pub package
+type Metadata struct {
+ Description string `json:"description,omitempty"`
+ ProjectURL string `json:"project_url,omitempty"`
+ RepositoryURL string `json:"repository_url,omitempty"`
+ DocumentationURL string `json:"documentation_url,omitempty"`
+ Readme string `json:"readme,omitempty"`
+ Pubspec interface{} `json:"pubspec"`
+}
+
+type pubspecPackage struct {
+ Name string `yaml:"name"`
+ Version string `yaml:"version"`
+ Description string `yaml:"description"`
+ Homepage string `yaml:"homepage"`
+ Repository string `yaml:"repository"`
+ Documentation string `yaml:"documentation"`
+}
+
+// ParsePackage parses the Pub package file
+func ParsePackage(r io.Reader) (*Package, error) {
+ gzr, err := gzip.NewReader(r)
+ if err != nil {
+ return nil, err
+ }
+ defer gzr.Close()
+
+ var p *Package
+ var readme string
+
+ tr := tar.NewReader(gzr)
+ for {
+ hd, err := tr.Next()
+ if err == io.EOF {
+ break
+ }
+ if err != nil {
+ return nil, err
+ }
+
+ if hd.Typeflag != tar.TypeReg {
+ continue
+ }
+
+ if hd.Name == "pubspec.yaml" {
+ if hd.Size > maxPubspecFileSize {
+ return nil, ErrPubspecFileTooLarge
+ }
+ p, err = ParsePubspecMetadata(tr)
+ if err != nil {
+ return nil, err
+ }
+ } else if strings.ToLower(hd.Name) == "readme.md" {
+ data, err := io.ReadAll(tr)
+ if err != nil {
+ return nil, err
+ }
+ readme = string(data)
+ }
+ }
+
+ if p == nil {
+ return nil, ErrMissingPubspecFile
+ }
+
+ p.Metadata.Readme = readme
+
+ return p, nil
+}
+
+// ParsePubspecMetadata parses a Pubspec file to retrieve the metadata of a Pub package
+func ParsePubspecMetadata(r io.Reader) (*Package, error) {
+ buf, err := io.ReadAll(io.LimitReader(r, maxPubspecFileSize))
+ if err != nil {
+ return nil, err
+ }
+
+ var p pubspecPackage
+ if err := yaml.Unmarshal(buf, &p); err != nil {
+ return nil, err
+ }
+
+ if !namePattern.MatchString(p.Name) {
+ return nil, ErrInvalidName
+ }
+
+ v, err := version.NewSemver(p.Version)
+ if err != nil {
+ return nil, ErrInvalidVersion
+ }
+
+ if !validation.IsValidURL(p.Homepage) {
+ p.Homepage = ""
+ }
+ if !validation.IsValidURL(p.Repository) {
+ p.Repository = ""
+ }
+
+ var pubspec interface{}
+ if err := yaml.Unmarshal(buf, &pubspec); err != nil {
+ return nil, err
+ }
+
+ return &Package{
+ Name: p.Name,
+ Version: v.String(),
+ Metadata: &Metadata{
+ Description: p.Description,
+ ProjectURL: p.Homepage,
+ RepositoryURL: p.Repository,
+ DocumentationURL: p.Documentation,
+ Pubspec: pubspec,
+ },
+ }, nil
+}
diff --git a/modules/packages/pub/metadata_test.go b/modules/packages/pub/metadata_test.go
new file mode 100644
index 0000000000..e43ed64fc6
--- /dev/null
+++ b/modules/packages/pub/metadata_test.go
@@ -0,0 +1,136 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package pub
+
+import (
+ "archive/tar"
+ "bytes"
+ "compress/gzip"
+ "io"
+ "strings"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+const (
+ packageName = "gitea"
+ packageVersion = "1.0.1"
+ description = "Package Description"
+ projectURL = "https://gitea.io"
+ repositoryURL = "https://gitea.io/gitea/gitea"
+ documentationURL = "https://docs.gitea.io"
+)
+
+const pubspecContent = `name: ` + packageName + `
+version: ` + packageVersion + `
+description: ` + description + `
+homepage: ` + projectURL + `
+repository: ` + repositoryURL + `
+documentation: ` + documentationURL + `
+
+environment:
+ sdk: '>=2.16.0 <3.0.0'
+
+dependencies:
+ flutter:
+ sdk: flutter
+ path: '>=1.8.0 <3.0.0'
+
+dev_dependencies:
+ http: '>=0.13.0'`
+
+func TestParsePackage(t *testing.T) {
+ createArchive := func(files map[string][]byte) io.Reader {
+ var buf bytes.Buffer
+ zw := gzip.NewWriter(&buf)
+ tw := tar.NewWriter(zw)
+ for filename, content := range files {
+ hdr := &tar.Header{
+ Name: filename,
+ Mode: 0o600,
+ Size: int64(len(content)),
+ }
+ tw.WriteHeader(hdr)
+ tw.Write(content)
+ }
+ tw.Close()
+ zw.Close()
+ return &buf
+ }
+
+ t.Run("MissingPubspecFile", func(t *testing.T) {
+ data := createArchive(map[string][]byte{"dummy.txt": {}})
+
+ pp, err := ParsePackage(data)
+ assert.Nil(t, pp)
+ assert.ErrorIs(t, err, ErrMissingPubspecFile)
+ })
+
+ t.Run("PubspecFileTooLarge", func(t *testing.T) {
+ data := createArchive(map[string][]byte{"pubspec.yaml": make([]byte, 200*1024)})
+
+ pp, err := ParsePackage(data)
+ assert.Nil(t, pp)
+ assert.ErrorIs(t, err, ErrPubspecFileTooLarge)
+ })
+
+ t.Run("InvalidPubspecFile", func(t *testing.T) {
+ data := createArchive(map[string][]byte{"pubspec.yaml": {}})
+
+ pp, err := ParsePackage(data)
+ assert.Nil(t, pp)
+ assert.Error(t, err)
+ })
+
+ t.Run("Valid", func(t *testing.T) {
+ data := createArchive(map[string][]byte{"pubspec.yaml": []byte(pubspecContent)})
+
+ pp, err := ParsePackage(data)
+ assert.NoError(t, err)
+ assert.NotNil(t, pp)
+ assert.Empty(t, pp.Metadata.Readme)
+ })
+
+ t.Run("ValidWithReadme", func(t *testing.T) {
+ data := createArchive(map[string][]byte{"pubspec.yaml": []byte(pubspecContent), "README.md": []byte("readme")})
+
+ pp, err := ParsePackage(data)
+ assert.NoError(t, err)
+ assert.NotNil(t, pp)
+ assert.Equal(t, "readme", pp.Metadata.Readme)
+ })
+}
+
+func TestParsePubspecMetadata(t *testing.T) {
+ t.Run("InvalidName", func(t *testing.T) {
+ for _, name := range []string{"123abc", "ab-cd"} {
+ pp, err := ParsePubspecMetadata(strings.NewReader(`name: ` + name))
+ assert.Nil(t, pp)
+ assert.ErrorIs(t, err, ErrInvalidName)
+ }
+ })
+
+ t.Run("InvalidVersion", func(t *testing.T) {
+ pp, err := ParsePubspecMetadata(strings.NewReader(`name: dummy
+version: invalid`))
+ assert.Nil(t, pp)
+ assert.ErrorIs(t, err, ErrInvalidVersion)
+ })
+
+ t.Run("Valid", func(t *testing.T) {
+ pp, err := ParsePubspecMetadata(strings.NewReader(pubspecContent))
+ assert.NoError(t, err)
+ assert.NotNil(t, pp)
+
+ assert.Equal(t, packageName, pp.Name)
+ assert.Equal(t, packageVersion, pp.Version)
+ assert.Equal(t, description, pp.Metadata.Description)
+ assert.Equal(t, projectURL, pp.Metadata.ProjectURL)
+ assert.Equal(t, repositoryURL, pp.Metadata.RepositoryURL)
+ assert.Equal(t, documentationURL, pp.Metadata.DocumentationURL)
+ assert.NotNil(t, pp.Metadata.Pubspec)
+ })
+}
diff --git a/modules/packages/rubygems/metadata.go b/modules/packages/rubygems/metadata.go
index 942f205fc3..05c1a8a719 100644
--- a/modules/packages/rubygems/metadata.go
+++ b/modules/packages/rubygems/metadata.go
@@ -80,7 +80,6 @@ type gemspec struct {
VersionRequirements requirement `yaml:"version_requirements"`
} `yaml:"dependencies"`
Description string `yaml:"description"`
- Email string `yaml:"email"`
Executables []string `yaml:"executables"`
Extensions []interface{} `yaml:"extensions"`
ExtraRdocFiles []string `yaml:"extra_rdoc_files"`
diff --git a/modules/packages/vagrant/metadata.go b/modules/packages/vagrant/metadata.go
new file mode 100644
index 0000000000..f52989b2e2
--- /dev/null
+++ b/modules/packages/vagrant/metadata.go
@@ -0,0 +1,97 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package vagrant
+
+import (
+ "archive/tar"
+ "compress/gzip"
+ "io"
+ "strings"
+
+ "code.gitea.io/gitea/modules/json"
+ "code.gitea.io/gitea/modules/validation"
+)
+
+const (
+ PropertyProvider = "vagrant.provider"
+)
+
+// Metadata represents the metadata of a Vagrant package
+type Metadata struct {
+ Author string `json:"author,omitempty"`
+ Description string `json:"description,omitempty"`
+ ProjectURL string `json:"project_url,omitempty"`
+ RepositoryURL string `json:"repository_url,omitempty"`
+}
+
+// ParseMetadataFromBox parses the metadata of a box file
+func ParseMetadataFromBox(r io.Reader) (*Metadata, error) {
+ gzr, err := gzip.NewReader(r)
+ if err != nil {
+ return nil, err
+ }
+ defer gzr.Close()
+
+ tr := tar.NewReader(gzr)
+ for {
+ hd, err := tr.Next()
+ if err == io.EOF {
+ break
+ }
+ if err != nil {
+ return nil, err
+ }
+
+ if hd.Typeflag != tar.TypeReg {
+ continue
+ }
+
+ if hd.Name == "info.json" {
+ return ParseInfoFile(tr)
+ }
+ }
+
+ return &Metadata{}, nil
+}
+
+// ParseInfoFile parses a info.json file to retrieve the metadata of a Vagrant package
+func ParseInfoFile(r io.Reader) (*Metadata, error) {
+ var values map[string]string
+ if err := json.NewDecoder(r).Decode(&values); err != nil {
+ return nil, err
+ }
+
+ m := &Metadata{}
+
+ // There is no defined format for this file, just try the common keys
+ for k, v := range values {
+ switch strings.ToLower(k) {
+ case "description":
+ fallthrough
+ case "short_description":
+ m.Description = v
+ case "website":
+ fallthrough
+ case "homepage":
+ fallthrough
+ case "url":
+ if validation.IsValidURL(v) {
+ m.ProjectURL = v
+ }
+ case "repository":
+ fallthrough
+ case "source":
+ if validation.IsValidURL(v) {
+ m.RepositoryURL = v
+ }
+ case "author":
+ fallthrough
+ case "authors":
+ m.Author = v
+ }
+ }
+
+ return m, nil
+}
diff --git a/modules/packages/vagrant/metadata_test.go b/modules/packages/vagrant/metadata_test.go
new file mode 100644
index 0000000000..9720c945ae
--- /dev/null
+++ b/modules/packages/vagrant/metadata_test.go
@@ -0,0 +1,111 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package vagrant
+
+import (
+ "archive/tar"
+ "bytes"
+ "compress/gzip"
+ "io"
+ "testing"
+
+ "code.gitea.io/gitea/modules/json"
+
+ "github.com/stretchr/testify/assert"
+)
+
+const (
+ author = "gitea"
+ description = "Package Description"
+ projectURL = "https://gitea.io"
+ repositoryURL = "https://gitea.io/gitea/gitea"
+)
+
+func TestParseMetadataFromBox(t *testing.T) {
+ createArchive := func(files map[string][]byte) io.Reader {
+ var buf bytes.Buffer
+ zw := gzip.NewWriter(&buf)
+ tw := tar.NewWriter(zw)
+ for filename, content := range files {
+ hdr := &tar.Header{
+ Name: filename,
+ Mode: 0o600,
+ Size: int64(len(content)),
+ }
+ tw.WriteHeader(hdr)
+ tw.Write(content)
+ }
+ tw.Close()
+ zw.Close()
+ return &buf
+ }
+
+ t.Run("MissingInfoFile", func(t *testing.T) {
+ data := createArchive(map[string][]byte{"dummy.txt": {}})
+
+ metadata, err := ParseMetadataFromBox(data)
+ assert.NotNil(t, metadata)
+ assert.NoError(t, err)
+ })
+
+ t.Run("Valid", func(t *testing.T) {
+ content, err := json.Marshal(map[string]string{
+ "description": description,
+ "author": author,
+ "website": projectURL,
+ "repository": repositoryURL,
+ })
+ assert.NoError(t, err)
+
+ data := createArchive(map[string][]byte{"info.json": content})
+
+ metadata, err := ParseMetadataFromBox(data)
+ assert.NotNil(t, metadata)
+ assert.NoError(t, err)
+
+ assert.Equal(t, author, metadata.Author)
+ assert.Equal(t, description, metadata.Description)
+ assert.Equal(t, projectURL, metadata.ProjectURL)
+ assert.Equal(t, repositoryURL, metadata.RepositoryURL)
+ })
+}
+
+func TestParseInfoFile(t *testing.T) {
+ t.Run("UnknownKeys", func(t *testing.T) {
+ content, err := json.Marshal(map[string]string{
+ "package": "",
+ "dummy": "",
+ })
+ assert.NoError(t, err)
+
+ metadata, err := ParseInfoFile(bytes.NewReader(content))
+ assert.NotNil(t, metadata)
+ assert.NoError(t, err)
+
+ assert.Empty(t, metadata.Author)
+ assert.Empty(t, metadata.Description)
+ assert.Empty(t, metadata.ProjectURL)
+ assert.Empty(t, metadata.RepositoryURL)
+ })
+
+ t.Run("Valid", func(t *testing.T) {
+ content, err := json.Marshal(map[string]string{
+ "description": description,
+ "author": author,
+ "website": projectURL,
+ "repository": repositoryURL,
+ })
+ assert.NoError(t, err)
+
+ metadata, err := ParseInfoFile(bytes.NewReader(content))
+ assert.NotNil(t, metadata)
+ assert.NoError(t, err)
+
+ assert.Equal(t, author, metadata.Author)
+ assert.Equal(t, description, metadata.Description)
+ assert.Equal(t, projectURL, metadata.ProjectURL)
+ assert.Equal(t, repositoryURL, metadata.RepositoryURL)
+ })
+}
diff --git a/modules/paginator/paginator.go b/modules/paginator/paginator.go
index 873cfe49d4..342ee8929c 100644
--- a/modules/paginator/paginator.go
+++ b/modules/paginator/paginator.go
@@ -1,5 +1,5 @@
// Copyright 2022 The Gitea Authors.
-// Copyright 2015 Unknwon. Licensed under the Apache License, Version 2.0
+// Copyright 2015 https://github.com/unknwon. Licensed under the Apache License, Version 2.0
package paginator
diff --git a/modules/paginator/paginator_test.go b/modules/paginator/paginator_test.go
index ce7b7275e1..404f76f6c4 100644
--- a/modules/paginator/paginator_test.go
+++ b/modules/paginator/paginator_test.go
@@ -1,5 +1,5 @@
// Copyright 2022 The Gitea Authors.
-// Copyright 2015 Unknwon. Licensed under the Apache License, Version 2.0
+// Copyright 2015 https://github.com/unknwon. Licensed under the Apache License, Version 2.0
package paginator
diff --git a/modules/pprof/pprof.go b/modules/pprof/pprof.go
index f080728766..8ce0ad484e 100644
--- a/modules/pprof/pprof.go
+++ b/modules/pprof/pprof.go
@@ -25,7 +25,7 @@ func DumpMemProfileForUsername(pprofDataPath, username string) error {
}
// DumpCPUProfileForUsername dumps a CPU profile at pprofDataPath as cpuprofile__
-// it returns the stop function which stops, writes and closes the CPU profile file
+// the stop function it returns stops, writes and closes the CPU profile file
func DumpCPUProfileForUsername(pprofDataPath, username string) (func(), error) {
f, err := os.CreateTemp(pprofDataPath, fmt.Sprintf("cpuprofile_%s_", username))
if err != nil {
diff --git a/modules/private/internal.go b/modules/private/internal.go
index a77a990627..21e5c9a279 100644
--- a/modules/private/internal.go
+++ b/modules/private/internal.go
@@ -10,20 +10,24 @@ import (
"fmt"
"net"
"net/http"
+ "os"
+ "strings"
"code.gitea.io/gitea/modules/httplib"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/proxyprotocol"
"code.gitea.io/gitea/modules/setting"
)
-func newRequest(ctx context.Context, url, method string) *httplib.Request {
+func newRequest(ctx context.Context, url, method, sourceIP string) *httplib.Request {
if setting.InternalToken == "" {
log.Fatal(`The INTERNAL_TOKEN setting is missing from the configuration file: %q.
Ensure you are running in the correct environment or set the correct configuration file with -c.`, setting.CustomConf)
}
return httplib.NewRequest(url, method).
SetContext(ctx).
+ Header("X-Real-IP", sourceIP).
Header("Authorization", fmt.Sprintf("Bearer %s", setting.InternalToken))
}
@@ -41,8 +45,16 @@ func decodeJSONError(resp *http.Response) *Response {
return &res
}
+func getClientIP() string {
+ sshConnEnv := strings.TrimSpace(os.Getenv("SSH_CONNECTION"))
+ if len(sshConnEnv) == 0 {
+ return "127.0.0.1"
+ }
+ return strings.Fields(sshConnEnv)[0]
+}
+
func newInternalRequest(ctx context.Context, url, method string) *httplib.Request {
- req := newRequest(ctx, url, method).SetTLSClientConfig(&tls.Config{
+ req := newRequest(ctx, url, method, getClientIP()).SetTLSClientConfig(&tls.Config{
InsecureSkipVerify: true,
ServerName: setting.Domain,
})
@@ -50,7 +62,32 @@ func newInternalRequest(ctx context.Context, url, method string) *httplib.Reques
req.SetTransport(&http.Transport{
DialContext: func(ctx context.Context, _, _ string) (net.Conn, error) {
var d net.Dialer
- return d.DialContext(ctx, "unix", setting.HTTPAddr)
+ conn, err := d.DialContext(ctx, "unix", setting.HTTPAddr)
+ if err != nil {
+ return conn, err
+ }
+ if setting.LocalUseProxyProtocol {
+ if err = proxyprotocol.WriteLocalHeader(conn); err != nil {
+ _ = conn.Close()
+ return nil, err
+ }
+ }
+ return conn, err
+ },
+ })
+ } else if setting.LocalUseProxyProtocol {
+ req.SetTransport(&http.Transport{
+ DialContext: func(ctx context.Context, network, address string) (net.Conn, error) {
+ var d net.Dialer
+ conn, err := d.DialContext(ctx, network, address)
+ if err != nil {
+ return conn, err
+ }
+ if err = proxyprotocol.WriteLocalHeader(conn); err != nil {
+ _ = conn.Close()
+ return nil, err
+ }
+ return conn, err
},
})
}
diff --git a/modules/proxyprotocol/conn.go b/modules/proxyprotocol/conn.go
new file mode 100644
index 0000000000..10333b204d
--- /dev/null
+++ b/modules/proxyprotocol/conn.go
@@ -0,0 +1,506 @@
+// Copyright 2020 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package proxyprotocol
+
+import (
+ "bufio"
+ "bytes"
+ "encoding/binary"
+ "io"
+ "net"
+ "strconv"
+ "strings"
+ "sync"
+ "time"
+
+ "code.gitea.io/gitea/modules/log"
+)
+
+var (
+ // v1Prefix is the string we look for at the start of a connection
+ // to check if this connection is using the proxy protocol
+ v1Prefix = []byte("PROXY ")
+ v1PrefixLen = len(v1Prefix)
+ v2Prefix = []byte("\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A")
+ v2PrefixLen = len(v2Prefix)
+)
+
+// Conn is used to wrap and underlying connection which is speaking the
+// Proxy Protocol. RemoteAddr() will return the address of the client
+// instead of the proxy address.
+type Conn struct {
+ bufReader *bufio.Reader
+ conn net.Conn
+ localAddr net.Addr
+ remoteAddr net.Addr
+ once sync.Once
+ proxyHeaderTimeout time.Duration
+ acceptUnknown bool
+}
+
+// NewConn is used to wrap a net.Conn speaking the proxy protocol into
+// a proxyprotocol.Conn
+func NewConn(conn net.Conn, timeout time.Duration) *Conn {
+ pConn := &Conn{
+ bufReader: bufio.NewReader(conn),
+ conn: conn,
+ proxyHeaderTimeout: timeout,
+ }
+ return pConn
+}
+
+// Read reads data from the connection.
+// It will initially read the proxy protocol header.
+// If there is an error parsing the header, it is returned and the socket is closed.
+func (p *Conn) Read(b []byte) (int, error) {
+ if err := p.readProxyHeaderOnce(); err != nil {
+ return 0, err
+ }
+ return p.bufReader.Read(b)
+}
+
+// ReadFrom reads data from a provided reader and copies it to the connection.
+func (p *Conn) ReadFrom(r io.Reader) (int64, error) {
+ if err := p.readProxyHeaderOnce(); err != nil {
+ return 0, err
+ }
+ if rf, ok := p.conn.(io.ReaderFrom); ok {
+ return rf.ReadFrom(r)
+ }
+ return io.Copy(p.conn, r)
+}
+
+// WriteTo reads data from the connection and writes it to the writer.
+// It will initially read the proxy protocol header.
+// If there is an error parsing the header, it is returned and the socket is closed.
+func (p *Conn) WriteTo(w io.Writer) (int64, error) {
+ if err := p.readProxyHeaderOnce(); err != nil {
+ return 0, err
+ }
+ return p.bufReader.WriteTo(w)
+}
+
+// Write writes data to the connection.
+// Write can be made to time out and return an error after a fixed
+// time limit; see SetDeadline and SetWriteDeadline.
+func (p *Conn) Write(b []byte) (int, error) {
+ if err := p.readProxyHeaderOnce(); err != nil {
+ return 0, err
+ }
+ return p.conn.Write(b)
+}
+
+// Close closes the connection.
+// Any blocked Read or Write operations will be unblocked and return errors.
+func (p *Conn) Close() error {
+ return p.conn.Close()
+}
+
+// LocalAddr returns the local network address.
+func (p *Conn) LocalAddr() net.Addr {
+ _ = p.readProxyHeaderOnce()
+ if p.localAddr != nil {
+ return p.localAddr
+ }
+ return p.conn.LocalAddr()
+}
+
+// RemoteAddr returns the address of the client if the proxy
+// protocol is being used, otherwise just returns the address of
+// the socket peer. If there is an error parsing the header, the
+// address of the client is not returned, and the socket is closed.
+// One implication of this is that the call could block if the
+// client is slow. Using a Deadline is recommended if this is called
+// before Read()
+func (p *Conn) RemoteAddr() net.Addr {
+ _ = p.readProxyHeaderOnce()
+ if p.remoteAddr != nil {
+ return p.remoteAddr
+ }
+ return p.conn.RemoteAddr()
+}
+
+// SetDeadline sets the read and write deadlines associated
+// with the connection. It is equivalent to calling both
+// SetReadDeadline and SetWriteDeadline.
+//
+// A deadline is an absolute time after which I/O operations
+// fail instead of blocking. The deadline applies to all future
+// and pending I/O, not just the immediately following call to
+// Read or Write. After a deadline has been exceeded, the
+// connection can be refreshed by setting a deadline in the future.
+//
+// If the deadline is exceeded a call to Read or Write or to other
+// I/O methods will return an error that wraps os.ErrDeadlineExceeded.
+// This can be tested using errors.Is(err, os.ErrDeadlineExceeded).
+// The error's Timeout method will return true, but note that there
+// are other possible errors for which the Timeout method will
+// return true even if the deadline has not been exceeded.
+//
+// An idle timeout can be implemented by repeatedly extending
+// the deadline after successful Read or Write calls.
+//
+// A zero value for t means I/O operations will not time out.
+func (p *Conn) SetDeadline(t time.Time) error {
+ return p.conn.SetDeadline(t)
+}
+
+// SetReadDeadline sets the deadline for future Read calls
+// and any currently-blocked Read call.
+// A zero value for t means Read will not time out.
+func (p *Conn) SetReadDeadline(t time.Time) error {
+ return p.conn.SetReadDeadline(t)
+}
+
+// SetWriteDeadline sets the deadline for future Write calls
+// and any currently-blocked Write call.
+// Even if write times out, it may return n > 0, indicating that
+// some of the data was successfully written.
+// A zero value for t means Write will not time out.
+func (p *Conn) SetWriteDeadline(t time.Time) error {
+ return p.conn.SetWriteDeadline(t)
+}
+
+// readProxyHeaderOnce will ensure that the proxy header has been read
+func (p *Conn) readProxyHeaderOnce() (err error) {
+ p.once.Do(func() {
+ if err = p.readProxyHeader(); err != nil && err != io.EOF {
+ log.Error("Failed to read proxy prefix: %v", err)
+ p.Close()
+ p.bufReader = bufio.NewReader(p.conn)
+ }
+ })
+ return err
+}
+
+func (p *Conn) readProxyHeader() error {
+ if p.proxyHeaderTimeout != 0 {
+ readDeadLine := time.Now().Add(p.proxyHeaderTimeout)
+ _ = p.conn.SetReadDeadline(readDeadLine)
+ defer func() {
+ _ = p.conn.SetReadDeadline(time.Time{})
+ }()
+ }
+
+ inp, err := p.bufReader.Peek(v1PrefixLen)
+ if err != nil {
+ return err
+ }
+
+ if bytes.Equal(inp, v1Prefix) {
+ return p.readV1ProxyHeader()
+ }
+
+ inp, err = p.bufReader.Peek(v2PrefixLen)
+ if err != nil {
+ return err
+ }
+ if bytes.Equal(inp, v2Prefix) {
+ return p.readV2ProxyHeader()
+ }
+
+ return &ErrBadHeader{inp}
+}
+
+func (p *Conn) readV2ProxyHeader() error {
+ // The binary header format starts with a constant 12 bytes block containing the
+ // protocol signature :
+ //
+ // \x0D \x0A \x0D \x0A \x00 \x0D \x0A \x51 \x55 \x49 \x54 \x0A
+ //
+ // Note that this block contains a null byte at the 5th position, so it must not
+ // be handled as a null-terminated string.
+
+ if _, err := p.bufReader.Discard(v2PrefixLen); err != nil {
+ // This shouldn't happen as we have already asserted that there should be enough in the buffer
+ return err
+ }
+
+ // The next byte (the 13th one) is the protocol version and command.
+ version, err := p.bufReader.ReadByte()
+ if err != nil {
+ return err
+ }
+
+ // The 14th byte contains the transport protocol and address family.otocol.
+ familyByte, err := p.bufReader.ReadByte()
+ if err != nil {
+ return err
+ }
+
+ // The 15th and 16th bytes is the address length in bytes in network endian order.
+ var addressLen uint16
+ if err := binary.Read(p.bufReader, binary.BigEndian, &addressLen); err != nil {
+ return err
+ }
+
+ // Now handle the version byte: (14th byte).
+ // The highest four bits contains the version. As of this specification, it must
+ // always be sent as \x2 and the receiver must only accept this value.
+ if version>>4 != 0x2 {
+ return &ErrBadHeader{append(v2Prefix, version, familyByte, uint8(addressLen>>8), uint8(addressLen&0xff))}
+ }
+
+ // The lowest four bits represents the command :
+ switch version & 0xf {
+ case 0x0:
+ // - \x0 : LOCAL : the connection was established on purpose by the proxy
+ // without being relayed. The connection endpoints are the sender and the
+ // receiver. Such connections exist when the proxy sends health-checks to the
+ // server. The receiver must accept this connection as valid and must use the
+ // real connection endpoints and discard the protocol block including the
+ // family which is ignored.
+
+ // We therefore ignore the 14th, 15th and 16th bytes
+ p.remoteAddr = p.conn.LocalAddr()
+ p.localAddr = p.conn.RemoteAddr()
+ return nil
+ case 0x1:
+ // - \x1 : PROXY : the connection was established on behalf of another node,
+ // and reflects the original connection endpoints. The receiver must then use
+ // the information provided in the protocol block to get original the address.
+ default:
+ // - other values are unassigned and must not be emitted by senders. Receivers
+ // must drop connections presenting unexpected values here.
+ return &ErrBadHeader{append(v2Prefix, version, familyByte, uint8(addressLen>>8), uint8(addressLen&0xff))}
+ }
+
+ // Now handle the familyByte byte: (15th byte).
+ // The highest 4 bits contain the address family, the lowest 4 bits contain the protocol
+
+ // The address family maps to the original socket family without necessarily
+ // matching the values internally used by the system. It may be one of :
+ //
+ // - 0x0 : AF_UNSPEC : the connection is forwarded for an unknown, unspecified
+ // or unsupported protocol. The sender should use this family when sending
+ // LOCAL commands or when dealing with unsupported protocol families. The
+ // receiver is free to accept the connection anyway and use the real endpoint
+ // addresses or to reject it. The receiver should ignore address information.
+ //
+ // - 0x1 : AF_INET : the forwarded connection uses the AF_INET address family
+ // (IPv4). The addresses are exactly 4 bytes each in network byte order,
+ // followed by transport protocol information (typically ports).
+ //
+ // - 0x2 : AF_INET6 : the forwarded connection uses the AF_INET6 address family
+ // (IPv6). The addresses are exactly 16 bytes each in network byte order,
+ // followed by transport protocol information (typically ports).
+ //
+ // - 0x3 : AF_UNIX : the forwarded connection uses the AF_UNIX address family
+ // (UNIX). The addresses are exactly 108 bytes each.
+ //
+ // - other values are unspecified and must not be emitted in version 2 of this
+ // protocol and must be rejected as invalid by receivers.
+
+ // The transport protocol is specified in the lowest 4 bits of the 14th byte :
+ //
+ // - 0x0 : UNSPEC : the connection is forwarded for an unknown, unspecified
+ // or unsupported protocol. The sender should use this family when sending
+ // LOCAL commands or when dealing with unsupported protocol families. The
+ // receiver is free to accept the connection anyway and use the real endpoint
+ // addresses or to reject it. The receiver should ignore address information.
+ //
+ // - 0x1 : STREAM : the forwarded connection uses a SOCK_STREAM protocol (eg:
+ // TCP or UNIX_STREAM). When used with AF_INET/AF_INET6 (TCP), the addresses
+ // are followed by the source and destination ports represented on 2 bytes
+ // each in network byte order.
+ //
+ // - 0x2 : DGRAM : the forwarded connection uses a SOCK_DGRAM protocol (eg:
+ // UDP or UNIX_DGRAM). When used with AF_INET/AF_INET6 (UDP), the addresses
+ // are followed by the source and destination ports represented on 2 bytes
+ // each in network byte order.
+ //
+ // - other values are unspecified and must not be emitted in version 2 of this
+ // protocol and must be rejected as invalid by receivers.
+
+ if familyByte>>4 == 0x0 || familyByte&0xf == 0x0 {
+ // - hi 0x0 : AF_UNSPEC : the connection is forwarded for an unknown address type
+ // or
+ // - lo 0x0 : UNSPEC : the connection is forwarded for an unspecified protocol
+ if !p.acceptUnknown {
+ p.conn.Close()
+ return &ErrBadHeader{append(v2Prefix, version, familyByte, uint8(addressLen>>8), uint8(addressLen&0xff))}
+ }
+ p.remoteAddr = p.conn.LocalAddr()
+ p.localAddr = p.conn.RemoteAddr()
+ _, err = p.bufReader.Discard(int(addressLen))
+ return err
+ }
+
+ // other address or protocol
+ if (familyByte>>4) > 0x3 || (familyByte&0xf) > 0x2 {
+ return &ErrBadHeader{append(v2Prefix, version, familyByte, uint8(addressLen>>8), uint8(addressLen&0xff))}
+ }
+
+ // Handle AF_UNIX addresses
+ if familyByte>>4 == 0x3 {
+ // - \x31 : UNIX stream : the forwarded connection uses SOCK_STREAM over the
+ // AF_UNIX protocol family. Address length is 2*108 = 216 bytes.
+ // - \x32 : UNIX datagram : the forwarded connection uses SOCK_DGRAM over the
+ // AF_UNIX protocol family. Address length is 2*108 = 216 bytes.
+ if addressLen != 216 {
+ return &ErrBadHeader{append(v2Prefix, version, familyByte, uint8(addressLen>>8), uint8(addressLen&0xff))}
+ }
+ remoteName := make([]byte, 108)
+ localName := make([]byte, 108)
+ if _, err := p.bufReader.Read(remoteName); err != nil {
+ return err
+ }
+ if _, err := p.bufReader.Read(localName); err != nil {
+ return err
+ }
+ protocol := "unix"
+ if familyByte&0xf == 2 {
+ protocol = "unixgram"
+ }
+
+ p.remoteAddr = &net.UnixAddr{
+ Name: string(remoteName),
+ Net: protocol,
+ }
+ p.localAddr = &net.UnixAddr{
+ Name: string(localName),
+ Net: protocol,
+ }
+ return nil
+ }
+
+ var remoteIP []byte
+ var localIP []byte
+ var remotePort uint16
+ var localPort uint16
+
+ if familyByte>>4 == 0x1 {
+ // AF_INET
+ // - \x11 : TCP over IPv4 : the forwarded connection uses TCP over the AF_INET
+ // protocol family. Address length is 2*4 + 2*2 = 12 bytes.
+ // - \x12 : UDP over IPv4 : the forwarded connection uses UDP over the AF_INET
+ // protocol family. Address length is 2*4 + 2*2 = 12 bytes.
+ if addressLen != 12 {
+ return &ErrBadHeader{append(v2Prefix, version, familyByte, uint8(addressLen>>8), uint8(addressLen&0xff))}
+ }
+
+ remoteIP = make([]byte, 4)
+ localIP = make([]byte, 4)
+ } else {
+ // AF_INET6
+ // - \x21 : TCP over IPv6 : the forwarded connection uses TCP over the AF_INET6
+ // protocol family. Address length is 2*16 + 2*2 = 36 bytes.
+ // - \x22 : UDP over IPv6 : the forwarded connection uses UDP over the AF_INET6
+ // protocol family. Address length is 2*16 + 2*2 = 36 bytes.
+ if addressLen != 36 {
+ return &ErrBadHeader{append(v2Prefix, version, familyByte, uint8(addressLen>>8), uint8(addressLen&0xff))}
+ }
+
+ remoteIP = make([]byte, 16)
+ localIP = make([]byte, 16)
+ }
+
+ if _, err := p.bufReader.Read(remoteIP); err != nil {
+ return err
+ }
+ if _, err := p.bufReader.Read(localIP); err != nil {
+ return err
+ }
+ if err := binary.Read(p.bufReader, binary.BigEndian, &remotePort); err != nil {
+ return err
+ }
+ if err := binary.Read(p.bufReader, binary.BigEndian, &localPort); err != nil {
+ return err
+ }
+
+ if familyByte&0xf == 1 {
+ p.remoteAddr = &net.TCPAddr{
+ IP: remoteIP,
+ Port: int(remotePort),
+ }
+ p.localAddr = &net.TCPAddr{
+ IP: localIP,
+ Port: int(localPort),
+ }
+ } else {
+ p.remoteAddr = &net.UDPAddr{
+ IP: remoteIP,
+ Port: int(remotePort),
+ }
+ p.localAddr = &net.UDPAddr{
+ IP: localIP,
+ Port: int(localPort),
+ }
+ }
+ return nil
+}
+
+func (p *Conn) readV1ProxyHeader() error {
+ // Read until a newline
+ header, err := p.bufReader.ReadString('\n')
+ if err != nil {
+ p.conn.Close()
+ return err
+ }
+
+ if header[len(header)-2] != '\r' {
+ return &ErrBadHeader{[]byte(header)}
+ }
+
+ // Strip the carriage return and new line
+ header = header[:len(header)-2]
+
+ // Split on spaces, should be (PROXY )
+ parts := strings.Split(header, " ")
+ if len(parts) < 2 {
+ p.conn.Close()
+ return &ErrBadHeader{[]byte(header)}
+ }
+
+ // Verify the type is known
+ switch parts[1] {
+ case "UNKNOWN":
+ if !p.acceptUnknown || len(parts) != 2 {
+ p.conn.Close()
+ return &ErrBadHeader{[]byte(header)}
+ }
+ p.remoteAddr = p.conn.LocalAddr()
+ p.localAddr = p.conn.RemoteAddr()
+ return nil
+ case "TCP4":
+ case "TCP6":
+ default:
+ p.conn.Close()
+ return &ErrBadAddressType{parts[1]}
+ }
+
+ if len(parts) != 6 {
+ p.conn.Close()
+ return &ErrBadHeader{[]byte(header)}
+ }
+
+ // Parse out the remote address
+ ip := net.ParseIP(parts[2])
+ if ip == nil {
+ p.conn.Close()
+ return &ErrBadRemote{parts[2], parts[4]}
+ }
+ port, err := strconv.Atoi(parts[4])
+ if err != nil {
+ p.conn.Close()
+ return &ErrBadRemote{parts[2], parts[4]}
+ }
+ p.remoteAddr = &net.TCPAddr{IP: ip, Port: port}
+
+ // Parse out the destination address
+ ip = net.ParseIP(parts[3])
+ if ip == nil {
+ p.conn.Close()
+ return &ErrBadLocal{parts[3], parts[5]}
+ }
+ port, err = strconv.Atoi(parts[5])
+ if err != nil {
+ p.conn.Close()
+ return &ErrBadLocal{parts[3], parts[5]}
+ }
+ p.localAddr = &net.TCPAddr{IP: ip, Port: port}
+
+ return nil
+}
diff --git a/modules/proxyprotocol/errors.go b/modules/proxyprotocol/errors.go
new file mode 100644
index 0000000000..2acf9d84b0
--- /dev/null
+++ b/modules/proxyprotocol/errors.go
@@ -0,0 +1,45 @@
+// Copyright 2020 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package proxyprotocol
+
+import "fmt"
+
+// ErrBadHeader is an error demonstrating a bad proxy header
+type ErrBadHeader struct {
+ Header []byte
+}
+
+func (e *ErrBadHeader) Error() string {
+ return fmt.Sprintf("Unexpected proxy header: %v", e.Header)
+}
+
+// ErrBadAddressType is an error demonstrating a bad proxy header with bad Address type
+type ErrBadAddressType struct {
+ Address string
+}
+
+func (e *ErrBadAddressType) Error() string {
+ return fmt.Sprintf("Unexpected proxy header address type: %s", e.Address)
+}
+
+// ErrBadRemote is an error demonstrating a bad proxy header with bad Remote
+type ErrBadRemote struct {
+ IP string
+ Port string
+}
+
+func (e *ErrBadRemote) Error() string {
+ return fmt.Sprintf("Unexpected proxy header remote IP and port: %s %s", e.IP, e.Port)
+}
+
+// ErrBadLocal is an error demonstrating a bad proxy header with bad Local
+type ErrBadLocal struct {
+ IP string
+ Port string
+}
+
+func (e *ErrBadLocal) Error() string {
+ return fmt.Sprintf("Unexpected proxy header local IP and port: %s %s", e.IP, e.Port)
+}
diff --git a/modules/proxyprotocol/listener.go b/modules/proxyprotocol/listener.go
new file mode 100644
index 0000000000..64d9b323e5
--- /dev/null
+++ b/modules/proxyprotocol/listener.go
@@ -0,0 +1,47 @@
+// Copyright 2020 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package proxyprotocol
+
+import (
+ "net"
+ "time"
+)
+
+// Listener is used to wrap an underlying listener,
+// whose connections may be using the HAProxy Proxy Protocol (version 1 or 2).
+// If the connection is using the protocol, the RemoteAddr() will return
+// the correct client address.
+//
+// Optionally define ProxyHeaderTimeout to set a maximum time to
+// receive the Proxy Protocol Header. Zero means no timeout.
+type Listener struct {
+ Listener net.Listener
+ ProxyHeaderTimeout time.Duration
+ AcceptUnknown bool // allow PROXY UNKNOWN
+}
+
+// Accept implements the Accept method in the Listener interface
+// it waits for the next call and returns a wrapped Conn.
+func (p *Listener) Accept() (net.Conn, error) {
+ // Get the underlying connection
+ conn, err := p.Listener.Accept()
+ if err != nil {
+ return nil, err
+ }
+
+ newConn := NewConn(conn, p.ProxyHeaderTimeout)
+ newConn.acceptUnknown = p.AcceptUnknown
+ return newConn, nil
+}
+
+// Close closes the underlying listener.
+func (p *Listener) Close() error {
+ return p.Listener.Close()
+}
+
+// Addr returns the underlying listener's network address.
+func (p *Listener) Addr() net.Addr {
+ return p.Listener.Addr()
+}
diff --git a/modules/proxyprotocol/util.go b/modules/proxyprotocol/util.go
new file mode 100644
index 0000000000..b12771b686
--- /dev/null
+++ b/modules/proxyprotocol/util.go
@@ -0,0 +1,15 @@
+// Copyright 2020 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package proxyprotocol
+
+import "io"
+
+var localHeader = append(v2Prefix, '\x20', '\x00', '\x00', '\x00', '\x00')
+
+// WriteLocalHeader will write the ProxyProtocol Header for a local connection to the provided writer
+func WriteLocalHeader(w io.Writer) error {
+ _, err := w.Write(localHeader)
+ return err
+}
diff --git a/modules/public/public.go b/modules/public/public.go
index 7804e945e7..ac1d80c860 100644
--- a/modules/public/public.go
+++ b/modules/public/public.go
@@ -11,6 +11,7 @@ import (
"path/filepath"
"strings"
+ "code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/httpcache"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
@@ -83,11 +84,11 @@ func AssetsHandlerFunc(opts *Options) http.HandlerFunc {
}
// parseAcceptEncoding parse Accept-Encoding: deflate, gzip;q=1.0, *;q=0.5 as compress methods
-func parseAcceptEncoding(val string) map[string]bool {
+func parseAcceptEncoding(val string) container.Set[string] {
parts := strings.Split(val, ";")
- types := make(map[string]bool)
+ types := make(container.Set[string])
for _, v := range strings.Split(parts[0], ",") {
- types[strings.TrimSpace(v)] = true
+ types.Add(strings.TrimSpace(v))
}
return types
}
diff --git a/modules/public/public_test.go b/modules/public/public_test.go
index 430e734564..8b58d6af33 100644
--- a/modules/public/public_test.go
+++ b/modules/public/public_test.go
@@ -7,28 +7,23 @@ package public
import (
"testing"
+ "code.gitea.io/gitea/modules/container"
+
"github.com/stretchr/testify/assert"
)
func TestParseAcceptEncoding(t *testing.T) {
kases := []struct {
Header string
- Expected map[string]bool
+ Expected container.Set[string]
}{
{
- Header: "deflate, gzip;q=1.0, *;q=0.5",
- Expected: map[string]bool{
- "deflate": true,
- "gzip": true,
- },
+ Header: "deflate, gzip;q=1.0, *;q=0.5",
+ Expected: container.SetOf("deflate", "gzip"),
},
{
- Header: " gzip, deflate, br",
- Expected: map[string]bool{
- "deflate": true,
- "gzip": true,
- "br": true,
- },
+ Header: " gzip, deflate, br",
+ Expected: container.SetOf("deflate", "gzip", "br"),
},
}
diff --git a/modules/public/serve_static.go b/modules/public/serve_static.go
index 9666880adf..10120bf85d 100644
--- a/modules/public/serve_static.go
+++ b/modules/public/serve_static.go
@@ -60,7 +60,7 @@ func AssetIsDir(name string) (bool, error) {
// serveContent serve http content
func serveContent(w http.ResponseWriter, req *http.Request, fi os.FileInfo, modtime time.Time, content io.ReadSeeker) {
encodings := parseAcceptEncoding(req.Header.Get("Accept-Encoding"))
- if encodings["gzip"] {
+ if encodings.Contains("gzip") {
if cf, ok := fi.(*vfsgen۰CompressedFileInfo); ok {
rdGzip := bytes.NewReader(cf.GzipBytes())
// all static files are managed by Gitea, so we can make sure every file has the correct ext name
diff --git a/modules/queue/queue_disk_channel_test.go b/modules/queue/queue_disk_channel_test.go
index 22b4f0f452..b1820e73ac 100644
--- a/modules/queue/queue_disk_channel_test.go
+++ b/modules/queue/queue_disk_channel_test.go
@@ -5,13 +5,11 @@
package queue
import (
- "os"
"sync"
"testing"
"time"
"code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/util"
"github.com/stretchr/testify/assert"
)
@@ -33,9 +31,7 @@ func TestPersistableChannelQueue(t *testing.T) {
queueShutdown := []func(){}
queueTerminate := []func(){}
- tmpDir, err := os.MkdirTemp("", "persistable-channel-queue-test-data")
- assert.NoError(t, err)
- defer util.RemoveAll(tmpDir)
+ tmpDir := t.TempDir()
queue, err := NewPersistableChannelQueue(handle, PersistableChannelQueueConfiguration{
DataDir: tmpDir,
@@ -223,9 +219,7 @@ func TestPersistableChannelQueue_Pause(t *testing.T) {
queueTerminate := []func(){}
terminated := make(chan struct{})
- tmpDir, err := os.MkdirTemp("", "persistable-channel-queue-pause-test-data")
- assert.NoError(t, err)
- defer util.RemoveAll(tmpDir)
+ tmpDir := t.TempDir()
queue, err = NewPersistableChannelQueue(handle, PersistableChannelQueueConfiguration{
DataDir: tmpDir,
diff --git a/modules/queue/queue_disk_test.go b/modules/queue/queue_disk_test.go
index d2d8e135cb..b0835c896f 100644
--- a/modules/queue/queue_disk_test.go
+++ b/modules/queue/queue_disk_test.go
@@ -5,13 +5,10 @@
package queue
import (
- "os"
"sync"
"testing"
"time"
- "code.gitea.io/gitea/modules/util"
-
"github.com/stretchr/testify/assert"
)
@@ -30,9 +27,7 @@ func TestLevelQueue(t *testing.T) {
queueShutdown := []func(){}
queueTerminate := []func(){}
- tmpDir, err := os.MkdirTemp("", "level-queue-test-data")
- assert.NoError(t, err)
- defer util.RemoveAll(tmpDir)
+ tmpDir := t.TempDir()
queue, err := NewLevelQueue(handle, LevelQueueConfiguration{
ByteFIFOQueueConfiguration: ByteFIFOQueueConfiguration{
diff --git a/modules/queue/unique_queue_channel.go b/modules/queue/unique_queue_channel.go
index 6e8d37a20c..d1bf7239eb 100644
--- a/modules/queue/unique_queue_channel.go
+++ b/modules/queue/unique_queue_channel.go
@@ -12,6 +12,7 @@ import (
"sync/atomic"
"time"
+ "code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/log"
)
@@ -33,7 +34,7 @@ type ChannelUniqueQueueConfiguration ChannelQueueConfiguration
type ChannelUniqueQueue struct {
*WorkerPool
lock sync.Mutex
- table map[string]bool
+ table container.Set[string]
shutdownCtx context.Context
shutdownCtxCancel context.CancelFunc
terminateCtx context.Context
@@ -58,7 +59,7 @@ func NewChannelUniqueQueue(handle HandlerFunc, cfg, exemplar interface{}) (Queue
shutdownCtx, shutdownCtxCancel := context.WithCancel(terminateCtx)
queue := &ChannelUniqueQueue{
- table: map[string]bool{},
+ table: make(container.Set[string]),
shutdownCtx: shutdownCtx,
shutdownCtxCancel: shutdownCtxCancel,
terminateCtx: terminateCtx,
@@ -73,7 +74,7 @@ func NewChannelUniqueQueue(handle HandlerFunc, cfg, exemplar interface{}) (Queue
bs, _ := json.Marshal(datum)
queue.lock.Lock()
- delete(queue.table, string(bs))
+ queue.table.Remove(string(bs))
queue.lock.Unlock()
if u := handle(datum); u != nil {
@@ -127,16 +128,15 @@ func (q *ChannelUniqueQueue) PushFunc(data Data, fn func() error) error {
q.lock.Unlock()
}
}()
- if _, ok := q.table[string(bs)]; ok {
+ if !q.table.Add(string(bs)) {
return ErrAlreadyInQueue
}
// FIXME: We probably need to implement some sort of limit here
// If the downstream queue blocks this table will grow without limit
- q.table[string(bs)] = true
if fn != nil {
err := fn()
if err != nil {
- delete(q.table, string(bs))
+ q.table.Remove(string(bs))
return err
}
}
@@ -155,8 +155,7 @@ func (q *ChannelUniqueQueue) Has(data Data) (bool, error) {
q.lock.Lock()
defer q.lock.Unlock()
- _, has := q.table[string(bs)]
- return has, nil
+ return q.table.Contains(string(bs)), nil
}
// Flush flushes the channel with a timeout - the Flush worker will be registered as a flush worker with the manager
diff --git a/modules/references/references_test.go b/modules/references/references_test.go
index adf86a3c6c..507adadb1f 100644
--- a/modules/references/references_test.go
+++ b/modules/references/references_test.go
@@ -309,7 +309,7 @@ func TestRegExp_mentionPattern(t *testing.T) {
pat string
exp string
}{
- {"@Unknwon", "@Unknwon"},
+ {"@User", "@User"},
{"@ANT_123", "@ANT_123"},
{"@xxx-DiN0-z-A..uru..s-xxx", "@xxx-DiN0-z-A..uru..s-xxx"},
{" @lol ", "@lol"},
diff --git a/modules/repository/collaborator.go b/modules/repository/collaborator.go
new file mode 100644
index 0000000000..9d20a25890
--- /dev/null
+++ b/modules/repository/collaborator.go
@@ -0,0 +1,43 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+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"
+)
+
+func addCollaborator(ctx context.Context, repo *repo_model.Repository, u *user_model.User) error {
+ collaboration := &repo_model.Collaboration{
+ RepoID: repo.ID,
+ UserID: u.ID,
+ }
+
+ has, err := db.GetByBean(ctx, collaboration)
+ if err != nil {
+ return err
+ } else if has {
+ return nil
+ }
+ collaboration.Mode = perm.AccessModeWrite
+
+ if err = db.Insert(ctx, collaboration); err != nil {
+ return err
+ }
+
+ return access_model.RecalculateUserAccess(ctx, repo, u.ID)
+}
+
+// AddCollaborator adds new collaboration to a repository with default access mode.
+func AddCollaborator(repo *repo_model.Repository, u *user_model.User) error {
+ return db.WithTx(func(ctx context.Context) error {
+ return addCollaborator(ctx, repo, u)
+ })
+}
diff --git a/modules/repository/collaborator_test.go b/modules/repository/collaborator_test.go
new file mode 100644
index 0000000000..1b927aa3b6
--- /dev/null
+++ b/modules/repository/collaborator_test.go
@@ -0,0 +1,281 @@
+// Copyright 2019 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+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.GetOwner(db.DefaultContext))
+ user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: userID})
+ assert.NoError(t, AddCollaborator(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(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(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(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(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(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(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(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(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))
+ }
+}
diff --git a/modules/repository/commits_test.go b/modules/repository/commits_test.go
index 37181d2dcd..7bd741d0c8 100644
--- a/modules/repository/commits_test.go
+++ b/modules/repository/commits_test.go
@@ -11,8 +11,10 @@ import (
"time"
repo_model "code.gitea.io/gitea/models/repo"
+ system_model "code.gitea.io/gitea/models/system"
"code.gitea.io/gitea/models/unittest"
"code.gitea.io/gitea/modules/git"
+ "code.gitea.io/gitea/modules/setting"
"github.com/stretchr/testify/assert"
)
@@ -49,7 +51,7 @@ func TestPushCommits_ToAPIPayloadCommits(t *testing.T) {
}
pushCommits.HeadCommit = &PushCommit{Sha1: "69554a6"}
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 16}).(*repo_model.Repository)
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 16})
payloadCommits, headCommit, err := pushCommits.ToAPIPayloadCommits(git.DefaultContext, repo.RepoPath(), "/user2/repo16")
assert.NoError(t, err)
assert.Len(t, payloadCommits, 3)
@@ -100,6 +102,14 @@ func TestPushCommits_ToAPIPayloadCommits(t *testing.T) {
assert.EqualValues(t, []string{"readme.md"}, headCommit.Modified)
}
+func enableGravatar(t *testing.T) {
+ err := system_model.SetSettingNoVersion(system_model.KeyPictureDisableGravatar, "false")
+ assert.NoError(t, err)
+ setting.GravatarSource = "https://secure.gravatar.com/avatar"
+ err = system_model.Init()
+ assert.NoError(t, err)
+}
+
func TestPushCommits_AvatarLink(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
@@ -123,6 +133,8 @@ func TestPushCommits_AvatarLink(t *testing.T) {
},
}
+ enableGravatar(t)
+
assert.Equal(t,
"https://secure.gravatar.com/avatar/ab53a2911ddf9b4817ac01ddcd3d975f?d=identicon&s=84",
pushCommits.AvatarLink("user2@example.com"))
diff --git a/modules/repository/create.go b/modules/repository/create.go
index 9204d7e422..966a6a2f21 100644
--- a/modules/repository/create.go
+++ b/modules/repository/create.go
@@ -10,14 +10,18 @@ import (
"os"
"path"
"strings"
- "unicode/utf8"
"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"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
@@ -25,8 +29,150 @@ import (
"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 bool) (err error) {
+ if err = repo_model.IsUsableRepoName(repo.Name); err != nil {
+ return err
+ }
+
+ has, err := repo_model.IsRepositoryExist(ctx, u, repo.Name)
+ if err != nil {
+ return fmt.Errorf("IsRepositoryExist: %v", 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
+ units := make([]repo_model.RepoUnit, 0, len(unit.DefaultRepoUnits))
+ for _, tp := range unit.DefaultRepoUnits {
+ 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, DefaultMergeStyle: repo_model.MergeStyle(setting.Repository.PullRequest.DefaultMergeStyle), AllowRebaseUpdate: true},
+ })
+ } 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: %v", err)
+ }
+
+ if err = user_model.IncrUserRepoNum(ctx, u.ID); err != nil {
+ return fmt.Errorf("IncrUserRepoNum: %v", 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: %v", err)
+ }
+ for _, t := range teams {
+ if t.IncludesAllRepositories {
+ if err := models.AddRepository(ctx, t, repo); err != nil {
+ return fmt.Errorf("AddRepository: %v", err)
+ }
+ }
+ }
+
+ if isAdmin, err := access_model.IsUserRepoAdmin(ctx, repo, doer); err != nil {
+ return fmt.Errorf("IsUserRepoAdmin: %v", 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: %v", err)
+ }
+ if err = repo_model.ChangeCollaborationAccessModeCtx(ctx, repo, doer.ID, perm.AccessModeAdmin); err != nil {
+ return fmt.Errorf("ChangeCollaborationAccessModeCtx: %v", err)
+ }
+ }
+ } else if err = access_model.RecalculateAccesses(ctx, repo); err != nil {
+ // Organization automatically called this in AddRepository method.
+ return fmt.Errorf("RecalculateAccesses: %v", err)
+ }
+
+ if setting.Service.AutoWatchNewRepos {
+ if err = repo_model.WatchRepo(ctx, doer.ID, repo.ID, true); err != nil {
+ return fmt.Errorf("WatchRepo: %v", err)
+ }
+ }
+
+ if err = webhook.CopyDefaultWebhooksToRepo(ctx, repo.ID); err != nil {
+ return fmt.Errorf("CopyDefaultWebhooksToRepo: %v", err)
+ }
+
+ return nil
+}
+
+// CreateRepoOptions contains the create repository options
+type CreateRepoOptions struct {
+ Name string
+ Description string
+ OriginalURL string
+ GitServiceType api.GitServiceType
+ Gitignores string
+ IssueLabels string
+ License string
+ Readme string
+ DefaultBranch string
+ IsPrivate bool
+ IsMirror bool
+ IsTemplate bool
+ AutoInit bool
+ Status repo_model.RepositoryStatus
+ TrustModel repo_model.TrustModelType
+ MirrorInterval string
+}
+
// CreateRepository creates a repository for the user/organization.
-func CreateRepository(doer, u *user_model.User, opts models.CreateRepoOptions) (*repo_model.Repository, error) {
+func CreateRepository(doer, u *user_model.User, opts CreateRepoOptions) (*repo_model.Repository, error) {
if !doer.IsAdmin && !u.CanCreateRepo() {
return nil, repo_model.ErrReachLimitOfRepo{
Limit: u.MaxRepoCreation,
@@ -66,7 +212,7 @@ func CreateRepository(doer, u *user_model.User, opts models.CreateRepoOptions) (
var rollbackRepo *repo_model.Repository
if err := db.WithTx(func(ctx context.Context) error {
- if err := models.CreateRepository(ctx, doer, u, repo, false); err != nil {
+ if err := CreateRepositoryByExample(ctx, doer, u, repo, false); err != nil {
return err
}
@@ -190,13 +336,6 @@ func CheckDaemonExportOK(ctx context.Context, repo *repo_model.Repository) error
func UpdateRepository(ctx context.Context, repo *repo_model.Repository, visibilityChanged bool) (err error) {
repo.LowerName = strings.ToLower(repo.Name)
- if utf8.RuneCountInString(repo.Description) > 255 {
- repo.Description = string([]rune(repo.Description)[:255])
- }
- if utf8.RuneCountInString(repo.Website) > 255 {
- repo.Website = string([]rune(repo.Website)[:255])
- }
-
e := db.GetEngine(ctx)
if _, err = e.ID(repo.ID).AllCols().Update(repo); err != nil {
@@ -220,7 +359,7 @@ func UpdateRepository(ctx context.Context, repo *repo_model.Repository, visibili
// If repo has become private, we need to set its actions to private.
if repo.IsPrivate {
- _, err = e.Where("repo_id = ?", repo.ID).Cols("is_private").Update(&models.Action{
+ _, err = e.Where("repo_id = ?", repo.ID).Cols("is_private").Update(&activities_model.Action{
IsPrivate: true,
})
if err != nil {
diff --git a/modules/repository/create_test.go b/modules/repository/create_test.go
index 2a47e93631..3040782845 100644
--- a/modules/repository/create_test.go
+++ b/modules/repository/create_test.go
@@ -9,6 +9,7 @@ import (
"testing"
"code.gitea.io/gitea/models"
+ activities_model "code.gitea.io/gitea/models/activities"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/organization"
"code.gitea.io/gitea/models/perm"
@@ -24,7 +25,7 @@ func TestIncludesAllRepositoriesTeams(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
testTeamRepositories := func(teamID int64, repoIds []int64) {
- team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: teamID}).(*organization.Team)
+ team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: teamID})
assert.NoError(t, team.GetRepositoriesCtx(db.DefaultContext), "%s: GetRepositories", team.Name)
assert.Len(t, team.Repos, team.NumRepos, "%s: len repo", team.Name)
assert.Len(t, team.Repos, len(repoIds), "%s: repo count", team.Name)
@@ -56,7 +57,7 @@ func TestIncludesAllRepositoriesTeams(t *testing.T) {
// Create repos.
repoIds := make([]int64, 0)
for i := 0; i < 3; i++ {
- r, err := CreateRepository(user, org.AsUser(), models.CreateRepoOptions{Name: fmt.Sprintf("repo-%d", i)})
+ r, err := CreateRepository(user, org.AsUser(), CreateRepoOptions{Name: fmt.Sprintf("repo-%d", i)})
assert.NoError(t, err, "CreateRepository %d", i)
if r != nil {
repoIds = append(repoIds, r.ID)
@@ -118,7 +119,7 @@ func TestIncludesAllRepositoriesTeams(t *testing.T) {
}
// Create repo and check teams repositories.
- r, err := CreateRepository(user, org.AsUser(), models.CreateRepoOptions{Name: "repo-last"})
+ r, err := CreateRepository(user, org.AsUser(), CreateRepoOptions{Name: "repo-last"})
assert.NoError(t, err, "CreateRepository last")
if r != nil {
repoIds = append(repoIds, r.ID)
@@ -162,7 +163,7 @@ func TestUpdateRepositoryVisibilityChanged(t *testing.T) {
assert.NoError(t, err)
// Check visibility of action has become private
- act := models.Action{}
+ act := activities_model.Action{}
_, err = db.GetEngine(db.DefaultContext).ID(3).Get(&act)
assert.NoError(t, err)
diff --git a/modules/repository/generate.go b/modules/repository/generate.go
index 8f7b4c885d..4d76d33993 100644
--- a/modules/repository/generate.go
+++ b/modules/repository/generate.go
@@ -15,7 +15,6 @@ import (
"strings"
"time"
- "code.gitea.io/gitea/models"
git_model "code.gitea.io/gitea/models/git"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
@@ -321,7 +320,7 @@ func GenerateRepository(ctx context.Context, doer, owner *user_model.User, templ
TrustModel: templateRepo.TrustModel,
}
- if err = models.CreateRepository(ctx, doer, owner, generateRepo, false); err != nil {
+ if err = CreateRepositoryByExample(ctx, doer, owner, generateRepo, false); err != nil {
return nil, err
}
diff --git a/modules/repository/hooks.go b/modules/repository/hooks.go
index c2eb3a7c75..7bc77552bd 100644
--- a/modules/repository/hooks.go
+++ b/modules/repository/hooks.go
@@ -8,6 +8,7 @@ import (
"fmt"
"os"
"path/filepath"
+ "runtime"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/setting"
@@ -153,6 +154,10 @@ func createDelegateHooks(repoPath string) (err error) {
}
func checkExecutable(filename string) bool {
+ // windows has no concept of a executable bit
+ if runtime.GOOS == "windows" {
+ return true
+ }
fileInfo, err := os.Stat(filename)
if err != nil {
return false
diff --git a/modules/repository/init.go b/modules/repository/init.go
index e984697cda..37ed0748b4 100644
--- a/modules/repository/init.go
+++ b/modules/repository/init.go
@@ -15,7 +15,6 @@ import (
"strings"
"time"
- "code.gitea.io/gitea/models"
issues_model "code.gitea.io/gitea/models/issues"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
@@ -214,7 +213,7 @@ func LoadRepoConfig() {
Licenses = sortedLicenses
}
-func prepareRepoCommit(ctx context.Context, repo *repo_model.Repository, tmpDir, repoPath string, opts models.CreateRepoOptions) error {
+func prepareRepoCommit(ctx context.Context, repo *repo_model.Repository, tmpDir, repoPath string, opts CreateRepoOptions) error {
commitTimeStr := time.Now().Format(time.RFC3339)
authorSig := repo.Owner.NewGitSig()
@@ -387,7 +386,7 @@ func checkInitRepository(ctx context.Context, owner, name string) (err error) {
}
// InitRepository initializes README and .gitignore if needed.
-func initRepository(ctx context.Context, repoPath string, u *user_model.User, repo *repo_model.Repository, opts models.CreateRepoOptions) (err error) {
+func initRepository(ctx context.Context, repoPath string, u *user_model.User, repo *repo_model.Repository, opts CreateRepoOptions) (err error) {
if err = checkInitRepository(ctx, repo.OwnerName, repo.Name); err != nil {
return err
}
diff --git a/modules/repository/repo.go b/modules/repository/repo.go
index 436045146a..b01be322d2 100644
--- a/modules/repository/repo.go
+++ b/modules/repository/repo.go
@@ -13,12 +13,12 @@ import (
"strings"
"time"
- "code.gitea.io/gitea/models"
"code.gitea.io/gitea/models/db"
git_model "code.gitea.io/gitea/models/git"
"code.gitea.io/gitea/models/organization"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/lfs"
"code.gitea.io/gitea/modules/log"
@@ -31,8 +31,8 @@ import (
)
/*
- GitHub, GitLab, Gogs: *.wiki.git
- BitBucket: *.git/wiki
+GitHub, GitLab, Gogs: *.wiki.git
+BitBucket: *.git/wiki
*/
var commonWikiURLSuffixes = []string{".wiki.git", ".git/wiki"}
@@ -276,15 +276,15 @@ func SyncReleasesWithTags(repo *repo_model.Repository, gitRepo *git.Repository)
return pullMirrorReleaseSync(repo, gitRepo)
}
- existingRelTags := make(map[string]struct{})
- opts := models.FindReleasesOptions{
+ existingRelTags := make(container.Set[string])
+ opts := repo_model.FindReleasesOptions{
IncludeDrafts: true,
IncludeTags: true,
ListOptions: db.ListOptions{PageSize: 50},
}
for page := 1; ; page++ {
opts.Page = page
- rels, err := models.GetReleasesByRepoID(repo.ID, opts)
+ rels, err := repo_model.GetReleasesByRepoID(repo.ID, opts)
if err != nil {
return fmt.Errorf("unable to GetReleasesByRepoID in Repo[%d:%s/%s]: %w", repo.ID, repo.OwnerName, repo.Name, err)
}
@@ -300,18 +300,18 @@ func SyncReleasesWithTags(repo *repo_model.Repository, gitRepo *git.Repository)
return fmt.Errorf("unable to GetTagCommitID for %q in Repo[%d:%s/%s]: %w", rel.TagName, repo.ID, repo.OwnerName, repo.Name, err)
}
if git.IsErrNotExist(err) || commitID != rel.Sha1 {
- if err := models.PushUpdateDeleteTag(repo, rel.TagName); err != nil {
+ if err := repo_model.PushUpdateDeleteTag(repo, rel.TagName); err != nil {
return fmt.Errorf("unable to PushUpdateDeleteTag: %q in Repo[%d:%s/%s]: %w", rel.TagName, repo.ID, repo.OwnerName, repo.Name, err)
}
} else {
- existingRelTags[strings.ToLower(rel.TagName)] = struct{}{}
+ existingRelTags.Add(strings.ToLower(rel.TagName))
}
}
}
_, err := gitRepo.WalkReferences(git.ObjectTag, 0, 0, func(sha1, refname string) error {
tagName := strings.TrimPrefix(refname, git.TagPrefix)
- if _, ok := existingRelTags[strings.ToLower(tagName)]; ok {
+ if existingRelTags.Contains(strings.ToLower(tagName)) {
return nil
}
@@ -359,7 +359,7 @@ func PushUpdateAddTag(repo *repo_model.Repository, gitRepo *git.Repository, tagN
return fmt.Errorf("unable to get CommitsCount: %w", err)
}
- rel := models.Release{
+ rel := repo_model.Release{
RepoID: repo.ID,
TagName: tagName,
LowerTagName: strings.ToLower(tagName),
@@ -372,7 +372,7 @@ func PushUpdateAddTag(repo *repo_model.Repository, gitRepo *git.Repository, tagN
rel.PublisherID = author.ID
}
- return models.SaveOrUpdateTag(repo, &rel)
+ return repo_model.SaveOrUpdateTag(repo, &rel)
}
// StoreMissingLfsObjectsInRepository downloads missing LFS objects
@@ -489,14 +489,14 @@ func pullMirrorReleaseSync(repo *repo_model.Repository, gitRepo *git.Repository)
//
// clear out existing releases
//
- if _, err := db.DeleteByBean(ctx, &models.Release{RepoID: repo.ID}); err != nil {
+ if _, err := db.DeleteByBean(ctx, &repo_model.Release{RepoID: repo.ID}); err != nil {
return fmt.Errorf("unable to clear releases for pull-mirror Repo[%d:%s/%s]: %w", repo.ID, repo.OwnerName, repo.Name, err)
}
//
// make release set identical to upstream tags
//
for _, tag := range tags {
- release := models.Release{
+ release := repo_model.Release{
RepoID: repo.ID,
TagName: tag.Name,
LowerTagName: strings.ToLower(tag.Name),
diff --git a/modules/setting/database.go b/modules/setting/database.go
index 8fdd5f2bcb..af4e780d76 100644
--- a/modules/setting/database.go
+++ b/modules/setting/database.go
@@ -39,6 +39,7 @@ var (
LogSQL bool
Charset string
Timeout int // seconds
+ SQLiteJournalMode string
UseSQLite3 bool
UseMySQL bool
UseMSSQL bool
@@ -91,6 +92,8 @@ func InitDBConfig() {
Database.Path = sec.Key("PATH").MustString(filepath.Join(AppDataPath, "gitea.db"))
Database.Timeout = sec.Key("SQLITE_TIMEOUT").MustInt(500)
+ Database.SQLiteJournalMode = sec.Key("SQLITE_JOURNAL_MODE").MustString("")
+
Database.MaxIdleConns = sec.Key("MAX_IDLE_CONNS").MustInt(2)
if Database.UseMySQL {
Database.ConnMaxLifetime = sec.Key("CONN_MAX_LIFETIME").MustDuration(3 * time.Second)
@@ -136,7 +139,12 @@ func DBConnStr() (string, error) {
if err := os.MkdirAll(path.Dir(Database.Path), os.ModePerm); err != nil {
return "", fmt.Errorf("Failed to create directories: %v", err)
}
- connStr = fmt.Sprintf("file:%s?cache=shared&mode=rwc&_busy_timeout=%d&_txlock=immediate", Database.Path, Database.Timeout)
+ journalMode := ""
+ if Database.SQLiteJournalMode != "" {
+ journalMode = "&_journal_mode=" + Database.SQLiteJournalMode
+ }
+ connStr = fmt.Sprintf("file:%s?cache=shared&mode=rwc&_busy_timeout=%d&_txlock=immediate%s",
+ Database.Path, Database.Timeout, journalMode)
default:
return "", fmt.Errorf("Unknown database type: %s", Database.Type)
}
diff --git a/modules/setting/lfs.go b/modules/setting/lfs.go
index 3179a67ce7..686b043657 100644
--- a/modules/setting/lfs.go
+++ b/modules/setting/lfs.go
@@ -62,7 +62,7 @@ func newLFSService() {
}
// Save secret
- CreateOrAppendToCustomConf(func(cfg *ini.File) {
+ CreateOrAppendToCustomConf("server.LFS_JWT_SECRET", func(cfg *ini.File) {
cfg.Section("server").Key("LFS_JWT_SECRET").SetValue(LFS.JWTSecretBase64)
})
}
diff --git a/modules/setting/mailer.go b/modules/setting/mailer.go
index 8a26f8b0c4..d6f1dae0f7 100644
--- a/modules/setting/mailer.go
+++ b/modules/setting/mailer.go
@@ -5,7 +5,9 @@
package setting
import (
+ "net"
"net/mail"
+ "strings"
"time"
"code.gitea.io/gitea/modules/log"
@@ -23,18 +25,19 @@ type Mailer struct {
FromName string
FromEmail string
SendAsPlainText bool
- MailerType string
SubjectPrefix string
// SMTP sender
- Host string
- User, Passwd string
- DisableHelo bool
- HeloHostname string
- SkipVerify bool
- UseCertificate bool
- CertFile, KeyFile string
- IsTLSEnabled bool
+ Protocol string
+ SMTPAddr string
+ SMTPPort string
+ User, Passwd string
+ EnableHelo bool
+ HeloHostname string
+ ForceTrustServerCert bool
+ UseClientCert bool
+ ClientCertFile string
+ ClientKeyFile string
// Sendmail sender
SendmailPath string
@@ -56,19 +59,19 @@ func newMailService() {
MailService = &Mailer{
Name: sec.Key("NAME").MustString(AppName),
SendAsPlainText: sec.Key("SEND_AS_PLAIN_TEXT").MustBool(false),
- MailerType: sec.Key("MAILER_TYPE").In("", []string{"smtp", "sendmail", "dummy"}),
- Host: sec.Key("HOST").String(),
- User: sec.Key("USER").String(),
- Passwd: sec.Key("PASSWD").String(),
- DisableHelo: sec.Key("DISABLE_HELO").MustBool(),
- HeloHostname: sec.Key("HELO_HOSTNAME").String(),
- SkipVerify: sec.Key("SKIP_VERIFY").MustBool(),
- UseCertificate: sec.Key("USE_CERTIFICATE").MustBool(),
- CertFile: sec.Key("CERT_FILE").String(),
- KeyFile: sec.Key("KEY_FILE").String(),
- IsTLSEnabled: sec.Key("IS_TLS_ENABLED").MustBool(),
- SubjectPrefix: sec.Key("SUBJECT_PREFIX").MustString(""),
+ Protocol: sec.Key("PROTOCOL").In("", []string{"smtp", "smtps", "smtp+startls", "smtp+unix", "sendmail", "dummy"}),
+ SMTPAddr: sec.Key("SMTP_ADDR").String(),
+ SMTPPort: sec.Key("SMTP_PORT").String(),
+ User: sec.Key("USER").String(),
+ Passwd: sec.Key("PASSWD").String(),
+ EnableHelo: sec.Key("ENABLE_HELO").MustBool(true),
+ HeloHostname: sec.Key("HELO_HOSTNAME").String(),
+ ForceTrustServerCert: sec.Key("FORCE_TRUST_SERVER_CERT").MustBool(false),
+ UseClientCert: sec.Key("USE_CLIENT_CERT").MustBool(false),
+ ClientCertFile: sec.Key("CLIENT_CERT_FILE").String(),
+ ClientKeyFile: sec.Key("CLIENT_KEY_FILE").String(),
+ SubjectPrefix: sec.Key("SUBJECT_PREFIX").MustString(""),
SendmailPath: sec.Key("SENDMAIL_PATH").MustString("sendmail"),
SendmailTimeout: sec.Key("SENDMAIL_TIMEOUT").MustDuration(5 * time.Minute),
@@ -77,26 +80,123 @@ func newMailService() {
MailService.From = sec.Key("FROM").MustString(MailService.User)
MailService.EnvelopeFrom = sec.Key("ENVELOPE_FROM").MustString("")
- // FIXME: DEPRECATED to be removed in v1.18.0
- deprecatedSetting("mailer", "ENABLE_HTML_ALTERNATIVE", "mailer", "SEND_AS_PLAIN_TEXT")
- if sec.HasKey("ENABLE_HTML_ALTERNATIVE") {
- MailService.SendAsPlainText = !sec.Key("ENABLE_HTML_ALTERNATIVE").MustBool(false)
- }
-
- // FIXME: DEPRECATED to be removed in v1.18.0
- deprecatedSetting("mailer", "USE_SENDMAIL", "mailer", "MAILER_TYPE")
- if sec.HasKey("USE_SENDMAIL") {
- if MailService.MailerType == "" && sec.Key("USE_SENDMAIL").MustBool(false) {
- MailService.MailerType = "sendmail"
+ // FIXME: DEPRECATED to be removed in v1.19.0
+ deprecatedSetting("mailer", "MAILER_TYPE", "mailer", "PROTOCOL")
+ if sec.HasKey("MAILER_TYPE") && !sec.HasKey("PROTOCOL") {
+ if sec.Key("MAILER_TYPE").String() == "sendmail" {
+ MailService.Protocol = "sendmail"
}
}
- parsed, err := mail.ParseAddress(MailService.From)
- if err != nil {
- log.Fatal("Invalid mailer.FROM (%s): %v", MailService.From, err)
+ // FIXME: DEPRECATED to be removed in v1.19.0
+ deprecatedSetting("mailer", "HOST", "mailer", "SMTP_ADDR")
+ if sec.HasKey("HOST") && !sec.HasKey("SMTP_ADDR") {
+ givenHost := sec.Key("HOST").String()
+ addr, port, err := net.SplitHostPort(givenHost)
+ if err != nil {
+ log.Fatal("Invalid mailer.HOST (%s): %v", givenHost, err)
+ }
+ MailService.SMTPAddr = addr
+ MailService.SMTPPort = port
+ }
+
+ // FIXME: DEPRECATED to be removed in v1.19.0
+ deprecatedSetting("mailer", "IS_TLS_ENABLED", "mailer", "PROTOCOL")
+ if sec.HasKey("IS_TLS_ENABLED") && !sec.HasKey("PROTOCOL") {
+ if sec.Key("IS_TLS_ENABLED").MustBool() {
+ MailService.Protocol = "smtps"
+ } else {
+ MailService.Protocol = "smtp+startls"
+ }
+ }
+
+ if MailService.SMTPPort == "" {
+ switch MailService.Protocol {
+ case "smtp":
+ MailService.SMTPPort = "25"
+ case "smtps":
+ MailService.SMTPPort = "465"
+ case "smtp+startls":
+ MailService.SMTPPort = "587"
+ }
+ }
+
+ if MailService.Protocol == "" {
+ if strings.ContainsAny(MailService.SMTPAddr, "/\\") {
+ MailService.Protocol = "smtp+unix"
+ } else {
+ switch MailService.SMTPPort {
+ case "25":
+ MailService.Protocol = "smtp"
+ case "465":
+ MailService.Protocol = "smtps"
+ case "587":
+ MailService.Protocol = "smtp+startls"
+ default:
+ log.Error("unable to infer unspecified mailer.PROTOCOL from mailer.SMTP_PORT = %q, assume using smtps", MailService.SMTPPort)
+ MailService.Protocol = "smtps"
+ }
+ }
+ }
+
+ // we want to warn if users use SMTP on a non-local IP;
+ // we might as well take the opportunity to check that it has an IP at all
+ ips := tryResolveAddr(MailService.SMTPAddr)
+ if MailService.Protocol == "smtp" {
+ for _, ip := range ips {
+ if !ip.IsLoopback() {
+ log.Warn("connecting over insecure SMTP protocol to non-local address is not recommended")
+ break
+ }
+ }
+ }
+
+ // FIXME: DEPRECATED to be removed in v1.19.0
+ deprecatedSetting("mailer", "DISABLE_HELO", "mailer", "ENABLE_HELO")
+ if sec.HasKey("DISABLE_HELO") && !sec.HasKey("ENABLE_HELO") {
+ MailService.EnableHelo = !sec.Key("DISABLE_HELO").MustBool()
+ }
+
+ // FIXME: DEPRECATED to be removed in v1.19.0
+ deprecatedSetting("mailer", "SKIP_VERIFY", "mailer", "FORCE_TRUST_SERVER_CERT")
+ if sec.HasKey("SKIP_VERIFY") && !sec.HasKey("FORCE_TRUST_SERVER_CERT") {
+ MailService.ForceTrustServerCert = sec.Key("SKIP_VERIFY").MustBool()
+ }
+
+ // FIXME: DEPRECATED to be removed in v1.19.0
+ deprecatedSetting("mailer", "USE_CERTIFICATE", "mailer", "USE_CLIENT_CERT")
+ if sec.HasKey("USE_CERTIFICATE") && !sec.HasKey("USE_CLIENT_CERT") {
+ MailService.UseClientCert = sec.Key("USE_CLIENT_CERT").MustBool()
+ }
+
+ // FIXME: DEPRECATED to be removed in v1.19.0
+ deprecatedSetting("mailer", "CERT_FILE", "mailer", "CLIENT_CERT_FILE")
+ if sec.HasKey("CERT_FILE") && !sec.HasKey("CLIENT_CERT_FILE") {
+ MailService.ClientCertFile = sec.Key("CERT_FILE").String()
+ }
+
+ // FIXME: DEPRECATED to be removed in v1.19.0
+ deprecatedSetting("mailer", "KEY_FILE", "mailer", "CLIENT_KEY_FILE")
+ if sec.HasKey("KEY_FILE") && !sec.HasKey("CLIENT_KEY_FILE") {
+ MailService.ClientKeyFile = sec.Key("KEY_FILE").String()
+ }
+
+ // FIXME: DEPRECATED to be removed in v1.19.0
+ deprecatedSetting("mailer", "ENABLE_HTML_ALTERNATIVE", "mailer", "SEND_AS_PLAIN_TEXT")
+ if sec.HasKey("ENABLE_HTML_ALTERNATIVE") && !sec.HasKey("SEND_AS_PLAIN_TEXT") {
+ MailService.SendAsPlainText = !sec.Key("ENABLE_HTML_ALTERNATIVE").MustBool(false)
+ }
+
+ if MailService.From != "" {
+ parsed, err := mail.ParseAddress(MailService.From)
+ if err != nil {
+ log.Fatal("Invalid mailer.FROM (%s): %v", MailService.From, err)
+ }
+ MailService.FromName = parsed.Name
+ MailService.FromEmail = parsed.Address
+ } else {
+ log.Error("no mailer.FROM provided, email system may not work.")
}
- MailService.FromName = parsed.Name
- MailService.FromEmail = parsed.Address
switch MailService.EnvelopeFrom {
case "":
@@ -105,7 +205,7 @@ func newMailService() {
MailService.EnvelopeFrom = ""
MailService.OverrideEnvelopeFrom = true
default:
- parsed, err = mail.ParseAddress(MailService.EnvelopeFrom)
+ parsed, err := mail.ParseAddress(MailService.EnvelopeFrom)
if err != nil {
log.Fatal("Invalid mailer.ENVELOPE_FROM (%s): %v", MailService.EnvelopeFrom, err)
}
@@ -113,11 +213,8 @@ func newMailService() {
MailService.EnvelopeFrom = parsed.Address
}
- if MailService.MailerType == "" {
- MailService.MailerType = "smtp"
- }
-
- if MailService.MailerType == "sendmail" {
+ if MailService.Protocol == "sendmail" {
+ var err error
MailService.SendmailArgs, err = shellquote.Split(sec.Key("SENDMAIL_ARGS").String())
if err != nil {
log.Error("Failed to parse Sendmail args: %s with error %v", CustomConf, err)
@@ -148,3 +245,21 @@ func newNotifyMailService() {
Service.EnableNotifyMail = true
log.Info("Notify Mail Service Enabled")
}
+
+func tryResolveAddr(addr string) []net.IP {
+ if strings.HasPrefix(addr, "[") && strings.HasSuffix(addr, "]") {
+ addr = addr[1 : len(addr)-1]
+ }
+ ip := net.ParseIP(addr)
+ if ip != nil {
+ ips := make([]net.IP, 1)
+ ips[0] = ip
+ return ips
+ }
+ ips, err := net.LookupIP(addr)
+ if err != nil {
+ log.Warn("could not look up mailer.SMTP_ADDR: %v", err)
+ return make([]net.IP, 0)
+ }
+ return ips
+}
diff --git a/modules/setting/picture.go b/modules/setting/picture.go
index a6d3447dcc..af9041ade3 100644
--- a/modules/setting/picture.go
+++ b/modules/setting/picture.go
@@ -4,14 +4,6 @@
package setting
-import (
- "net/url"
-
- "code.gitea.io/gitea/modules/log"
-
- "strk.kbt.io/projects/go/libravatar"
-)
-
// settings
var (
// Picture settings
@@ -30,10 +22,8 @@ var (
}
GravatarSource string
- GravatarSourceURL *url.URL
- DisableGravatar bool
- EnableFederatedAvatar bool
- LibravatarService *libravatar.Libravatar
+ DisableGravatar bool // Depreciated: migrated to database
+ EnableFederatedAvatar bool // Depreciated: migrated to database
RepoAvatar = struct {
Storage
@@ -69,38 +59,30 @@ func newPictureService() {
default:
GravatarSource = source
}
- DisableGravatar = sec.Key("DISABLE_GRAVATAR").MustBool()
- EnableFederatedAvatar = sec.Key("ENABLE_FEDERATED_AVATAR").MustBool(!InstallLock)
- if OfflineMode {
- DisableGravatar = true
- EnableFederatedAvatar = false
- }
- if DisableGravatar {
- EnableFederatedAvatar = false
- }
- if EnableFederatedAvatar || !DisableGravatar {
- var err error
- GravatarSourceURL, err = url.Parse(GravatarSource)
- if err != nil {
- log.Fatal("Failed to parse Gravatar URL(%s): %v",
- GravatarSource, err)
- }
- }
- if EnableFederatedAvatar {
- LibravatarService = libravatar.New()
- if GravatarSourceURL.Scheme == "https" {
- LibravatarService.SetUseHTTPS(true)
- LibravatarService.SetSecureFallbackHost(GravatarSourceURL.Host)
- } else {
- LibravatarService.SetUseHTTPS(false)
- LibravatarService.SetFallbackHost(GravatarSourceURL.Host)
- }
- }
+ DisableGravatar = sec.Key("DISABLE_GRAVATAR").MustBool(GetDefaultDisableGravatar())
+ deprecatedSettingDB("", "DISABLE_GRAVATAR")
+ EnableFederatedAvatar = sec.Key("ENABLE_FEDERATED_AVATAR").MustBool(GetDefaultEnableFederatedAvatar(DisableGravatar))
+ deprecatedSettingDB("", "ENABLE_FEDERATED_AVATAR")
newRepoAvatarService()
}
+func GetDefaultDisableGravatar() bool {
+ return !OfflineMode
+}
+
+func GetDefaultEnableFederatedAvatar(disableGravatar bool) bool {
+ v := !InstallLock
+ if OfflineMode {
+ v = false
+ }
+ if disableGravatar {
+ v = false
+ }
+ return v
+}
+
func newRepoAvatarService() {
sec := Cfg.Section("picture")
diff --git a/modules/setting/queue.go b/modules/setting/queue.go
index cb86cbdfe0..d3bb33b248 100644
--- a/modules/setting/queue.go
+++ b/modules/setting/queue.go
@@ -9,6 +9,7 @@ import (
"strconv"
"time"
+ "code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/log"
ini "gopkg.in/ini.v1"
@@ -109,8 +110,8 @@ func NewQueueService() {
// Now handle the old issue_indexer configuration
// FIXME: DEPRECATED to be removed in v1.18.0
section := Cfg.Section("queue.issue_indexer")
- directlySet := toDirectlySetKeysMap(section)
- if !directlySet["TYPE"] && defaultType == "" {
+ directlySet := toDirectlySetKeysSet(section)
+ if !directlySet.Contains("TYPE") && defaultType == "" {
switch typ := Cfg.Section("indexer").Key("ISSUE_INDEXER_QUEUE_TYPE").MustString(""); typ {
case "levelqueue":
_, _ = section.NewKey("TYPE", "level")
@@ -124,25 +125,25 @@ func NewQueueService() {
log.Fatal("Unsupported indexer queue type: %v", typ)
}
}
- if !directlySet["LENGTH"] {
+ if !directlySet.Contains("LENGTH") {
length := Cfg.Section("indexer").Key("UPDATE_BUFFER_LEN").MustInt(0)
if length != 0 {
_, _ = section.NewKey("LENGTH", strconv.Itoa(length))
}
}
- if !directlySet["BATCH_LENGTH"] {
+ if !directlySet.Contains("BATCH_LENGTH") {
fallback := Cfg.Section("indexer").Key("ISSUE_INDEXER_QUEUE_BATCH_NUMBER").MustInt(0)
if fallback != 0 {
_, _ = section.NewKey("BATCH_LENGTH", strconv.Itoa(fallback))
}
}
- if !directlySet["DATADIR"] {
+ if !directlySet.Contains("DATADIR") {
queueDir := filepath.ToSlash(Cfg.Section("indexer").Key("ISSUE_INDEXER_QUEUE_DIR").MustString(""))
if queueDir != "" {
_, _ = section.NewKey("DATADIR", queueDir)
}
}
- if !directlySet["CONN_STR"] {
+ if !directlySet.Contains("CONN_STR") {
connStr := Cfg.Section("indexer").Key("ISSUE_INDEXER_QUEUE_CONN_STR").MustString("")
if connStr != "" {
_, _ = section.NewKey("CONN_STR", connStr)
@@ -178,19 +179,19 @@ func handleOldLengthConfiguration(queueName, oldSection, oldKey string, defaultV
}
section := Cfg.Section("queue." + queueName)
- directlySet := toDirectlySetKeysMap(section)
- if !directlySet["LENGTH"] {
+ directlySet := toDirectlySetKeysSet(section)
+ if !directlySet.Contains("LENGTH") {
_, _ = section.NewKey("LENGTH", strconv.Itoa(value))
}
}
-// toDirectlySetKeysMap returns a bool map of keys directly set by this section
+// toDirectlySetKeysSet returns a set of keys directly set by this section
// Note: we cannot use section.HasKey(...) as that will immediately set the Key if a parent section has the Key
// but this section does not.
-func toDirectlySetKeysMap(section *ini.Section) map[string]bool {
- sectionMap := map[string]bool{}
+func toDirectlySetKeysSet(section *ini.Section) container.Set[string] {
+ sections := make(container.Set[string])
for _, key := range section.Keys() {
- sectionMap[key.Name()] = true
+ sections.Add(key.Name())
}
- return sectionMap
+ return sections
}
diff --git a/modules/setting/repository.go b/modules/setting/repository.go
index 733bc6d90e..d0406dbf90 100644
--- a/modules/setting/repository.go
+++ b/modules/setting/repository.go
@@ -48,6 +48,7 @@ var (
DefaultBranch string
AllowAdoptionOfUnadoptedRepositories bool
AllowDeleteOfUnadoptedRepositories bool
+ DisableDownloadSourceArchives bool
// Repository editor settings
Editor struct {
diff --git a/modules/setting/service.go b/modules/setting/service.go
index bd97e10b0f..10e3899950 100644
--- a/modules/setting/service.go
+++ b/modules/setting/service.go
@@ -38,6 +38,7 @@ var Service = struct {
EnableReverseProxyAuth bool
EnableReverseProxyAutoRegister bool
EnableReverseProxyEmail bool
+ EnableReverseProxyFullName bool
EnableCaptcha bool
RequireExternalRegistrationCaptcha bool
RequireExternalRegistrationPassword bool
@@ -47,6 +48,9 @@ var Service = struct {
RecaptchaURL string
HcaptchaSecret string
HcaptchaSitekey string
+ McaptchaSecret string
+ McaptchaSitekey string
+ McaptchaURL string
DefaultKeepEmailPrivate bool
DefaultAllowCreateOrganization bool
DefaultUserIsRestricted bool
@@ -124,6 +128,7 @@ func newService() {
Service.EnableReverseProxyAuth = sec.Key("ENABLE_REVERSE_PROXY_AUTHENTICATION").MustBool()
Service.EnableReverseProxyAutoRegister = sec.Key("ENABLE_REVERSE_PROXY_AUTO_REGISTRATION").MustBool()
Service.EnableReverseProxyEmail = sec.Key("ENABLE_REVERSE_PROXY_EMAIL").MustBool()
+ Service.EnableReverseProxyFullName = sec.Key("ENABLE_REVERSE_PROXY_FULL_NAME").MustBool()
Service.EnableCaptcha = sec.Key("ENABLE_CAPTCHA").MustBool(false)
Service.RequireExternalRegistrationCaptcha = sec.Key("REQUIRE_EXTERNAL_REGISTRATION_CAPTCHA").MustBool(Service.EnableCaptcha)
Service.RequireExternalRegistrationPassword = sec.Key("REQUIRE_EXTERNAL_REGISTRATION_PASSWORD").MustBool()
@@ -133,6 +138,9 @@ func newService() {
Service.RecaptchaURL = sec.Key("RECAPTCHA_URL").MustString("https://www.google.com/recaptcha/")
Service.HcaptchaSecret = sec.Key("HCAPTCHA_SECRET").MustString("")
Service.HcaptchaSitekey = sec.Key("HCAPTCHA_SITEKEY").MustString("")
+ Service.McaptchaURL = sec.Key("MCAPTCHA_URL").MustString("https://demo.mcaptcha.org/")
+ Service.McaptchaSecret = sec.Key("MCAPTCHA_SECRET").MustString("")
+ Service.McaptchaSitekey = sec.Key("MCAPTCHA_SITEKEY").MustString("")
Service.DefaultKeepEmailPrivate = sec.Key("DEFAULT_KEEP_EMAIL_PRIVATE").MustBool()
Service.DefaultAllowCreateOrganization = sec.Key("DEFAULT_ALLOW_CREATE_ORGANIZATION").MustBool(true)
Service.DefaultUserIsRestricted = sec.Key("DEFAULT_USER_IS_RESTRICTED").MustBool(false)
diff --git a/modules/setting/setting.go b/modules/setting/setting.go
index 23e3280dc9..f93be2fbd1 100644
--- a/modules/setting/setting.go
+++ b/modules/setting/setting.go
@@ -21,7 +21,7 @@ import (
"text/template"
"time"
- "code.gitea.io/gitea/modules/generate"
+ "code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/user"
@@ -59,6 +59,7 @@ const (
ImageCaptcha = "image"
ReCaptcha = "recaptcha"
HCaptcha = "hcaptcha"
+ MCaptcha = "mcaptcha"
)
// settings
@@ -91,47 +92,56 @@ var (
// LocalURL is the url for locally running applications to contact Gitea. It always has a '/' suffix
// It maps to ini:"LOCAL_ROOT_URL"
LocalURL string
+ // AssetVersion holds a opaque value that is used for cache-busting assets
+ AssetVersion string
// Server settings
- Protocol Scheme
- Domain string
- HTTPAddr string
- HTTPPort string
- RedirectOtherPort bool
- PortToRedirect string
- OfflineMode bool
- CertFile string
- KeyFile string
- StaticRootPath string
- StaticCacheTime time.Duration
- EnableGzip bool
- LandingPageURL LandingPage
- LandingPageCustom string
- UnixSocketPermission uint32
- EnablePprof bool
- PprofDataPath string
- EnableAcme bool
- AcmeTOS bool
- AcmeLiveDirectory string
- AcmeEmail string
- AcmeURL string
- AcmeCARoot string
- SSLMinimumVersion string
- SSLMaximumVersion string
- SSLCurvePreferences []string
- SSLCipherSuites []string
- GracefulRestartable bool
- GracefulHammerTime time.Duration
- StartupTimeout time.Duration
- PerWriteTimeout = 30 * time.Second
- PerWritePerKbTimeout = 10 * time.Second
- StaticURLPrefix string
- AbsoluteAssetURL string
+ Protocol Scheme
+ UseProxyProtocol bool // `ini:"USE_PROXY_PROTOCOL"`
+ ProxyProtocolTLSBridging bool //`ini:"PROXY_PROTOCOL_TLS_BRIDGING"`
+ ProxyProtocolHeaderTimeout time.Duration
+ ProxyProtocolAcceptUnknown bool
+ Domain string
+ HTTPAddr string
+ HTTPPort string
+ LocalUseProxyProtocol bool
+ RedirectOtherPort bool
+ RedirectorUseProxyProtocol bool
+ PortToRedirect string
+ OfflineMode bool
+ CertFile string
+ KeyFile string
+ StaticRootPath string
+ StaticCacheTime time.Duration
+ EnableGzip bool
+ LandingPageURL LandingPage
+ LandingPageCustom string
+ UnixSocketPermission uint32
+ EnablePprof bool
+ PprofDataPath string
+ EnableAcme bool
+ AcmeTOS bool
+ AcmeLiveDirectory string
+ AcmeEmail string
+ AcmeURL string
+ AcmeCARoot string
+ SSLMinimumVersion string
+ SSLMaximumVersion string
+ SSLCurvePreferences []string
+ SSLCipherSuites []string
+ GracefulRestartable bool
+ GracefulHammerTime time.Duration
+ StartupTimeout time.Duration
+ PerWriteTimeout = 30 * time.Second
+ PerWritePerKbTimeout = 10 * time.Second
+ StaticURLPrefix string
+ AbsoluteAssetURL string
SSH = struct {
Disabled bool `ini:"DISABLE_SSH"`
StartBuiltinServer bool `ini:"START_SSH_SERVER"`
BuiltinServerUser string `ini:"BUILTIN_SSH_SERVER_USER"`
+ UseProxyProtocol bool `ini:"SSH_SERVER_USE_PROXY_PROTOCOL"`
Domain string `ini:"SSH_DOMAIN"`
Port int `ini:"SSH_PORT"`
User string `ini:"SSH_USER"`
@@ -185,6 +195,7 @@ var (
CookieRememberName string
ReverseProxyAuthUser string
ReverseProxyAuthEmail string
+ ReverseProxyAuthFullName string
ReverseProxyLimit int
ReverseProxyTrustedProxies []string
MinPasswordLength int
@@ -224,11 +235,12 @@ var (
DefaultTheme string
Themes []string
Reactions []string
- ReactionsMap map[string]bool `ini:"-"`
+ ReactionsLookup container.Set[string] `ini:"-"`
CustomEmojis []string
CustomEmojisMap map[string]string `ini:"-"`
SearchRepoDescription bool
UseServiceWorker bool
+ OnlyShowRelevantRepos bool
Notification struct {
MinTimeout time.Duration
@@ -262,8 +274,8 @@ var (
}{
ExplorePagingNum: 20,
SitemapPagingNum: 20,
- IssuePagingNum: 10,
- RepoSearchPagingNum: 10,
+ IssuePagingNum: 20,
+ RepoSearchPagingNum: 20,
MembersPagingNum: 20,
FeedMaxCommitNum: 5,
FeedPagingNum: 20,
@@ -332,10 +344,12 @@ var (
EnableHardLineBreakInDocuments bool
CustomURLSchemes []string `ini:"CUSTOM_URL_SCHEMES"`
FileExtensions []string
+ EnableMath bool
}{
EnableHardLineBreakInComments: true,
EnableHardLineBreakInDocuments: false,
FileExtensions: strings.Split(".md,.markdown,.mdown,.mkd", ","),
+ EnableMath: true,
}
// Admin settings
@@ -592,6 +606,13 @@ func deprecatedSetting(oldSection, oldKey, newSection, newKey string) {
}
}
+// deprecatedSettingDB add a hint that the configuration has been moved to database but still kept in app.ini
+func deprecatedSettingDB(oldSection, oldKey string) {
+ if Cfg.Section(oldSection).HasKey(oldKey) {
+ log.Error("Deprecated `[%s]` `%s` present which has been copied to database table sys_setting", oldSection, oldKey)
+ }
+}
+
// loadFromConf initializes configuration context.
// NOTE: do not print any log except error.
func loadFromConf(allowEmpty bool, extraConfig string) {
@@ -715,6 +736,10 @@ func loadFromConf(allowEmpty bool, extraConfig string) {
HTTPAddr = filepath.Join(AppWorkPath, HTTPAddr)
}
}
+ UseProxyProtocol = sec.Key("USE_PROXY_PROTOCOL").MustBool(false)
+ ProxyProtocolTLSBridging = sec.Key("PROXY_PROTOCOL_TLS_BRIDGING").MustBool(false)
+ ProxyProtocolHeaderTimeout = sec.Key("PROXY_PROTOCOL_HEADER_TIMEOUT").MustDuration(5 * time.Second)
+ ProxyProtocolAcceptUnknown = sec.Key("PROXY_PROTOCOL_ACCEPT_UNKNOWN").MustBool(false)
GracefulRestartable = sec.Key("ALLOW_GRACEFUL_RESTARTS").MustBool(true)
GracefulHammerTime = sec.Key("GRACEFUL_HAMMER_TIME").MustDuration(60 * time.Second)
StartupTimeout = sec.Key("STARTUP_TIMEOUT").MustDuration(0 * time.Second)
@@ -746,6 +771,7 @@ func loadFromConf(allowEmpty bool, extraConfig string) {
}
AbsoluteAssetURL = MakeAbsoluteAssetURL(AppURL, StaticURLPrefix)
+ AssetVersion = strings.ReplaceAll(AppVer, "+", "~") // make sure the version string is clear (no real escaping is needed)
manifestBytes := MakeManifestData(AppName, AppURL, AbsoluteAssetURL)
ManifestData = `application/json;base64,` + base64.StdEncoding.EncodeToString(manifestBytes)
@@ -768,8 +794,10 @@ func loadFromConf(allowEmpty bool, extraConfig string) {
}
LocalURL = sec.Key("LOCAL_ROOT_URL").MustString(defaultLocalURL)
LocalURL = strings.TrimRight(LocalURL, "/") + "/"
+ LocalUseProxyProtocol = sec.Key("LOCAL_USE_PROXY_PROTOCOL").MustBool(UseProxyProtocol)
RedirectOtherPort = sec.Key("REDIRECT_OTHER_PORT").MustBool(false)
PortToRedirect = sec.Key("PORT_TO_REDIRECT").MustString("80")
+ RedirectorUseProxyProtocol = sec.Key("REDIRECTOR_USE_PROXY_PROTOCOL").MustBool(UseProxyProtocol)
OfflineMode = sec.Key("OFFLINE_MODE").MustBool()
DisableRouterLog = sec.Key("DISABLE_ROUTER_LOG").MustBool()
if len(StaticRootPath) == 0 {
@@ -834,6 +862,7 @@ func loadFromConf(allowEmpty bool, extraConfig string) {
SSH.KeygenPath = sec.Key("SSH_KEYGEN_PATH").MustString("ssh-keygen")
SSH.Port = sec.Key("SSH_PORT").MustInt(22)
SSH.ListenPort = sec.Key("SSH_LISTEN_PORT").MustInt(SSH.Port)
+ SSH.UseProxyProtocol = sec.Key("SSH_SERVER_USE_PROXY_PROTOCOL").MustBool(false)
// When disable SSH, start builtin server value is ignored.
if SSH.Disabled {
@@ -901,13 +930,20 @@ func loadFromConf(allowEmpty bool, extraConfig string) {
sec = Cfg.Section("security")
InstallLock = sec.Key("INSTALL_LOCK").MustBool(false)
- SecretKey = sec.Key("SECRET_KEY").MustString("!#@FDEWREWR&*(")
LogInRememberDays = sec.Key("LOGIN_REMEMBER_DAYS").MustInt(7)
CookieUserName = sec.Key("COOKIE_USERNAME").MustString("gitea_awesome")
+ SecretKey = loadSecret(sec, "SECRET_KEY_URI", "SECRET_KEY")
+ if SecretKey == "" {
+ // FIXME: https://github.com/go-gitea/gitea/issues/16832
+ // Until it supports rotating an existing secret key, we shouldn't move users off of the widely used default value
+ SecretKey = "!#@FDEWREWR&*(" // nolint:gosec
+ }
+
CookieRememberName = sec.Key("COOKIE_REMEMBER_NAME").MustString("gitea_incredible")
ReverseProxyAuthUser = sec.Key("REVERSE_PROXY_AUTHENTICATION_USER").MustString("X-WEBAUTH-USER")
ReverseProxyAuthEmail = sec.Key("REVERSE_PROXY_AUTHENTICATION_EMAIL").MustString("X-WEBAUTH-EMAIL")
+ ReverseProxyAuthFullName = sec.Key("REVERSE_PROXY_AUTHENTICATION_FULL_NAME").MustString("X-WEBAUTH-FULLNAME")
ReverseProxyLimit = sec.Key("REVERSE_PROXY_LIMIT").MustInt(1)
ReverseProxyTrustedProxies = sec.Key("REVERSE_PROXY_TRUSTED_PROXIES").Strings(",")
@@ -925,11 +961,7 @@ func loadFromConf(allowEmpty bool, extraConfig string) {
PasswordCheckPwn = sec.Key("PASSWORD_CHECK_PWN").MustBool(false)
SuccessfulTokensCacheSize = sec.Key("SUCCESSFUL_TOKENS_CACHE_SIZE").MustInt(20)
- InternalToken = loadInternalToken(sec)
- if InstallLock && InternalToken == "" {
- // if Gitea has been installed but the InternalToken hasn't been generated (upgrade from an old release), we should generate
- generateSaveInternalToken()
- }
+ InternalToken = loadSecret(sec, "INTERNAL_TOKEN_URI", "INTERNAL_TOKEN")
cfgdata := sec.Key("PASSWORD_COMPLEXITY").Strings(",")
if len(cfgdata) == 0 {
@@ -1067,6 +1099,7 @@ func loadFromConf(allowEmpty bool, extraConfig string) {
UI.DefaultShowFullName = Cfg.Section("ui").Key("DEFAULT_SHOW_FULL_NAME").MustBool(false)
UI.SearchRepoDescription = Cfg.Section("ui").Key("SEARCH_REPO_DESCRIPTION").MustBool(true)
UI.UseServiceWorker = Cfg.Section("ui").Key("USE_SERVICE_WORKER").MustBool(false)
+ UI.OnlyShowRelevantRepos = Cfg.Section("ui").Key("ONLY_SHOW_RELEVANT_REPOS").MustBool(false)
HasRobotsTxt, err = util.IsFile(path.Join(CustomPath, "robots.txt"))
if err != nil {
@@ -1075,9 +1108,9 @@ func loadFromConf(allowEmpty bool, extraConfig string) {
newMarkup()
- UI.ReactionsMap = make(map[string]bool)
+ UI.ReactionsLookup = make(container.Set[string])
for _, reaction := range UI.Reactions {
- UI.ReactionsMap[reaction] = true
+ UI.ReactionsLookup.Add(reaction)
}
UI.CustomEmojisMap = make(map[string]string)
for _, emoji := range UI.CustomEmojis {
@@ -1117,51 +1150,36 @@ func parseAuthorizedPrincipalsAllow(values []string) ([]string, bool) {
return authorizedPrincipalsAllow, true
}
-func loadInternalToken(sec *ini.Section) string {
- uri := sec.Key("INTERNAL_TOKEN_URI").String()
- if uri == "" {
- return sec.Key("INTERNAL_TOKEN").String()
+func loadSecret(sec *ini.Section, uriKey, verbatimKey string) string {
+ // don't allow setting both URI and verbatim string
+ uri := sec.Key(uriKey).String()
+ verbatim := sec.Key(verbatimKey).String()
+ if uri != "" && verbatim != "" {
+ log.Fatal("Cannot specify both %s and %s", uriKey, verbatimKey)
}
+
+ // if we have no URI, use verbatim
+ if uri == "" {
+ return verbatim
+ }
+
tempURI, err := url.Parse(uri)
if err != nil {
- log.Fatal("Failed to parse INTERNAL_TOKEN_URI (%s): %v", uri, err)
+ log.Fatal("Failed to parse %s (%s): %v", uriKey, uri, err)
}
switch tempURI.Scheme {
case "file":
buf, err := os.ReadFile(tempURI.RequestURI())
- if err != nil && !os.IsNotExist(err) {
- log.Fatal("Failed to open InternalTokenURI (%s): %v", uri, err)
- }
- // No token in the file, generate one and store it.
- if len(buf) == 0 {
- token, err := generate.NewInternalToken()
- if err != nil {
- log.Fatal("Error generate internal token: %v", err)
- }
- err = os.WriteFile(tempURI.RequestURI(), []byte(token), 0o600)
- if err != nil {
- log.Fatal("Error writing to InternalTokenURI (%s): %v", uri, err)
- }
- return token
+ if err != nil {
+ log.Fatal("Failed to read %s (%s): %v", uriKey, tempURI.RequestURI(), err)
}
return strings.TrimSpace(string(buf))
+
+ // only file URIs are allowed
default:
log.Fatal("Unsupported URI-Scheme %q (INTERNAL_TOKEN_URI = %q)", tempURI.Scheme, uri)
+ return ""
}
- return ""
-}
-
-// generateSaveInternalToken generates and saves the internal token to app.ini
-func generateSaveInternalToken() {
- token, err := generate.NewInternalToken()
- if err != nil {
- log.Fatal("Error generate internal token: %v", err)
- }
-
- InternalToken = token
- CreateOrAppendToCustomConf(func(cfg *ini.File) {
- cfg.Section("security").Key("INTERNAL_TOKEN").SetValue(token)
- })
}
// MakeAbsoluteAssetURL returns the absolute asset url prefix without a trailing slash
@@ -1225,7 +1243,12 @@ func MakeManifestData(appName, appURL, absoluteAssetURL string) []byte {
// CreateOrAppendToCustomConf creates or updates the custom config.
// Use the callback to set individual values.
-func CreateOrAppendToCustomConf(callback func(cfg *ini.File)) {
+func CreateOrAppendToCustomConf(purpose string, callback func(cfg *ini.File)) {
+ if CustomConf == "" {
+ log.Error("Custom config path must not be empty")
+ return
+ }
+
cfg := ini.Empty()
isFile, err := util.IsFile(CustomConf)
if err != nil {
@@ -1240,8 +1263,6 @@ func CreateOrAppendToCustomConf(callback func(cfg *ini.File)) {
callback(cfg)
- log.Info("Settings saved to: %q", CustomConf)
-
if err := os.MkdirAll(filepath.Dir(CustomConf), os.ModePerm); err != nil {
log.Fatal("failed to create '%s': %v", CustomConf, err)
return
@@ -1249,6 +1270,7 @@ func CreateOrAppendToCustomConf(callback func(cfg *ini.File)) {
if err := cfg.SaveTo(CustomConf); err != nil {
log.Fatal("error saving to custom config: %v", err)
}
+ log.Info("Settings for %s saved to: %q", purpose, CustomConf)
// Change permissions to be more restrictive
fi, err := os.Stat(CustomConf)
diff --git a/modules/ssh/init.go b/modules/ssh/init.go
index f6332bb18b..72cb6df7a4 100644
--- a/modules/ssh/init.go
+++ b/modules/ssh/init.go
@@ -18,6 +18,7 @@ import (
func Init() error {
if setting.SSH.Disabled {
+ builtinUnused()
return nil
}
diff --git a/modules/ssh/ssh.go b/modules/ssh/ssh.go
index 2affeb781a..6b601c008f 100644
--- a/modules/ssh/ssh.go
+++ b/modules/ssh/ssh.go
@@ -11,6 +11,7 @@ import (
"crypto/rsa"
"crypto/x509"
"encoding/pem"
+ "errors"
"fmt"
"io"
"net"
@@ -74,11 +75,20 @@ func sessionHandler(session ssh.Session) {
ctx, cancel := context.WithCancel(session.Context())
defer cancel()
+ gitProtocol := ""
+ for _, env := range session.Environ() {
+ if strings.HasPrefix(env, "GIT_PROTOCOL=") {
+ _, gitProtocol, _ = strings.Cut(env, "=")
+ break
+ }
+ }
+
cmd := exec.CommandContext(ctx, setting.AppPath, args...)
cmd.Env = append(
os.Environ(),
"SSH_ORIGINAL_COMMAND="+command,
"SKIP_MINWINSVC=1",
+ "GIT_PROTOCOL="+gitProtocol,
)
stdout, err := cmd.StdoutPipe()
@@ -142,10 +152,14 @@ func sessionHandler(session ssh.Session) {
// Wait for the command to exit and log any errors we get
err = cmd.Wait()
if err != nil {
- log.Error("SSH: Wait: %v", err)
+ // Cannot use errors.Is here because ExitError doesn't implement Is
+ // Thus errors.Is will do equality test NOT type comparison
+ if _, ok := err.(*exec.ExitError); !ok {
+ log.Error("SSH: Wait: %v", err)
+ }
}
- if err := session.Exit(getExitStatusFromError(err)); err != nil {
+ if err := session.Exit(getExitStatusFromError(err)); err != nil && !errors.Is(err, io.EOF) {
log.Error("Session failed to exit. %s", err)
}
}
diff --git a/modules/ssh/ssh_graceful.go b/modules/ssh/ssh_graceful.go
index 9b91baf09e..166ea0b982 100644
--- a/modules/ssh/ssh_graceful.go
+++ b/modules/ssh/ssh_graceful.go
@@ -17,7 +17,7 @@ func listen(server *ssh.Server) {
gracefulServer.PerWriteTimeout = setting.SSH.PerWriteTimeout
gracefulServer.PerWritePerKbTimeout = setting.SSH.PerWritePerKbTimeout
- err := gracefulServer.ListenAndServe(server.Serve)
+ err := gracefulServer.ListenAndServe(server.Serve, setting.SSH.UseProxyProtocol)
if err != nil {
select {
case <-graceful.GetManager().IsShutdown():
diff --git a/modules/storage/local.go b/modules/storage/local.go
index 701b0b1a9f..5d5b06b648 100644
--- a/modules/storage/local.go
+++ b/modules/storage/local.go
@@ -102,6 +102,10 @@ func (l *LocalStorage) Save(path string, r io.Reader, size int64) (int64, error)
if err := util.Rename(tmp.Name(), p); err != nil {
return 0, err
}
+ // Golang's tmp file (os.CreateTemp) always have 0o600 mode, so we need to change the file to follow the umask (as what Create/MkDir does)
+ if err := util.ApplyUmask(p, os.ModePerm); err != nil {
+ return 0, err
+ }
tmpRemoved = true
diff --git a/modules/structs/hook.go b/modules/structs/hook.go
index 07d51915de..8321a15a8f 100644
--- a/modules/structs/hook.go
+++ b/modules/structs/hook.go
@@ -267,15 +267,16 @@ func (p *ReleasePayload) JSONPayload() ([]byte, error) {
// PushPayload represents a payload information of push event.
type PushPayload struct {
- Ref string `json:"ref"`
- Before string `json:"before"`
- After string `json:"after"`
- CompareURL string `json:"compare_url"`
- Commits []*PayloadCommit `json:"commits"`
- HeadCommit *PayloadCommit `json:"head_commit"`
- Repo *Repository `json:"repository"`
- Pusher *User `json:"pusher"`
- Sender *User `json:"sender"`
+ Ref string `json:"ref"`
+ Before string `json:"before"`
+ After string `json:"after"`
+ CompareURL string `json:"compare_url"`
+ Commits []*PayloadCommit `json:"commits"`
+ TotalCommits int `json:"total_commits"`
+ HeadCommit *PayloadCommit `json:"head_commit"`
+ Repo *Repository `json:"repository"`
+ Pusher *User `json:"pusher"`
+ Sender *User `json:"sender"`
}
// JSONPayload FIXME
@@ -397,6 +398,39 @@ type ReviewPayload struct {
Content string `json:"content"`
}
+// __ __.__ __ .__
+// / \ / \__| | _|__|
+// \ \/\/ / | |/ / |
+// \ /| | <| |
+// \__/\ / |__|__|_ \__|
+// \/ \/
+
+// HookWikiAction an action that happens to a wiki page
+type HookWikiAction string
+
+const (
+ // HookWikiCreated created
+ HookWikiCreated HookWikiAction = "created"
+ // HookWikiEdited edited
+ HookWikiEdited HookWikiAction = "edited"
+ // HookWikiDeleted deleted
+ HookWikiDeleted HookWikiAction = "deleted"
+)
+
+// WikiPayload payload for repository webhooks
+type WikiPayload struct {
+ Action HookWikiAction `json:"action"`
+ Repository *Repository `json:"repository"`
+ Sender *User `json:"sender"`
+ Page string `json:"page"`
+ Comment string `json:"comment"`
+}
+
+// JSONPayload JSON representation of the payload
+func (p *WikiPayload) JSONPayload() ([]byte, error) {
+ return json.MarshalIndent(p, "", " ")
+}
+
//__________ .__ __
//\______ \ ____ ______ ____ _____|__|/ |_ ___________ ___.__.
// | _// __ \\____ \ / _ \/ ___/ \ __\/ _ \_ __ < | |
diff --git a/modules/structs/issue.go b/modules/structs/issue.go
index c72487fe4d..27ec81f728 100644
--- a/modules/structs/issue.go
+++ b/modules/structs/issue.go
@@ -5,7 +5,7 @@
package structs
import (
- "strings"
+ "path/filepath"
"time"
)
@@ -120,19 +120,57 @@ type IssueDeadline struct {
Deadline *time.Time `json:"due_date"`
}
+// IssueFormFieldType defines issue form field type, can be "markdown", "textarea", "input", "dropdown" or "checkboxes"
+type IssueFormFieldType string
+
+const (
+ IssueFormFieldTypeMarkdown IssueFormFieldType = "markdown"
+ IssueFormFieldTypeTextarea IssueFormFieldType = "textarea"
+ IssueFormFieldTypeInput IssueFormFieldType = "input"
+ IssueFormFieldTypeDropdown IssueFormFieldType = "dropdown"
+ IssueFormFieldTypeCheckboxes IssueFormFieldType = "checkboxes"
+)
+
+// IssueFormField represents a form field
+// swagger:model
+type IssueFormField struct {
+ Type IssueFormFieldType `json:"type" yaml:"type"`
+ ID string `json:"id" yaml:"id"`
+ Attributes map[string]interface{} `json:"attributes" yaml:"attributes"`
+ Validations map[string]interface{} `json:"validations" yaml:"validations"`
+}
+
// IssueTemplate represents an issue template for a repository
// swagger:model
type IssueTemplate struct {
- Name string `json:"name" yaml:"name"`
- Title string `json:"title" yaml:"title"`
- About string `json:"about" yaml:"about"`
- Labels []string `json:"labels" yaml:"labels"`
- Ref string `json:"ref" yaml:"ref"`
- Content string `json:"content" yaml:"-"`
- FileName string `json:"file_name" yaml:"-"`
+ Name string `json:"name" yaml:"name"`
+ Title string `json:"title" yaml:"title"`
+ About string `json:"about" yaml:"about"` // Using "description" in a template file is compatible
+ Labels []string `json:"labels" yaml:"labels"`
+ Ref string `json:"ref" yaml:"ref"`
+ Content string `json:"content" yaml:"-"`
+ Fields []*IssueFormField `json:"body" yaml:"body"`
+ FileName string `json:"file_name" yaml:"-"`
}
-// Valid checks whether an IssueTemplate is considered valid, e.g. at least name and about
-func (it IssueTemplate) Valid() bool {
- return strings.TrimSpace(it.Name) != "" && strings.TrimSpace(it.About) != ""
+// IssueTemplateType defines issue template type
+type IssueTemplateType string
+
+const (
+ IssueTemplateTypeMarkdown IssueTemplateType = "md"
+ IssueTemplateTypeYaml IssueTemplateType = "yaml"
+)
+
+// Type returns the type of IssueTemplate, can be "md", "yaml" or empty for known
+func (it IssueTemplate) Type() IssueTemplateType {
+ if it.Name == "config.yaml" || it.Name == "config.yml" {
+ // ignore config.yaml which is a special configuration file
+ return ""
+ }
+ if ext := filepath.Ext(it.FileName); ext == ".md" {
+ return IssueTemplateTypeMarkdown
+ } else if ext == ".yaml" || ext == ".yml" {
+ return "yaml"
+ }
+ return IssueTemplateTypeYaml
}
diff --git a/modules/structs/mirror.go b/modules/structs/mirror.go
new file mode 100644
index 0000000000..8e8a8a2705
--- /dev/null
+++ b/modules/structs/mirror.go
@@ -0,0 +1,25 @@
+// Copyright 2021 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package structs
+
+// CreatePushMirrorOption represents need information to create a push mirror of a repository.
+type CreatePushMirrorOption struct {
+ RemoteAddress string `json:"remote_address"`
+ RemoteUsername string `json:"remote_username"`
+ RemotePassword string `json:"remote_password"`
+ Interval string `json:"interval"`
+}
+
+// PushMirror represents information of a push mirror
+// swagger:model
+type PushMirror struct {
+ RepoName string `json:"repo_name"`
+ RemoteName string `json:"remote_name"`
+ RemoteAddress string `json:"remote_address"`
+ CreatedUnix string `json:"created"`
+ LastUpdateUnix string `json:"last_update"`
+ LastError string `json:"last_error"`
+ Interval string `json:"interval"`
+}
diff --git a/modules/structs/org.go b/modules/structs/org.go
index d8bd59e1ec..1e98c59ba4 100644
--- a/modules/structs/org.go
+++ b/modules/structs/org.go
@@ -7,7 +7,7 @@ package structs
// Organization represents an organization
type Organization struct {
ID int64 `json:"id"`
- UserName string `json:"username"`
+ Name string `json:"name"`
FullName string `json:"full_name"`
AvatarURL string `json:"avatar_url"`
Description string `json:"description"`
@@ -15,6 +15,8 @@ type Organization struct {
Location string `json:"location"`
Visibility string `json:"visibility"`
RepoAdminChangeTeamAccess bool `json:"repo_admin_change_team_access"`
+ // deprecated
+ UserName string `json:"username"`
}
// OrganizationPermissions list different users permissions on an organization
diff --git a/modules/structs/org_team.go b/modules/structs/org_team.go
index 53e3fcf62d..10bd9e62ce 100644
--- a/modules/structs/org_team.go
+++ b/modules/structs/org_team.go
@@ -17,7 +17,7 @@ type Team struct {
// example: ["repo.code","repo.issues","repo.ext_issues","repo.wiki","repo.pulls","repo.releases","repo.projects","repo.ext_wiki"]
// Deprecated: This variable should be replaced by UnitsMap and will be dropped in later versions.
Units []string `json:"units"`
- // example: {"repo.code":"read","repo.issues":"write","repo.ext_issues":"none","repo.wiki":"admin","repo.pulls":"owner","repo.releases":"none","repo.projects":"none","repo.ext_wiki":"none"]
+ // example: {"repo.code":"read","repo.issues":"write","repo.ext_issues":"none","repo.wiki":"admin","repo.pulls":"owner","repo.releases":"none","repo.projects":"none","repo.ext_wiki":"none"}
UnitsMap map[string]string `json:"units_map"`
CanCreateOrgRepo bool `json:"can_create_org_repo"`
}
@@ -33,7 +33,7 @@ type CreateTeamOption struct {
// example: ["repo.code","repo.issues","repo.ext_issues","repo.wiki","repo.pulls","repo.releases","repo.projects","repo.ext_wiki"]
// Deprecated: This variable should be replaced by UnitsMap and will be dropped in later versions.
Units []string `json:"units"`
- // example: {"repo.code":"read","repo.issues":"write","repo.ext_issues":"none","repo.wiki":"admin","repo.pulls":"owner","repo.releases":"none","repo.projects":"none","repo.ext_wiki":"none"]
+ // example: {"repo.code":"read","repo.issues":"write","repo.ext_issues":"none","repo.wiki":"admin","repo.pulls":"owner","repo.releases":"none","repo.projects":"none","repo.ext_wiki":"none"}
UnitsMap map[string]string `json:"units_map"`
CanCreateOrgRepo bool `json:"can_create_org_repo"`
}
@@ -49,7 +49,7 @@ type EditTeamOption struct {
// example: ["repo.code","repo.issues","repo.ext_issues","repo.wiki","repo.pulls","repo.releases","repo.projects","repo.ext_wiki"]
// Deprecated: This variable should be replaced by UnitsMap and will be dropped in later versions.
Units []string `json:"units"`
- // example: {"repo.code":"read","repo.issues":"write","repo.ext_issues":"none","repo.wiki":"admin","repo.pulls":"owner","repo.releases":"none","repo.projects":"none","repo.ext_wiki":"none"]
+ // example: {"repo.code":"read","repo.issues":"write","repo.ext_issues":"none","repo.wiki":"admin","repo.pulls":"owner","repo.releases":"none","repo.projects":"none","repo.ext_wiki":"none"}
UnitsMap map[string]string `json:"units_map"`
CanCreateOrgRepo *bool `json:"can_create_org_repo"`
}
diff --git a/modules/structs/pull.go b/modules/structs/pull.go
index b63b3edfd3..f627241b26 100644
--- a/modules/structs/pull.go
+++ b/modules/structs/pull.go
@@ -95,3 +95,16 @@ type EditPullRequestOption struct {
RemoveDeadline *bool `json:"unset_due_date"`
AllowMaintainerEdit *bool `json:"allow_maintainer_edit"`
}
+
+// ChangedFile store information about files affected by the pull request
+type ChangedFile struct {
+ Filename string `json:"filename"`
+ PreviousFilename string `json:"previous_filename,omitempty"`
+ Status string `json:"status"`
+ Additions int `json:"additions"`
+ Deletions int `json:"deletions"`
+ Changes int `json:"changes"`
+ HTMLURL string `json:"html_url,omitempty"`
+ ContentsURL string `json:"contents_url,omitempty"`
+ RawURL string `json:"raw_url,omitempty"`
+}
diff --git a/modules/structs/repo.go b/modules/structs/repo.go
index 6a5736898d..cf6601704e 100644
--- a/modules/structs/repo.go
+++ b/modules/structs/repo.go
@@ -34,8 +34,10 @@ type ExternalTracker struct {
ExternalTrackerURL string `json:"external_tracker_url"`
// External Issue Tracker URL Format. Use the placeholders {user}, {repo} and {index} for the username, repository name and issue index.
ExternalTrackerFormat string `json:"external_tracker_format"`
- // External Issue Tracker Number Format, either `numeric` or `alphanumeric`
+ // External Issue Tracker Number Format, either `numeric`, `alphanumeric`, or `regexp`
ExternalTrackerStyle string `json:"external_tracker_style"`
+ // External Issue Tracker issue regular expression
+ ExternalTrackerRegexpPattern string `json:"external_tracker_regexp_pattern"`
}
// ExternalWiki represents setting for external wiki
@@ -111,7 +113,7 @@ type CreateRepoOption struct {
// unique: true
Name string `json:"name" binding:"Required;AlphaDashDot;MaxSize(100)"`
// Description of the repository to create
- Description string `json:"description" binding:"MaxSize(255)"`
+ Description string `json:"description" binding:"MaxSize(2048)"`
// Whether the repository is private
Private bool `json:"private"`
// Label-Set to use
@@ -140,9 +142,9 @@ type EditRepoOption struct {
// unique: true
Name *string `json:"name,omitempty" binding:"OmitEmpty;AlphaDashDot;MaxSize(100);"`
// a short description of the repository.
- Description *string `json:"description,omitempty" binding:"MaxSize(255)"`
+ Description *string `json:"description,omitempty" binding:"MaxSize(2048)"`
// a URL with more information about the repository.
- Website *string `json:"website,omitempty" binding:"MaxSize(255)"`
+ Website *string `json:"website,omitempty" binding:"MaxSize(1024)"`
// either `true` to make the repository private or `false` to make it public.
// Note: you will get a 422 error if the organization restricts changing repository visibility to organization
// owners and a non-owner tries to change the value of private.
@@ -151,13 +153,13 @@ type EditRepoOption struct {
Template *bool `json:"template,omitempty"`
// either `true` to enable issues for this repository or `false` to disable them.
HasIssues *bool `json:"has_issues,omitempty"`
- // set this structure to configure internal issue tracker (requires has_issues)
+ // set this structure to configure internal issue tracker
InternalTracker *InternalTracker `json:"internal_tracker,omitempty"`
- // set this structure to use external issue tracker (requires has_issues)
+ // set this structure to use external issue tracker
ExternalTracker *ExternalTracker `json:"external_tracker,omitempty"`
// either `true` to enable the wiki for this repository or `false` to disable it.
HasWiki *bool `json:"has_wiki,omitempty"`
- // set this structure to use external wiki instead of internal (requires has_wiki)
+ // set this structure to use external wiki instead of internal
ExternalWiki *ExternalWiki `json:"external_wiki,omitempty"`
// sets the default branch for this repository.
DefaultBranch *string `json:"default_branch,omitempty"`
@@ -165,25 +167,25 @@ type EditRepoOption struct {
HasPullRequests *bool `json:"has_pull_requests,omitempty"`
// either `true` to enable project unit, or `false` to disable them.
HasProjects *bool `json:"has_projects,omitempty"`
- // either `true` to ignore whitespace for conflicts, or `false` to not ignore whitespace. `has_pull_requests` must be `true`.
+ // either `true` to ignore whitespace for conflicts, or `false` to not ignore whitespace.
IgnoreWhitespaceConflicts *bool `json:"ignore_whitespace_conflicts,omitempty"`
- // either `true` to allow merging pull requests with a merge commit, or `false` to prevent merging pull requests with merge commits. `has_pull_requests` must be `true`.
+ // either `true` to allow merging pull requests with a merge commit, or `false` to prevent merging pull requests with merge commits.
AllowMerge *bool `json:"allow_merge_commits,omitempty"`
- // either `true` to allow rebase-merging pull requests, or `false` to prevent rebase-merging. `has_pull_requests` must be `true`.
+ // either `true` to allow rebase-merging pull requests, or `false` to prevent rebase-merging.
AllowRebase *bool `json:"allow_rebase,omitempty"`
- // either `true` to allow rebase with explicit merge commits (--no-ff), or `false` to prevent rebase with explicit merge commits. `has_pull_requests` must be `true`.
+ // either `true` to allow rebase with explicit merge commits (--no-ff), or `false` to prevent rebase with explicit merge commits.
AllowRebaseMerge *bool `json:"allow_rebase_explicit,omitempty"`
- // either `true` to allow squash-merging pull requests, or `false` to prevent squash-merging. `has_pull_requests` must be `true`.
+ // either `true` to allow squash-merging pull requests, or `false` to prevent squash-merging.
AllowSquash *bool `json:"allow_squash_merge,omitempty"`
- // either `true` to allow mark pr as merged manually, or `false` to prevent it. `has_pull_requests` must be `true`.
+ // either `true` to allow mark pr as merged manually, or `false` to prevent it.
AllowManualMerge *bool `json:"allow_manual_merge,omitempty"`
- // either `true` to enable AutodetectManualMerge, or `false` to prevent it. `has_pull_requests` must be `true`, Note: In some special cases, misjudgments can occur.
+ // either `true` to enable AutodetectManualMerge, or `false` to prevent it. Note: In some special cases, misjudgments can occur.
AutodetectManualMerge *bool `json:"autodetect_manual_merge,omitempty"`
- // either `true` to allow updating pull request branch by rebase, or `false` to prevent it. `has_pull_requests` must be `true`.
+ // either `true` to allow updating pull request branch by rebase, or `false` to prevent it.
AllowRebaseUpdate *bool `json:"allow_rebase_update,omitempty"`
// set to `true` to delete pr branch after merge by default
DefaultDeleteBranchAfterMerge *bool `json:"default_delete_branch_after_merge,omitempty"`
- // set to a merge style to be used by this repository: "merge", "rebase", "rebase-merge", or "squash". `has_pull_requests` must be `true`.
+ // set to a merge style to be used by this repository: "merge", "rebase", "rebase-merge", or "squash".
DefaultMergeStyle *string `json:"default_merge_style,omitempty"`
// set to `true` to archive this repository.
Archived *bool `json:"archived,omitempty"`
@@ -208,7 +210,7 @@ type GenerateRepoOption struct {
// Default branch of the new repository
DefaultBranch string `json:"default_branch"`
// Description of the repository to create
- Description string `json:"description" binding:"MaxSize(255)"`
+ Description string `json:"description" binding:"MaxSize(2048)"`
// Whether the repository is private
Private bool `json:"private"`
// include git content of default branch in template repo
@@ -316,7 +318,7 @@ type MigrateRepoOptions struct {
LFS bool `json:"lfs"`
LFSEndpoint string `json:"lfs_endpoint"`
Private bool `json:"private"`
- Description string `json:"description" binding:"MaxSize(255)"`
+ Description string `json:"description" binding:"MaxSize(2048)"`
Wiki bool `json:"wiki"`
Milestones bool `json:"milestones"`
Labels bool `json:"labels"`
diff --git a/modules/structs/repo_file.go b/modules/structs/repo_file.go
index 135e6484cd..ce1a9fe4be 100644
--- a/modules/structs/repo_file.go
+++ b/modules/structs/repo_file.go
@@ -87,9 +87,10 @@ type FileLinksResponse struct {
// ContentsResponse contains information about a repo's entry's (dir, file, symlink, submodule) metadata and content
type ContentsResponse struct {
- Name string `json:"name"`
- Path string `json:"path"`
- SHA string `json:"sha"`
+ Name string `json:"name"`
+ Path string `json:"path"`
+ SHA string `json:"sha"`
+ LastCommitSHA string `json:"last_commit_sha"`
// `type` will be `file`, `dir`, `symlink`, or `submodule`
Type string `json:"type"`
Size int64 `json:"size"`
diff --git a/modules/sync/status_pool.go b/modules/sync/status_pool.go
index acbd93ab17..99e5ce9cb3 100644
--- a/modules/sync/status_pool.go
+++ b/modules/sync/status_pool.go
@@ -6,6 +6,8 @@ package sync
import (
"sync"
+
+ "code.gitea.io/gitea/modules/container"
)
// StatusTable is a table maintains true/false values.
@@ -14,13 +16,13 @@ import (
// in different goroutines.
type StatusTable struct {
lock sync.RWMutex
- pool map[string]struct{}
+ pool container.Set[string]
}
// NewStatusTable initializes and returns a new StatusTable object.
func NewStatusTable() *StatusTable {
return &StatusTable{
- pool: make(map[string]struct{}),
+ pool: make(container.Set[string]),
}
}
@@ -28,32 +30,29 @@ func NewStatusTable() *StatusTable {
// Returns whether set value was set to true
func (p *StatusTable) StartIfNotRunning(name string) bool {
p.lock.Lock()
- _, ok := p.pool[name]
- if !ok {
- p.pool[name] = struct{}{}
- }
+ added := p.pool.Add(name)
p.lock.Unlock()
- return !ok
+ return added
}
// Start sets value of given name to true in the pool.
func (p *StatusTable) Start(name string) {
p.lock.Lock()
- p.pool[name] = struct{}{}
+ p.pool.Add(name)
p.lock.Unlock()
}
// Stop sets value of given name to false in the pool.
func (p *StatusTable) Stop(name string) {
p.lock.Lock()
- delete(p.pool, name)
+ p.pool.Remove(name)
p.lock.Unlock()
}
// IsRunning checks if value of given name is set to true in the pool.
func (p *StatusTable) IsRunning(name string) bool {
p.lock.RLock()
- _, ok := p.pool[name]
+ exists := p.pool.Contains(name)
p.lock.RUnlock()
- return ok
+ return exists
}
diff --git a/modules/appstate/appstate.go b/modules/system/appstate.go
similarity index 97%
rename from modules/appstate/appstate.go
rename to modules/system/appstate.go
index f65f5367e2..deee8cd029 100644
--- a/modules/appstate/appstate.go
+++ b/modules/system/appstate.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
-package appstate
+package system
// StateStore is the interface to get/set app state items
type StateStore interface {
diff --git a/modules/appstate/appstate_test.go b/modules/system/appstate_test.go
similarity index 98%
rename from modules/appstate/appstate_test.go
rename to modules/system/appstate_test.go
index e4a0d72850..fb0c2aaf9f 100644
--- a/modules/appstate/appstate_test.go
+++ b/modules/system/appstate_test.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
-package appstate
+package system
import (
"path/filepath"
diff --git a/modules/appstate/db.go b/modules/system/db.go
similarity index 77%
rename from modules/appstate/db.go
rename to modules/system/db.go
index 2538d1b5c8..b1c283c488 100644
--- a/modules/appstate/db.go
+++ b/modules/system/db.go
@@ -2,10 +2,10 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
-package appstate
+package system
import (
- "code.gitea.io/gitea/models/appstate"
+ "code.gitea.io/gitea/models/system"
"code.gitea.io/gitea/modules/json"
"github.com/yuin/goldmark/util"
@@ -16,7 +16,7 @@ type DBStore struct{}
// Get reads the state item
func (f *DBStore) Get(item StateItem) error {
- content, err := appstate.GetAppStateContent(item.Name())
+ content, err := system.GetAppStateContent(item.Name())
if err != nil {
return err
}
@@ -32,5 +32,5 @@ func (f *DBStore) Set(item StateItem) error {
if err != nil {
return err
}
- return appstate.SaveAppStateContent(item.Name(), util.BytesToReadOnlyString(b))
+ return system.SaveAppStateContent(item.Name(), util.BytesToReadOnlyString(b))
}
diff --git a/modules/appstate/item_runtime.go b/modules/system/item_runtime.go
similarity index 96%
rename from modules/appstate/item_runtime.go
rename to modules/system/item_runtime.go
index 7fdc53f642..ef758a5675 100644
--- a/modules/appstate/item_runtime.go
+++ b/modules/system/item_runtime.go
@@ -2,7 +2,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
-package appstate
+package system
// RuntimeState contains app state for runtime, and we can save remote version for update checker here in future
type RuntimeState struct {
diff --git a/modules/system/setting.go b/modules/system/setting.go
new file mode 100644
index 0000000000..aebf24a501
--- /dev/null
+++ b/modules/system/setting.go
@@ -0,0 +1,46 @@
+// Copyright 2021 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package system
+
+import (
+ "strconv"
+
+ "code.gitea.io/gitea/models/system"
+ "code.gitea.io/gitea/modules/cache"
+)
+
+func genKey(key string) string {
+ return "system.setting." + key
+}
+
+// GetSetting returns the setting value via the key
+func GetSetting(key string) (string, error) {
+ return cache.GetString(genKey(key), func() (string, error) {
+ res, err := system.GetSetting(key)
+ if err != nil {
+ return "", err
+ }
+ return res.SettingValue, nil
+ })
+}
+
+// GetSettingBool return bool value of setting,
+// none existing keys and errors are ignored and result in false
+func GetSettingBool(key string) bool {
+ s, _ := GetSetting(key)
+ b, _ := strconv.ParseBool(s)
+ return b
+}
+
+// SetSetting sets the setting value
+func SetSetting(key, value string, version int) error {
+ cache.Remove(genKey(key))
+
+ return system.SetSetting(&system.Setting{
+ SettingKey: key,
+ SettingValue: value,
+ Version: version,
+ })
+}
diff --git a/modules/system/user_setting.go b/modules/system/user_setting.go
new file mode 100644
index 0000000000..eaf146c08d
--- /dev/null
+++ b/modules/system/user_setting.go
@@ -0,0 +1,34 @@
+// Copyright 2021 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package system
+
+import (
+ "fmt"
+
+ "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/cache"
+)
+
+func genUserKey(userID int64, key string) string {
+ return fmt.Sprintf("user_%d.setting.%s", userID, key)
+}
+
+// GetUserSetting returns the user setting value via the key
+func GetUserSetting(userID int64, key string) (string, error) {
+ return cache.GetString(genUserKey(userID, key), func() (string, error) {
+ res, err := user.GetSetting(userID, key)
+ if err != nil {
+ return "", err
+ }
+ return res.SettingValue, nil
+ })
+}
+
+// SetUserSetting sets the user setting value
+func SetUserSetting(userID int64, key, value string) error {
+ cache.Remove(genUserKey(userID, key))
+
+ return user.SetUserSetting(userID, key, value)
+}
diff --git a/modules/templates/base.go b/modules/templates/base.go
index 282019f826..d234d531f3 100644
--- a/modules/templates/base.go
+++ b/modules/templates/base.go
@@ -5,15 +5,16 @@
package templates
import (
+ "fmt"
+ "io/fs"
"os"
+ "path/filepath"
"strings"
"time"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
-
- "github.com/unrolled/render"
)
// Vars represents variables to be render in golang templates
@@ -35,10 +36,11 @@ func BaseVars() Vars {
"IsLandingPageExplore": setting.LandingPageURL == setting.LandingPageExplore,
"IsLandingPageOrganizations": setting.LandingPageURL == setting.LandingPageOrganizations,
- "ShowRegistrationButton": setting.Service.ShowRegistrationButton,
- "ShowMilestonesDashboardPage": setting.Service.ShowMilestonesDashboardPage,
- "ShowFooterBranding": setting.ShowFooterBranding,
- "ShowFooterVersion": setting.ShowFooterVersion,
+ "ShowRegistrationButton": setting.Service.ShowRegistrationButton,
+ "ShowMilestonesDashboardPage": setting.Service.ShowMilestonesDashboardPage,
+ "ShowFooterBranding": setting.ShowFooterBranding,
+ "ShowFooterVersion": setting.ShowFooterVersion,
+ "DisableDownloadSourceArchives": setting.Repository.DisableDownloadSourceArchives,
"EnableSwagger": setting.API.EnableSwagger,
"EnableOpenIDSignIn": setting.Service.EnableOpenIDSignIn,
@@ -46,8 +48,16 @@ func BaseVars() Vars {
}
}
-func getDirAssetNames(dir string) []string {
+func getDirTemplateAssetNames(dir string) []string {
+ return getDirAssetNames(dir, false)
+}
+
+func getDirAssetNames(dir string, mailer bool) []string {
var tmpls []string
+
+ if mailer {
+ dir += filepath.Join(dir, "mail")
+ }
f, err := os.Stat(dir)
if err != nil {
if os.IsNotExist(err) {
@@ -66,8 +76,13 @@ func getDirAssetNames(dir string) []string {
log.Warn("Failed to read %s templates dir. %v", dir, err)
return tmpls
}
+
+ prefix := "templates/"
+ if mailer {
+ prefix += "mail/"
+ }
for _, filePath := range files {
- if strings.HasPrefix(filePath, "mail/") {
+ if !mailer && strings.HasPrefix(filePath, "mail/") {
continue
}
@@ -75,20 +90,39 @@ func getDirAssetNames(dir string) []string {
continue
}
- tmpls = append(tmpls, "templates/"+filePath)
+ tmpls = append(tmpls, prefix+filePath)
}
return tmpls
}
-// HTMLRenderer returns a render.
-func HTMLRenderer() *render.Render {
- return render.New(render.Options{
- Extensions: []string{".tmpl"},
- Directory: "templates",
- Funcs: NewFuncMap(),
- Asset: GetAsset,
- AssetNames: GetAssetNames,
- IsDevelopment: !setting.IsProd,
- DisableHTTPErrorRendering: true,
- })
+func walkAssetDir(root string, skipMail bool, callback func(path, name string, d fs.DirEntry, err error) error) error {
+ mailRoot := filepath.Join(root, "mail")
+ if err := filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error {
+ name := path[len(root):]
+ if len(name) > 0 && name[0] == '/' {
+ name = name[1:]
+ }
+ if err != nil {
+ if os.IsNotExist(err) {
+ return callback(path, name, d, err)
+ }
+ return err
+ }
+ if skipMail && path == mailRoot && d.IsDir() {
+ return fs.SkipDir
+ }
+ if util.CommonSkip(d.Name()) {
+ if d.IsDir() {
+ return fs.SkipDir
+ }
+ return nil
+ }
+ if strings.HasSuffix(d.Name(), ".tmpl") || d.IsDir() {
+ return callback(path, name, d, err)
+ }
+ return nil
+ }); err != nil && !os.IsNotExist(err) {
+ return fmt.Errorf("unable to get files for template assets in %s: %w", root, err)
+ }
+ return nil
}
diff --git a/modules/templates/dynamic.go b/modules/templates/dynamic.go
index de6968c314..a86e71a8c8 100644
--- a/modules/templates/dynamic.go
+++ b/modules/templates/dynamic.go
@@ -8,15 +8,12 @@ package templates
import (
"html/template"
+ "io/fs"
"os"
- "path"
"path/filepath"
- "strings"
texttmpl "text/template"
- "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/util"
)
var (
@@ -36,77 +33,57 @@ func GetAsset(name string) ([]byte, error) {
return os.ReadFile(filepath.Join(setting.StaticRootPath, name))
}
-// GetAssetNames returns assets list
-func GetAssetNames() []string {
- tmpls := getDirAssetNames(filepath.Join(setting.CustomPath, "templates"))
- tmpls2 := getDirAssetNames(filepath.Join(setting.StaticRootPath, "templates"))
+// GetAssetFilename returns the filename of the provided asset
+func GetAssetFilename(name string) (string, error) {
+ filename := filepath.Join(setting.CustomPath, name)
+ _, err := os.Stat(filename)
+ if err != nil && !os.IsNotExist(err) {
+ return filename, err
+ } else if err == nil {
+ return filename, nil
+ }
+
+ filename = filepath.Join(setting.StaticRootPath, name)
+ _, err = os.Stat(filename)
+ return filename, err
+}
+
+// walkTemplateFiles calls a callback for each template asset
+func walkTemplateFiles(callback func(path, name string, d fs.DirEntry, err error) error) error {
+ if err := walkAssetDir(filepath.Join(setting.CustomPath, "templates"), true, callback); err != nil && !os.IsNotExist(err) {
+ return err
+ }
+ if err := walkAssetDir(filepath.Join(setting.StaticRootPath, "templates"), true, callback); err != nil && !os.IsNotExist(err) {
+ return err
+ }
+ return nil
+}
+
+// GetTemplateAssetNames returns list of template names
+func GetTemplateAssetNames() []string {
+ tmpls := getDirTemplateAssetNames(filepath.Join(setting.CustomPath, "templates"))
+ tmpls2 := getDirTemplateAssetNames(filepath.Join(setting.StaticRootPath, "templates"))
return append(tmpls, tmpls2...)
}
-// Mailer provides the templates required for sending notification mails.
-func Mailer() (*texttmpl.Template, *template.Template) {
- for _, funcs := range NewTextFuncMap() {
- subjectTemplates.Funcs(funcs)
+func walkMailerTemplates(callback func(path, name string, d fs.DirEntry, err error) error) error {
+ if err := walkAssetDir(filepath.Join(setting.StaticRootPath, "templates", "mail"), false, callback); err != nil && !os.IsNotExist(err) {
+ return err
}
- for _, funcs := range NewFuncMap() {
- bodyTemplates.Funcs(funcs)
+ if err := walkAssetDir(filepath.Join(setting.CustomPath, "templates", "mail"), false, callback); err != nil && !os.IsNotExist(err) {
+ return err
}
-
- staticDir := path.Join(setting.StaticRootPath, "templates", "mail")
-
- isDir, err := util.IsDir(staticDir)
- if err != nil {
- log.Warn("Unable to check if templates dir %s is a directory. Error: %v", staticDir, err)
- }
- if isDir {
- files, err := util.StatDir(staticDir)
-
- if err != nil {
- log.Warn("Failed to read %s templates dir. %v", staticDir, err)
- } else {
- for _, filePath := range files {
- if !strings.HasSuffix(filePath, ".tmpl") {
- continue
- }
-
- content, err := os.ReadFile(path.Join(staticDir, filePath))
- if err != nil {
- log.Warn("Failed to read static %s template. %v", filePath, err)
- continue
- }
-
- buildSubjectBodyTemplate(subjectTemplates, bodyTemplates, strings.TrimSuffix(filePath, ".tmpl"), content)
- }
- }
- }
-
- customDir := path.Join(setting.CustomPath, "templates", "mail")
-
- isDir, err = util.IsDir(customDir)
- if err != nil {
- log.Warn("Unable to check if templates dir %s is a directory. Error: %v", customDir, err)
- }
- if isDir {
- files, err := util.StatDir(customDir)
-
- if err != nil {
- log.Warn("Failed to read %s templates dir. %v", customDir, err)
- } else {
- for _, filePath := range files {
- if !strings.HasSuffix(filePath, ".tmpl") {
- continue
- }
-
- content, err := os.ReadFile(path.Join(customDir, filePath))
- if err != nil {
- log.Warn("Failed to read custom %s template. %v", filePath, err)
- continue
- }
-
- buildSubjectBodyTemplate(subjectTemplates, bodyTemplates, strings.TrimSuffix(filePath, ".tmpl"), content)
- }
- }
- }
-
- return subjectTemplates, bodyTemplates
+ return nil
+}
+
+// BuiltinAsset will read the provided asset from the embedded assets
+// (This always returns os.ErrNotExist)
+func BuiltinAsset(name string) ([]byte, error) {
+ return nil, os.ErrNotExist
+}
+
+// BuiltinAssetNames returns the names of the embedded assets
+// (This always returns nil)
+func BuiltinAssetNames() []string {
+ return nil
}
diff --git a/modules/templates/helper.go b/modules/templates/helper.go
index 8a15cec2c6..7bd2bc0a1c 100644
--- a/modules/templates/helper.go
+++ b/modules/templates/helper.go
@@ -24,11 +24,12 @@ import (
"time"
"unicode"
- "code.gitea.io/gitea/models"
+ activities_model "code.gitea.io/gitea/models/activities"
"code.gitea.io/gitea/models/avatars"
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/organization"
repo_model "code.gitea.io/gitea/models/repo"
+ system_model "code.gitea.io/gitea/models/system"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/emoji"
@@ -41,6 +42,7 @@ import (
"code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/svg"
+ system_module "code.gitea.io/gitea/modules/system"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/services/gitdiff"
@@ -81,8 +83,11 @@ func NewFuncMap() []template.FuncMap {
"AppDomain": func() string {
return setting.Domain
},
+ "AssetVersion": func() string {
+ return setting.AssetVersion
+ },
"DisableGravatar": func() bool {
- return setting.DisableGravatar
+ return system_module.GetSettingBool(system_model.KeyPictureDisableGravatar)
},
"DefaultShowFullName": func() bool {
return setting.UI.DefaultShowFullName
@@ -150,7 +155,6 @@ func NewFuncMap() []template.FuncMap {
"DiffTypeToStr": DiffTypeToStr,
"DiffLineTypeToStr": DiffLineTypeToStr,
"ShortSha": base.ShortSha,
- "MD5": base.EncodeMD5,
"ActionContent2Commits": ActionContent2Commits,
"PathEscape": url.PathEscape,
"PathEscapeSegments": util.PathEscapeSegments,
@@ -159,6 +163,7 @@ func NewFuncMap() []template.FuncMap {
"RenderCommitMessageLink": RenderCommitMessageLink,
"RenderCommitMessageLinkSubject": RenderCommitMessageLinkSubject,
"RenderCommitBody": RenderCommitBody,
+ "RenderCodeBlock": RenderCodeBlock,
"RenderIssueTitle": RenderIssueTitle,
"RenderEmoji": RenderEmoji,
"RenderEmojiPlain": emoji.ReplaceAliases,
@@ -374,18 +379,18 @@ func NewFuncMap() []template.FuncMap {
// the table is NOT sorted with this header
return ""
},
- "RenderLabels": func(labels []*issues_model.Label) template.HTML {
- html := ``
+ "RenderLabels": func(labels []*issues_model.Label, repoLink string) template.HTML {
+ htmlCode := ``
for _, label := range labels {
// Protect against nil value in labels - shouldn't happen but would cause a panic if so
if label == nil {
continue
}
- html += fmt.Sprintf("%s
",
- label.ForegroundColor(), label.Color, RenderEmoji(label.Name))
+ htmlCode += fmt.Sprintf("%s ",
+ repoLink, label.ID, label.ForegroundColor(), label.Color, html.EscapeString(label.Description), RenderEmoji(label.Name))
}
- html += ""
- return template.HTML(html)
+ htmlCode += ""
+ return template.HTML(htmlCode)
},
"MermaidMaxSourceCharacters": func() int {
return setting.MermaidMaxSourceCharacters
@@ -453,6 +458,7 @@ func NewFuncMap() []template.FuncMap {
}
return items
},
+ "HasPrefix": strings.HasPrefix,
}}
}
@@ -628,7 +634,7 @@ func SVG(icon string, others ...interface{}) template.HTML {
// Avatar renders user avatars. args: user, size (int), class (string)
func Avatar(item interface{}, others ...interface{}) template.HTML {
- size, class := parseOthers(avatars.DefaultAvatarPixelSize, "ui avatar image vm", others...)
+ size, class := parseOthers(avatars.DefaultAvatarPixelSize, "ui avatar vm", others...)
switch t := item.(type) {
case *user_model.User:
@@ -652,14 +658,14 @@ func Avatar(item interface{}, others ...interface{}) template.HTML {
}
// AvatarByAction renders user avatars from action. args: action, size (int), class (string)
-func AvatarByAction(action *models.Action, others ...interface{}) template.HTML {
+func AvatarByAction(action *activities_model.Action, others ...interface{}) template.HTML {
action.LoadActUser()
return Avatar(action.ActUser, others...)
}
// RepoAvatar renders repo avatars. args: repo, size(int), class (string)
func RepoAvatar(repo *repo_model.Repository, others ...interface{}) template.HTML {
- size, class := parseOthers(avatars.DefaultAvatarPixelSize, "ui avatar image", others...)
+ size, class := parseOthers(avatars.DefaultAvatarPixelSize, "ui avatar", others...)
src := repo.RelAvatarLink()
if src != "" {
@@ -670,7 +676,7 @@ func RepoAvatar(repo *repo_model.Repository, others ...interface{}) template.HTM
// AvatarByEmail renders avatars by email address. args: email, name, size (int), class (string)
func AvatarByEmail(email, name string, others ...interface{}) template.HTML {
- size, class := parseOthers(avatars.DefaultAvatarPixelSize, "ui avatar image", others...)
+ size, class := parseOthers(avatars.DefaultAvatarPixelSize, "ui avatar", others...)
src := avatars.GenerateEmailAvatarFastLink(email, size*setting.Avatar.RenderedSizeFactor)
if src != "" {
@@ -792,6 +798,16 @@ func RenderCommitBody(ctx context.Context, msg, urlPrefix string, metas map[stri
return template.HTML(renderedMessage)
}
+// Match text that is between back ticks.
+var codeMatcher = regexp.MustCompile("`([^`]+)`")
+
+// RenderCodeBlock renders "`…`" as highlighted "" block.
+// Intended for issue and PR titles, these containers should have styles for "" elements
+func RenderCodeBlock(htmlEscapedTextToRender template.HTML) template.HTML {
+ htmlWithCodeTags := codeMatcher.ReplaceAllString(string(htmlEscapedTextToRender), "$1
") // replace with HTML tags
+ return template.HTML(htmlWithCodeTags)
+}
+
// RenderIssueTitle renders issue/pull title with defined post processors
func RenderIssueTitle(ctx context.Context, text, urlPrefix string, metas map[string]string) template.HTML {
renderedText, err := markup.RenderIssueTitle(&markup.RenderContext{
@@ -851,7 +867,7 @@ func IsMultilineCommitMessage(msg string) bool {
// Actioner describes an action
type Actioner interface {
- GetOpType() models.ActionType
+ GetOpType() activities_model.ActionType
GetActUserName() string
GetRepoUserName() string
GetRepoName() string
@@ -864,33 +880,33 @@ type Actioner interface {
}
// ActionIcon accepts an action operation type and returns an icon class name.
-func ActionIcon(opType models.ActionType) string {
+func ActionIcon(opType activities_model.ActionType) string {
switch opType {
- case models.ActionCreateRepo, models.ActionTransferRepo, models.ActionRenameRepo:
+ case activities_model.ActionCreateRepo, activities_model.ActionTransferRepo, activities_model.ActionRenameRepo:
return "repo"
- case models.ActionCommitRepo, models.ActionPushTag, models.ActionDeleteTag, models.ActionDeleteBranch:
+ case activities_model.ActionCommitRepo, activities_model.ActionPushTag, activities_model.ActionDeleteTag, activities_model.ActionDeleteBranch:
return "git-commit"
- case models.ActionCreateIssue:
+ case activities_model.ActionCreateIssue:
return "issue-opened"
- case models.ActionCreatePullRequest:
+ case activities_model.ActionCreatePullRequest:
return "git-pull-request"
- case models.ActionCommentIssue, models.ActionCommentPull:
+ case activities_model.ActionCommentIssue, activities_model.ActionCommentPull:
return "comment-discussion"
- case models.ActionMergePullRequest:
+ case activities_model.ActionMergePullRequest:
return "git-merge"
- case models.ActionCloseIssue, models.ActionClosePullRequest:
+ case activities_model.ActionCloseIssue, activities_model.ActionClosePullRequest:
return "issue-closed"
- case models.ActionReopenIssue, models.ActionReopenPullRequest:
+ case activities_model.ActionReopenIssue, activities_model.ActionReopenPullRequest:
return "issue-reopened"
- case models.ActionMirrorSyncPush, models.ActionMirrorSyncCreate, models.ActionMirrorSyncDelete:
+ case activities_model.ActionMirrorSyncPush, activities_model.ActionMirrorSyncCreate, activities_model.ActionMirrorSyncDelete:
return "mirror"
- case models.ActionApprovePullRequest:
+ case activities_model.ActionApprovePullRequest:
return "check"
- case models.ActionRejectPullRequest:
+ case activities_model.ActionRejectPullRequest:
return "diff"
- case models.ActionPublishRelease:
+ case activities_model.ActionPublishRelease:
return "tag"
- case models.ActionPullReviewDismissed:
+ case activities_model.ActionPullReviewDismissed:
return "x"
default:
return "question"
@@ -972,11 +988,11 @@ type remoteAddress struct {
Password string
}
-func mirrorRemoteAddress(ctx context.Context, m *repo_model.Repository, remoteName string) remoteAddress {
+func mirrorRemoteAddress(ctx context.Context, m *repo_model.Repository, remoteName string, ignoreOriginalURL bool) remoteAddress {
a := remoteAddress{}
remoteURL := m.OriginalURL
- if remoteURL == "" {
+ if ignoreOriginalURL || remoteURL == "" {
var err error
remoteURL, err = git.GetRemoteAddress(ctx, m.RepoPath(), remoteName)
if err != nil {
diff --git a/modules/templates/htmlrenderer.go b/modules/templates/htmlrenderer.go
new file mode 100644
index 0000000000..81ea660161
--- /dev/null
+++ b/modules/templates/htmlrenderer.go
@@ -0,0 +1,249 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package templates
+
+import (
+ "bytes"
+ "context"
+ "fmt"
+ "regexp"
+ "strconv"
+ "strings"
+
+ "code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/watcher"
+
+ "github.com/unrolled/render"
+)
+
+var (
+ rendererKey interface{} = "templatesHtmlRenderer"
+
+ templateError = regexp.MustCompile(`^template: (.*):([0-9]+): (.*)`)
+ notDefinedError = regexp.MustCompile(`^template: (.*):([0-9]+): function "(.*)" not defined`)
+ unexpectedError = regexp.MustCompile(`^template: (.*):([0-9]+): unexpected "(.*)" in operand`)
+ expectedEndError = regexp.MustCompile(`^template: (.*):([0-9]+): expected end; found (.*)`)
+)
+
+// HTMLRenderer returns the current html renderer for the context or creates and stores one within the context for future use
+func HTMLRenderer(ctx context.Context) (context.Context, *render.Render) {
+ rendererInterface := ctx.Value(rendererKey)
+ if rendererInterface != nil {
+ renderer, ok := rendererInterface.(*render.Render)
+ if ok {
+ return ctx, renderer
+ }
+ }
+
+ rendererType := "static"
+ if !setting.IsProd {
+ rendererType = "auto-reloading"
+ }
+ log.Log(1, log.DEBUG, "Creating "+rendererType+" HTML Renderer")
+
+ compilingTemplates := true
+ defer func() {
+ if !compilingTemplates {
+ return
+ }
+
+ panicked := recover()
+ if panicked == nil {
+ return
+ }
+
+ // OK try to handle the panic...
+ err, ok := panicked.(error)
+ if ok {
+ handlePanicError(err)
+ }
+ log.Fatal("PANIC: Unable to compile templates!\n%v\n\nStacktrace:\n%s", panicked, log.Stack(2))
+ }()
+
+ renderer := render.New(render.Options{
+ Extensions: []string{".tmpl"},
+ Directory: "templates",
+ Funcs: NewFuncMap(),
+ Asset: GetAsset,
+ AssetNames: GetTemplateAssetNames,
+ UseMutexLock: !setting.IsProd,
+ IsDevelopment: false,
+ DisableHTTPErrorRendering: true,
+ })
+ compilingTemplates = false
+ if !setting.IsProd {
+ watcher.CreateWatcher(ctx, "HTML Templates", &watcher.CreateWatcherOpts{
+ PathsCallback: walkTemplateFiles,
+ BetweenCallback: renderer.CompileTemplates,
+ })
+ }
+ return context.WithValue(ctx, rendererKey, renderer), renderer
+}
+
+func handlePanicError(err error) {
+ wrapFatal(handleNotDefinedPanicError(err))
+ wrapFatal(handleUnexpected(err))
+ wrapFatal(handleExpectedEnd(err))
+ wrapFatal(handleGenericTemplateError(err))
+}
+
+func wrapFatal(format string, args []interface{}) {
+ if format == "" {
+ return
+ }
+ log.FatalWithSkip(1, format, args...)
+}
+
+func handleGenericTemplateError(err error) (string, []interface{}) {
+ groups := templateError.FindStringSubmatch(err.Error())
+ if len(groups) != 4 {
+ return "", nil
+ }
+
+ templateName, lineNumberStr, message := groups[1], groups[2], groups[3]
+
+ filename, assetErr := GetAssetFilename("templates/" + templateName + ".tmpl")
+ if assetErr != nil {
+ return "", nil
+ }
+
+ lineNumber, _ := strconv.Atoi(lineNumberStr)
+
+ line := getLineFromAsset(templateName, lineNumber, "")
+
+ return "PANIC: Unable to compile templates!\n%s in template file %s at line %d:\n\n%s\nStacktrace:\n\n%s", []interface{}{message, filename, lineNumber, log.NewColoredValue(line, log.Reset), log.Stack(2)}
+}
+
+func handleNotDefinedPanicError(err error) (string, []interface{}) {
+ groups := notDefinedError.FindStringSubmatch(err.Error())
+ if len(groups) != 4 {
+ return "", nil
+ }
+
+ templateName, lineNumberStr, functionName := groups[1], groups[2], groups[3]
+
+ functionName, _ = strconv.Unquote(`"` + functionName + `"`)
+
+ filename, assetErr := GetAssetFilename("templates/" + templateName + ".tmpl")
+ if assetErr != nil {
+ return "", nil
+ }
+
+ lineNumber, _ := strconv.Atoi(lineNumberStr)
+
+ line := getLineFromAsset(templateName, lineNumber, functionName)
+
+ return "PANIC: Unable to compile templates!\nUndefined function %q in template file %s at line %d:\n\n%s", []interface{}{functionName, filename, lineNumber, log.NewColoredValue(line, log.Reset)}
+}
+
+func handleUnexpected(err error) (string, []interface{}) {
+ groups := unexpectedError.FindStringSubmatch(err.Error())
+ if len(groups) != 4 {
+ return "", nil
+ }
+
+ templateName, lineNumberStr, unexpected := groups[1], groups[2], groups[3]
+ unexpected, _ = strconv.Unquote(`"` + unexpected + `"`)
+
+ filename, assetErr := GetAssetFilename("templates/" + templateName + ".tmpl")
+ if assetErr != nil {
+ return "", nil
+ }
+
+ lineNumber, _ := strconv.Atoi(lineNumberStr)
+
+ line := getLineFromAsset(templateName, lineNumber, unexpected)
+
+ return "PANIC: Unable to compile templates!\nUnexpected %q in template file %s at line %d:\n\n%s", []interface{}{unexpected, filename, lineNumber, log.NewColoredValue(line, log.Reset)}
+}
+
+func handleExpectedEnd(err error) (string, []interface{}) {
+ groups := expectedEndError.FindStringSubmatch(err.Error())
+ if len(groups) != 4 {
+ return "", nil
+ }
+
+ templateName, lineNumberStr, unexpected := groups[1], groups[2], groups[3]
+
+ filename, assetErr := GetAssetFilename("templates/" + templateName + ".tmpl")
+ if assetErr != nil {
+ return "", nil
+ }
+
+ lineNumber, _ := strconv.Atoi(lineNumberStr)
+
+ line := getLineFromAsset(templateName, lineNumber, unexpected)
+
+ return "PANIC: Unable to compile templates!\nMissing end with unexpected %q in template file %s at line %d:\n\n%s", []interface{}{unexpected, filename, lineNumber, log.NewColoredValue(line, log.Reset)}
+}
+
+const dashSeparator = "----------------------------------------------------------------------\n"
+
+func getLineFromAsset(templateName string, targetLineNum int, target string) string {
+ bs, err := GetAsset("templates/" + templateName + ".tmpl")
+ if err != nil {
+ return fmt.Sprintf("(unable to read template file: %v)", err)
+ }
+
+ sb := &strings.Builder{}
+
+ // Write the header
+ sb.WriteString(dashSeparator)
+
+ var lineBs []byte
+
+ // Iterate through the lines from the asset file to find the target line
+ for start, currentLineNum := 0, 1; currentLineNum <= targetLineNum && start < len(bs); currentLineNum++ {
+ // Find the next new line
+ end := bytes.IndexByte(bs[start:], '\n')
+
+ // adjust the end to be a direct pointer in to []byte
+ if end < 0 {
+ end = len(bs)
+ } else {
+ end += start
+ }
+
+ // set lineBs to the current line []byte
+ lineBs = bs[start:end]
+
+ // move start to after the current new line position
+ start = end + 1
+
+ // Write 2 preceding lines + the target line
+ if targetLineNum-currentLineNum < 3 {
+ _, _ = sb.Write(lineBs)
+ _ = sb.WriteByte('\n')
+ }
+ }
+
+ // If there is a provided target to look for in the line add a pointer to it
+ // e.g. ^^^^^^^
+ if target != "" {
+ idx := bytes.Index(lineBs, []byte(target))
+
+ if idx >= 0 {
+ // take the current line and replace preceding text with whitespace (except for tab)
+ for i := range lineBs[:idx] {
+ if lineBs[i] != '\t' {
+ lineBs[i] = ' '
+ }
+ }
+
+ // write the preceding "space"
+ _, _ = sb.Write(lineBs[:idx])
+
+ // Now write the ^^ pointer
+ _, _ = sb.WriteString(strings.Repeat("^", len(target)))
+ _ = sb.WriteByte('\n')
+ }
+ }
+
+ // Finally write the footer
+ sb.WriteString(dashSeparator)
+
+ return sb.String()
+}
diff --git a/modules/templates/mailer.go b/modules/templates/mailer.go
new file mode 100644
index 0000000000..0cac1280f3
--- /dev/null
+++ b/modules/templates/mailer.go
@@ -0,0 +1,92 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package templates
+
+import (
+ "context"
+ "html/template"
+ "io/fs"
+ "os"
+ "strings"
+ texttmpl "text/template"
+
+ "code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/watcher"
+)
+
+// Mailer provides the templates required for sending notification mails.
+func Mailer(ctx context.Context) (*texttmpl.Template, *template.Template) {
+ for _, funcs := range NewTextFuncMap() {
+ subjectTemplates.Funcs(funcs)
+ }
+ for _, funcs := range NewFuncMap() {
+ bodyTemplates.Funcs(funcs)
+ }
+
+ refreshTemplates := func() {
+ for _, assetPath := range BuiltinAssetNames() {
+ if !strings.HasPrefix(assetPath, "mail/") {
+ continue
+ }
+
+ if !strings.HasSuffix(assetPath, ".tmpl") {
+ continue
+ }
+
+ content, err := BuiltinAsset(assetPath)
+ if err != nil {
+ log.Warn("Failed to read embedded %s template. %v", assetPath, err)
+ continue
+ }
+
+ assetName := strings.TrimPrefix(strings.TrimSuffix(assetPath, ".tmpl"), "mail/")
+
+ log.Trace("Adding built-in mailer template for %s", assetName)
+ buildSubjectBodyTemplate(subjectTemplates,
+ bodyTemplates,
+ assetName,
+ content)
+ }
+
+ if err := walkMailerTemplates(func(path, name string, d fs.DirEntry, err error) error {
+ if err != nil {
+ return err
+ }
+ if d.IsDir() {
+ return nil
+ }
+
+ content, err := os.ReadFile(path)
+ if err != nil {
+ log.Warn("Failed to read custom %s template. %v", path, err)
+ return nil
+ }
+
+ assetName := strings.TrimSuffix(name, ".tmpl")
+ log.Trace("Adding mailer template for %s from %q", assetName, path)
+ buildSubjectBodyTemplate(subjectTemplates,
+ bodyTemplates,
+ assetName,
+ content)
+ return nil
+ }); err != nil && !os.IsNotExist(err) {
+ log.Warn("Error whilst walking mailer templates directories. %v", err)
+ }
+ }
+
+ refreshTemplates()
+
+ if !setting.IsProd {
+ // Now subjectTemplates and bodyTemplates are both synchronized
+ // thus it is safe to call refresh from a different goroutine
+ watcher.CreateWatcher(ctx, "Mailer Templates", &watcher.CreateWatcherOpts{
+ PathsCallback: walkMailerTemplates,
+ BetweenCallback: refreshTemplates,
+ })
+ }
+
+ return subjectTemplates, bodyTemplates
+}
diff --git a/modules/templates/static.go b/modules/templates/static.go
index 351e48b4da..7f7cbe702f 100644
--- a/modules/templates/static.go
+++ b/modules/templates/static.go
@@ -9,6 +9,7 @@ package templates
import (
"html/template"
"io"
+ "io/fs"
"os"
"path"
"path/filepath"
@@ -16,10 +17,8 @@ import (
texttmpl "text/template"
"time"
- "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/timeutil"
- "code.gitea.io/gitea/modules/util"
)
var (
@@ -32,6 +31,18 @@ func GlobalModTime(filename string) time.Time {
return timeutil.GetExecutableModTime()
}
+// GetAssetFilename returns the filename of the provided asset
+func GetAssetFilename(name string) (string, error) {
+ filename := filepath.Join(setting.CustomPath, name)
+ _, err := os.Stat(filename)
+ if err != nil && !os.IsNotExist(err) {
+ return name, err
+ } else if err == nil {
+ return filename, nil
+ }
+ return "(builtin) " + name, nil
+}
+
// GetAsset get a special asset, only for chi
func GetAsset(name string) ([]byte, error) {
bs, err := os.ReadFile(filepath.Join(setting.CustomPath, name))
@@ -40,95 +51,42 @@ func GetAsset(name string) ([]byte, error) {
} else if err == nil {
return bs, nil
}
- return Asset(strings.TrimPrefix(name, "templates/"))
+ return BuiltinAsset(strings.TrimPrefix(name, "templates/"))
}
-// GetAssetNames only for chi
-func GetAssetNames() []string {
+// GetFiles calls a callback for each template asset
+func walkTemplateFiles(callback func(path, name string, d fs.DirEntry, err error) error) error {
+ if err := walkAssetDir(filepath.Join(setting.CustomPath, "templates"), true, callback); err != nil && !os.IsNotExist(err) {
+ return err
+ }
+ return nil
+}
+
+// GetTemplateAssetNames only for chi
+func GetTemplateAssetNames() []string {
realFS := Assets.(vfsgen۰FS)
tmpls := make([]string, 0, len(realFS))
for k := range realFS {
+ if strings.HasPrefix(k, "/mail/") {
+ continue
+ }
tmpls = append(tmpls, "templates/"+k[1:])
}
customDir := path.Join(setting.CustomPath, "templates")
- customTmpls := getDirAssetNames(customDir)
+ customTmpls := getDirTemplateAssetNames(customDir)
return append(tmpls, customTmpls...)
}
-// Mailer provides the templates required for sending notification mails.
-func Mailer() (*texttmpl.Template, *template.Template) {
- for _, funcs := range NewTextFuncMap() {
- subjectTemplates.Funcs(funcs)
+func walkMailerTemplates(callback func(path, name string, d fs.DirEntry, err error) error) error {
+ if err := walkAssetDir(filepath.Join(setting.CustomPath, "templates", "mail"), false, callback); err != nil && !os.IsNotExist(err) {
+ return err
}
- for _, funcs := range NewFuncMap() {
- bodyTemplates.Funcs(funcs)
- }
-
- for _, assetPath := range AssetNames() {
- if !strings.HasPrefix(assetPath, "mail/") {
- continue
- }
-
- if !strings.HasSuffix(assetPath, ".tmpl") {
- continue
- }
-
- content, err := Asset(assetPath)
- if err != nil {
- log.Warn("Failed to read embedded %s template. %v", assetPath, err)
- continue
- }
-
- buildSubjectBodyTemplate(subjectTemplates,
- bodyTemplates,
- strings.TrimPrefix(
- strings.TrimSuffix(
- assetPath,
- ".tmpl",
- ),
- "mail/",
- ),
- content)
- }
-
- customDir := path.Join(setting.CustomPath, "templates", "mail")
- isDir, err := util.IsDir(customDir)
- if err != nil {
- log.Warn("Failed to check if custom directory %s is a directory. %v", err)
- }
- if isDir {
- files, err := util.StatDir(customDir)
-
- if err != nil {
- log.Warn("Failed to read %s templates dir. %v", customDir, err)
- } else {
- for _, filePath := range files {
- if !strings.HasSuffix(filePath, ".tmpl") {
- continue
- }
-
- content, err := os.ReadFile(path.Join(customDir, filePath))
- if err != nil {
- log.Warn("Failed to read custom %s template. %v", filePath, err)
- continue
- }
-
- buildSubjectBodyTemplate(subjectTemplates,
- bodyTemplates,
- strings.TrimSuffix(
- filePath,
- ".tmpl",
- ),
- content)
- }
- }
- }
-
- return subjectTemplates, bodyTemplates
+ return nil
}
-func Asset(name string) ([]byte, error) {
+// BuiltinAsset reads the provided asset from the builtin embedded assets
+func BuiltinAsset(name string) ([]byte, error) {
f, err := Assets.Open("/" + name)
if err != nil {
return nil, err
@@ -137,7 +95,8 @@ func Asset(name string) ([]byte, error) {
return io.ReadAll(f)
}
-func AssetNames() []string {
+// BuiltinAssetNames returns the names of the built-in embedded assets
+func BuiltinAssetNames() []string {
realFS := Assets.(vfsgen۰FS)
results := make([]string, 0, len(realFS))
for k := range realFS {
@@ -146,7 +105,8 @@ func AssetNames() []string {
return results
}
-func AssetIsDir(name string) (bool, error) {
+// BuiltinAssetIsDir returns if a provided asset is a directory
+func BuiltinAssetIsDir(name string) (bool, error) {
if f, err := Assets.Open("/" + name); err != nil {
return false, err
} else {
diff --git a/modules/test/context_tests.go b/modules/test/context_tests.go
index a08439e93f..963f79c3c6 100644
--- a/modules/test/context_tests.go
+++ b/modules/test/context_tests.go
@@ -56,7 +56,7 @@ func MockContext(t *testing.T, path string) *context.Context {
// LoadRepo load a repo into a test context.
func LoadRepo(t *testing.T, ctx *context.Context, repoID int64) {
ctx.Repo = &context.Repository{}
- ctx.Repo.Repository = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repoID}).(*repo_model.Repository)
+ ctx.Repo.Repository = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repoID})
var err error
ctx.Repo.Owner, err = user_model.GetUserByID(ctx.Repo.Repository.OwnerID)
assert.NoError(t, err)
@@ -81,7 +81,7 @@ func LoadRepoCommit(t *testing.T, ctx *context.Context) {
// LoadUser load a user into a test context.
func LoadUser(t *testing.T, ctx *context.Context, userID int64) {
- ctx.Doer = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: userID}).(*user_model.User)
+ ctx.Doer = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: userID})
}
// LoadGitRepo load a git repo into a test context. Requires that ctx.Repo has
diff --git a/modules/timeutil/since.go b/modules/timeutil/since.go
index 5e89b0faa2..8473d45051 100644
--- a/modules/timeutil/since.go
+++ b/modules/timeutil/since.go
@@ -234,7 +234,7 @@ func TimeSince(then time.Time, lang translation.Locale) template.HTML {
}
func htmlTimeSince(then, now time.Time, lang translation.Locale) template.HTML {
- return template.HTML(fmt.Sprintf(`%s`,
+ return template.HTML(fmt.Sprintf(`%s`,
then.In(setting.DefaultUILocation).Format(GetTimeFormat(lang.Language())),
timeSince(then, now, lang)))
}
@@ -245,7 +245,7 @@ func TimeSinceUnix(then TimeStamp, lang translation.Locale) template.HTML {
}
func htmlTimeSinceUnix(then, now TimeStamp, lang translation.Locale) template.HTML {
- return template.HTML(fmt.Sprintf(`%s`,
+ return template.HTML(fmt.Sprintf(`%s`,
then.FormatInLocation(GetTimeFormat(lang.Language()), setting.DefaultUILocation),
timeSinceUnix(int64(then), int64(now), lang)))
}
diff --git a/modules/timeutil/since_test.go b/modules/timeutil/since_test.go
index 8bdb9d7546..9350b5e96b 100644
--- a/modules/timeutil/since_test.go
+++ b/modules/timeutil/since_test.go
@@ -5,6 +5,7 @@
package timeutil
import (
+ "context"
"fmt"
"os"
"testing"
@@ -31,7 +32,7 @@ func TestMain(m *testing.M) {
setting.Names = []string{"english"}
setting.Langs = []string{"en-US"}
// setup
- translation.InitLocales()
+ translation.InitLocales(context.Background())
BaseDate = time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC)
// run the tests
@@ -119,7 +120,7 @@ func TestHtmlTimeSince(t *testing.T) {
// test that `diff` yields a result containing `expected`
test := func(expected string, diff time.Duration) {
actual := htmlTimeSince(BaseDate, BaseDate.Add(diff), translation.NewLocale("en-US"))
- assert.Contains(t, actual, `title="Sat Jan 1 00:00:00 UTC 2000"`)
+ assert.Contains(t, actual, `data-content="Sat Jan 1 00:00:00 UTC 2000"`)
assert.Contains(t, actual, expected)
}
test("1 second", time.Second)
diff --git a/modules/timeutil/timestamp.go b/modules/timeutil/timestamp.go
index 88008d1fad..40fcb8603f 100644
--- a/modules/timeutil/timestamp.go
+++ b/modules/timeutil/timestamp.go
@@ -54,6 +54,11 @@ func (ts TimeStamp) AsTime() (tm time.Time) {
return ts.AsTimeInLocation(setting.DefaultUILocation)
}
+// AsLocalTime convert timestamp as time.Time in local location
+func (ts TimeStamp) AsLocalTime() time.Time {
+ return time.Unix(int64(ts), 0)
+}
+
// AsTimeInLocation convert timestamp as time.Time in Local locale
func (ts TimeStamp) AsTimeInLocation(loc *time.Location) (tm time.Time) {
tm = time.Unix(int64(ts), 0).In(loc)
diff --git a/modules/translation/i18n/errors.go b/modules/translation/i18n/errors.go
new file mode 100644
index 0000000000..a81b0bc1ac
--- /dev/null
+++ b/modules/translation/i18n/errors.go
@@ -0,0 +1,14 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package i18n
+
+import (
+ "code.gitea.io/gitea/modules/util"
+)
+
+var (
+ ErrLocaleAlreadyExist = util.SilentWrap{Message: "lang already exists", Err: util.ErrAlreadyExist}
+ ErrUncertainArguments = util.SilentWrap{Message: "arguments to i18n should not contain uncertain slices", Err: util.ErrInvalidArgument}
+)
diff --git a/modules/translation/i18n/format.go b/modules/translation/i18n/format.go
new file mode 100644
index 0000000000..3fb9e6d6d0
--- /dev/null
+++ b/modules/translation/i18n/format.go
@@ -0,0 +1,42 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package i18n
+
+import (
+ "fmt"
+ "reflect"
+)
+
+// Format formats provided arguments for a given translated message
+func Format(format string, args ...interface{}) (msg string, err error) {
+ if len(args) == 0 {
+ return format, nil
+ }
+
+ fmtArgs := make([]interface{}, 0, len(args))
+ for _, arg := range args {
+ val := reflect.ValueOf(arg)
+ if val.Kind() == reflect.Slice {
+ // Previously, we would accept Tr(lang, key, a, [b, c], d, [e, f]) as Sprintf(msg, a, b, c, d, e, f)
+ // but this is an unstable behavior.
+ //
+ // So we restrict the accepted arguments to either:
+ //
+ // 1. Tr(lang, key, [slice-items]) as Sprintf(msg, items...)
+ // 2. Tr(lang, key, args...) as Sprintf(msg, args...)
+ if len(args) == 1 {
+ for i := 0; i < val.Len(); i++ {
+ fmtArgs = append(fmtArgs, val.Index(i).Interface())
+ }
+ } else {
+ err = ErrUncertainArguments
+ break
+ }
+ } else {
+ fmtArgs = append(fmtArgs, arg)
+ }
+ }
+ return fmt.Sprintf(format, fmtArgs...), err
+}
diff --git a/modules/translation/i18n/i18n.go b/modules/translation/i18n/i18n.go
index bb906f3c08..d8ed43a1cd 100644
--- a/modules/translation/i18n/i18n.go
+++ b/modules/translation/i18n/i18n.go
@@ -5,297 +5,48 @@
package i18n
import (
- "errors"
- "fmt"
- "os"
- "reflect"
- "sync"
- "time"
-
- "code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/setting"
-
- "gopkg.in/ini.v1"
+ "io"
)
-var (
- ErrLocaleAlreadyExist = errors.New("lang already exists")
+var DefaultLocales = NewLocaleStore()
- DefaultLocales = NewLocaleStore(true)
-)
-
-type locale struct {
- // This mutex will be set if we have live-reload enabled (e.g. dev mode)
- reloadMu *sync.RWMutex
-
- store *LocaleStore
- langName string
-
- idxToMsgMap map[int]string // the map idx is generated by store's trKeyToIdxMap
-
- sourceFileName string
- sourceFileInfo os.FileInfo
- lastReloadCheckTime time.Time
+type Locale interface {
+ // Tr translates a given key and arguments for a language
+ Tr(trKey string, trArgs ...interface{}) string
+ // Has reports if a locale has a translation for a given key
+ Has(trKey string) bool
}
-type LocaleStore struct {
- // This mutex will be set if we have live-reload enabled (e.g. dev mode)
- reloadMu *sync.RWMutex
+// LocaleStore provides the functions common to all locale stores
+type LocaleStore interface {
+ io.Closer
- langNames []string
- langDescs []string
- localeMap map[string]*locale
-
- // this needs to be locked when live-reloading
- trKeyToIdxMap map[string]int
-
- defaultLang string
-}
-
-func NewLocaleStore(isProd bool) *LocaleStore {
- store := &LocaleStore{localeMap: make(map[string]*locale), trKeyToIdxMap: make(map[string]int)}
- if !isProd {
- store.reloadMu = &sync.RWMutex{}
- }
- return store
-}
-
-// AddLocaleByIni adds locale by ini into the store
-// if source is a string, then the file is loaded. In dev mode, this file will be checked for live-reloading
-// if source is a []byte, then the content is used
-// Note: this is not concurrent safe
-func (store *LocaleStore) AddLocaleByIni(langName, langDesc string, source interface{}) error {
- if _, ok := store.localeMap[langName]; ok {
- return ErrLocaleAlreadyExist
- }
-
- l := &locale{store: store, langName: langName}
- if store.reloadMu != nil {
- l.reloadMu = &sync.RWMutex{}
- l.reloadMu.Lock() // Arguably this is not necessary as AddLocaleByIni isn't concurrent safe - but for consistency we do this
- defer l.reloadMu.Unlock()
- }
-
- if fileName, ok := source.(string); ok {
- l.sourceFileName = fileName
- l.sourceFileInfo, _ = os.Stat(fileName) // live-reload only works for regular files. the error can be ignored
- }
-
- var err error
- l.idxToMsgMap, err = store.readIniToIdxToMsgMap(source)
- if err != nil {
- return err
- }
-
- store.langNames = append(store.langNames, langName)
- store.langDescs = append(store.langDescs, langDesc)
-
- store.localeMap[l.langName] = l
-
- return nil
-}
-
-// readIniToIdxToMsgMap will read a provided ini and creates an idxToMsgMap
-func (store *LocaleStore) readIniToIdxToMsgMap(source interface{}) (map[int]string, error) {
- iniFile, err := ini.LoadSources(ini.LoadOptions{
- IgnoreInlineComment: true,
- UnescapeValueCommentSymbols: true,
- }, source)
- if err != nil {
- return nil, fmt.Errorf("unable to load ini: %w", err)
- }
- iniFile.BlockMode = false
-
- idxToMsgMap := make(map[int]string)
-
- if store.reloadMu != nil {
- store.reloadMu.Lock()
- defer store.reloadMu.Unlock()
- }
-
- for _, section := range iniFile.Sections() {
- for _, key := range section.Keys() {
-
- var trKey string
- if section.Name() == "" || section.Name() == "DEFAULT" {
- trKey = key.Name()
- } else {
- trKey = section.Name() + "." + key.Name()
- }
-
- // Instead of storing the key strings in multiple different maps we compute a idx which will act as numeric code for key
- // This reduces the size of the locale idxToMsgMaps
- idx, ok := store.trKeyToIdxMap[trKey]
- if !ok {
- idx = len(store.trKeyToIdxMap)
- store.trKeyToIdxMap[trKey] = idx
- }
- idxToMsgMap[idx] = key.Value()
- }
- }
- iniFile = nil
- return idxToMsgMap, nil
-}
-
-func (store *LocaleStore) idxForTrKey(trKey string) (int, bool) {
- if store.reloadMu != nil {
- store.reloadMu.RLock()
- defer store.reloadMu.RUnlock()
- }
- idx, ok := store.trKeyToIdxMap[trKey]
- return idx, ok
-}
-
-// HasLang reports if a language is available in the store
-func (store *LocaleStore) HasLang(langName string) bool {
- _, ok := store.localeMap[langName]
- return ok
-}
-
-// ListLangNameDesc reports if a language available in the store
-func (store *LocaleStore) ListLangNameDesc() (names, desc []string) {
- return store.langNames, store.langDescs
-}
-
-// SetDefaultLang sets default language as a fallback
-func (store *LocaleStore) SetDefaultLang(lang string) {
- store.defaultLang = lang
-}
-
-// Tr translates content to target language. fall back to default language.
-func (store *LocaleStore) Tr(lang, trKey string, trArgs ...interface{}) string {
- l, ok := store.localeMap[lang]
- if !ok {
- l, ok = store.localeMap[store.defaultLang]
- }
-
- if ok {
- return l.Tr(trKey, trArgs...)
- }
- return trKey
-}
-
-// reloadIfNeeded will check if the locale needs to be reloaded
-// this function will assume that the l.reloadMu has been RLocked if it already exists
-func (l *locale) reloadIfNeeded() {
- if l.reloadMu == nil {
- return
- }
-
- now := time.Now()
- if now.Sub(l.lastReloadCheckTime) < time.Second || l.sourceFileInfo == nil || l.sourceFileName == "" {
- return
- }
-
- l.reloadMu.RUnlock()
- l.reloadMu.Lock() // (NOTE: a pre-emption can occur between these two locks so we need to recheck)
- defer l.reloadMu.RLock()
- defer l.reloadMu.Unlock()
-
- if now.Sub(l.lastReloadCheckTime) < time.Second || l.sourceFileInfo == nil || l.sourceFileName == "" {
- return
- }
-
- l.lastReloadCheckTime = now
- sourceFileInfo, err := os.Stat(l.sourceFileName)
- if err != nil || sourceFileInfo.ModTime().Equal(l.sourceFileInfo.ModTime()) {
- return
- }
-
- idxToMsgMap, err := l.store.readIniToIdxToMsgMap(l.sourceFileName)
- if err == nil {
- l.idxToMsgMap = idxToMsgMap
- } else {
- log.Error("Unable to live-reload the locale file %q, err: %v", l.sourceFileName, err)
- }
-
- // We will set the sourceFileInfo to this file to prevent repeated attempts to re-load this broken file
- l.sourceFileInfo = sourceFileInfo
-}
-
-// Tr translates content to locale language. fall back to default language.
-func (l *locale) Tr(trKey string, trArgs ...interface{}) string {
- if l.reloadMu != nil {
- l.reloadMu.RLock()
- defer l.reloadMu.RUnlock()
- l.reloadIfNeeded()
- }
-
- msg, _ := l.tryTr(trKey, trArgs...)
- return msg
-}
-
-func (l *locale) tryTr(trKey string, trArgs ...interface{}) (msg string, found bool) {
- trMsg := trKey
-
- // convert the provided trKey to a common idx from the store
- idx, ok := l.store.idxForTrKey(trKey)
-
- if ok {
- if msg, found = l.idxToMsgMap[idx]; found {
- trMsg = msg // use the translation that we have found
- } else if l.langName != l.store.defaultLang {
- // No translation available in our current language... fallback to the default language
-
- // Attempt to get the default language from the locale store
- if def, ok := l.store.localeMap[l.store.defaultLang]; ok {
-
- if def.reloadMu != nil {
- def.reloadMu.RLock()
- def.reloadIfNeeded()
- }
- if msg, found = def.idxToMsgMap[idx]; found {
- trMsg = msg // use the translation that we have found
- }
- if def.reloadMu != nil {
- def.reloadMu.RUnlock()
- }
- }
- }
- }
-
- if !found && !setting.IsProd {
- log.Error("missing i18n translation key: %q", trKey)
- }
-
- if len(trArgs) == 0 {
- return trMsg, found
- }
-
- fmtArgs := make([]interface{}, 0, len(trArgs))
- for _, arg := range trArgs {
- val := reflect.ValueOf(arg)
- if val.Kind() == reflect.Slice {
- // Previously, we would accept Tr(lang, key, a, [b, c], d, [e, f]) as Sprintf(msg, a, b, c, d, e, f)
- // but this is an unstable behavior.
- //
- // So we restrict the accepted arguments to either:
- //
- // 1. Tr(lang, key, [slice-items]) as Sprintf(msg, items...)
- // 2. Tr(lang, key, args...) as Sprintf(msg, args...)
- if len(trArgs) == 1 {
- for i := 0; i < val.Len(); i++ {
- fmtArgs = append(fmtArgs, val.Index(i).Interface())
- }
- } else {
- log.Error("the args for i18n shouldn't contain uncertain slices, key=%q, args=%v", trKey, trArgs)
- break
- }
- } else {
- fmtArgs = append(fmtArgs, arg)
- }
- }
-
- return fmt.Sprintf(trMsg, fmtArgs...), found
+ // Tr translates a given key and arguments for a language
+ Tr(lang, trKey string, trArgs ...interface{}) string
+ // Has reports if a locale has a translation for a given key
+ Has(lang, trKey string) bool
+ // SetDefaultLang sets the default language to fall back to
+ SetDefaultLang(lang string)
+ // ListLangNameDesc provides paired slices of language names to descriptors
+ ListLangNameDesc() (names, desc []string)
+ // Locale return the locale for the provided language or the default language if not found
+ Locale(langName string) (Locale, bool)
+ // HasLang returns whether a given language is present in the store
+ HasLang(langName string) bool
+ // AddLocaleByIni adds a new language to the store
+ AddLocaleByIni(langName, langDesc string, source, moreSource []byte) error
}
// ResetDefaultLocales resets the current default locales
// NOTE: this is not synchronized
-func ResetDefaultLocales(isProd bool) {
- DefaultLocales = NewLocaleStore(isProd)
+func ResetDefaultLocales() {
+ if DefaultLocales != nil {
+ _ = DefaultLocales.Close()
+ }
+ DefaultLocales = NewLocaleStore()
}
-// Tr use default locales to translate content to target language.
-func Tr(lang, trKey string, trArgs ...interface{}) string {
- return DefaultLocales.Tr(lang, trKey, trArgs...)
+// GetLocales returns the locale from the default locales
+func GetLocale(lang string) (Locale, bool) {
+ return DefaultLocales.Locale(lang)
}
diff --git a/modules/translation/i18n/i18n_test.go b/modules/translation/i18n/i18n_test.go
index 32f7585b32..0f4605c1bb 100644
--- a/modules/translation/i18n/i18n_test.go
+++ b/modules/translation/i18n/i18n_test.go
@@ -10,7 +10,7 @@ import (
"github.com/stretchr/testify/assert"
)
-func Test_Tr(t *testing.T) {
+func TestLocaleStore(t *testing.T) {
testData1 := []byte(`
.dot.name = Dot Name
fmt = %[1]s %[2]s
@@ -27,36 +27,52 @@ fmt = %[2]s %[1]s
sub = Changed Sub String
`)
- for _, isProd := range []bool{true, false} {
- ls := NewLocaleStore(isProd)
- assert.NoError(t, ls.AddLocaleByIni("lang1", "Lang1", testData1))
- assert.NoError(t, ls.AddLocaleByIni("lang2", "Lang2", testData2))
- ls.SetDefaultLang("lang1")
+ ls := NewLocaleStore()
+ assert.NoError(t, ls.AddLocaleByIni("lang1", "Lang1", testData1, nil))
+ assert.NoError(t, ls.AddLocaleByIni("lang2", "Lang2", testData2, nil))
+ ls.SetDefaultLang("lang1")
- result := ls.Tr("lang1", "fmt", "a", "b")
- assert.Equal(t, "a b", result)
+ result := ls.Tr("lang1", "fmt", "a", "b")
+ assert.Equal(t, "a b", result)
- result = ls.Tr("lang2", "fmt", "a", "b")
- assert.Equal(t, "b a", result)
+ result = ls.Tr("lang2", "fmt", "a", "b")
+ assert.Equal(t, "b a", result)
- result = ls.Tr("lang1", "section.sub")
- assert.Equal(t, "Sub String", result)
+ result = ls.Tr("lang1", "section.sub")
+ assert.Equal(t, "Sub String", result)
- result = ls.Tr("lang2", "section.sub")
- assert.Equal(t, "Changed Sub String", result)
+ result = ls.Tr("lang2", "section.sub")
+ assert.Equal(t, "Changed Sub String", result)
- result = ls.Tr("", ".dot.name")
- assert.Equal(t, "Dot Name", result)
+ result = ls.Tr("", ".dot.name")
+ assert.Equal(t, "Dot Name", result)
- result = ls.Tr("lang2", "section.mixed")
- assert.Equal(t, `test value; more text`, result)
+ result = ls.Tr("lang2", "section.mixed")
+ assert.Equal(t, `test value; more text`, result)
- langs, descs := ls.ListLangNameDesc()
- assert.Equal(t, []string{"lang1", "lang2"}, langs)
- assert.Equal(t, []string{"Lang1", "Lang2"}, descs)
+ langs, descs := ls.ListLangNameDesc()
+ assert.Equal(t, []string{"lang1", "lang2"}, langs)
+ assert.Equal(t, []string{"Lang1", "Lang2"}, descs)
- result, found := ls.localeMap["lang1"].tryTr("no-such")
- assert.Equal(t, "no-such", result)
- assert.False(t, found)
- }
+ found := ls.Has("lang1", "no-such")
+ assert.False(t, found)
+ assert.NoError(t, ls.Close())
+}
+
+func TestLocaleStoreMoreSource(t *testing.T) {
+ testData1 := []byte(`
+a=11
+b=12
+`)
+
+ testData2 := []byte(`
+b=21
+c=22
+`)
+
+ ls := NewLocaleStore()
+ assert.NoError(t, ls.AddLocaleByIni("lang1", "Lang1", testData1, testData2))
+ assert.Equal(t, "11", ls.Tr("lang1", "a"))
+ assert.Equal(t, "21", ls.Tr("lang1", "b"))
+ assert.Equal(t, "22", ls.Tr("lang1", "c"))
}
diff --git a/modules/translation/i18n/localestore.go b/modules/translation/i18n/localestore.go
new file mode 100644
index 0000000000..853519e224
--- /dev/null
+++ b/modules/translation/i18n/localestore.go
@@ -0,0 +1,159 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package i18n
+
+import (
+ "fmt"
+
+ "code.gitea.io/gitea/modules/log"
+
+ "gopkg.in/ini.v1"
+)
+
+// This file implements the static LocaleStore that will not watch for changes
+
+type locale struct {
+ store *localeStore
+ langName string
+ idxToMsgMap map[int]string // the map idx is generated by store's trKeyToIdxMap
+}
+
+type localeStore struct {
+ // After initializing has finished, these fields are read-only.
+ langNames []string
+ langDescs []string
+
+ localeMap map[string]*locale
+ trKeyToIdxMap map[string]int
+
+ defaultLang string
+}
+
+// NewLocaleStore creates a static locale store
+func NewLocaleStore() LocaleStore {
+ return &localeStore{localeMap: make(map[string]*locale), trKeyToIdxMap: make(map[string]int)}
+}
+
+// AddLocaleByIni adds locale by ini into the store
+func (store *localeStore) AddLocaleByIni(langName, langDesc string, source, moreSource []byte) error {
+ if _, ok := store.localeMap[langName]; ok {
+ return ErrLocaleAlreadyExist
+ }
+
+ store.langNames = append(store.langNames, langName)
+ store.langDescs = append(store.langDescs, langDesc)
+
+ l := &locale{store: store, langName: langName, idxToMsgMap: make(map[int]string)}
+ store.localeMap[l.langName] = l
+
+ iniFile, err := ini.LoadSources(ini.LoadOptions{
+ IgnoreInlineComment: true,
+ UnescapeValueCommentSymbols: true,
+ }, source, moreSource)
+ if err != nil {
+ return fmt.Errorf("unable to load ini: %w", err)
+ }
+ iniFile.BlockMode = false
+
+ for _, section := range iniFile.Sections() {
+ for _, key := range section.Keys() {
+ var trKey string
+ if section.Name() == "" || section.Name() == "DEFAULT" {
+ trKey = key.Name()
+ } else {
+ trKey = section.Name() + "." + key.Name()
+ }
+ idx, ok := store.trKeyToIdxMap[trKey]
+ if !ok {
+ idx = len(store.trKeyToIdxMap)
+ store.trKeyToIdxMap[trKey] = idx
+ }
+ l.idxToMsgMap[idx] = key.Value()
+ }
+ }
+ iniFile = nil
+
+ return nil
+}
+
+func (store *localeStore) HasLang(langName string) bool {
+ _, ok := store.localeMap[langName]
+ return ok
+}
+
+func (store *localeStore) ListLangNameDesc() (names, desc []string) {
+ return store.langNames, store.langDescs
+}
+
+// SetDefaultLang sets default language as a fallback
+func (store *localeStore) SetDefaultLang(lang string) {
+ store.defaultLang = lang
+}
+
+// Tr translates content to target language. fall back to default language.
+func (store *localeStore) Tr(lang, trKey string, trArgs ...interface{}) string {
+ l, _ := store.Locale(lang)
+
+ return l.Tr(trKey, trArgs...)
+}
+
+// Has returns whether the given language has a translation for the provided key
+func (store *localeStore) Has(lang, trKey string) bool {
+ l, _ := store.Locale(lang)
+
+ return l.Has(trKey)
+}
+
+// Locale returns the locale for the lang or the default language
+func (store *localeStore) Locale(lang string) (Locale, bool) {
+ l, found := store.localeMap[lang]
+ if !found {
+ var ok bool
+ l, ok = store.localeMap[store.defaultLang]
+ if !ok {
+ // no default - return an empty locale
+ l = &locale{store: store, idxToMsgMap: make(map[int]string)}
+ }
+ }
+ return l, found
+}
+
+// Close implements io.Closer
+func (store *localeStore) Close() error {
+ return nil
+}
+
+// Tr translates content to locale language. fall back to default language.
+func (l *locale) Tr(trKey string, trArgs ...interface{}) string {
+ format := trKey
+
+ idx, ok := l.store.trKeyToIdxMap[trKey]
+ if ok {
+ if msg, ok := l.idxToMsgMap[idx]; ok {
+ format = msg // use the found translation
+ } else if def, ok := l.store.localeMap[l.store.defaultLang]; ok {
+ // try to use default locale's translation
+ if msg, ok := def.idxToMsgMap[idx]; ok {
+ format = msg
+ }
+ }
+ }
+
+ msg, err := Format(format, trArgs...)
+ if err != nil {
+ log.Error("Error whilst formatting %q in %s: %v", trKey, l.langName, err)
+ }
+ return msg
+}
+
+// Has returns whether a key is present in this locale or not
+func (l *locale) Has(trKey string) bool {
+ idx, ok := l.store.trKeyToIdxMap[trKey]
+ if !ok {
+ return false
+ }
+ _, ok = l.idxToMsgMap[idx]
+ return ok
+}
diff --git a/modules/translation/translation.go b/modules/translation/translation.go
index fcc101d963..fc311aa61b 100644
--- a/modules/translation/translation.go
+++ b/modules/translation/translation.go
@@ -5,15 +5,16 @@
package translation
import (
- "path"
+ "context"
"sort"
"strings"
+ "sync"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/options"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/translation/i18n"
- "code.gitea.io/gitea/modules/util"
+ "code.gitea.io/gitea/modules/watcher"
"golang.org/x/text/language"
)
@@ -31,6 +32,7 @@ type LangType struct {
}
var (
+ lock *sync.RWMutex
matcher language.Matcher
allLangs []*LangType
allLangMap map[string]*LangType
@@ -43,57 +45,60 @@ func AllLangs() []*LangType {
}
// InitLocales loads the locales
-func InitLocales() {
- i18n.ResetDefaultLocales(setting.IsProd)
- localeNames, err := options.Dir("locale")
- if err != nil {
- log.Fatal("Failed to list locale files: %v", err)
+func InitLocales(ctx context.Context) {
+ if lock != nil {
+ lock.Lock()
+ defer lock.Unlock()
+ } else if !setting.IsProd && lock == nil {
+ lock = &sync.RWMutex{}
}
- localFiles := make(map[string]interface{}, len(localeNames))
- for _, name := range localeNames {
- if options.IsDynamic() {
- // Try to check if CustomPath has the file, otherwise fallback to StaticRootPath
- value := path.Join(setting.CustomPath, "options/locale", name)
+ refreshLocales := func() {
+ i18n.ResetDefaultLocales()
+ localeNames, err := options.Dir("locale")
+ if err != nil {
+ log.Fatal("Failed to list locale files: %v", err)
+ }
- isFile, err := util.IsFile(value)
- if err != nil {
- log.Fatal("Failed to load %s locale file. %v", name, err)
- }
-
- if isFile {
- localFiles[name] = value
- } else {
- localFiles[name] = path.Join(setting.StaticRootPath, "options/locale", name)
- }
- } else {
- localFiles[name], err = options.Locale(name)
+ localeData := make(map[string][]byte, len(localeNames))
+ for _, name := range localeNames {
+ localeData[name], err = options.Locale(name)
if err != nil {
log.Fatal("Failed to load %s locale file. %v", name, err)
}
}
- }
- supportedTags = make([]language.Tag, len(setting.Langs))
- for i, lang := range setting.Langs {
- supportedTags[i] = language.Raw.Make(lang)
- }
+ supportedTags = make([]language.Tag, len(setting.Langs))
+ for i, lang := range setting.Langs {
+ supportedTags[i] = language.Raw.Make(lang)
+ }
- matcher = language.NewMatcher(supportedTags)
- for i := range setting.Names {
- key := "locale_" + setting.Langs[i] + ".ini"
+ matcher = language.NewMatcher(supportedTags)
+ for i := range setting.Names {
+ var localeDataBase []byte
+ if i == 0 && setting.Langs[0] != "en-US" {
+ // Only en-US has complete translations. When use other language as default, the en-US should still be used as fallback.
+ localeDataBase = localeData["locale_en-US.ini"]
+ if localeDataBase == nil {
+ log.Fatal("Failed to load locale_en-US.ini file.")
+ }
+ }
- if err = i18n.DefaultLocales.AddLocaleByIni(setting.Langs[i], setting.Names[i], localFiles[key]); err != nil {
- log.Error("Failed to set messages to %s: %v", setting.Langs[i], err)
+ key := "locale_" + setting.Langs[i] + ".ini"
+ if err = i18n.DefaultLocales.AddLocaleByIni(setting.Langs[i], setting.Names[i], localeDataBase, localeData[key]); err != nil {
+ log.Error("Failed to set messages to %s: %v", setting.Langs[i], err)
+ }
+ }
+ if len(setting.Langs) != 0 {
+ defaultLangName := setting.Langs[0]
+ if defaultLangName != "en-US" {
+ log.Info("Use the first locale (%s) in LANGS setting option as default", defaultLangName)
+ }
+ i18n.DefaultLocales.SetDefaultLang(defaultLangName)
}
}
- if len(setting.Langs) != 0 {
- defaultLangName := setting.Langs[0]
- if defaultLangName != "en-US" {
- log.Info("Use the first locale (%s) in LANGS setting option as default", defaultLangName)
- }
- i18n.DefaultLocales.SetDefaultLang(defaultLangName)
- }
+
+ refreshLocales()
langs, descs := i18n.DefaultLocales.ListLangNameDesc()
allLangs = make([]*LangType, 0, len(langs))
@@ -108,6 +113,17 @@ func InitLocales() {
sort.Slice(allLangs, func(i, j int) bool {
return strings.ToLower(allLangs[i].Name) < strings.ToLower(allLangs[j].Name)
})
+
+ if !setting.IsProd {
+ watcher.CreateWatcher(ctx, "Locales", &watcher.CreateWatcherOpts{
+ PathsCallback: options.WalkLocales,
+ BetweenCallback: func() {
+ lock.Lock()
+ defer lock.Unlock()
+ refreshLocales()
+ },
+ })
+ }
}
// Match matches accept languages
@@ -118,16 +134,24 @@ func Match(tags ...language.Tag) language.Tag {
// locale represents the information of localization.
type locale struct {
+ i18n.Locale
Lang, LangName string // these fields are used directly in templates: .i18n.Lang
}
// NewLocale return a locale
func NewLocale(lang string) Locale {
+ if lock != nil {
+ lock.RLock()
+ defer lock.RUnlock()
+ }
+
langName := "unknown"
if l, ok := allLangMap[lang]; ok {
langName = l.Name
}
+ i18nLocale, _ := i18n.GetLocale(lang)
return &locale{
+ Locale: i18nLocale,
Lang: lang,
LangName: langName,
}
@@ -137,11 +161,6 @@ func (l *locale) Language() string {
return l.Lang
}
-// Tr translates content to target language.
-func (l *locale) Tr(format string, args ...interface{}) string {
- return i18n.Tr(l.Lang, format, args...)
-}
-
// Language specific rules for translating plural texts
var trNLangRules = map[string]func(int64) int{
// the default rule is "en-US" if a language isn't listed here
diff --git a/modules/typesniffer/typesniffer.go b/modules/typesniffer/typesniffer.go
index b6a6646d50..e50928e8c2 100644
--- a/modules/typesniffer/typesniffer.go
+++ b/modules/typesniffer/typesniffer.go
@@ -70,6 +70,16 @@ func (ct SniffedType) IsRepresentableAsText() bool {
return ct.IsText() || ct.IsSvgImage()
}
+// IsBrowsableType returns whether a non-text type can be displayed in a browser
+func (ct SniffedType) IsBrowsableBinaryType() bool {
+ return ct.IsImage() || ct.IsSvgImage() || ct.IsPDF() || ct.IsVideo() || ct.IsAudio()
+}
+
+// GetMimeType returns the mime type
+func (ct SniffedType) GetMimeType() string {
+ return strings.SplitN(ct.contentType, ";", 2)[0]
+}
+
// DetectContentType extends http.DetectContentType with more content types. Defaults to text/unknown if input is empty.
func DetectContentType(data []byte) SniffedType {
if len(data) == 0 {
diff --git a/modules/updatechecker/update_checker.go b/modules/updatechecker/update_checker.go
index 9c1569b15e..816fb3764c 100644
--- a/modules/updatechecker/update_checker.go
+++ b/modules/updatechecker/update_checker.go
@@ -8,10 +8,10 @@ import (
"io"
"net/http"
- "code.gitea.io/gitea/modules/appstate"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/proxy"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/system"
"github.com/hashicorp/go-version"
)
@@ -64,13 +64,13 @@ func GiteaUpdateChecker(httpEndpoint string) error {
// UpdateRemoteVersion updates the latest available version of Gitea
func UpdateRemoteVersion(version string) (err error) {
- return appstate.AppState.Set(&CheckerState{LatestVersion: version})
+ return system.AppState.Set(&CheckerState{LatestVersion: version})
}
// GetRemoteVersion returns the current remote version (or currently installed version if fail to fetch from DB)
func GetRemoteVersion() string {
item := new(CheckerState)
- if err := appstate.AppState.Get(item); err != nil {
+ if err := system.AppState.Get(item); err != nil {
return ""
}
return item.LatestVersion
diff --git a/modules/util/error.go b/modules/util/error.go
new file mode 100644
index 0000000000..08e491dbaf
--- /dev/null
+++ b/modules/util/error.go
@@ -0,0 +1,37 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package util
+
+import (
+ "errors"
+)
+
+// Common Errors forming the base of our error system
+//
+// Many Errors returned by Gitea can be tested against these errors
+// using errors.Is.
+var (
+ ErrInvalidArgument = errors.New("invalid argument")
+ ErrPermissionDenied = errors.New("permission denied")
+ ErrAlreadyExist = errors.New("resource already exists")
+ ErrNotExist = errors.New("resource does not exist")
+)
+
+// SilentWrap provides a simple wrapper for a wrapped error where the wrapped error message plays no part in the error message
+// Especially useful for "untyped" errors created with "errors.New(…)" that can be classified as 'invalid argument', 'permission denied', 'exists already', or 'does not exist'
+type SilentWrap struct {
+ Message string
+ Err error
+}
+
+// Error returns the message
+func (w SilentWrap) Error() string {
+ return w.Message
+}
+
+// Unwrap returns the underlying error
+func (w SilentWrap) Unwrap() error {
+ return w.Err
+}
diff --git a/modules/util/file_unix.go b/modules/util/file_unix.go
new file mode 100644
index 0000000000..ec9d4ec167
--- /dev/null
+++ b/modules/util/file_unix.go
@@ -0,0 +1,28 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+//go:build !windows
+
+package util
+
+import (
+ "os"
+
+ "golang.org/x/sys/unix"
+)
+
+var defaultUmask int
+
+func init() {
+ // at the moment, the umask could only be gotten by calling unix.Umask(newUmask)
+ // use 0o077 as temp new umask to reduce the risks if this umask is used anywhere else before the correct umask is recovered
+ tempUmask := 0o077
+ defaultUmask = unix.Umask(tempUmask)
+ unix.Umask(defaultUmask)
+}
+
+func ApplyUmask(f string, newMode os.FileMode) error {
+ mod := newMode & ^os.FileMode(defaultUmask)
+ return os.Chmod(f, mod)
+}
diff --git a/modules/util/file_unix_test.go b/modules/util/file_unix_test.go
new file mode 100644
index 0000000000..41311aa13f
--- /dev/null
+++ b/modules/util/file_unix_test.go
@@ -0,0 +1,36 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+//go:build !windows
+
+package util
+
+import (
+ "os"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestApplyUmask(t *testing.T) {
+ f, err := os.CreateTemp(t.TempDir(), "test-filemode-")
+ assert.NoError(t, err)
+
+ err = os.Chmod(f.Name(), 0o777)
+ assert.NoError(t, err)
+ st, err := os.Stat(f.Name())
+ assert.NoError(t, err)
+ assert.EqualValues(t, 0o777, st.Mode().Perm()&0o777)
+
+ oldDefaultUmask := defaultUmask
+ defaultUmask = 0o037
+ defer func() {
+ defaultUmask = oldDefaultUmask
+ }()
+ err = ApplyUmask(f.Name(), os.ModePerm)
+ assert.NoError(t, err)
+ st, err = os.Stat(f.Name())
+ assert.NoError(t, err)
+ assert.EqualValues(t, 0o740, st.Mode().Perm()&0o777)
+}
diff --git a/modules/util/file_windows.go b/modules/util/file_windows.go
new file mode 100644
index 0000000000..6ad3e88ba5
--- /dev/null
+++ b/modules/util/file_windows.go
@@ -0,0 +1,16 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+//go:build windows
+
+package util
+
+import (
+ "os"
+)
+
+func ApplyUmask(f string, newMode os.FileMode) error {
+ // do nothing for Windows, because Windows doesn't use umask
+ return nil
+}
diff --git a/modules/util/filebuffer/file_backed_buffer.go b/modules/util/filebuffer/file_backed_buffer.go
index 128030b4c5..8e3e138e04 100644
--- a/modules/util/filebuffer/file_backed_buffer.go
+++ b/modules/util/filebuffer/file_backed_buffer.go
@@ -103,35 +103,45 @@ func (b *FileBackedBuffer) Size() int64 {
return b.size
}
-func (b *FileBackedBuffer) switchToReader() {
+func (b *FileBackedBuffer) switchToReader() error {
if b.reader != nil {
- return
+ return nil
}
if b.file != nil {
+ if _, err := b.file.Seek(0, io.SeekStart); err != nil {
+ return err
+ }
b.reader = b.file
} else {
b.reader = bytes.NewReader(b.buffer.Bytes())
}
+ return nil
}
// Read implements io.Reader
func (b *FileBackedBuffer) Read(p []byte) (int, error) {
- b.switchToReader()
+ if err := b.switchToReader(); err != nil {
+ return 0, err
+ }
return b.reader.Read(p)
}
// ReadAt implements io.ReaderAt
func (b *FileBackedBuffer) ReadAt(p []byte, off int64) (int, error) {
- b.switchToReader()
+ if err := b.switchToReader(); err != nil {
+ return 0, err
+ }
return b.reader.ReadAt(p, off)
}
// Seek implements io.Seeker
func (b *FileBackedBuffer) Seek(offset int64, whence int) (int64, error) {
- b.switchToReader()
+ if err := b.switchToReader(); err != nil {
+ return 0, err
+ }
return b.reader.Seek(offset, whence)
}
diff --git a/modules/util/filebuffer/file_backed_buffer_test.go b/modules/util/filebuffer/file_backed_buffer_test.go
new file mode 100644
index 0000000000..83ef58561d
--- /dev/null
+++ b/modules/util/filebuffer/file_backed_buffer_test.go
@@ -0,0 +1,36 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package filebuffer
+
+import (
+ "io"
+ "strings"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestFileBackedBuffer(t *testing.T) {
+ cases := []struct {
+ MaxMemorySize int
+ Data string
+ }{
+ {5, "test"},
+ {5, "testtest"},
+ }
+
+ for _, c := range cases {
+ buf, err := CreateFromReader(strings.NewReader(c.Data), c.MaxMemorySize)
+ assert.NoError(t, err)
+
+ assert.EqualValues(t, len(c.Data), buf.Size())
+
+ data, err := io.ReadAll(buf)
+ assert.NoError(t, err)
+ assert.Equal(t, c.Data, string(data))
+
+ assert.NoError(t, buf.Close())
+ }
+}
diff --git a/modules/util/path.go b/modules/util/path.go
index 0ccc7a1dc2..3d4ddec21c 100644
--- a/modules/util/path.go
+++ b/modules/util/path.go
@@ -12,7 +12,6 @@ import (
"path/filepath"
"regexp"
"runtime"
- "strings"
)
// EnsureAbsolutePath ensure that a path is absolute, making it
@@ -91,7 +90,7 @@ func statDir(dirPath, recPath string, includeDir, isDirOnly, followSymlinks bool
statList := make([]string, 0)
for _, fi := range fis {
- if strings.Contains(fi.Name(), ".DS_Store") {
+ if CommonSkip(fi.Name()) {
continue
}
@@ -199,3 +198,21 @@ func HomeDir() (home string, err error) {
return home, nil
}
+
+// CommonSkip will check a provided name to see if it represents file or directory that should not be watched
+func CommonSkip(name string) bool {
+ if name == "" {
+ return true
+ }
+
+ switch name[0] {
+ case '.':
+ return true
+ case 't', 'T':
+ return name[1:] == "humbs.db"
+ case 'd', 'D':
+ return name[1:] == "esktop.ini"
+ }
+
+ return false
+}
diff --git a/modules/util/sec_to_time.go b/modules/util/sec_to_time.go
index 9ce6fe1a13..13461915e6 100644
--- a/modules/util/sec_to_time.go
+++ b/modules/util/sec_to_time.go
@@ -18,10 +18,22 @@ import (
// 45677465s -> 1 year 6 months
func SecToTime(duration int64) string {
formattedTime := ""
- years := duration / (3600 * 24 * 7 * 4 * 12)
- months := (duration / (3600 * 24 * 30)) % 12
- weeks := (duration / (3600 * 24 * 7)) % 4
- days := (duration / (3600 * 24)) % 7
+
+ // The following four variables are calculated by taking
+ // into account the previously calculated variables, this avoids
+ // pitfalls when using remainders. As that could lead to incorrect
+ // results when the calculated number equals the quotient number.
+ remainingDays := duration / (60 * 60 * 24)
+ years := remainingDays / 365
+ remainingDays -= years * 365
+ months := remainingDays * 12 / 365
+ remainingDays -= months * 365 / 12
+ weeks := remainingDays / 7
+ remainingDays -= weeks * 7
+ days := remainingDays
+
+ // The following three variables are calculated without depending
+ // on the previous calculated variables.
hours := (duration / 3600) % 24
minutes := (duration / 60) % 60
seconds := duration % 60
diff --git a/modules/util/sec_to_time_test.go b/modules/util/sec_to_time_test.go
index 854190462b..1e256aa865 100644
--- a/modules/util/sec_to_time_test.go
+++ b/modules/util/sec_to_time_test.go
@@ -11,10 +11,21 @@ import (
)
func TestSecToTime(t *testing.T) {
- assert.Equal(t, SecToTime(66), "1 minute 6 seconds")
- assert.Equal(t, SecToTime(52410), "14 hours 33 minutes")
- assert.Equal(t, SecToTime(563418), "6 days 12 hours")
- assert.Equal(t, SecToTime(1563418), "2 weeks 4 days")
- assert.Equal(t, SecToTime(3937125), "1 month 2 weeks")
- assert.Equal(t, SecToTime(45677465), "1 year 5 months")
+ second := int64(1)
+ minute := 60 * second
+ hour := 60 * minute
+ day := 24 * hour
+ year := 365 * day
+
+ assert.Equal(t, "1 minute 6 seconds", SecToTime(minute+6*second))
+ assert.Equal(t, "1 hour", SecToTime(hour))
+ assert.Equal(t, "1 hour", SecToTime(hour+second))
+ assert.Equal(t, "14 hours 33 minutes", SecToTime(14*hour+33*minute+30*second))
+ assert.Equal(t, "6 days 12 hours", SecToTime(6*day+12*hour+30*minute+18*second))
+ assert.Equal(t, "2 weeks 4 days", SecToTime((2*7+4)*day+2*hour+16*minute+58*second))
+ assert.Equal(t, "4 weeks", SecToTime(4*7*day))
+ assert.Equal(t, "4 weeks 1 day", SecToTime((4*7+1)*day))
+ assert.Equal(t, "1 month 2 weeks", SecToTime((6*7+3)*day+13*hour+38*minute+45*second))
+ assert.Equal(t, "11 months", SecToTime(year-25*day))
+ assert.Equal(t, "1 year 5 months", SecToTime(year+163*day+10*hour+11*minute+5*second))
}
diff --git a/modules/util/string.go b/modules/util/string.go
index 4301f75f99..2da2bc5dc4 100644
--- a/modules/util/string.go
+++ b/modules/util/string.go
@@ -17,13 +17,13 @@ func isSnakeCaseLowerOrNumber(c byte) bool {
// ToSnakeCase convert the input string to snake_case format.
//
// Some samples.
-// "FirstName" => "first_name"
-// "HTTPServer" => "http_server"
-// "NoHTTPS" => "no_https"
-// "GO_PATH" => "go_path"
-// "GO PATH" => "go_path" // space is converted to underscore.
-// "GO-PATH" => "go_path" // hyphen is converted to underscore.
//
+// "FirstName" => "first_name"
+// "HTTPServer" => "http_server"
+// "NoHTTPS" => "no_https"
+// "GO_PATH" => "go_path"
+// "GO PATH" => "go_path" // space is converted to underscore.
+// "GO-PATH" => "go_path" // hyphen is converted to underscore.
func ToSnakeCase(input string) string {
if len(input) == 0 {
return ""
diff --git a/modules/validation/binding.go b/modules/validation/binding.go
index 7baa36ccc5..f08f632426 100644
--- a/modules/validation/binding.go
+++ b/modules/validation/binding.go
@@ -9,6 +9,8 @@ import (
"regexp"
"strings"
+ "code.gitea.io/gitea/modules/git"
+
"gitea.com/go-chi/binding"
"github.com/gobwas/glob"
)
@@ -24,30 +26,6 @@ const (
ErrRegexPattern = "RegexPattern"
)
-// GitRefNamePatternInvalid is regular expression with unallowed characters in git reference name
-// They cannot have ASCII control characters (i.e. bytes whose values are lower than \040, or \177 DEL), space, tilde ~, caret ^, or colon : anywhere.
-// They cannot have question-mark ?, asterisk *, or open bracket [ anywhere
-var GitRefNamePatternInvalid = regexp.MustCompile(`[\000-\037\177 \\~^:?*[]+`)
-
-// CheckGitRefAdditionalRulesValid check name is valid on additional rules
-func CheckGitRefAdditionalRulesValid(name string) bool {
- // Additional rules as described at https://www.kernel.org/pub/software/scm/git/docs/git-check-ref-format.html
- if strings.HasPrefix(name, "/") || strings.HasSuffix(name, "/") ||
- strings.HasSuffix(name, ".") || strings.Contains(name, "..") ||
- strings.Contains(name, "//") || strings.Contains(name, "@{") ||
- name == "@" {
- return false
- }
- parts := strings.Split(name, "/")
- for _, part := range parts {
- if strings.HasSuffix(part, ".lock") || strings.HasPrefix(part, ".") {
- return false
- }
- }
-
- return true
-}
-
// AddBindingRules adds additional binding rules
func AddBindingRules() {
addGitRefNameBindingRule()
@@ -67,16 +45,10 @@ func addGitRefNameBindingRule() {
IsValid: func(errs binding.Errors, name string, val interface{}) (bool, binding.Errors) {
str := fmt.Sprintf("%v", val)
- if GitRefNamePatternInvalid.MatchString(str) {
+ if !git.IsValidRefPattern(str) {
errs.Add([]string{name}, ErrGitRefName, "GitRefName")
return false, errs
}
-
- if !CheckGitRefAdditionalRulesValid(str) {
- errs.Add([]string{name}, ErrGitRefName, "GitRefName")
- return false, errs
- }
-
return true, errs
},
})
diff --git a/modules/watcher/watcher.go b/modules/watcher/watcher.go
new file mode 100644
index 0000000000..d737f6ccbb
--- /dev/null
+++ b/modules/watcher/watcher.go
@@ -0,0 +1,115 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package watcher
+
+import (
+ "context"
+ "io/fs"
+ "os"
+
+ "code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/process"
+
+ "github.com/fsnotify/fsnotify"
+)
+
+// CreateWatcherOpts are options to configure the watcher
+type CreateWatcherOpts struct {
+ // PathsCallback is used to set the required paths to watch
+ PathsCallback func(func(path, name string, d fs.DirEntry, err error) error) error
+
+ // BeforeCallback is called before any files are watched
+ BeforeCallback func()
+
+ // Between Callback is called between after a watched event has occurred
+ BetweenCallback func()
+
+ // AfterCallback is called as this watcher ends
+ AfterCallback func()
+}
+
+// CreateWatcher creates a watcher labelled with the provided description and running with the provided options.
+// The created watcher will create a subcontext from the provided ctx and register it with the process manager.
+func CreateWatcher(ctx context.Context, desc string, opts *CreateWatcherOpts) {
+ go run(ctx, desc, opts)
+}
+
+func run(ctx context.Context, desc string, opts *CreateWatcherOpts) {
+ if opts.BeforeCallback != nil {
+ opts.BeforeCallback()
+ }
+ if opts.AfterCallback != nil {
+ defer opts.AfterCallback()
+ }
+ ctx, _, finished := process.GetManager().AddTypedContext(ctx, "Watcher: "+desc, process.SystemProcessType, true)
+ defer finished()
+
+ log.Trace("Watcher loop starting for %s", desc)
+ defer log.Trace("Watcher loop ended for %s", desc)
+
+ watcher, err := fsnotify.NewWatcher()
+ if err != nil {
+ log.Error("Unable to create watcher for %s: %v", desc, err)
+ return
+ }
+ if err := opts.PathsCallback(func(path, _ string, d fs.DirEntry, err error) error {
+ if err != nil && !os.IsNotExist(err) {
+ return err
+ }
+ log.Trace("Watcher: %s watching %q", desc, path)
+ _ = watcher.Add(path)
+ return nil
+ }); err != nil {
+ log.Error("Unable to create watcher for %s: %v", desc, err)
+ _ = watcher.Close()
+ return
+ }
+
+ // Note we don't call the BetweenCallback here
+
+ for {
+ select {
+ case event, ok := <-watcher.Events:
+ if !ok {
+ _ = watcher.Close()
+ return
+ }
+ log.Debug("Watched file for %s had event: %v", desc, event)
+ case err, ok := <-watcher.Errors:
+ if !ok {
+ _ = watcher.Close()
+ return
+ }
+ log.Error("Error whilst watching files for %s: %v", desc, err)
+ case <-ctx.Done():
+ _ = watcher.Close()
+ return
+ }
+
+ // Recreate the watcher - only call the BetweenCallback after the new watcher is set-up
+ _ = watcher.Close()
+ watcher, err = fsnotify.NewWatcher()
+ if err != nil {
+ log.Error("Unable to create watcher for %s: %v", desc, err)
+ return
+ }
+ if err := opts.PathsCallback(func(path, _ string, _ fs.DirEntry, err error) error {
+ if err != nil {
+ return err
+ }
+ _ = watcher.Add(path)
+ return nil
+ }); err != nil {
+ log.Error("Unable to create watcher for %s: %v", desc, err)
+ _ = watcher.Close()
+ return
+ }
+
+ // Inform our BetweenCallback that there has been an event
+ if opts.BetweenCallback != nil {
+ opts.BetweenCallback()
+ }
+ }
+}
diff --git a/modules/web/middleware/binding.go b/modules/web/middleware/binding.go
index 88a3920f6e..636e655b9e 100644
--- a/modules/web/middleware/binding.go
+++ b/modules/web/middleware/binding.go
@@ -136,7 +136,16 @@ func Validate(errs binding.Errors, data map[string]interface{}, f Form, l transl
case validation.ErrRegexPattern:
data["ErrorMsg"] = trName + l.Tr("form.regex_pattern_error", errs[0].Message)
default:
- data["ErrorMsg"] = l.Tr("form.unknown_error") + " " + errs[0].Classification
+ msg := errs[0].Classification
+ if msg != "" && errs[0].Message != "" {
+ msg += ": "
+ }
+
+ msg += errs[0].Message
+ if msg == "" {
+ msg = l.Tr("form.unknown_error")
+ }
+ data["ErrorMsg"] = trName + ": " + msg
}
return errs
}
diff --git a/options/license/AGPL-1.0 b/options/license/AGPL-1.0
deleted file mode 100644
index 3c7a40e177..0000000000
--- a/options/license/AGPL-1.0
+++ /dev/null
@@ -1,48 +0,0 @@
-AFFERO GENERAL PUBLIC LICENSE
-Version 1, March 2002 Copyright © 2002 Affero Inc.
-510 Third Street - Suite 225, San Francisco, CA 94107, USA
-This license is a modified version of the GNU General Public License copyright (C) 1989, 1991 Free Software Foundation, Inc. made with their permission. Section 2(d) has been added to cover use of software over a computer network.
-Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.
-Preamble
-The licenses for most software are designed to take away your freedom to share and change it. By contrast, the Affero General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This Public License applies to most of Affero's software and to any other program whose authors commit to using it. (Some other Affero software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too.
-When we speak of free software, we are referring to freedom, not price. This General Public License is designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things.
-To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it.
-For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights.
-We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software.
-Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations.
-Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all.
-The precise terms and conditions for copying, distribution and modification follow.
-TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
- 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this Affero General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you".
- Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does.
- 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program.
- You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee.
- 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions:
- a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change.
- b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License.
- c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.)
- d) If the Program as you received it is intended to interact with users through a computer network and if, in the version you received, any user interacting with the Program was given the opportunity to request transmission to that user of the Program's complete source code, you must not remove that facility from your modified version of the Program or work based on the Program, and must offer an equivalent opportunity for all users interacting with your Program through a computer network to request immediate transmission by HTTP of the complete source code of your modified version or other derivative work.
- These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it.
- Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program.
- In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License.
- 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following:
- a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,
- b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or,
- c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.)
- The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable.
- If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code.
- 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance.
- 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it.
- 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License.
- 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program.
- If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances.
- It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice.
- This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License.
- 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License.
- 9. Affero Inc. may publish revised and/or new versions of the Affero General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns.
- Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by Affero, Inc. If the Program does not specify a version number of this License, you may choose any version ever published by Affero, Inc.
- You may also choose to redistribute modified versions of this program under any version of the Free Software Foundation's GNU General Public License version 3 or higher, so long as that version of the GNU GPL includes terms and conditions substantially equivalent to those of this license.
- 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by Affero, Inc., write to us; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally.
- NO WARRANTY
- 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
- 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
diff --git a/options/license/BSD-2-Clause-FreeBSD b/options/license/BSD-2-Clause-FreeBSD
deleted file mode 100644
index 004ec94575..0000000000
--- a/options/license/BSD-2-Clause-FreeBSD
+++ /dev/null
@@ -1,27 +0,0 @@
-The FreeBSD Copyright Copyright 1992-2012 The FreeBSD Project. All rights
-reserved.
-
-Redistribution and use in source and binary forms, with or without modification,
-are permitted provided that the following conditions are met:
-
-1. Redistributions of source code must retain the above copyright notice,
-this list of conditions and the following disclaimer.
-
-2. Redistributions in binary form must reproduce the above copyright notice,
-this list of conditions and the following disclaimer in the documentation
-and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE FREEBSD PROJECT ``AS IS'' AND ANY EXPRESS
-OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
-OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
-NO EVENT SHALL THE FREEBSD PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
-INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
-BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
-OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
-OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
-ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-The views and conclusions contained in the software and documentation are
-those of the authors and should not be interpreted as representing official
-policies, either expressed or implied, of the FreeBSD Project.
diff --git a/options/license/BSD-2-Clause-NetBSD b/options/license/BSD-2-Clause-NetBSD
deleted file mode 100644
index a842566867..0000000000
--- a/options/license/BSD-2-Clause-NetBSD
+++ /dev/null
@@ -1,24 +0,0 @@
-Copyright (c) 2008 The NetBSD Foundation, Inc. All rights reserved.
-
-This code is derived from software contributed to The NetBSD Foundation by
-
-Redistribution and use in source and binary forms, with or without modification,
-are permitted provided that the following conditions are met:
-
-1. Redistributions of source code must retain the above copyright notice,
-this list of conditions and the following disclaimer.
-
-2. Redistributions in binary form must reproduce the above copyright notice,
-this list of conditions and the following disclaimer in the documentation
-and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
-``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE
-LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
-USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/options/license/CC-BY-3.0-IGO b/options/license/CC-BY-3.0-IGO
new file mode 100644
index 0000000000..13ab9536e1
--- /dev/null
+++ b/options/license/CC-BY-3.0-IGO
@@ -0,0 +1,101 @@
+Creative Commons Attribution 3.0 IGO
+
+CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM ITS USE. THE LICENSOR IS NOT NECESSARILY AN INTERGOVERNMENTAL ORGANIZATION (IGO), AS DEFINED IN THE LICENSE BELOW.
+
+License
+
+THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("LICENSE"). THE LICENSOR (DEFINED BELOW) HOLDS COPYRIGHT AND OTHER RIGHTS IN THE WORK. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE IS PROHIBITED.
+
+BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION FOR YOUR ACCEPTANCE AND AGREEMENT TO THE TERMS OF THE LICENSE.
+
+1. Definitions
+
+ a. "IGO" means, solely and exclusively for purposes of this License, an organization established by a treaty or other instrument governed by international law and possessing its own international legal personality. Other organizations established to carry out activities across national borders and that accordingly enjoy immunity from legal process are also IGOs for the sole and exclusive purposes of this License. IGOs may include as members, in addition to states, other entities.
+
+ b. "Work" means the literary and/or artistic work eligible for copyright protection, whatever may be the mode or form of its expression including digital form, and offered under the terms of this License. It is understood that a database, which by reason of the selection and arrangement of its contents constitutes an intellectual creation, is considered a Work.
+
+ c. "Licensor" means the individual, individuals, entity or entities that offer(s) the Work under the terms of this License and may be, but is not necessarily, an IGO.
+
+ d. "You" means an individual or entity exercising rights under this License.
+
+ e. "Reproduce" means to make a copy of the Work in any manner or form, and by any means.
+
+ f. "Distribute" means the activity of making publicly available the Work or Adaptation (or copies of the Work or Adaptation), as applicable, by sale, rental, public lending or any other known form of transfer of ownership or possession of the Work or copy of the Work.
+
+ g. "Publicly Perform" means to perform public recitations of the Work and to communicate to the public those public recitations, by any means or process, including by wire or wireless means or public digital performances; to make available to the public Works in such a way that members of the public may access these Works from a place and at a place individually chosen by them; to perform the Work to the public by any means or process and the communication to the public of the performances of the Work, including by public digital performance; to broadcast and rebroadcast the Work by any means including signs, sounds or images.
+
+ h. "Adaptation" means a work derived from or based upon the Work, or upon the Work and other pre-existing works. Adaptations may include works such as translations, derivative works, or any alterations and arrangements of any kind involving the Work. For purposes of this License, where the Work is a musical work, performance, or phonogram, the synchronization of the Work in timed-relation with a moving image is an Adaptation. For the avoidance of doubt, including the Work in a Collection is not an Adaptation.
+
+ i. "Collection" means a collection of literary or artistic works or other works or subject matter other than works listed in Section 1(b) which by reason of the selection and arrangement of their contents, constitute intellectual creations, in which the Work is included in its entirety in unmodified form along with one or more other contributions, each constituting separate and independent works in themselves, which together are assembled into a collective whole. For the avoidance of doubt, a Collection will not be considered as an Adaptation.
+
+2. Scope of this License. Nothing in this License is intended to reduce, limit, or restrict any uses free from copyright protection.
+
+3. License Grant. Subject to the terms and conditions of this License, the Licensor hereby grants You a worldwide, royalty-free, non-exclusive license to exercise the rights in the Work as follows:
+
+ a. to Reproduce, Distribute and Publicly Perform the Work, to incorporate the Work into one or more Collections, and to Reproduce, Distribute and Publicly Perform the Work as incorporated in the Collections; and,
+
+ b. to create, Reproduce, Distribute and Publicly Perform Adaptations, provided that You clearly label, demarcate or otherwise identify that changes were made to the original Work.
+
+ c. For the avoidance of doubt:
+
+ i. Non-waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme cannot be waived, the Licensor reserves the exclusive right to collect such royalties for any exercise by You of the rights granted under this License;
+
+ ii. Waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme can be waived, the Licensor waives the exclusive right to collect such royalties for any exercise by You of the rights granted under this License; and,
+
+ iii. Voluntary License Schemes. To the extent possible, the Licensor waives the right to collect royalties from You for the exercise of the Licensed Rights, whether directly or through a collecting society under any voluntary licensing scheme.
+
+This License lasts for the duration of the term of the copyright in the Work licensed by the Licensor. The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats. All rights not expressly granted by the Licensor are hereby reserved.
+
+4. Restrictions. The license granted in Section 3 above is expressly made subject to and limited by the following restrictions:
+
+ a. You may Distribute or Publicly Perform the Work only under the terms of this License. You must include a copy of, or the Uniform Resource Identifier (URI) for, this License with every copy of the Work You Distribute or Publicly Perform. You may not offer or impose any terms on the Work that restrict the terms of this License or the ability of the recipient of the Work to exercise the rights granted to that recipient under the terms of the License. You may not sublicense the Work (see section 8(a)). You must keep intact all notices that refer to this License and to the disclaimer of warranties with every copy of the Work You Distribute or Publicly Perform. When You Distribute or Publicly Perform the Work, You may not impose any effective technological measures on the Work that restrict the ability of a recipient of the Work from You to exercise the rights granted to that recipient under the terms of the License. This Section 4(a) applies to the Work as incorporated in a Collection, but this does not require the Collection apart from the Work itself to be made subject to the terms of this License. If You create a Collection, upon notice from a Licensor You must, to the extent practicable, remove from the Collection any credit (inclusive of any logo, trademark, official mark or official emblem) as required by Section 4(b), as requested. If You create an Adaptation, upon notice from a Licensor You must, to the extent practicable, remove from the Adaptation any credit (inclusive of any logo, trademark, official mark or official emblem) as required by Section 4(b), as requested.
+
+ b. If You Distribute, or Publicly Perform the Work or any Adaptations or Collections, You must, unless a request has been made pursuant to Section 4(a), keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) any attributions that the Licensor indicates be associated with the Work as indicated in a copyright notice, (ii) the title of the Work if supplied; (iii) to the extent reasonably practicable, the URI, if any, that the Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work; and, (iv) consistent with Section 3(b), in the case of an Adaptation, a credit identifying the use of the Work in the Adaptation. The credit required by this Section 4(b) may be implemented in any reasonable manner; provided, however, that in the case of an Adaptation or Collection, at a minimum such credit will appear, if a credit for all contributors to the Adaptation or Collection appears, then as part of these credits and in a manner at least as prominent as the credits for the other contributors. For the avoidance of doubt, You may only use the credit required by this Section for the purpose of attribution in the manner set out above and, by exercising Your rights under this License, You may not implicitly or explicitly assert or imply any connection with, sponsorship or endorsement by the Licensor or others designated for attribution, of You or Your use of the Work, without the separate, express prior written permission of the Licensor or such others.
+
+ c. Except as otherwise agreed in writing by the Licensor, if You Reproduce, Distribute or Publicly Perform the Work either by itself or as part of any Adaptations or Collections, You must not distort, mutilate, modify or take other derogatory action in relation to the Work which would be prejudicial to the honor or reputation of the Licensor where moral rights apply.
+
+5. Representations, Warranties and Disclaimer
+
+THE LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE.
+
+6. Limitation on Liability
+
+IN NO EVENT WILL THE LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF THE LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+7. Termination
+
+ a. Subject to the terms and conditions set forth in this License, the license granted here lasts for the duration of the term of the copyright in the Work licensed by the Licensor as stated in Section 3. Notwithstanding the above, the Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated below.
+
+ b. If You fail to comply with this License, then this License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Adaptations or Collections from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License. Notwithstanding the foregoing, this License reinstates automatically as of the date the violation is cured, provided it is cured within 30 days of You discovering the violation, or upon express reinstatement by the Licensor. For the avoidance of doubt, this Section 7(b) does not affect any rights the Licensor may have to seek remedies for violations of this License by You.
+
+8. Miscellaneous
+
+ a. Each time You Distribute or Publicly Perform the Work or a Collection, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License.
+
+ b. Each time You Distribute or Publicly Perform an Adaptation, the Licensor offers to the recipient a license to the original Work on the same terms and conditions as the license granted to You under this License.
+
+ c. If any provision of this License is invalid or unenforceable, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.
+
+ d. No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the Licensor.
+
+ e. This License constitutes the entire agreement between You and the Licensor with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. The Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You.
+
+ f. The rights granted under, and the subject matter referenced, in this License were drafted utilizing the terminology of the Berne Convention for the Protection of Literary and Artistic Works (as amended on September 28, 1979), the Rome Convention of 1961, the WIPO Copyright Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996 and the Universal Copyright Convention (as revised on July 24, 1971). Interpretation of the scope of the rights granted by the Licensor and the conditions imposed on You under this License, this License, and the rights and conditions set forth herein shall be made with reference to copyright as determined in accordance with general principles of international law, including the above mentioned conventions.
+
+ g. Nothing in this License constitutes or may be interpreted as a limitation upon or waiver of any privileges and immunities that may apply to the Licensor or You, including immunity from the legal processes of any jurisdiction, national court or other authority.
+
+ h. Where the Licensor is an IGO, any and all disputes arising under this License that cannot be settled amicably shall be resolved in accordance with the following procedure:
+
+ i. Pursuant to a notice of mediation communicated by reasonable means by either You or the Licensor to the other, the dispute shall be submitted to non-binding mediation conducted in accordance with rules designated by the Licensor in the copyright notice published with the Work, or if none then in accordance with those communicated in the notice of mediation. The language used in the mediation proceedings shall be English unless otherwise agreed.
+
+ ii. If any such dispute has not been settled within 45 days following the date on which the notice of mediation is provided, either You or the Licensor may, pursuant to a notice of arbitration communicated by reasonable means to the other, elect to have the dispute referred to and finally determined by arbitration. The arbitration shall be conducted in accordance with the rules designated by the Licensor in the copyright notice published with the Work, or if none then in accordance with the UNCITRAL Arbitration Rules as then in force. The arbitral tribunal shall consist of a sole arbitrator and the language of the proceedings shall be English unless otherwise agreed. The place of arbitration shall be where the Licensor has its headquarters. The arbitral proceedings shall be conducted remotely (e.g., via telephone conference or written submissions) whenever practicable.
+
+ iii. Interpretation of this License in any dispute submitted to mediation or arbitration shall be as set forth in Section 8(f), above.
+
+Creative Commons Notice
+
+Creative Commons is not a party to this License, and makes no warranty whatsoever in connection with the Work. Creative Commons will not be liable to You or any party on any legal theory for any damages whatsoever, including without limitation any general, special, incidental or consequential damages arising in connection to this license. Notwithstanding the foregoing two (2) sentences, if Creative Commons has expressly identified itself as the Licensor hereunder, it shall have all rights and obligations of the Licensor.
+
+Except for the limited purpose of indicating to the public that the Work is licensed under the CCPL, Creative Commons does not authorize the use by either party of the trademark "Creative Commons" or any related trademark or logo of Creative Commons without the prior written consent of Creative Commons. Any permitted use will be in compliance with Creative Commons' then-current trademark usage guidelines, as may be published on its website or otherwise made available upon request from time to time. For the avoidance of doubt, this trademark restriction does not form part of this License.
+
+Creative Commons may be contacted at https://creativecommons.org/.
diff --git a/options/license/Fair b/options/license/Fair
index 32b6d6aff4..430fdb05f9 100644
--- a/options/license/Fair
+++ b/options/license/Fair
@@ -5,5 +5,3 @@ Fair License
Usage of the works is permitted provided that this instrument is retained with the works, so that any entity that uses the works is notified of this instrument.
DISCLAIMER: THE WORKS ARE WITHOUT WARRANTY.
-
-[2004, Fair License: rhid.com/fair (this URL no longer works)]
diff --git a/options/license/LZMA-SDK-9.11-to-9.20 b/options/license/LZMA-SDK-9.11-to-9.20
new file mode 100644
index 0000000000..5da25bf883
--- /dev/null
+++ b/options/license/LZMA-SDK-9.11-to-9.20
@@ -0,0 +1,8 @@
+LICENSE
+-------
+
+LZMA SDK is written and placed in the public domain by Igor Pavlov.
+
+Some code in LZMA is based on public domain code from another developers:
+ 1) PPMd var.H (2001): Dmitry Shkarin
+ 2) SHA-256: Wei Dai (Crypto++ library)
diff --git a/options/license/LZMA-SDK-9.22 b/options/license/LZMA-SDK-9.22
new file mode 100644
index 0000000000..ef4768d2a7
--- /dev/null
+++ b/options/license/LZMA-SDK-9.22
@@ -0,0 +1,15 @@
+LICENSE
+-------
+
+LZMA SDK is written and placed in the public domain by Igor Pavlov.
+
+Some code in LZMA SDK is based on public domain code from another developers:
+ 1) PPMd var.H (2001): Dmitry Shkarin
+ 2) SHA-256: Wei Dai (Crypto++ library)
+
+Anyone is free to copy, modify, publish, use, compile, sell, or distribute the
+original LZMA SDK code, either in source code form or as a compiled binary, for
+any purpose, commercial or non-commercial, and by any means.
+
+LZMA SDK code is compatible with open source licenses, for example, you can
+include it to GNU GPL or GNU LGPL code.
diff --git a/options/license/MIT-CMU b/options/license/MIT-CMU
index 2b11a7b08f..0ca287d982 100644
--- a/options/license/MIT-CMU
+++ b/options/license/MIT-CMU
@@ -2,6 +2,6 @@
By obtaining, using, and/or copying this software and/or its associated documentation, you agree that you have read, understood, and will comply with the following terms and conditions:
-Permission to use, copy, modify, and distribute this software and its associated documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appears in all copies, and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of the copyright holder not be used in advertising or publicity pertaining to distribution of the software without specific, written permission.
+Permission to use, copy, modify, and distribute this software and its associated documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appears in all copies, and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of the copyright holder not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission.
THE COPYRIGHT HOLDER DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM THE LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
diff --git a/options/license/MS-LPL b/options/license/MS-LPL
new file mode 100644
index 0000000000..ea8bffcaae
--- /dev/null
+++ b/options/license/MS-LPL
@@ -0,0 +1,24 @@
+Microsoft Limited Public License (Ms-LPL)
+
+This license governs use of the accompanying software. If you use the software, you accept this license. If you do not accept the license, do not use the software.
+
+1. Definitions
+The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here as under U.S. copyright law. A "contribution" is the original software, or any additions or changes to the software. A "contributor" is any person that distributes its contribution under this license. "Licensed patents" are a contributor's patent claims that read directly on its contribution.
+
+2. Grant of Rights
+ (A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create.
+
+ (B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software.
+
+3. Conditions and Limitations
+ (A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks.
+
+ (B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically.
+
+ (C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software.
+
+ (D) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license.
+
+ (E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees, or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement.
+
+ (F) Platform Limitation- The licenses granted in sections 2(A) & 2(B) extend only to the software or derivative works that you create that run on a Microsoft Windows operating system product.
diff --git a/options/license/NICTA-1.0 b/options/license/NICTA-1.0
new file mode 100644
index 0000000000..04622e308d
--- /dev/null
+++ b/options/license/NICTA-1.0
@@ -0,0 +1,61 @@
+NICTA Public Software Licence
+Version 1.0
+
+Copyright © 2004 National ICT Australia Ltd
+
+All rights reserved.
+
+By this licence, National ICT Australia Ltd (NICTA) grants permission,
+free of charge, to any person who obtains a copy of this software
+and any associated documentation files ("the Software") to use and
+deal with the Software in source code and binary forms without
+restriction, with or without modification, and to permit persons
+to whom the Software is furnished to do so, provided that the
+following conditions are met:
+
+- Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimers.
+- Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimers in
+ the documentation and/or other materials provided with the
+ distribution.
+- The name of NICTA may not be used to endorse or promote products
+ derived from this Software without specific prior written permission.
+
+EXCEPT AS EXPRESSLY STATED IN THIS LICENCE AND TO THE FULL EXTENT
+PERMITTED BY APPLICABLE LAW, THE SOFTWARE IS PROVIDED "AS-IS" AND
+NICTA MAKES NO REPRESENTATIONS, WARRANTIES OR CONDITIONS OF ANY
+KIND, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, ANY
+REPRESENTATIONS, WARRANTIES OR CONDITIONS REGARDING THE CONTENTS
+OR ACCURACY OF THE SOFTWARE, OR OF TITLE, MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, THE ABSENCE OF LATENT
+OR OTHER DEFECTS, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR
+NOT DISCOVERABLE.
+
+TO THE FULL EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT WILL
+NICTA BE LIABLE ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
+NEGLIGENCE) FOR ANY LOSS OR DAMAGE WHATSOEVER, INCLUDING (WITHOUT
+LIMITATION) LOSS OF PRODUCTION OR OPERATION TIME, LOSS, DAMAGE OR
+CORRUPTION OF DATA OR RECORDS; OR LOSS OF ANTICIPATED SAVINGS,
+OPPORTUNITY, REVENUE, PROFIT OR GOODWILL, OR OTHER ECONOMIC LOSS;
+OR ANY SPECIAL, INCIDENTAL, INDIRECT, CONSEQUENTIAL, PUNITIVE OR
+EXEMPLARY DAMAGES ARISING OUT OF OR IN CONNECTION WITH THIS LICENCE,
+THE SOFTWARE OR THE USE OF THE SOFTWARE, EVEN IF NICTA HAS BEEN
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+If applicable legislation implies warranties or conditions, or
+imposes obligations or liability on NICTA in respect of the Software
+that cannot be wholly or partly excluded, restricted or modified,
+NICTA's liability is limited, to the full extent permitted by the
+applicable legislation, at its option, to:
+
+a. in the case of goods, any one or more of the following:
+ i. the replacement of the goods or the supply of equivalent goods;
+ ii. the repair of the goods;
+ iii. the payment of the cost of replacing the goods or of acquiring
+ equivalent goods;
+ iv. the payment of the cost of having the goods repaired; or
+b. in the case of services:
+ i. the supplying of the services again; or
+ ii. the payment of the cost of having the services supplied
+ again.
diff --git a/options/license/Python-2.0.1 b/options/license/Python-2.0.1
new file mode 100644
index 0000000000..22f32578d4
--- /dev/null
+++ b/options/license/Python-2.0.1
@@ -0,0 +1,193 @@
+PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
+--------------------------------------------
+
+1. This LICENSE AGREEMENT is between the Python Software Foundation
+("PSF"), and the Individual or Organization ("Licensee") accessing and
+otherwise using this software ("Python") in source or binary form and
+its associated documentation.
+
+2. Subject to the terms and conditions of this License Agreement, PSF hereby
+grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce,
+analyze, test, perform and/or display publicly, prepare derivative works,
+distribute, and otherwise use Python alone or in any derivative version,
+provided, however, that PSF's License Agreement and PSF's notice of copyright,
+i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
+2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022 Python Software Foundation;
+All Rights Reserved" are retained in Python alone or in any derivative version
+prepared by Licensee.
+
+3. In the event Licensee prepares a derivative work that is based on
+or incorporates Python or any part thereof, and wants to make
+the derivative work available to others as provided herein, then
+Licensee hereby agrees to include in any such work a brief summary of
+the changes made to Python.
+
+4. PSF is making Python available to Licensee on an "AS IS"
+basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
+IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND
+DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
+FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT
+INFRINGE ANY THIRD PARTY RIGHTS.
+
+5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
+FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
+A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON,
+OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
+
+6. This License Agreement will automatically terminate upon a material
+breach of its terms and conditions.
+
+7. Nothing in this License Agreement shall be deemed to create any
+relationship of agency, partnership, or joint venture between PSF and
+Licensee. This License Agreement does not grant permission to use PSF
+trademarks or trade name in a trademark sense to endorse or promote
+products or services of Licensee, or any third party.
+
+8. By copying, installing or otherwise using Python, Licensee
+agrees to be bound by the terms and conditions of this License
+Agreement.
+
+
+BEOPEN.COM LICENSE AGREEMENT FOR PYTHON 2.0
+-------------------------------------------
+
+BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1
+
+1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an
+office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the
+Individual or Organization ("Licensee") accessing and otherwise using
+this software in source or binary form and its associated
+documentation ("the Software").
+
+2. Subject to the terms and conditions of this BeOpen Python License
+Agreement, BeOpen hereby grants Licensee a non-exclusive,
+royalty-free, world-wide license to reproduce, analyze, test, perform
+and/or display publicly, prepare derivative works, distribute, and
+otherwise use the Software alone or in any derivative version,
+provided, however, that the BeOpen Python License is retained in the
+Software, alone or in any derivative version prepared by Licensee.
+
+3. BeOpen is making the Software available to Licensee on an "AS IS"
+basis. BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
+IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND
+DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
+FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT
+INFRINGE ANY THIRD PARTY RIGHTS.
+
+4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE
+SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS
+AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY
+DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
+
+5. This License Agreement will automatically terminate upon a material
+breach of its terms and conditions.
+
+6. This License Agreement shall be governed by and interpreted in all
+respects by the law of the State of California, excluding conflict of
+law provisions. Nothing in this License Agreement shall be deemed to
+create any relationship of agency, partnership, or joint venture
+between BeOpen and Licensee. This License Agreement does not grant
+permission to use BeOpen trademarks or trade names in a trademark
+sense to endorse or promote products or services of Licensee, or any
+third party. As an exception, the "BeOpen Python" logos available at
+http://www.pythonlabs.com/logos.html may be used according to the
+permissions granted on that web page.
+
+7. By copying, installing or otherwise using the software, Licensee
+agrees to be bound by the terms and conditions of this License
+Agreement.
+
+
+CNRI LICENSE AGREEMENT FOR PYTHON 1.6.1
+---------------------------------------
+
+1. This LICENSE AGREEMENT is between the Corporation for National
+Research Initiatives, having an office at 1895 Preston White Drive,
+Reston, VA 20191 ("CNRI"), and the Individual or Organization
+("Licensee") accessing and otherwise using Python 1.6.1 software in
+source or binary form and its associated documentation.
+
+2. Subject to the terms and conditions of this License Agreement, CNRI
+hereby grants Licensee a nonexclusive, royalty-free, world-wide
+license to reproduce, analyze, test, perform and/or display publicly,
+prepare derivative works, distribute, and otherwise use Python 1.6.1
+alone or in any derivative version, provided, however, that CNRI's
+License Agreement and CNRI's notice of copyright, i.e., "Copyright (c)
+1995-2001 Corporation for National Research Initiatives; All Rights
+Reserved" are retained in Python 1.6.1 alone or in any derivative
+version prepared by Licensee. Alternately, in lieu of CNRI's License
+Agreement, Licensee may substitute the following text (omitting the
+quotes): "Python 1.6.1 is made available subject to the terms and
+conditions in CNRI's License Agreement. This Agreement together with
+Python 1.6.1 may be located on the internet using the following
+unique, persistent identifier (known as a handle): 1895.22/1013. This
+Agreement may also be obtained from a proxy server on the internet
+using the following URL: http://hdl.handle.net/1895.22/1013".
+
+3. In the event Licensee prepares a derivative work that is based on
+or incorporates Python 1.6.1 or any part thereof, and wants to make
+the derivative work available to others as provided herein, then
+Licensee hereby agrees to include in any such work a brief summary of
+the changes made to Python 1.6.1.
+
+4. CNRI is making Python 1.6.1 available to Licensee on an "AS IS"
+basis. CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
+IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND
+DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
+FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT
+INFRINGE ANY THIRD PARTY RIGHTS.
+
+5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
+1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
+A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1,
+OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
+
+6. This License Agreement will automatically terminate upon a material
+breach of its terms and conditions.
+
+7. This License Agreement shall be governed by the federal
+intellectual property law of the United States, including without
+limitation the federal copyright law, and, to the extent such
+U.S. federal law does not apply, by the law of the Commonwealth of
+Virginia, excluding Virginia's conflict of law provisions.
+Notwithstanding the foregoing, with regard to derivative works based
+on Python 1.6.1 that incorporate non-separable material that was
+previously distributed under the GNU General Public License (GPL), the
+law of the Commonwealth of Virginia shall govern this License
+Agreement only as to issues arising under or with respect to
+Paragraphs 4, 5, and 7 of this License Agreement. Nothing in this
+License Agreement shall be deemed to create any relationship of
+agency, partnership, or joint venture between CNRI and Licensee. This
+License Agreement does not grant permission to use CNRI trademarks or
+trade name in a trademark sense to endorse or promote products or
+services of Licensee, or any third party.
+
+8. By clicking on the "ACCEPT" button where indicated, or by copying,
+installing or otherwise using Python 1.6.1, Licensee agrees to be
+bound by the terms and conditions of this License Agreement.
+
+ ACCEPT
+
+
+CWI LICENSE AGREEMENT FOR PYTHON 0.9.0 THROUGH 1.2
+--------------------------------------------------
+
+Copyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam,
+The Netherlands. All rights reserved.
+
+Permission to use, copy, modify, and distribute this software and its
+documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appear in all copies and that
+both that copyright notice and this permission notice appear in
+supporting documentation, and that the name of Stichting Mathematisch
+Centrum or CWI not be used in advertising or publicity pertaining to
+distribution of the software without specific, written prior
+permission.
+
+STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO
+THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE
+FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
diff --git a/options/license/Spencer-86 b/options/license/Spencer-86
index 8577e881c1..ace415744d 100644
--- a/options/license/Spencer-86
+++ b/options/license/Spencer-86
@@ -7,5 +7,3 @@ Permission is granted to anyone to use this software for any purpose on any comp
2. The origin of this software must not be misrepresented, either by explicit claim or by omission.
3. Altered versions must be plainly marked as such, and must not be misrepresented as being the original software.
-
-Beware that some of this code is subtly aware of the way operator precedence is structured in regular expressions. Serious changes in regular-expression syntax might require a total rethink.
diff --git a/options/license/Verbatim-man-pages b/options/license/Verbatim-man-pages
deleted file mode 100644
index 8a10d8e684..0000000000
--- a/options/license/Verbatim-man-pages
+++ /dev/null
@@ -1,21 +0,0 @@
-Copyright (c) 0000, Obelix the Gaul .
-
-Permission is granted to make and distribute verbatim copies of this
-manual provided the copyright notice and this permission notice are
-preserved on all copies.
-
-Permission is granted to copy and distribute modified versions of
-this manual under the conditions for verbatim copying, provided that
-the entire resulting derived work is distributed under the terms of
-a permission notice identical to this one.
-
-Since the Linux kernel and libraries are constantly changing, this
-manual page may be incorrect or out-of-date. The author(s) assume
-no responsibility for errors or omissions, or for damages resulting
-from the use of the information contained herein. The author(s) may
-not have taken the same level of care in the production of this
-manual, which is licensed free of charge, as they might when working
-professionally.
-
-Formatted or processed versions of this manual, if unaccompanied by
-the source, must acknowledge the copyright and authors of this work.
diff --git a/options/license/checkmk b/options/license/checkmk
new file mode 100644
index 0000000000..46c6b74278
--- /dev/null
+++ b/options/license/checkmk
@@ -0,0 +1,9 @@
+# Copyright (c) 2006, 2010 Micah Cowan
+#
+# Redistribution of this program in any form, with or without
+# modifications, is permitted, provided that the above copyright is
+# retained in distributions of this program in source form.
+#
+# (This is a free, non-copyleft license compatible with pretty much any
+# other free or proprietary license, including the GPL. It's essentially
+# a scaled-down version of the "modified" BSD license.)
diff --git a/options/license/gnu-javamail-exception b/options/license/gnu-javamail-exception
index 6b24ec821a..8f3b9ab0d0 100644
--- a/options/license/gnu-javamail-exception
+++ b/options/license/gnu-javamail-exception
@@ -1 +1 @@
-As a special exception, if you link this library with other files to produce an executable, this library does not by itself cause the resulting executable to be covered by the GNU General Public License. This exception does not however invalidate any other reasons why the executable file might be covered by the GNU General Public License."
+As a special exception, if you link this library with other files to produce an executable, this library does not by itself cause the resulting executable to be covered by the GNU General Public License. This exception does not however invalidate any other reasons why the executable file might be covered by the GNU General Public License.
diff --git a/options/license/mpi-permissive b/options/license/mpi-permissive
new file mode 100644
index 0000000000..2abcbe3ab0
--- /dev/null
+++ b/options/license/mpi-permissive
@@ -0,0 +1,15 @@
+* Copyright (C) 2000-2004 by Etnus, LLC
+ *
+ * Permission is hereby granted to use, reproduce, prepare derivative
+ * works, and to redistribute to others.
+ *
+ * DISCLAIMER
+ *
+ * Neither Etnus, nor any of their employees, makes any warranty
+ * express or implied, or assumes any legal liability or
+ * responsibility for the accuracy, completeness, or usefulness of any
+ * information, apparatus, product, or process disclosed, or
+ * represents that its use would not infringe privately owned rights.
+ *
+ * This code was written by
+ * James Cownie: Etnus, LLC.
diff --git a/options/license/mpich2 b/options/license/mpich2
index 5c32b0ec1d..1fa4acbc62 100644
--- a/options/license/mpich2
+++ b/options/license/mpich2
@@ -3,11 +3,17 @@ COPYRIGHT
The following is a notice of limited availability of the code, and disclaimer which must be included in the prologue of the code and in all source listings of the code.
Copyright Notice
-+ 2002 University of Chicago
+1998--2020, Argonne National Laboratory
Permission is hereby granted to use, reproduce, prepare derivative works, and to redistribute to others. This software was authored by:
-Argonne National Laboratory Group W. Gropp: (630) 252-4318; FAX: (630) 252-5986; e-mail: gropp@mcs.anl.gov E. Lusk: (630) 252-7852; FAX: (630) 252-5986; e-mail: lusk@mcs.anl.gov Mathematics and Computer Science Division Argonne National Laboratory, Argonne IL 60439
+Mathematics and Computer Science Division
+Argonne National Laboratory, Argonne IL 60439
+
+(and)
+
+Department of Computer Science
+University of Illinois at Urbana-Champaign
GOVERNMENT LICENSE
diff --git a/options/locale/TRANSLATORS b/options/locale/TRANSLATORS
index 3884207f0a..e67255f2fb 100644
--- a/options/locale/TRANSLATORS
+++ b/options/locale/TRANSLATORS
@@ -72,7 +72,7 @@ Thomas Fanninger
Tilmann Bach
Toni Villena Jiménez
Viktor Sperl
-Vladimir Jigulin mogaika AT yandex DOT ru
+Vladimir Jigulin
Vladimir Vissoultchev
Yaşar Çiv
YJSoft
diff --git a/options/locale/locale_bg-BG.ini b/options/locale/locale_bg-BG.ini
index c6faee5cf6..10759528e5 100644
--- a/options/locale/locale_bg-BG.ini
+++ b/options/locale/locale_bg-BG.ini
@@ -8,7 +8,6 @@ sign_out=Изход
sign_up=Регистриране
link_account=Свържи профил
register=Регистрация
-website=Уебсайт
version=Версия
powered_by=С подкрепата на %s
page=Страница
@@ -129,7 +128,6 @@ log_root_path_helper=Директория, в която да се съхран
optional_title=Опционални настройки
email_title=Имейл настройки
-smtp_host=SMTP сървър
smtp_from=Изпрати имейл като
smtp_from_helper=E-mail адрес, който да се използва от Gitea. Въведете само E-mail адреса или име и E-mail във формат "Name ".
mailer_user=SMTP потребител
@@ -201,6 +199,7 @@ org_no_results=Не бяха намерени съответстващи орг
code_search_results=Резултати от търсене за '%s'
code_last_indexed_at=Последно индексиран %s
+
[auth]
create_new_account=Регистриране на акаунт
register_helper_msg=Вече имате профил? Впишете се сега!
@@ -1151,9 +1150,7 @@ config.queue_length=Дължина на опашка
config.deliver_timeout=Време за отказ при изпращане
config.mailer_enabled=Активен
-config.mailer_disable_helo=Изключи HELO
config.mailer_name=Име
-config.mailer_host=Сървър
config.mailer_user=Потребител
config.oauth_config=OAuth конфигурация
@@ -1193,6 +1190,7 @@ config.log_config=Конфигурация на журнал
config.log_mode=Режим на журнал
config.disabled_logger=Изключено
+
monitor.cron=Cron задачи
monitor.name=Име
monitor.schedule=График
diff --git a/options/locale/locale_cs-CZ.ini b/options/locale/locale_cs-CZ.ini
index cc449cae9f..dcf99124c2 100644
--- a/options/locale/locale_cs-CZ.ini
+++ b/options/locale/locale_cs-CZ.ini
@@ -2,13 +2,13 @@ home=Domů
dashboard=Přehled
explore=Procházet
help=Nápověda
+logo=Logo
sign_in=Přihlásit se
sign_in_with=Přihlásit se pomocí
sign_out=Odhlásit se
sign_up=Registrovat se
link_account=Propojit účet
register=Registrovat se
-website=Webové stránky
version=Verze
powered_by=Běží na %s
page=Strana
@@ -146,6 +146,8 @@ sqlite_helper=Cesta k souboru SQLite3 databáze.
Pokud spouštíte Gitea jako
reinstall_error=Pokoušíte se nainstalovat do existující databáze Gitea
reinstall_confirm_message=Přeinstalování s existující databází Gitea může způsobit více problémů. Ve většině případů byste měli použít existující „app.ini“ pro spuštění Gitea. Pokud víte, co děláte, potvrďte následující:
reinstall_confirm_check_1=Data šifrovaná pomocí SECRET_KEY v souboru api.ini mohou být ztracena: uživatelé nemusí být schopni se přihlásit s 2FA/OTP a zrcadla nemusí fungovat správně. Zaškrtnutím tohoto políčka potvrdíte, že aktuální soubor app.ini obsahuje správný SECRET_KEY.
+reinstall_confirm_check_2=Může být nutné znovu synchronizovat repozitáře a nastavení. Zaškrtnutím tohoto políčka potvrzujete, že budete háčky pro repozitáře a soubor authorized_keys znovu synchronizovat ručně. Potvrzujete, že zajistíte správnost nastavení repozitáře a zrcadla.
+reinstall_confirm_check_3=Potvrzujete, že jste si naprosto jisti, že tato Gitea je spuštěna se správným umístěním souboru app.ini a že jste si jisti, že musíte provést novou instalaci. Potvrzujete, že berete na vědomí výše uvedená rizika.
err_empty_db_path=Cesta k SQLite3 databázi nemůže být prázdná.
no_admin_and_disable_registration=Nemůžete vypnout registraci účtů bez vytvoření účtu správce.
err_empty_admin_password=Heslo administrátora nemůže být prázdné.
@@ -176,7 +178,8 @@ log_root_path_helper=Soubory protokolu budou zapsány do tohoto adresáře.
optional_title=Dodatečná nastavení
email_title=Nastavení e-mailu
-smtp_host=Server SMTP
+smtp_addr=Server SMTP
+smtp_port=Port SMTP
smtp_from=Odeslat e-mail jako
smtp_from_helper=E-mailová adresa, kterou bude Gitea používat. Zadejte běžnou e-mailovou adresu, nebo použijte formát "Jméno".
mailer_user=Uživatelské jméno SMTP
@@ -211,8 +214,11 @@ install_btn_confirm=Nainstalovat Gitea
test_git_failed=Chyba při testu příkazu 'git': %v
sqlite3_not_available=Tato verze Gitea nepodporuje SQLite3. Stáhněte si oficiální binární verzi od %s (nikoli verzi „gobuild“).
invalid_db_setting=Nastavení databáze je neplatné: %v
+invalid_db_table=Databázová tabulka „%s“ je neplatná: %v
invalid_repo_path=Kořenový adresář repozitářů není správný: %v
+invalid_app_data_path=Cesta k datům aplikace je neplatná: %v
run_user_not_match="Run as" uživatelské jméno není aktuální uživatelské jméno: %s -> %s
+internal_token_failed=Nepodařilo se vytvořit interní token: %v
secret_key_failed=Nepodařilo se vytvořit tajný klíč: %v
save_config_failed=Uložení konfigurace se nezdařilo: %v
invalid_admin_setting=Nastavení účtu správce není správné: %v
@@ -242,6 +248,7 @@ view_home=Zobrazit %s
search_repos=Nalézt repozitář…
filter=Ostatní filtry
filter_by_team_repositories=Filtrovat podle repozitářů týmu
+feed_of=Kanál z „%s“
show_archived=Archivováno
show_both_archived_unarchived=Zobrazeny jak archivované tak nearchivované
@@ -270,6 +277,9 @@ org_no_results=Nebyly nalezeny žádné odpovídající organizace.
code_no_results=Nebyl nalezen žádný zdrojový kód odpovídající hledanému výrazu.
code_search_results=Výsledky hledání pro „%s“
code_last_indexed_at=Naposledy indexováno %s
+relevant_repositories_tooltip=Repozitáře, které jsou rozštěpení nebo nemají žádné téma, ikonu a žádný popis jsou skryty.
+relevant_repositories=Zobrazují se pouze relevantní repositáře, zobrazit nefiltrované výsledky.
+
[auth]
create_new_account=Registrovat účet
@@ -316,7 +326,9 @@ oauth_signup_submit=Dokončit účet
oauth_signin_tab=Propojit s existujícím účtem
oauth_signin_title=Přihlaste se pro ověření propojeného účtu
oauth_signin_submit=Propojit účet
+oauth.signin.error=Došlo k chybě při zpracování žádosti o autorizaci. Pokud tato chyba přetrvává, obraťte se na správce webu.
oauth.signin.error.access_denied=Žádost o autorizaci byla zamítnuta.
+oauth.signin.error.temporarily_unavailable=Autorizace se nezdařila, protože ověřovací server je dočasně nedostupný. Opakujte akci později.
openid_connect_submit=Připojit
openid_connect_title=Připojení k existujícímu účtu
openid_connect_desc=Zvolené OpenID URI není známé. Přidružte nový účet zde.
@@ -367,7 +379,7 @@ issue_assigned.pull=@%[1]s vás přiřadil/a k požadavku na natažení %[2]v re
issue_assigned.issue=@%[1]s vás přiřadil/a k úkolu %[2]v repozitáři %[3]s.
issue.x_mentioned_you=@%s vás zmínil/a:
-issue.action.force_push=%[1]s vynutil/a nahrání %[2]s z %[3]do %[4].
+issue.action.force_push=%[1]s vynutil/a nahrání %[2]s z %[3]s do %[4]s.
issue.action.push_1=@%[1]s nahrál/a %[3]d commit do %[2]s
issue.action.push_n=@%[1]s nahrál/a %[3]d commity do %[2]s
issue.action.close=@%[1]s uzavřel/a #%[2]d.
@@ -433,6 +445,7 @@ size_error=` musí být minimálně velikosti %s.`
min_size_error=` musí obsahovat nejméně %s znaků.`
max_size_error=` musí obsahovat maximálně %s znaků.`
email_error=` není správná e-mailová adresa.`
+url_error=`„%s“ není platná adresa URL.`
include_error=` musí obsahovat řetězec „%s“.`
glob_pattern_error=`zástupný vzor je neplatný: %s.`
regex_pattern_error=` regex vzor je neplatný: %s.`
@@ -444,6 +457,7 @@ lang_select_error=Vyberte jazyk ze seznamu.
username_been_taken=Uživatelské jméno je již obsazeno.
username_change_not_local_user=Uživatelé, kteří jsou ověřováni jinak než lokálně, si nemohou změnit uživatelské jméno.
repo_name_been_taken=Název repozitáře je již použit.
+repository_force_private=Vynucené soukromí je povoleno: soukromé repozitáře nelze zveřejnit.
repository_files_already_exist=Soubory pro tento repozitář již existují. Obraťte se na správce systému.
repository_files_already_exist.adopt=Soubory pro tento repozitář již existují a mohou být pouze přijaty.
repository_files_already_exist.delete=Soubory pro tento repozitář již existují. Musíte je odstranit.
@@ -479,7 +493,9 @@ auth_failed=Ověření selhalo: %v
still_own_repo=Váš účet vlastní jeden nebo více repozitářů; smažte je nebo převeďte.
still_has_org=Váš účet je člen jedné nebo více organizací; nejdříve je opusťte.
+still_own_packages=Váš účet vlastní jeden nebo více balíčků. Nejprve je musíte odstranit.
org_still_own_repo=Organizace stále vlastní jeden nebo více repozitářů; smažte je nebo převeďte.
+org_still_own_packages=Organizace stále vlastní jeden nebo více balíčků; nejprve je smažte.
target_branch_not_exist=Cílová větev neexistuje.
@@ -542,6 +558,7 @@ continue=Pokračovat
cancel=Zrušit
language=Jazyk
ui=Motiv vzhledu
+hidden_comment_types=Skryté typy komentářů
comment_type_group_reference=Reference
comment_type_group_label=Štítek
comment_type_group_milestone=Milník
@@ -552,9 +569,11 @@ comment_type_group_time_tracking=Sledování času
comment_type_group_deadline=Uzávěrka
comment_type_group_dependency=Závislost
comment_type_group_lock=Stav zámku
+comment_type_group_review_request=Žádost o posouzení
comment_type_group_pull_request_push=Přidané commity
comment_type_group_project=Projekt
comment_type_group_issue_ref=Referenční číslo úkolu
+saved_successfully=Vaše nastavení bylo úspěšně uloženo.
privacy=Soukromí
keep_activity_private=Skrýt aktivitu z profilové stránky
keep_activity_private_popup=Učinit aktivitu viditelnou pouze pro vás a administrátory
@@ -640,10 +659,18 @@ gpg_token_required=Musíte zadat podpis pro níže uvedený token
gpg_token=Token
gpg_token_help=Podpis můžete vygenerovat pomocí:
gpg_token_code=echo "%s" | gpg -a --default-key %s --detach-sig
+gpg_token_signature=Zakódovaný podpis GPG
key_signature_gpg_placeholder=Začíná s „-----BEGIN PGP SIGNATURE-----“
+verify_gpg_key_success=GPG klíč „%s“ byl ověřen.
ssh_key_verified=Ověřený klíč
+ssh_key_verified_long=Klíč byl ověřen pomocí tokenu a může být použit k ověření commitů shodujících se s libovolnou vaší aktivovanou e-mailovou adresou pro tohoto uživatele.
ssh_key_verify=Ověřit
+ssh_invalid_token_signature=Zadaný SSH klíč, podpis nebo token se neshodují nebo je token zastaralý.
+ssh_token_required=Musíte zadat podpis pro níže uvedený token
ssh_token=Token
+ssh_token_help=Podpis můžete vygenerovat pomocí:
+ssh_token_signature=Zakódovaný podpis SSH
+key_signature_ssh_placeholder=Začíná s „-----BEGIN SSH SIGNATURE-----“
verify_ssh_key_success=SSH klíč „%s“ byl ověřen.
subkeys=Podklíče
key_id=ID klíče
@@ -671,7 +698,7 @@ no_activity=Žádná aktuální aktivita
can_read_info=Čtení
can_write_info=Zápis
key_state_desc=Tento klíč byl použit během posledních 7 dní
-token_state_desc=Tato poukázka byla použita během posledních 7 dní
+token_state_desc=Tento token byl použit během posledních 7 dní
principal_state_desc=Tento SSH Principal certifikát byl použit během posledních 7 dní
show_openid=Zobrazit na profilu
hide_openid=Odstranit z profilu
@@ -682,23 +709,26 @@ social_desc=Tyto účty sociálních síti jsou propojeny s vaším Gitea účte
unbind=Odpojit
unbind_success=Účet sociální sítě byl odpojen od vašeho Gitea účtu.
-manage_access_token=Spravovat přístupové poukázky
-generate_new_token=Vygenerovat novou poukázku
-tokens_desc=Tyto poukázky umožňují přístup k vašemu účtu pomocí Gitea API.
-new_token_desc=Aplikace používající poukázku mají plný přístup k vašemu účtu.
-token_name=Název poukázky
-generate_token=Vygenerovat poukázku
-generate_token_success=Váše nová poukázka byla vytvořena. Zkopírujte ji nyní protože se již znovu nezobrazí.
+manage_access_token=Spravovat přístupové tokeny
+generate_new_token=Vygenerovat nový token
+tokens_desc=Tyto tokeny umožňují přístup k vašemu účtu pomocí Gitea API.
+new_token_desc=Aplikace používající token mají plný přístup k vašemu účtu.
+token_name=Název tokenu
+generate_token=Vygenerovat token
+generate_token_success=Váš nový token byl vytvořen. Zkopírujte jej nyní protože se již znovu nezobrazí.
generate_token_name_duplicate=%s byl již použit jako název aplikace. Použijte prosím nový.
delete_token=Smazat
-access_token_deletion=Odstranit přístupovou poukázku
-delete_token_success=Poukázka byla odstraněna. Aplikace, které ji používají již nemají přístup k vašemu účtu.
+access_token_deletion=Odstranit přístupový token
+access_token_deletion_cancel_action=Zrušit
+access_token_deletion_confirm_action=Smazat
+access_token_deletion_desc=Smazání tokenu zruší přístup k vašemu účtu pro aplikace, které jej používají. Tuto akci nelze vrátit. Pokračovat?
+delete_token_success=Token byl odstraněn. Aplikace, které jej používají již nemají přístup k vašemu účtu.
manage_oauth2_applications=Spravovat OAuth2 aplikace
edit_oauth2_application=Upravit OAuth2 aplikaci
oauth2_applications_desc=OAuth2 aplikace umožní aplikacím třetích stran bezpečně ověřit uživatele v této instanci Gitea.
remove_oauth2_application=Odstranit OAuth2 aplikaci
-remove_oauth2_application_desc=Odstraněním OAuth2 aplikace odeberete přístup všem podepsaným přístupovým poukázkám. Pokračovat?
+remove_oauth2_application_desc=Odstraněním OAuth2 aplikace odeberete přístup všem podepsaným přístupovým tokenům. Pokračovat?
remove_oauth2_application_success=Aplikace byla odstraněna.
create_oauth2_application=Vytvořit novou OAuth2 aplikaci
create_oauth2_application_button=Vytvořit aplikaci
@@ -730,12 +760,12 @@ twofa_desc=Dvoufaktorový způsob ověřování zvýší zabezpečení vašeho
twofa_is_enrolled=Váš účet aktuálně používá dvoufaktorové ověřování.
twofa_not_enrolled=Váš účet aktuálně nepoužívá dvoufaktorové ověřování.
twofa_disable=Zakázat dvoufaktorové ověřování
-twofa_scratch_token_regenerate=Obnovit pomocnou poukázku
-twofa_scratch_token_regenerated=Vaše pomocná poukázka je nyní %s. Uložte ji na bezpečném místě.
+twofa_scratch_token_regenerate=Obnovit pomocný token
+twofa_scratch_token_regenerated=Váš pomocný token je nyní %s. Uložte jej na bezpečném místě.
twofa_enroll=Povolit dvoufaktorové ověřování
twofa_disable_note=Dvoufaktorové ověřování můžete zakázat, když bude potřeba.
twofa_disable_desc=Zakážete-li dvoufaktorové ověřování, bude váš účet méně zabezpečený. Pokračovat?
-regenerate_scratch_token_desc=Jestli jste někam založili vaši pomocnou poukázku nebo jste ji již použili k přihlášení, můžete ji resetovat zde.
+regenerate_scratch_token_desc=Jestli jste někam založili váš pomocný token nebo jste jej již použili k přihlášení, můžete jej resetovat zde.
twofa_disabled=Dvoufaktorové ověřování bylo zakázáno.
scan_this_image=Naskenujte tento obrázek s vaší ověřovací aplikací:
or_enter_secret=Nebo zadejte tajný kód: %s
@@ -744,13 +774,16 @@ passcode_invalid=Přístupový kód není platný. Zkuste to znovu.
twofa_enrolled=Ve vašem účtu bylo povoleno dvoufaktorové ověřování. Uložte si pomocný token (%s) na bezpečném místě, protože bude zobrazen pouze jednou!
twofa_failed_get_secret=Nepodařilo se získat tajemství.
+webauthn_desc=Bezpečnostní klíče jsou hardwarová zařízení obsahující kryptografické klíče. Mohou být použity pro dvoufaktorové ověřování. Bezpečnostní klíče musí podporovat WebAuthn Authenticator standard.
webauthn_register_key=Přidat bezpečnostní klíč
webauthn_nickname=Přezdívka
webauthn_delete_key=Odstranit bezpečnostní klíč
+webauthn_delete_key_desc=Pokud odstraníte bezpečnostní klíč, již se s ním nebudete moci přihlásit. Pokračovat?
manage_account_links=Správa propojených účtů
manage_account_links_desc=Tyto externí účty jsou propojeny s vaším Gitea účtem.
account_links_not_available=K vašemu Gitea účtu nejsou aktuálně připojené žádné externí účty.
+link_account=Propojit účet
remove_account_link=Odstranit propojený účet
remove_account_link_desc=Odstraněním propojeného účtu zrušíte jeho přístup k vašemu Gitea účtu. Pokračovat?
remove_account_link_success=Propojený účet byl odstraněn.
@@ -769,6 +802,7 @@ email_notifications.enable=Povolit e-mailová oznámení
email_notifications.onmention=E-mail pouze při zmínce
email_notifications.disable=Zakázat e-mailová oznámení
email_notifications.submit=Nastavit předvolby e-mailu
+email_notifications.andyourown=A Vaše vlastní upozornění
visibility=Viditelnost uživatele
visibility.public=Veřejný
@@ -797,6 +831,7 @@ visibility_fork_helper=(Změna tohoto ovlivní všechny rozštěpení repozitá
clone_helper=Potřebujete pomoci s klonováním? Navštivte nápovědu.
fork_repo=Rozštěpení repozitáře
fork_from=Rozštěpit z
+already_forked=Již jsi rozštěpil %s
fork_to_different_account=Rozštěpit na jiný účet
fork_visibility_helper=Viditelnost rozštěpeného repozitáře nemůže být změněna.
use_template=Použít tuto šablonu
@@ -830,7 +865,9 @@ default_branch=Výchozí větev
default_branch_helper=Výchozí větev je základní větev pro požadavky na natažení a commity kódu.
mirror_prune=Vyčistit
mirror_prune_desc=Odstranit zastaralé reference na vzdálené sledování
+mirror_interval=Interval zrcadlení (platné časové jednotky jsou „h“, „m“ a „s“). 0 zakáže periodickou synchronizaci. (Minimální interval: %s)
mirror_interval_invalid=Interval zrcadlení není platný.
+mirror_sync_on_commit=Synchronizovat při nahrávání revizí
mirror_address=Klonovat z URL
mirror_address_desc=Zadejte požadované přístupové údaje do sekce Ověření.
mirror_address_url_invalid=Poskytnutá URL je neplatná. Všechny komponenty musíte správně nahradit escape sekvencí.
@@ -859,6 +896,7 @@ delete_preexisting_label=Smazat
delete_preexisting=Odstranit již existující soubory
delete_preexisting_content=Odstranit soubory v %s
delete_preexisting_success=Smazány nepřijaté soubory v %s
+blame_prior=Zobrazit blame před touto změnou
transfer.accept=Přijmout převod
transfer.accept_desc=Převést do „%s“
@@ -878,6 +916,7 @@ desc.archived=Archivováno
template.items=Položky šablony
template.git_content=Obsah gitu (výchozí větev)
template.git_hooks=Háčky Gitu
+template.git_hooks_tooltip=Momentálně nemůžete po přidání upravovat nebo odebrat háčky gitu. Vyberte pouze v případě, že důvěřujete šabloně repozitáře.
template.webhooks=Webové háčky
template.topics=Témata
template.avatar=Avatar
@@ -897,6 +936,7 @@ form.name_pattern_not_allowed=Vzor „%s“ není povolený v názvu repozitář
need_auth=Ověření
migrate_options=Možnosti migrace
migrate_service=Migrační služba
+migrate_options_mirror_helper=Tento repozitář bude zrcadlem
migrate_options_lfs=Migrovat LFS soubory
migrate_options_lfs_endpoint.label=Koncový bod LFS
migrate_options_lfs_endpoint.description=Migrace se pokusí použít váš vzdálený Git pro určení LFS serveru. Můžete také zadat vlastní koncový bod, pokud jsou data LFS repozitáře uložena někde jinde.
@@ -913,8 +953,10 @@ migrate_items_releases=Vydání
migrate_repo=Migrovat repozitář
migrate.clone_address=Migrovat / klonovat z URL
migrate.clone_address_desc=HTTP(S) nebo URL pro klonování existujícího repozitáře
+migrate.github_token_desc=Můžete sem vložit jeden nebo více tokenů oddělených čárkou, abyste urychlili migraci kvůli omezení rychlosti rozhraní GitHub API. VAROVÁNÍ: Zneužití této funkce může vést k porušení zásad poskytovatele služeb a zablokování účtu.
migrate.clone_local_path=nebo místní cesta serveru
migrate.permission_denied=Není dovoleno importovat místní repozitáře.
+migrate.permission_denied_blocked=Nelze importovat z nepovolených hostitelů, prosím požádejte správce, aby zkontroloval nastavení ALLOWED_DOMAINS/ALLOW_LOCALETWORKS/BLOCKED_DOMAINS.
migrate.invalid_local_path=Místní cesta je neplatná, buď neexistuje nebo není adresářem.
migrate.invalid_lfs_endpoint=Koncový bod LFS není platný.
migrate.failed=Přenesení selhalo: %v
@@ -962,6 +1004,7 @@ clone_this_repo=Naklonovat tento repozitář
create_new_repo_command=Vytvořit nový repozitář na příkazové řádce
push_exist_repo=Nahrání existujícího repozitáře z příkazové řádky
empty_message=Tento repozitář nemá žádný obsah.
+broken_message=Data gitu, která jsou základem tohoto repozitáře, nelze číst. Kontaktujte správce této instance nebo smažte tento repositář.
code=Zdrojový kód
code.desc=Přístup ke zdrojovým kódům, souborům, commitům a větvím.
@@ -975,6 +1018,7 @@ tags=Značky
issues=Úkoly
pulls=Požadavky na natažení
project_board=Projekty
+packages=Balíčky
labels=Štítky
org_labels_desc=Štítky na úrovni organizace, které mohou být použity se všemi repozitáři v rámci této organizace
org_labels_desc_manage=spravovat
@@ -994,10 +1038,18 @@ file_view_rendered=Zobrazit vykreslené
file_view_raw=Zobrazit v surovém stavu
file_permalink=Trvalý odkaz
file_too_large=Soubor je příliš velký pro zobrazení.
-bidi_bad_header=`Tento soubor obsahuje neočekávané obousměrné znaky Unicode!`
-line_unicode=`Tento řádek má skryté unicode znaky`
+invisible_runes_header=`Tento soubor obsahuje neviditelné znaky Unicode!`
+invisible_runes_description=`Tento soubor obsahuje neviditelné znaky Unicode, které mohou být zpracovány jinak než níže uvedeným způsobem. Pokud je váš případ úmyslný a legitimní, můžete toto varování bezpečně ignorovat. Použijte tlačítko Escape sekvence k odhalení skrytých znaků.`
+ambiguous_runes_header=`Tento soubor obsahuje nejednoznačné znaky Unicode!`
+ambiguous_runes_description=`Tento soubor obsahuje nejednoznačné znaky Unicode, které mohou být zaměněny s ostatními v aktuálním prostředí. Pokud je váš případ úmyslný a legitimní, můžete toto varování bezpečně ignorovat. Použijte tlačítko Escape sekvence pro zvýraznění těchto znaků.`
+invisible_runes_line=`Tento řádek má neviditelné znaky Unicode`
+ambiguous_runes_line=`Tento řádek má nejednoznačné znaky Unicode`
+ambiguous_character=`%[1]c [U+%04[1]X] je zaměnitelný s %[2]c [U+%04[2]X]`
+escape_control_characters=Escape sekvence
+unescape_control_characters=Bez escape sekvencí
file_copy_permalink=Kopírovat trvalý odkaz
+view_git_blame=Zobrazit Git Blame
video_not_supported_in_browser=Váš prohlížeč nepodporuje značku pro HTML5 video.
audio_not_supported_in_browser=Váš prohlížeč nepodporuje značku pro HTML5 audio.
stored_lfs=Uloženo pomocí Git LFS
@@ -1013,6 +1065,7 @@ normal_view=Normální zobrazení
line=řádek
lines=řádky
+editor.add_file=Přidat soubor
editor.new_file=Nový soubor
editor.upload_file=Nahrát soubor
editor.edit_file=Upravit soubor
@@ -1036,6 +1089,10 @@ editor.add_tmpl=Přidán „“
editor.add=Přidat „%s“
editor.update=Aktualizovat „%s“
editor.delete=Smazat „%s“
+editor.patch=Použít záplatu
+editor.patching=Záplatování:
+editor.fail_to_apply_patch=Nelze použít záplatu „%s“
+editor.new_patch=Nová záplata
editor.commit_message_desc=Přidat volitelný rozšířený popis…
editor.signoff_desc=Přidat Signed-off-by podpis přispěvatele na konec zprávy o commitu.
editor.commit_directly_to_this_branch=Odevzdat přímo do větve %s.
@@ -1060,6 +1117,8 @@ editor.commit_empty_file_text=Soubor, který se chystáte odevzdat, je prázdný
editor.no_changes_to_show=Žádné změny k zobrazení.
editor.fail_to_update_file=Nepodařilo se aktualizovat/vytvořit soubor „%s“.
editor.fail_to_update_file_summary=Chybové hlášení:
+editor.push_rejected_no_message=Změna byla serverem zamítnuta bez zprávy. Prosím, zkontrolujte háčky Gitu.
+editor.push_rejected=Změna byla serverem zamítnuta. Prosím, zkontrolujte háčky Gitu.
editor.push_rejected_summary=Úplná zpráva o odmítnutí:
editor.add_subdir=Přidat adresář…
editor.unable_to_upload_files=Nepodařilo se nahrát soubor „%s“. Chyba: %v
@@ -1069,6 +1128,8 @@ editor.cannot_commit_to_protected_branch=Nelze vytvořit commit v chráněné v
editor.no_commit_to_branch=Nelze odevzdat přímo do větve, protože:
editor.user_no_push_to_branch=Uživatel nemůže nahrávat do větve
editor.require_signed_commit=Větev vyžaduje podepsaný commit
+editor.cherry_pick=Cherry-pick %s na:
+editor.revert=Vrátit %s na:
commits.desc=Procházet historii změn zdrojového kódu.
commits.commits=Commity
@@ -1095,7 +1156,9 @@ commit.revert-header=Vrátit: %s
commit.revert-content=Vyberte větev pro návrat na:
commit.cherry-pick=Cherry-pick
commit.cherry-pick-header=Cherry-pick: %s
+commit.cherry-pick-content=Vyberte větev pro Cherry-pick na:
+ext_issues=Přístup k externím úkolům
ext_issues.desc=Odkaz na externí systém úkolů.
projects=Projekty
@@ -1168,6 +1231,8 @@ issues.new.add_reviewer_title=Požádat o posouzení
issues.choose.get_started=Začínáme
issues.choose.blank=Výchozí
issues.choose.blank_about=Vytvořit úkol z výchozí šablony.
+issues.choose.ignore_invalid_templates=Neplatné šablony byly ignorovány
+issues.choose.invalid_templates=%v nalezených neplatných šablon
issues.no_ref=Není určena žádná větev/značka
issues.create=Vytvořit úkol
issues.new_label=Nový štítek
@@ -1197,6 +1262,7 @@ issues.add_assignee_at=`byl přiřazen %s %s`
issues.remove_assignee_at=`byl odstraněn z přiřazení %s %s`
issues.remove_self_assignment=`odstranil/a jejich přiřazení %s`
issues.change_title_at=`změnil/a název z %s na %s %s`
+issues.change_ref_at=`změnil/a referenci z %s na %s %s`
issues.remove_ref_at=`odstranil/a referenci %s %s`
issues.add_ref_at=`přidal/a referenci %s %s`
issues.delete_branch_at=`odstranil/a větev %s %s`
@@ -1207,6 +1273,8 @@ issues.filter_milestone=Milník
issues.filter_milestone_no_select=Všechny milníky
issues.filter_assignee=Zpracovatel
issues.filter_assginee_no_select=Všichni zpracovatelé
+issues.filter_poster=Autor
+issues.filter_poster_no_select=Všichni autoři
issues.filter_type=Typ
issues.filter_type.all_issues=Všechny úkoly
issues.filter_type.assigned_to_you=Přiřazené vám
@@ -1226,6 +1294,7 @@ issues.filter_sort.moststars=Nejvíce hvězdiček
issues.filter_sort.feweststars=Nejméně hvězdiček
issues.filter_sort.mostforks=Nejvíce rozštěpení
issues.filter_sort.fewestforks=Nejméně rozštěpení
+issues.keyword_search_unavailable=V současné době vyhledávání podle klíčového slova není dostupné. Obraťte se na správce webu.
issues.action_open=Otevřít
issues.action_close=Zavřít
issues.action_label=Štítek
@@ -1234,12 +1303,16 @@ issues.action_milestone_no_select=Žádný milník
issues.action_assignee=Zpracovatel
issues.action_assignee_no_select=Bez zpracovatele
issues.opened_by=otevřeno %[1]s uživatelem %[3]s
+pulls.merged_by=od %[3]s byl sloučen %[1]s
+pulls.merged_by_fake=od %[2]s byl sloučen %[1]s
+issues.closed_by=od %[3]s byl uzavřen %[1]s
issues.opened_by_fake=otevřeno %[1]s uživatelem %[2]s
issues.closed_by_fake=od %[2]s byl uzavřen %[1]s
issues.previous=Předchozí
issues.next=Další
issues.open_title=otevřený
issues.closed_title=zavřený
+issues.draft_title=Koncept
issues.num_comments=%d komentářů
issues.commented_at=`okomentoval %s`
issues.delete_comment_confirm=Jste si jist, že chcete smazat tento komentář?
@@ -1356,6 +1429,7 @@ issues.due_date_form_remove=Odstranit
issues.due_date_not_writer=Potřebujete práva na zápis do repozitáře pro úpravy termínu dokončení úkolu.
issues.due_date_not_set=Žádný termín dokončení.
issues.due_date_added=přidal/a termín dokončení %s %s
+issues.due_date_modified=upravil/a termín termínu z %[2]s na %[1]s %[3]s
issues.due_date_remove=odstranil/a termín dokončení %s %s
issues.due_date_overdue=Zpožděné
issues.due_date_invalid=Termín dokončení není platný nebo je mimo rozsah. Použijte prosím formát „rrrr-mm-dd“.
@@ -1400,6 +1474,7 @@ issues.review.add_review_request=vyžádal posouzení od %s %s
issues.review.remove_review_request=odstranil žádost o posouzení na %s %s
issues.review.remove_review_request_self=odmítl posoudit %s
issues.review.pending=Čekající
+issues.review.pending.tooltip=Tento komentář není momentálně viditelný pro ostatní uživatele. Chcete-li odeslat Vaše čekající komentáře, vyberte „%s“ → „%s/%s/%s“ v horní části stránky.
issues.review.review=Posouzení
issues.review.reviewers=Posuzovatelé
issues.review.outdated=Zastaralé
@@ -1418,8 +1493,9 @@ issues.content_history.created=vytvořeno
issues.content_history.delete_from_history=Smazat z historie
issues.content_history.delete_from_history_confirm=Smazat z historie?
issues.content_history.options=Možnosti
+issues.reference_link=Reference: %s
-compare.compare_base=základ
+compare.compare_base=základní
compare.compare_head=porovnat
pulls.desc=Povolit požadavky na natažení a posuzování kódu.
@@ -1430,13 +1506,18 @@ pulls.allow_edits_from_maintainers=Povolit úpravy od správců
pulls.allow_edits_from_maintainers_desc=Uživatelé s přístupem k zápisu do základní větve mohou také nahrávat do této větve
pulls.allow_edits_from_maintainers_err=Aktualizace se nezdařila
pulls.compare_changes_desc=Vyberte větev pro sloučení a větev pro natažení.
+pulls.has_viewed_file=Zobrazeno
+pulls.has_changed_since_last_review=Změněno od vašeho posledního posouzení
+pulls.viewed_files_label=%[1]d / %[2]d souborů zobrazeno
pulls.compare_base=sloučit do
pulls.compare_compare=natáhnout z
pulls.switch_comparison_type=Přepnout typ porovnání
+pulls.switch_head_and_base=Prohodit hlavní a základní větev
pulls.filter_branch=Filtrovat větev
pulls.no_results=Nebyly nalezeny žádné výsledky.
pulls.nothing_to_compare=Tyto větve jsou stejné. Není potřeba vytvářet požadavek na natažení.
pulls.nothing_to_compare_and_allow_empty_pr=Tyto větve jsou stejné. Tento požadavek na natažení bude prázdný.
+pulls.has_pull_request=`Požadavek na natažení mezi těmito větvemi již existuje: %[2]s#%[3]d`
pulls.create=Vytvořit požadavek na natažení
pulls.title_desc=chce sloučit %[1]d commity z větve %[2]s
do %[3]s
pulls.merged_title_desc=sloučil %[1]d commity z větve %[2]s
do větve %[3]s
před %[4]s
@@ -1460,6 +1541,8 @@ pulls.remove_prefix=Odstranit prefix %s
pulls.data_broken=Tento požadavek na natažení je rozbitý kvůli chybějícím informacím o rozštěpení.
pulls.files_conflicted=Tento požadavek na natažení obsahuje změny, které kolidují s cílovou větví.
pulls.is_checking=Právě probíhá kontrola konfliktů při sloučení. Zkuste to za chvíli.
+pulls.is_ancestor=Tato větev je již součástí cílové větve. Není co sloučit.
+pulls.is_empty=Změny na této větvi jsou již na cílové větvi. Toto bude prázdný commit.
pulls.required_status_check_failed=Některé požadované kontroly nebyly úspěšné.
pulls.required_status_check_missing=Některé požadované kontroly chybí.
pulls.required_status_check_administrator=Jako administrátor stále můžete sloučit tento požadavek na natažení.
@@ -1488,6 +1571,7 @@ pulls.no_merge_wip=Požadavek na natažení nemůže být sloučen protože je o
pulls.no_merge_not_ready=Tento požadavek na natažení není připraven na sloučení, zkontrolujte stav posouzení a kontrolu stavu.
pulls.no_merge_access=Nemáte oprávnění sloučit tento požadavek na natažení.
pulls.merge_pull_request=Vytvořit slučovací commit
+pulls.rebase_merge_pull_request=Rebase pak fast-forward
pulls.rebase_merge_commit_pull_request=Rebase a poté vytvořit slučovací commit
pulls.squash_merge_pull_request=Vytvořit squash commit
pulls.merge_manually=Sloučeno ručně
@@ -1500,9 +1584,12 @@ pulls.merge_conflict_summary=Chybové hlášení
pulls.rebase_conflict=Sloučení selhalo: Došlo ke konfliktu při rebase commitu: %[1]s. Tip: Zkuste jinou strategii
pulls.rebase_conflict_summary=Chybové hlášení
; %[2]s
%[3]s
-pulls.unrelated_histories=Sloučení selhalo: Základní revize nesdílí společnou historii. Tip: Zkuste jinou strategii
+pulls.unrelated_histories=Sloučení selhalo: Hlavní a základní revize nesdílí společnou historii. Tip: Zkuste jinou strategii
pulls.merge_out_of_date=Sloučení selhalo: Základ byl aktualizován při generování sloučení. Tip: Zkuste to znovu.
+pulls.head_out_of_date=Sloučení selhalo: Hlavní revize byla aktualizován při generování sloučení. Tip: Zkuste to znovu.
+pulls.push_rejected=Sloučení selhalo: Nahrání bylo zamítnuto. Zkontrolujte háčky Gitu pro tento repozitář.
pulls.push_rejected_summary=Úplná zpráva o odmítnutí
+pulls.push_rejected_no_message=Sloučení se nezdařilo: Nahrání bylo odmítnuto, ale nebyla nalezena žádná vzdálená zpráva.
Zkontrolujte háčky gitu pro tento repozitář
pulls.open_unmerged_pull_exists=`Nemůžete provést operaci znovuotevření protože je tu čekající požadavek na natažení (#%d) s identickými vlastnostmi.`
pulls.status_checking=Některé kontroly jsou nedořešeny
pulls.status_checks_success=Všechny kontroly byly úspěšné
@@ -1522,9 +1609,20 @@ pulls.merge_instruction_hint=`Můžete také zobrazit
+search.code_no_results=Nebyl nalezen žádný zdrojový kód odpovídající hledanému výrazu.
+search.code_search_unavailable=V současné době není vyhledávání kódu dostupné. Obraťte se na správce webu.
settings=Nastavení
settings.desc=Nastavení je místo, kde můžete měnit nastavení repozitáře
@@ -1681,6 +1782,7 @@ settings.hooks=Webové háčky
settings.githooks=Háčky Gitu
settings.basic_settings=Základní nastavení
settings.mirror_settings=Nastavení zrcadla
+settings.mirror_settings.docs=Nastavte váš projekt pro automatické nahrávání a/nebo stahování změn z/do jiného repozitáře. Větve, značky a commity budou synchronizovány automaticky. Jak mohu zrcadlit repozitáře?
settings.mirror_settings.mirrored_repository=Zrcadlený repozitář
settings.mirror_settings.direction=Směr
settings.mirror_settings.direction.pull=Natáhnout
@@ -1691,10 +1793,6 @@ settings.mirror_settings.push_mirror.remote_url=URL vzdáleného Git repozitář
settings.mirror_settings.push_mirror.add=Přidat zrcadlo pro nahrání
settings.sync_mirror=Synchronizovat nyní
settings.mirror_sync_in_progress=Právě probíhá synchronizace zrcadla. Zkuste to za chvíli.
-settings.email_notifications.enable=Povolit e-mailová oznámení
-settings.email_notifications.onmention=E-mail pouze při zmínce
-settings.email_notifications.disable=Zakázat e-mailová oznámení
-settings.email_notifications.submit=Nastavit předvolby e-mailu
settings.site=Webová stránka
settings.update_settings=Aktualizovat nastavení
settings.branches.update_default_branch=Aktualizovat výchozí větev
@@ -1716,6 +1814,9 @@ settings.tracker_url_format_error=Formát URL externího systému úkolu není p
settings.tracker_issue_style=Formát čísel externího systému úkolů
settings.tracker_issue_style.numeric=Číselný
settings.tracker_issue_style.alphanumeric=Alfanumerický
+settings.tracker_issue_style.regexp=Regulární výraz
+settings.tracker_issue_style.regexp_pattern=Vzor regulárního výrazu
+settings.tracker_issue_style.regexp_pattern_desc=První zachycená skupina bude použita místo {index}
.
settings.tracker_url_format_desc=Použijte zástupné symboly {user}
, {repo}
a {index}
pro uživatelské jméno, jméno repozitáře a číslo úkolu.
settings.enable_timetracker=Povolit sledování času
settings.allow_only_contributors_to_track_time=Povolit sledování času pouze přispěvatelům
@@ -1727,6 +1828,9 @@ settings.pulls.allow_rebase_merge_commit=Povolit rebase s vyžádaným slučovac
settings.pulls.allow_squash_commits=Povolit squash pro slučovací commity
settings.pulls.allow_manual_merge=Povolit označování požadavků na natažení jako ručně sloučené
settings.pulls.enable_autodetect_manual_merge=Povolit autodetekci ručních sloučení (Poznámka: V některých zvláštních případech může dojít k nesprávnému rozhodnutí)
+settings.pulls.allow_rebase_update=Povolit aktualizaci větve požadavku na natažení pomocí rebase
+settings.pulls.default_delete_branch_after_merge=Ve výchozím nastavení mazat větev požadavku na natažení po jeho sloučení
+settings.packages_desc=Povolit registr balíčků repozitáře
settings.projects_desc=Povolit projekty v repozitáři
settings.admin_settings=Nastavení správce
settings.admin_enable_health_check=Povolit kontrolu stavu repozitáře (git fsck)
@@ -1760,6 +1864,7 @@ settings.transfer_form_title=Zadejte jméno repozitáře pro potvrzení:
settings.transfer_in_progress=V současné době probíhá převod. Zrušte jej, pokud chcete převést tento repozitář jinému uživateli.
settings.transfer_notices_1=- Ztratíte přístup k repozitáři, pokud jej převedete na uživatele.
settings.transfer_notices_2=- Zůstane vám přístup k repozitáři, pokud jej převedete na organizaci kterou (spolu)vlastníte.
+settings.transfer_notices_3=- Pokud je repozitář soukromý a je předán jednotlivému uživateli, tato akce se ujistí, že uživatel má alespoň oprávnění ke čtení (a v případě potřeby změní oprávnění).
settings.transfer_owner=Nový vlastník
settings.transfer_perform=Provést převod
settings.transfer_started=Tento repozitář byl označen pro převod a čeká na potvrzení od „%s“
@@ -1793,6 +1898,7 @@ settings.confirm_delete=Smazat repozitář
settings.add_collaborator=Přidat spolupracovníka
settings.add_collaborator_success=Spolupracovník byl přidán.
settings.add_collaborator_inactive_user=Nelze přidat neaktivního uživatele jako spolupracovníka.
+settings.add_collaborator_owner=Vlastníka nelze přidat jako spolupracovníka.
settings.add_collaborator_duplicate=Spolupracovník je již přidán k tomuto repozitáři.
settings.delete_collaborator=Odstranit
settings.collaborator_deletion=Odstranit spolupracovníka
@@ -1824,6 +1930,8 @@ settings.webhook.headers=Hlavičky
settings.webhook.payload=Obsah
settings.webhook.body=Tělo zprávy
settings.webhook.replay.description=Zopakovat tento webový háček.
+settings.webhook.delivery.success=Událost byla přidána do fronty doručení. Může to trvat několik sekund, než se zobrazí v historii doručení.
+settings.githooks_desc=Jelikož háčky Gitu jsou spravovány Gitem samotným, můžete upravit soubory háčků k provádění uživatelských operací.
settings.githook_edit_desc=Je-li háček neaktivní, bude zobrazen vzorový obsah. Nebude-li zadán žádný obsah, háček bude vypnut.
settings.githook_name=Název háčku
settings.githook_content=Obsah háčku
@@ -1849,6 +1957,8 @@ settings.event_delete=Smazat
settings.event_delete_desc=Větev nebo značka smazána.
settings.event_fork=Rozštěpit
settings.event_fork_desc=Repozitář rozštěpen.
+settings.event_wiki=Wiki
+settings.event_wiki_desc=Wiki stránka vytvořena, přejmenována nebo smazána.
settings.event_release=Vydání
settings.event_release_desc=Vydání v tomto repozitáři bylo publikováno, aktualizováno nebo smazáno.
settings.event_push=Nahrát
@@ -1884,6 +1994,7 @@ settings.event_pull_request_sync_desc=Požadavek na natažení synchronizován.
settings.event_package=Balíček
settings.event_package_desc=Balíček vytvořen nebo odstraněn v repozitáři.
settings.branch_filter=Filtr větví
+settings.branch_filter_desc=Povolené větve pro události nahrání, vytvoření větve a smazání větve jsou určeny pomocí zástupného vzoru. Pokud je prázdný nebo *
, všechny události jsou ohlášeny. Podívejte se na dokumentaci syntaxe na github.com/gobwas/glob. Příklady: master
, {master,release*}
.
settings.active=Aktivní
settings.active_helper=Informace o spuštěných událostech budou odeslány na URL webového háčku.
settings.add_hook_success=Webový háček byl přidán.
@@ -1892,9 +2003,10 @@ settings.update_hook_success=Webový háček byl aktualizován.
settings.delete_webhook=Odstranit webový háček
settings.recent_deliveries=Nedávné dodávky
settings.hook_type=Typ háčku
-settings.slack_token=Poukázka
+settings.slack_token=Token
settings.slack_domain=Doména
settings.slack_channel=Kanál
+settings.add_web_hook_desc=Integrovat %s do vašeho repozitáře.
settings.web_hook_name_gitea=Gitea
settings.web_hook_name_gogs=Gogs
settings.web_hook_name_slack=Slack
@@ -1964,6 +2076,7 @@ settings.require_signed_commits_desc=Odmítnout nahrání do této větve pokud
settings.protect_protected_file_patterns=Chráněné vzory souborů (oddělené středníkem „\;“):
settings.protect_protected_file_patterns_desc=Chráněné soubory, které nemají povoleno být měněny přímo, i když uživatel má právo přidávat, upravovat nebo mazat soubory v této větvi. Více vzorů lze oddělit pomocí středníku („\;“). Podívejte se na github.com/gobwas/glob dokumentaci pro syntaxi vzoru. Příklady: .drone.yml
, /docs/**/*.txt
.
settings.protect_unprotected_file_patterns=Nechráněné vzory souborů (oddělené středníkem „\;“):
+settings.protect_unprotected_file_patterns_desc=Nechráněné soubory, které je možné měnit přímo, pokud má uživatel právo zápisu, čímž se obejde omezení push. Více vzorů lze oddělit pomocí středníku ('\;'). Podívejte se na github.com/gobwas/glob dokumentaci pro syntaxi vzoru. Příklady: .drone.yml
, /docs/**/*.txt
.
settings.add_protected_branch=Zapnout ochranu
settings.delete_protected_branch=Vypnout ochranu
settings.update_protect_branch_success=Ochrana větví pro větev „%s“ byla aktualizována.
@@ -1991,7 +2104,8 @@ settings.tags.protection.allowed.teams=Povolené týmy
settings.tags.protection.allowed.noone=Nikdo
settings.tags.protection.create=Chránit značku
settings.tags.protection.none=Neexistují žádné chráněné značky.
-settings.bot_token=Poukázka pro robota
+settings.tags.protection.pattern.description=Můžete použít jediné jméno nebo vzor glob nebo regulární výraz, který bude odpovídat více značek. Přečtěte si více v průvodci chráněnými značkami.
+settings.bot_token=Token pro robota
settings.chat_id=ID chatu
settings.matrix.homeserver_url=URL adresa Homeserveru
settings.matrix.room_id=ID místnosti
@@ -2050,7 +2164,7 @@ diff.git-notes=Poznámky
diff.data_not_available=Rozdílový obsah není dostupný
diff.options_button=Možnosti rozdílového porovnání
diff.show_diff_stats=Zobrazit statistiky
-diff.download_patch=Stáhněte soubor opravy
+diff.download_patch=Stáhněte soubor záplaty
diff.download_diff=Stáhněte rozdílový soubor
diff.show_split_view=Rozdělené zobrazení
diff.show_unified_view=Jednotný pohled
@@ -2073,7 +2187,9 @@ diff.file_suppressed=Rozdílový obsah nebyl zobrazen, protože je příliš vel
diff.file_suppressed_line_too_long=Rozdílový obsah nebyl zobrazen, protože některé řádky jsou příliš dlouhá
diff.too_many_files=Některé soubory nejsou zobrazny, neboť je v této revizi změněno mnoho souborů
diff.show_more=Zobrazit více
+diff.load=Načíst rozdílové porovnání
diff.generated=vygenerováno
+diff.vendored=vendorováno
diff.comment.placeholder=Zanechat komentář
diff.comment.markdown_info=Je podporována úprava vzhledu pomocí markdown.
diff.comment.add_single_comment=Přidat jeden komentář
@@ -2179,6 +2295,8 @@ topic.done=Hotovo
topic.count_prompt=Nelze vybrat více než 25 témat
topic.format_prompt=Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.
+find_file.go_to_file=Přejít na soubor
+find_file.no_matching=Nebyl nalezen žádný odpovídající soubor
error.csv.too_large=Tento soubor nelze vykreslit, protože je příliš velký.
error.csv.unexpected=Tento soubor nelze vykreslit, protože obsahuje neočekávaný znak na řádku %d ve sloupci %d.
@@ -2260,7 +2378,9 @@ teams.leave.detail=Opustit %s?
teams.can_create_org_repo=Vytvořit repozitáře
teams.can_create_org_repo_helper=Členové mohou vytvářet nové repozitáře v organizaci. Tvůrce získá přístup správce do nového repozitáře.
teams.none_access=Bez přístupu
+teams.none_access_helper=Členové nemohou prohlížet ani dělat žádnou jinou akci pro tuto jednotku.
teams.general_access=Obecný přístup
+teams.general_access_helper=O oprávnění členů bude rozhodnuto níže uvedenou tabulkou oprávnění.
teams.read_access=Čtení
teams.read_access_helper=Členové mohou zobrazit a klonovat repozitáře týmu.
teams.write_access=Zápis
@@ -2314,9 +2434,11 @@ first_page=První
last_page=Poslední
total=Celkem: %d
+dashboard.new_version_hint=Gitea %s je nyní k dispozici, používáte %s. Pro více informací si přečtěte blog.
dashboard.statistic=Souhrn
dashboard.operations=Operace údržby
dashboard.system_status=Status systému
+dashboard.statistic_info=Databáze Gitea obsahuje %d uživatelů, %d organizací, %d veřejných klíčů, %d repozitářů, %d hlídání, %d oblíbení, ~%d akcí, %d přístupů, %d úkolů, %d komentářů, %d účtů sociálních sítí, %d sledování, %d zrcadel, %d vydání, %d zdrojů ověřování, %d webových háčků, %d milníků, %d štítků, %d háčků, %d týmů, %d úkolů změn, %d příloh.
dashboard.operation_name=Název operace
dashboard.operation_switch=Přepnout
dashboard.operation_run=Spustit
@@ -2388,6 +2510,7 @@ dashboard.gc_times=Časy GC
dashboard.delete_old_actions=Odstranit všechny staré akce z databáze
dashboard.delete_old_actions.started=Začalo odstraňování všech starých akcí z databáze.
dashboard.update_checker=Kontrola aktualizací
+dashboard.delete_old_system_notices=Odstranit všechna stará systémová upozornění z databáze
users.user_manage_panel=Správa uživatelských účtů
users.new_account=Vytvořit uživatelský účet
@@ -2422,8 +2545,12 @@ users.allow_import_local=Může importovat lokální repozitáře
users.allow_create_organization=Může vytvářet organizace
users.update_profile=Aktualizovat uživatelský účet
users.delete_account=Smazat uživatelský účet
+users.cannot_delete_self=Nemůžete smazat sami sebe
users.still_own_repo=Tento uživatel stále vlastní jeden nebo více repozitářů. Tyto repozitáře nejprve smažte nebo je převeďte.
users.still_has_org=Uživatel je člen organizace. Nejprve odstraňte uživatele ze všech organizací.
+users.purge=Vymazat uživatele
+users.purge_help=Vynuceně smazat uživatele a všechny repositáře, organizace a balíčky vlastněné uživatelem. Všechny komentáře budou také smazány.
+users.still_own_packages=Tento uživatel stále vlastní jeden nebo více balíčků. Nejprve odstraňte tyto balíčky.
users.deletion_success=Uživatelský účet byl smazán.
users.reset_2fa=Resetovat 2FA
users.list_status_filter.menu_text=Filtr
@@ -2514,6 +2641,7 @@ auths.attribute_name=Atribut křestního jména
auths.attribute_surname=Atribut příjmení
auths.attribute_mail=Atribut e-mailové adresy
auths.attribute_ssh_public_key=Atribut veřejného SSH klíče
+auths.attribute_avatar=Atributy avataru
auths.attributes_in_bind=Získat atributy v kontextu Bind DN
auths.allow_deactivate_all=Povolit prázdný výsledek hledání pro deaktivaci všech uživatelů
auths.use_paged_search=Použijte vyhledávání ve stránce
@@ -2522,6 +2650,7 @@ auths.filter=Uživatelský filtr
auths.admin_filter=Správcovský filtr
auths.restricted_filter=Filtr omezení
auths.restricted_filter_helper=Ponechte prázdné, pokud nechcete nastavit žádné uživatele jako omezené. Použijte hvězdičku („*“) pro nastavení všech uživatelů, kteří neodpovídají filtru administrátora, jako omezené.
+auths.verify_group_membership=Ověřit členství ve skupině v LDAP (ponechte prázdný filtr pro přeskočení)
auths.group_search_base=Základní DN pro hledání skupin
auths.group_attribute_list_users=Skupinový atribut obsahující seznam uživatelů
auths.user_attribute_in_group=Atribut uživatele ve skupině
@@ -2536,7 +2665,9 @@ auths.allowed_domains=Povolené domény
auths.allowed_domains_helper=Nechte prázdné k povolení všech domén. Oddělte více domén pomocí čárky („,“).
auths.skip_tls_verify=Přeskočit ověření TLS
auths.force_smtps=Vynutit SMTPS
+auths.force_smtps_helper=SMTPS se vždy používá na portu 465. Nastavením této hodnoty vynutíte použití SMTPS na jiných portech. (V opačném případě se na ostatních portech použije STARTTLS, pokud je podporován hostiteslkým serverem.)
auths.helo_hostname=HELO Hostname
+auths.helo_hostname_helper=Název hostitele odeslaný s HELO. Chcete-li odeslat aktuální název hostitele, ponechte prázdné.
auths.disable_helo=Zakázat HELO
auths.pam_service_name=Název služby PAM
auths.pam_email_domain=PAM e-mailová doména (volitelné)
@@ -2546,11 +2677,21 @@ auths.oauth2_clientID=Klientské ID (klíč)
auths.oauth2_clientSecret=Tajný klíč klienta
auths.openIdConnectAutoDiscoveryURL=OpenID URL pro automatické objevování
auths.oauth2_use_custom_url=Použijte vlastní URL místo výchozích
-auths.oauth2_tokenURL=URL poukázky
+auths.oauth2_tokenURL=URL tokenu
auths.oauth2_authURL=Autorizační URL
auths.oauth2_profileURL=URL profilu
auths.oauth2_emailURL=URL e-mailu
auths.skip_local_two_fa=Přeskočit lokální 2FA
+auths.skip_local_two_fa_helper=Ponechání nenastavené hodnoty znamená, že místní uživatelé s nastavenou funkcí 2FA budou muset při přihlašování stále projít funkcí 2FA
+auths.oauth2_tenant=Nájemník
+auths.oauth2_scopes=Další rozsahy
+auths.oauth2_required_claim_name=Požadovaný název tvrzení
+auths.oauth2_required_claim_name_helper=Nastavte toto jméno pro omezení přihlášení z tohoto zdroje pro uživatele s tvrzením s tímto jménem
+auths.oauth2_required_claim_value=Požadovaná hodnota tvrzení
+auths.oauth2_required_claim_value_helper=Nastavte tuto hodnotu pro omezení přihlášení z tohoto zdroje pro uživatele s tvrzením s tímto jménem a hodnotou
+auths.oauth2_group_claim_name=Název tvrzení poskytující názvy skupin pro tento zdroj. (nepovinné)
+auths.oauth2_admin_group=Hodnota tvrzení pro skupinu uživatelů administrátorů. (Volitelné - vyžaduje název tvrzení výše)
+auths.oauth2_restricted_group=Hodnota tvrzení pro skupinu omezených uživatelů. (Volitelné - vyžaduje název tvrzení výše)
auths.enable_auto_register=Povolit zaregistrování se
auths.sspi_auto_create_users=Automaticky vytvářet uživatele
auths.sspi_auto_create_users_helper=Povolit SSPI autentizační metodě automaticky vytvářet nové účty pro uživatele, kteří se poprvé přihlásili
@@ -2665,16 +2806,19 @@ config.queue_length=Délka fronty
config.deliver_timeout=Časový limit doručení
config.skip_tls_verify=Přeskočit verifikaci TLS
-config.mailer_config=Konfigurace služby SMTP
+config.mailer_config=Nastavení odesílání e-mailů
config.mailer_enabled=Zapnutý
-config.mailer_disable_helo=Zakázat HELO
+config.mailer_enable_helo=Povolit HELO
config.mailer_name=Název
-config.mailer_host=Server
+config.mailer_protocol=Protokol
+config.mailer_smtp_addr=Adresa SMTP
+config.mailer_smtp_port=Port SMTP
config.mailer_user=Uživatel
config.mailer_use_sendmail=Použít Sendmail
config.mailer_sendmail_path=Cesta k Sendmail
config.mailer_sendmail_args=Dodatečné argumenty pro Sendmail
config.mailer_sendmail_timeout=Časový limit Sandmail
+config.mailer_use_dummy=Fiktivní
config.test_email_placeholder=E-mail (např.: test@example.com)
config.send_test_mail=Odeslat zkušební e-mail
config.test_mail_failed=Odeslání testovacího e-mailu na „%s“ selhalo: %v
@@ -2727,6 +2871,7 @@ config.access_log_template=Šablona
config.xorm_log_mode=Režim logování XORM
config.xorm_log_sql=Logovat SQL
+
monitor.cron=Naplánované úlohy
monitor.name=Název
monitor.schedule=Rozvrh
@@ -2734,6 +2879,8 @@ monitor.next=Příští čas spuštění
monitor.previous=Předešlý čas spuštění
monitor.execute_times=Vykonání
monitor.process=Spuštěné procesy
+monitor.stacktrace=Výpisy zásobníku
+monitor.goroutines=%d Go-rutiny
monitor.desc=Popis
monitor.start=Čas zahájení
monitor.execute_time=Doba provádění
@@ -2741,6 +2888,7 @@ monitor.last_execution_result=Výsledek
monitor.process.cancel=Zrušit proces
monitor.process.cancel_desc=Zrušení procesu může způsobit ztrátu dat
monitor.process.cancel_notices=Zrušit: %s?
+monitor.process.children=Potomek
monitor.queues=Fronty
monitor.queue=Fronta: %s
monitor.queue.name=Název
@@ -2748,6 +2896,7 @@ monitor.queue.type=Typ
monitor.queue.exemplar=Typ vzoru
monitor.queue.numberworkers=Počet workerů
monitor.queue.maxnumberworkers=Maximální počet workerů
+monitor.queue.numberinqueue=Číslo ve frontě
monitor.queue.review=Konfigurace posouzení
monitor.queue.review_add=Posoudit/přidat workery
monitor.queue.configuration=Výchozí konfigurace
@@ -2755,6 +2904,7 @@ monitor.queue.nopool.title=Žádný fond workerů
monitor.queue.nopool.desc=Tato fronta obaluje jiné fronty ale sama o sobě nemá fond workerů.
monitor.queue.wrapped.desc=Zabalená fronta zabalí pomalou startující frontu ukládáním požadavků do vyrovnávací paměti. Nemá vlastní fond workerů.
monitor.queue.persistable-channel.desc=Trvalý kanál obaluje dvě fronty, frontu kanálu, která má vlastní fond workerů a vyrovnávací frontu pro přetrvávající požadavky z předchozích vypnutí. Nemá sám o sobě svůj fond workerů.
+monitor.queue.flush=Vyprázdnit worker
monitor.queue.pool.timeout=Časový limit
monitor.queue.pool.addworkers.title=Přidat workery
monitor.queue.pool.addworkers.submit=Přidat workery
@@ -2829,18 +2979,23 @@ comment_issue=`okomentoval/a problém %[3]s#%[2]s`
comment_pull=`okomentoval/a požadavek na natažení %[3]s#%[2]s`
merge_pull_request=`sloučil/a požadavek na natažení %[3]s#%[2]s`
transfer_repo=předal/a repozitář %s
uživateli/organizaci %s
-delete_tag=smazána značka %[2]s z %[3]s
+push_tag=nahrál/a značku %[3]s do %[4]s
+delete_tag=smazal/a značku %[2]s z %[3]s
delete_branch=smazal/a větev %[2]s z %[3]s
compare_branch=Porovnat
compare_commits=Porovnat %d revizí
compare_commits_general=Porovnat revize
mirror_sync_push=synchronizoval/a commity do %[3]s v %[4]s ze zrcadla
+mirror_sync_create=synchronizoval/a novou referenci %[3]s do %[4]s ze zrcadla
mirror_sync_delete=synchronizoval/a a smazal/a referenci %[2]s
v %[3]s ze zrcadla
approve_pull_request=`schválil/a %[3]s#%[2]s`
+reject_pull_request=`navrhl/a změny pro %[3]s#%[2]s`
publish_release=`vydal/a "%[4]s" v %[3]s`
review_dismissed=`zamítl/a posouzení z %[4]s pro %[3]s#%[2]s`
review_dismissed_reason=Důvod:
create_branch=vytvořil/a větev %[3]s v %[4]s
+starred_repo=si oblíbil/a %[2]s
+watched_repo=začal/a sledovat %[2]s
[tool]
ago=před %s
@@ -2901,9 +3056,15 @@ error.unit_not_allowed=Nejste oprávněni přistupovat k této části repozitá
title=Balíčky
desc=Správa balíčků repozitáře.
empty=Zatím nejsou žádné balíčky.
+empty.documentation=Další informace o registru balíčků naleznete v dokumentaci.
+empty.repo=Nahráli jste balíček, ale nezobrazil se zde? Přejděte na nastavení balíčku a propojte jej s tímto repozitářem.
filter.type=Typ
filter.type.all=Vše
filter.no_result=Váš filtr nepřinesl žádné výsledky.
+filter.container.tagged=Označeno
+filter.container.untagged=Neoznačeno
+published_by=Zveřejněno %[1]s od %[3]s
+published_by_in=Zveřejněno %[1]s od %[3]s v %[5]s
installation=Instalace
about=O tomto balíčku
requirements=Požadavky
@@ -2913,25 +3074,38 @@ details=Podrobnosti
details.author=Autor
details.project_site=Stránka projektu
details.license=Licence
+assets=Prostředky
versions=Verze
+versions.on=
versions.view_all=Zobrazit všechny
dependency.id=ID
dependency.version=Verze
+composer.registry=Nastavit tento registr v souboru ~/.composer/config.json
:
+composer.install=Pro instalaci balíčku pomocí Compposer spusťte následující příkaz:
+composer.documentation=Další informace o registru Composer naleznete v dokumentaci.
composer.dependencies=Závislosti
composer.dependencies.development=Vývojové závislosti
conan.details.repository=Repozitář
conan.registry=Nastavte tento registr z příkazového řádku:
+conan.install=Pro instalaci balíčku pomocí Conan spusťte následující příkaz:
+conan.documentation=Další informace o registru Conan naleznete v dokumentaci.
container.details.type=Typ obrazu
container.details.platform=Platforma
container.details.repository_site=Stránka repositáře
container.details.documentation_site=Stránka dokumentace
container.pull=Stáhněte obraz z příkazové řádky:
+container.digest=Výběr:
+container.documentation=Další informace o registru Container naleznete v dokumentaci.
+container.multi_arch=OS/architektura
container.layers=Vrstvy obrazů
container.labels=Štítky
container.labels.key=Klíč
container.labels.value=Hodnota
generic.download=Stáhnout balíček z příkazové řádky:
+generic.documentation=Další informace o obecném registru naleznete v dokumentaci.
helm.registry=Nastavte tento registr z příkazového řádku:
+helm.install=Pro instalaci balíčku spusťte následující příkaz:
+helm.documentation=Další informace o Helm registru naleznete v dokumentaci.
maven.registry=Nastavte tento registr ve vašem projektu pom.xml
souboru:
maven.install=Pro použití balíčku uveďte následující v bloku dependencies
v souboru pom.xml
:
maven.install2=Spustit pomocí příkazové řádky:
@@ -2947,8 +3121,13 @@ npm.install2=nebo ho přidejte do souboru package.json:
npm.documentation=Další informace o npm registru naleznete v dokumentaci.
npm.dependencies=Závislosti
npm.dependencies.development=Vývojové závislosti
+npm.dependencies.peer=Vzájemné závislosti
npm.dependencies.optional=Volitelné závislosti
npm.details.tag=Značka
+pub.install=Chcete-li nainstalovat balíček pomocí Dart, spusťte následující příkaz:
+pub.documentation=Další informace o registru Pub naleznete v dokumentaci.
+pub.details.repository_site=Stránka repositáře
+pub.details.documentation_site=Stránka dokumentace
pypi.requires=Vyžaduje Python
pypi.install=Pro instalaci balíčku pomocí pip spusťte následující příkaz:
pypi.documentation=Další informace o registru PyPI naleznete v dokumentaci.
@@ -2959,6 +3138,8 @@ rubygems.dependencies.development=Vývojové závislosti
rubygems.required.ruby=Vyžaduje verzi Ruby
rubygems.required.rubygems=Vyžaduje verzi RubyGem
rubygems.documentation=Další informace o registru RubyGems naleznete v dokumentaci.
+vagrant.install=Pro přidání Vagrant box spusťte následující příkaz:
+vagrant.documentation=Další informace o registru Vagrant naleznete v dokumentaci.
settings.link=Propojit tento balíček s repozitářem
settings.link.description=Pokud propojíte balíček s repozitářem, je tento balíček uveden v seznamu balíčků repozitáře.
settings.link.select=Vybrat repozitář
diff --git a/options/locale/locale_de-DE.ini b/options/locale/locale_de-DE.ini
index 796b4b835b..86c442716d 100644
--- a/options/locale/locale_de-DE.ini
+++ b/options/locale/locale_de-DE.ini
@@ -9,7 +9,6 @@ sign_out=Abmelden
sign_up=Registrieren
link_account=Account verbinden
register=Registrieren
-website=Webseite
version=Version
powered_by=Powered by %s
page=Seite
@@ -179,7 +178,6 @@ log_root_path_helper=Log-Dateien werden in diesem Verzeichnis gespeichert.
optional_title=Optionale Einstellungen
email_title=E-Mail-Einstellungen
-smtp_host=SMTP-Server
smtp_from=E-Mail senden als
smtp_from_helper=E-Mail-Adresse, die von Gitea genutzt werden soll. Bitte gib die E-Mail-Adresse im Format „"Name" “ ein.
mailer_user=SMTP-Benutzername
@@ -229,7 +227,7 @@ default_keep_email_private_popup=E-Mail-Adressen von neuen Benutzern standardmä
default_allow_create_organization=Erstellen von Organisationen standardmäßig erlauben
default_allow_create_organization_popup=Neuen Nutzern das Erstellen von Organisationen standardmäßig erlauben.
default_enable_timetracking=Zeiterfassung standardmäßig aktivieren
-default_enable_timetracking_popup=Zeiterfassung standardmäßig für neue Repositories aktivieren.
+default_enable_timetracking_popup=Zeiterfassung standardmäßig für neue Repositorys aktivieren.
no_reply_address=Versteckte E-Mail-Domain
no_reply_address_helper=Domain-Name für Benutzer mit einer versteckten Emailadresse. Zum Beispiel wird der Benutzername „Joe“ in Git als „joe@noreply.example.org“ protokolliert, wenn die versteckte E-Mail-Domain „noreply.example.org“ festgelegt ist.
password_algorithm=Passwort Hashing Algorithmus
@@ -239,15 +237,15 @@ password_algorithm_helper=Lege den Passwort Hashing Algorithmus fest. Unterschie
uname_holder=E-Mail-Adresse oder Benutzername
password_holder=Passwort
switch_dashboard_context=Kontext der Übersichtsseite wechseln
-my_repos=Repositories
-show_more_repos=Zeige mehr Repositories…
-collaborative_repos=Gemeinschaftliche Repositories
+my_repos=Repositorys
+show_more_repos=Zeige mehr Repositorys…
+collaborative_repos=Gemeinschaftliche Repositorys
my_orgs=Meine Organisationen
my_mirrors=Meine Mirrors
view_home=%s ansehen
search_repos=Finde ein Repository…
filter=Andere Filter
-filter_by_team_repositories=Nach Team Repositories filtern
+filter_by_team_repositories=Nach Team-Repositorys filtern
feed_of=Feed von "%s"
show_archived=Archiviert
@@ -260,10 +258,10 @@ show_both_private_public=Öffentliche und private anzeigen
show_only_private=Nur private anzeigen
show_only_public=Nur öffentliche anzeigen
-issues.in_your_repos=Eigene Repositories
+issues.in_your_repos=Eigene Repositorys
[explore]
-repos=Repositories
+repos=Repositorys
users=Benutzer
organizations=Organisationen
search=Suche
@@ -271,12 +269,15 @@ code=Code
search.fuzzy=Ähnlich
search.match=Genau
code_search_unavailable=Derzeit ist die Code-Suche nicht verfügbar. Bitte wende dich an den Website-Administrator.
-repo_no_results=Keine passenden Repositories gefunden.
+repo_no_results=Keine passenden Repositorys gefunden.
user_no_results=Keine passenden Benutzer gefunden.
org_no_results=Keine passenden Organisationen gefunden.
code_no_results=Es konnte kein passender Code für deinen Suchbegriff gefunden werden.
code_search_results=Suchergebnisse für „%s“
code_last_indexed_at=Zuletzt indexiert %s
+relevant_repositories_tooltip=Repositorys, die Forks sind oder die kein Thema, kein Symbol und keine Beschreibung haben, werden ausgeblendet.
+relevant_repositories=Es werden nur relevante Repositorys angezeigt, zeigt ungefilterte Ergebnisse an.
+
[auth]
create_new_account=Konto anlegen
@@ -338,7 +339,7 @@ email_domain_blacklisted=Du kannst dich nicht mit deiner E-Mail-Adresse registri
authorize_application=Anwendung autorisieren
authorize_redirect_notice=Du wirst zu %s weitergeleitet, wenn du diese Anwendung autorisierst.
authorize_application_created_by=Diese Anwendung wurde von %s erstellt.
-authorize_application_description=Wenn du diese Anwendung autorisierst, wird sie die Berechtigung erhalten, alle Informationen zu deinem Account zu bearbeiten oder zu lesen. Dies beinhaltet auch private Repositories und Organisationen.
+authorize_application_description=Wenn du diese Anwendung autorisierst, wird sie die Berechtigung erhalten, alle Informationen zu deinem Account zu bearbeiten oder zu lesen. Dies beinhaltet auch private Repositorys und Organisationen.
authorize_title="%s" den Zugriff auf deinen Account gestatten?
authorization_failed=Autorisierung fehlgeschlagen
authorization_failed_desc=Die Autorisierung ist fehlgeschlagen, da wir eine ungültige Anfrage festgestellt haben. Bitte kontaktiere den Betreiber der Anwendung, die du gerade autorisieren wolltest.
@@ -454,7 +455,7 @@ lang_select_error=Wähle eine Sprache aus der Liste aus.
username_been_taken=Der Benutzername ist bereits vergeben.
username_change_not_local_user=Nicht-lokale Benutzer dürfen ihren Nutzernamen nicht ändern.
repo_name_been_taken=Der Repository-Name wird schon verwendet.
-repository_force_private=Privat erzwingen ist aktiviert: Private Repositories können nicht veröffentlicht werden.
+repository_force_private=Privat erzwingen ist aktiviert: Private Repositorys können nicht veröffentlicht werden.
repository_files_already_exist=Dateien für dieses Repository sind bereits vorhanden. Kontaktiere den Systemadministrator.
repository_files_already_exist.adopt=Dateien für dieses Repository existieren bereits und können nur übernommen werden.
repository_files_already_exist.delete=Dateien für dieses Repository sind bereits vorhanden. Du must sie löschen.
@@ -488,7 +489,7 @@ invalid_ssh_principal=Ungültige Identität: %s
unable_verify_ssh_key=Dein SSH-Key kann nicht überprüft werden, probiere es erneut.
auth_failed=Authentifizierung fehlgeschlagen: %v
-still_own_repo=Dein Konto besitzt ein oder mehrere Repositories. Diese müssen zuerst gelöscht oder übertragen werden.
+still_own_repo=Dein Konto besitzt ein oder mehrere Repositorys. Diese müssen zuerst gelöscht oder übertragen werden.
still_has_org=Dein Account ist Mitglied in mindestens einer Organisation. Bitte verlasse diese zuerst.
still_own_packages=Ihr Konto besitzt ein oder mehrere Pakete; löschen Sie diese zuerst.
org_still_own_repo=Diese Organisation besitzt noch mindestens ein Repository. Bitte lösche oder übertrage diese zuerst.
@@ -499,7 +500,7 @@ target_branch_not_exist=Der Ziel-Branch existiert nicht.
[user]
change_avatar=Profilbild ändern…
join_on=Beigetreten am
-repositories=Repositories
+repositories=Repositorys
activity=Öffentliche Aktivität
followers=Follower
starred=Favoriten
@@ -799,6 +800,7 @@ email_notifications.enable=E-Mail Benachrichtigungen aktivieren
email_notifications.onmention=Nur E-Mail bei Erwähnung
email_notifications.disable=E-Mail Benachrichtigungen deaktivieren
email_notifications.submit=E-Mail-Einstellungen festlegen
+email_notifications.andyourown=Und deine Eigenen Benachrichtigungen
visibility=Nutzer Sichtbarkeit
visibility.public=Öffentlich
@@ -930,6 +932,7 @@ form.name_pattern_not_allowed='%s' ist nicht erlaubt für Repository-Namen.
need_auth=Authentifizierung
migrate_options=Migrationsoptionen
migrate_service=Migrationsdienst
+migrate_options_mirror_helper=Dieses Repository wird ein Spiegel sein
migrate_options_lfs=LFS-Dateien migrieren
migrate_options_lfs_endpoint.label=LFS-Endpunkt
migrate_options_lfs_endpoint.description=Migration wird versuchen, über den entfernten Git-Server den LFS-Server zu bestimmen. Du kannst auch einen eigenen Endpunkt angeben, wenn die LFS-Dateien woanders gespeichert werden.
@@ -969,7 +972,7 @@ migrate.gogs.description=Daten von notabug.org oder anderen Gogs Instanzen migri
migrate.onedev.description=Daten von code.onedev.io oder anderen OneDev Instanzen migrieren.
migrate.codebase.description=Daten von codebasehq.com migrieren.
migrate.gitbucket.description=Daten von GitBucket Instanzen migrieren.
-migrate.migrating_git=Git Daten werden migriert
+migrate.migrating_git=Git-Daten werden migriert
migrate.migrating_topics=Themen werden migriert
migrate.migrating_milestones=Meilensteine werden migriert
migrate.migrating_labels=Labels werden migriert
@@ -1031,13 +1034,7 @@ file_view_rendered=Ansicht rendern
file_view_raw=Originalformat anzeigen
file_permalink=Permalink
file_too_large=Die Datei ist zu groß zum Anzeigen.
-bidi_bad_header=`Diese Datei enthält unerwartete Bidirektionale Unicode-Zeichen!`
-bidi_bad_description=`Diese Datei enthält unerwartete Bidirektionale Unicode-Zeichen, die anders verarbeitet werden können als nachstehend angezeigt. Wenn dein Anwendungsfall absichtlich und legitim ist, kannst du diese Warnung ignorieren. Benutze den "Escape" Button, um versteckte Zeichen anzuzeigen.`
-bidi_bad_description_escaped=`Diese Datei enthält unerwartete Unicode-Zeichen. Versteckte Unicode-Zeichen werden unten escaped. Benutze den "Unescapen" Button, um zu sehen, wie sie ansonsten aussehen.`
-unicode_header=`Diese Datei enthält versteckte Unicode-Zeichen!`
-unicode_description=`Diese Datei enthält versteckte Unicode-Zeichen, die anders verarbeitet werden können als unten angezeigt. Wenn dein Anwendungsfall absichtlich und legitim ist, kannst du diese Warnung ignorieren. Benutze den Escape Button, um versteckte Zeichen anzuzeigen.`
-unicode_description_escaped=`Diese Datei enthält versteckte Unicode-Zeichen. Versteckte Unicode-Zeichen werden unten escaped. Benutze den "Unescapen" Button, um zu sehen, wie sie ansonsten aussehen.`
-line_unicode=`Diese Zeile hat versteckte Unicode-Zeichen`
+ambiguous_character=`%[1]c [U+%04[1]X] kann mit %[2]c [U+%04[2]X] verwechselt werden`
escape_control_characters=Escapen
unescape_control_characters=Unescapen
@@ -1058,6 +1055,7 @@ normal_view=Normale Ansicht
line=zeile
lines=Zeilen
+editor.add_file=Datei hinzufügen
editor.new_file=Neue Datei
editor.upload_file=Datei hochladen
editor.edit_file=Datei bearbeiten
@@ -1263,6 +1261,8 @@ issues.filter_milestone=Meilenstein
issues.filter_milestone_no_select=Alle Meilensteine
issues.filter_assignee=Zuständig
issues.filter_assginee_no_select=Alle Zuständigen
+issues.filter_poster=Autor
+issues.filter_poster_no_select=Alle Autoren
issues.filter_type=Typ
issues.filter_type.all_issues=Alle Issues
issues.filter_type.assigned_to_you=Dir zugewiesen
@@ -1417,6 +1417,7 @@ issues.due_date_form_remove=Entfernen
issues.due_date_not_writer=Du musst Schreibrechte in diesem Repository haben, um das Fälligkeitsdatum zu ändern.
issues.due_date_not_set=Kein Fälligkeitsdatum gesetzt.
issues.due_date_added=hat %[2]s das Fälligkeitsdatum %[1]s hinzugefügt
+issues.due_date_modified=ändert das Abgabedatum von %[2]s auf %[1]s %[3]s s
issues.due_date_remove=hat %[2]s das Fälligkeitsdatum %[1]s entfernt
issues.due_date_overdue=Überfällig
issues.due_date_invalid=Das Fälligkeitsdatum ist ungültig oder außerhalb des zulässigen Bereichs. Bitte verwende das Format „jjjj-mm-tt“.
@@ -1528,6 +1529,7 @@ pulls.remove_prefix=%s Präfix entfernen
pulls.data_broken=Dieser Pull-Requests ist kaputt, da Fork-Informationen gelöscht wurden.
pulls.files_conflicted=Dieser Pull-Request hat Änderungen, die im Widerspruch zum Ziel-Branch stehen.
pulls.is_checking=Die Konfliktprüfung läuft noch. Bitte aktualisiere die Seite in wenigen Augenblicken.
+pulls.is_ancestor=Dieser Branch ist bereits im Zielbranch enthalten. Es gibt nichts zu mergen.
pulls.required_status_check_failed=Einige erforderliche Prüfungen waren nicht erfolgreich.
pulls.required_status_check_missing=Einige erforderliche Prüfungen fehlen.
pulls.required_status_check_administrator=Als Administrator kannst du diesen Pull-Request weiterhin zusammenführen.
@@ -1778,10 +1780,6 @@ settings.mirror_settings.push_mirror.remote_url=URL zum Git-Remote-Repository
settings.mirror_settings.push_mirror.add=Push-Mirror hinzufügen
settings.sync_mirror=Jetzt synchronisieren
settings.mirror_sync_in_progress=Mirror-Synchronisierung wird zurzeit ausgeführt. Komm in ein paar Minuten zurück.
-settings.email_notifications.enable=E-Mail Benachrichtigungen aktivieren
-settings.email_notifications.onmention=E-Mail-Benachrichtigungen nur bei Erwähnung
-settings.email_notifications.disable=E-Mail Benachrichtigungen deaktivieren
-settings.email_notifications.submit=E-Mail-Einstellungen festlegen
settings.site=Webseite
settings.update_settings=Einstellungen speichern
settings.branches.update_default_branch=Standardbranch aktualisieren
@@ -2339,7 +2337,7 @@ settings.delete_prompt=Die Organisation wird dauerhaft gelöscht. Dies K
settings.confirm_delete_account=Löschen bestätigen
settings.delete_org_title=Organisation löschen
settings.delete_org_desc=Diese Organisation wird dauerhaft gelöscht. Fortfahren?
-settings.hooks_desc=Webhooks hinzufügen, die für alle Repositories dieser Organisation ausgelöst werden.
+settings.hooks_desc=Webhooks hinzufügen, die für alle Repositorys dieser Organisation ausgelöst werden.
settings.labels_desc=Labels hinzufügen, die für alle Repositories dieser Organisation genutzt werden können.
@@ -2443,7 +2441,7 @@ dashboard.cron.error=Fehler in Cron: %s: %[3]s
dashboard.cron.finished=Cron: %[1]s ist beendet
dashboard.delete_inactive_accounts=Alle nicht aktivierten Konten löschen
dashboard.delete_inactive_accounts.started=Löschen aller nicht aktivierten Account-Aufgabe gestartet.
-dashboard.delete_repo_archives=Lösche alle Repository Archive (ZIP, TAR.GZ, …)
+dashboard.delete_repo_archives=Lösche alle Repository-Archive (ZIP, TAR.GZ, …)
dashboard.delete_repo_archives.started=Löschen aller Repository-Archive gestartet.
dashboard.delete_missing_repos=Alle Repository-Datensätze mit verloren gegangenen Git-Dateien löschen
dashboard.delete_missing_repos.started=Alle Repositories löschen, die die Git-Dateien-Aufgabe nicht gestartet haben.
@@ -2532,8 +2530,9 @@ users.allow_create_organization=Darf Organisationen erstellen
users.update_profile=Benutzerkonto aktualisieren
users.delete_account=Benutzerkonto löschen
users.cannot_delete_self=Du kannst dich nicht selbst löschen
-users.still_own_repo=Dieser Benutzer besitzt noch mindestens ein Repository. Bitte lösche oder übertrage diese zuerst.
+users.still_own_repo=Dieser Benutzer besitzt noch mindestens ein Repository. Bitte lösche oder übertrage diese(s) zuerst.
users.still_has_org=Dieser Nutzer ist Mitglied einer Organisation. Du musst ihn zuerst aus allen Organisationen entfernen.
+users.purge_help=Erzwinge das Löschen des Benutzers inklusive aller seiner Repositorys, Organisationen, Pakete und Kommentare.
users.still_own_packages=Dieser Benutzer besitzt noch ein oder mehrere Pakete. Lösche diese Pakete zuerst.
users.deletion_success=Der Account wurde gelöscht.
users.reset_2fa=2FA zurücksetzen
@@ -2790,11 +2789,10 @@ config.queue_length=Warteschlangenlänge
config.deliver_timeout=Zeitlimit für Zustellung
config.skip_tls_verify=TLS-Verifikation überspringen
-config.mailer_config=SMTP-Mailer-Konfiguration
config.mailer_enabled=Aktiviert
-config.mailer_disable_helo=HELO deaktivieren
+config.mailer_enable_helo=HELO aktivieren
config.mailer_name=Name
-config.mailer_host=Host
+config.mailer_protocol=Protokoll
config.mailer_user=Benutzer
config.mailer_use_sendmail=Sendmail benutzen
config.mailer_sendmail_path=Sendmail-Pfad
@@ -2852,6 +2850,7 @@ config.access_log_template=Vorlage
config.xorm_log_mode=XORM Log-Modus
config.xorm_log_sql=SQL protokollieren
+
monitor.cron=Cron-Aufgaben
monitor.name=Name
monitor.schedule=Zeitplan
@@ -3101,6 +3100,7 @@ npm.dependencies.development=Entwicklungsabhängigkeiten
npm.dependencies.peer=Peer Abhängigkeiten
npm.dependencies.optional=Optionale Abhängigkeiten
npm.details.tag=Tag
+pub.install=Um das Paket mit Dart zu installieren, führe den folgenden Befehl aus:
pypi.requires=Erfordert Python
pypi.install=Nutze folgenden Befehl, um das Paket mit pip zu installieren:
pypi.documentation=Weitere Informationen zur PyPI-Paketverwaltung findest du in der Dokumentation.
diff --git a/options/locale/locale_el-GR.ini b/options/locale/locale_el-GR.ini
index 51bd62d91f..abf504dda0 100644
--- a/options/locale/locale_el-GR.ini
+++ b/options/locale/locale_el-GR.ini
@@ -9,7 +9,6 @@ sign_out=Έξοδος
sign_up=Εγγραφή
link_account=Σύνδεση λογαριασμού
register=Εγγραφή
-website=Ιστοσελίδα
version=Έκδοση
powered_by=Με τη δύναμη του %s
page=Σελίδα
@@ -179,7 +178,8 @@ log_root_path_helper=Τα αρχεία καταγραφής θα γράφοντ
optional_title=Προαιρετικές Ρυθμίσεις
email_title=Ρυθμίσεις Email
-smtp_host=Διακομιστής SMTP
+smtp_addr=Διακομιστής SMTP
+smtp_port=Θύρα SMTP
smtp_from=Αποστολή Email Ως
smtp_from_helper=Η διεύθυνση email που θα χρησιμοποιεί το Gitea. Εισάγετε μια απλή διεύθυνση ηλεκτρονικού ταχυδρομείου ή χρησιμοποιήστε τη μορφή "Όνομα" .
mailer_user=Όνομα Χρήστη SMTP
@@ -277,6 +277,9 @@ org_no_results=Δεν βρέθηκαν οργανισμοί που να ταιρ
code_no_results=Δεν βρέθηκε πηγαίος κώδικας που να ταιριάζει με τον όρο αναζήτησης.
code_search_results=Αποτελέσματα αναζήτησης για '%s'
code_last_indexed_at=Τελευταίο δημιουργία ευρετηρίου στις %s
+relevant_repositories_tooltip=Τα αποθετήρια που είναι forks ή που δεν έχουν θέμα, εικονίδιο και περιγραφή είναι κρυμμένα.
+relevant_repositories=Εμφανίζονται μόνο τα σχετικά αποθετήρια, εμφάνιση χωρίς φίλτρο.
+
[auth]
create_new_account=Εγγραφή Λογαριασμού
@@ -799,6 +802,7 @@ email_notifications.enable=Ενεργοποίηση Ειδοποιήσεων Μ
email_notifications.onmention=Email Μόνο κατά την Αναφορά
email_notifications.disable=Απενεργοποίηση Ειδοποιήσεων μέσω Email
email_notifications.submit=Ορισμός Προτιμότερου Email
+email_notifications.andyourown=Και Τις Δικές Σας Ειδοποιήσεις
visibility=Ορατότητα χρήστη
visibility.public=Δημόσια
@@ -1034,13 +1038,13 @@ file_view_rendered=Προβολή Απόδοσης
file_view_raw=Προβολή Ακατέργαστου
file_permalink=Permalink
file_too_large=Το αρχείο είναι πολύ μεγάλο για να εμφανιστεί.
-bidi_bad_header=`Αυτό το αρχείο περιέχει μη αναμενόμενους χαρακτήρες Unicode!`
-bidi_bad_description=`Αυτό το αρχείο περιέχει μη αναμενόμενους χαρακτήρες Bidirectional Unicode που ίσως να επεξεργάζονται διαφορετικά από ότι εμφανίζεται παρακάτω. Αν η χρήση αυτή είναι σκόπιμη και νόμιμη, μπορείτε να αγνοήσετε με ασφάλεια αυτή την προειδοποίηση. Χρησιμοποιήστε το κουμπί Escape για να αποκαλύψετε κρυμμένους χαρακτήρες.`
-bidi_bad_description_escaped=`Αυτό το αρχείο περιέχει μη αναμενόμενους χαρακτήρες Bidirectional Unicode. Οι κρυμμένοι χαρακτήρες unicode εμφανίζονται κωδικοποιημένοι παρακάτω. Χρησιμοποιήστε το κουμπί Unescape για να δείτε πώς αποδίδονται.`
-unicode_header=`Αυτό το αρχείο περιέχει κρυφούς χαρακτήρες Unicode!`
-unicode_description=`Αυτό το αρχείο περιέχει κρυφούς χαρακτήρες Unicode που μπορεί να επεξεργάζονται διαφορετικά από όπως εμφανίζονται παρακάτω. Αν η χρήση είναι σκόπιμη και νόμιμη, μπορείτε να αγνοήσετε με ασφάλεια αυτή την προειδοποίηση. Χρησιμοποιήστε το κουμπί Escape για να αποκαλύψετε τους κρυφούς χαρακτήρες.`
-unicode_description_escaped=`Αυτό το αρχείο περιέχει κρυφούς χαρακτήρες Unicode. Οι κρυφοί χαρακτήρες unicode εμφανίζονται κωδικοποιημένοι παρακάτω. Χρησιμοποιήστε το κουμπί Unescape για να δείτε πώς αποδίδονται.`
-line_unicode=`Αυτή η γραμμή έχει κρυφούς χαρακτήρες unicode`
+invisible_runes_header=`Αυτό το αρχείο περιέχει αόρατους χαρακτήρες Unicode!`
+invisible_runes_description=`Αυτό το αρχείο περιέχει αόρατους χαρακτήρες Unicode που μπορεί να επεξεργάζονται διαφορετικά από ό, τι εμφανίζονται παρακάτω. Αν η περίπτωση χρήσης σας είναι σκόπιμη και νόμιμη, μπορείτε να αγνοήσετε με ασφάλεια αυτή την προειδοποίηση. Χρησιμοποιήστε το κουμπί Escape για να αποκαλύψετε κρυμμένους χαρακτήρες.`
+ambiguous_runes_header=`Αυτό το αρχείο περιέχει ασαφείς χαρακτήρες Unicode!`
+ambiguous_runes_description=`Αυτό το αρχείο περιέχει ασαφείς χαρακτήρες Unicode που μπορεί να συγχέονται με άλλους στην τοπική σας γλώσσα. Αν η περίπτωση χρήσης σας είναι σκόπιμη και νόμιμη, μπορείτε να αγνοήσετε με ασφάλεια αυτή την προειδοποίηση. Χρησιμοποιήστε το κουμπί Escape για να επισημάνετε αυτούς τους χαρακτήρες.`
+invisible_runes_line=`Αυτή η γραμμή έχει αόρατους χαρακτήρες unicode `
+ambiguous_runes_line=`Αυτή η γραμμή έχει ασαφείς χαρακτήρες unicode `
+ambiguous_character=`ο %[1]c [U+%04[1]X] μπορεί να μπερδευτεί με τον %[2]c [U+%04[2]X]`
escape_control_characters=Escape
unescape_control_characters=Unescape
@@ -1061,6 +1065,7 @@ normal_view=Κανονική Προβολή
line=γραμμή
lines=γραμμές
+editor.add_file=Προσθήκη Αρχείου
editor.new_file=Νέο Αρχείο
editor.upload_file=Ανέβασμα Αρχείου
editor.edit_file=Επεξεργασία Αρχείου
@@ -1226,6 +1231,8 @@ issues.new.add_reviewer_title=Αίτηση επανεξέτασης
issues.choose.get_started=Ας Αρχίσουμε
issues.choose.blank=Προεπιλογή
issues.choose.blank_about=Δημιουργήστε ένα ζήτημα από το προεπιλεγμένο πρότυπο.
+issues.choose.ignore_invalid_templates=Μη έγκυρα πρότυπα έχουν αγνοηθεί
+issues.choose.invalid_templates=%v βρέθηκε μη έγκυρο πρότυπο(α)
issues.no_ref=Δεν Έχει Οριστεί Κλάδος/Ετικέτα
issues.create=Δημιουργία Ζητήματος
issues.new_label=Νέο Σήμα
@@ -1266,6 +1273,8 @@ issues.filter_milestone=Ορόσημο
issues.filter_milestone_no_select=Όλα τα ορόσημα
issues.filter_assignee=Αποδέκτης
issues.filter_assginee_no_select=Όλοι οι αποδέκτες
+issues.filter_poster=Συγγραφέας
+issues.filter_poster_no_select=Όλοι οι συγγραφείς
issues.filter_type=Τύπος
issues.filter_type.all_issues=Όλα τα ζητήματα
issues.filter_type.assigned_to_you=Ανατέθηκαν σε εσάς
@@ -1784,10 +1793,6 @@ settings.mirror_settings.push_mirror.remote_url=URL Απομακρυσμένου
settings.mirror_settings.push_mirror.add=Προσθήκη Είδωλου Push
settings.sync_mirror=Συγχρονισμός Τώρα
settings.mirror_sync_in_progress=Ο συγχρονισμός ειδώλου είναι σε εξέλιξη. Ελέγξτε ξανά σε λίγο.
-settings.email_notifications.enable=Ενεργοποίηση Ειδοποιήσεων Email
-settings.email_notifications.onmention=Email Μόνο σε Αναφορά
-settings.email_notifications.disable=Απενεργοποίηση Ειδοποιήσεων Email
-settings.email_notifications.submit=Ορισμός Προτίμησης Email
settings.site=Ιστοσελίδα
settings.update_settings=Ενημέρωση Ρυθμίσεων
settings.branches.update_default_branch=Ενημέρωση Προεπιλεγμένου Κλάδου
@@ -1951,6 +1956,8 @@ settings.event_delete=Διαγραφή
settings.event_delete_desc=Ο κλάδος ή η ετικέτα διαγράφηκε.
settings.event_fork=Fork
settings.event_fork_desc=Το αποθετήριο έγινε fork.
+settings.event_wiki=Wiki
+settings.event_wiki_desc=Η σελίδα Wiki δημιουργήθηκε, μετονομάστηκε, επεξεργάστηκε ή διαγράφηκε.
settings.event_release=Έκδοση
settings.event_release_desc=Η έκδοση δημοσιεύτηκε, ενημερώθηκε ή διαγράφηκε από ένα αποθετήριο.
settings.event_push=Push
@@ -2798,16 +2805,19 @@ config.queue_length=Μέγεθος Ουράς
config.deliver_timeout=Χρονικό Όριο Παράδοσης
config.skip_tls_verify=Παράλειψη Επαλήθευσης TLS
-config.mailer_config=Ρυθμίσεις SMTP Mailer
+config.mailer_config=Ρυθμίσεις Αλληλογραφίας
config.mailer_enabled=Ενεργοποιημένο
-config.mailer_disable_helo=Απενεργοποίηση HELO
+config.mailer_enable_helo=Ενεργοποίηση HELO
config.mailer_name=Όνομα
-config.mailer_host=Διακομιστής
+config.mailer_protocol=Πρωτόκολλο
+config.mailer_smtp_addr=Διευθ SMTP
+config.mailer_smtp_port=Θύρα SMTP
config.mailer_user=Χρήστης
config.mailer_use_sendmail=Χρήση Sendmail
config.mailer_sendmail_path=Διαδρομή Sendmail
config.mailer_sendmail_args=Επιπλέον παράμετροι για το Sendmail
config.mailer_sendmail_timeout=Χρονικό Όριο Sendmail
+config.mailer_use_dummy=Ψεύτικο
config.test_email_placeholder=Email (π.χ. test@example.com)
config.send_test_mail=Αποστολή Δοκιμαστικού Email
config.test_mail_failed=Αποτυχία αποστολής δοκιμαστικού email στο'%s': %v
@@ -2860,6 +2870,7 @@ config.access_log_template=Πρότυπο
config.xorm_log_mode=Λειτουργία Καταγραφών XORM
config.xorm_log_sql=Καταγραφή SQL
+
monitor.cron=Προγραμματισμένες Εργασίες
monitor.name=Όνομα
monitor.schedule=Πρόγραμμα
@@ -2892,6 +2903,7 @@ monitor.queue.nopool.title=Χωρίς Δεξαμενή Εργατών
monitor.queue.nopool.desc=Αυτή η ουρά συμπεριλαμβάνει άλλες ουρές και δεν έχει η ίδια δεξαμενή εργατών.
monitor.queue.wrapped.desc=Μια ουρά συμπερίληψης, περιλαμβάνει μια ουρά αργής εκκίνησης, κρατώντας προσωρινά τις αιτήσεις πού βρίσκονται στην ουρά, μέσα σε ένα κανάλι. Δεν διαθέτει μια δεξαμενή εργατών.
monitor.queue.persistable-channel.desc=Ένα ανθεκτικό-κανάλι περιλαμβάνει δύο ουρές, μια ουρά καναλιών που έχει τη δική του δεξαμενή εργατών και μια ουρά επιπέδου για κρατημένα αιτήματα από προηγούμενες απενεργοποιήσεις. Δεν έχει το ίδιο δεξαμενή εργατών.
+monitor.queue.flush=Εκκαθάριση εργάτη
monitor.queue.pool.timeout=Λήξη ορίου χρόνου
monitor.queue.pool.addworkers.title=Προσθήκη Εργατών
monitor.queue.pool.addworkers.submit=Προσθήκη Εργατών
@@ -3044,6 +3056,7 @@ title=Πακέτα
desc=Διαχείριση πακέτων μητρώου.
empty=Δεν υπάρχουν πακέτα ακόμα.
empty.documentation=Για περισσότερες πληροφορίες σχετικά με το μητρώο πακέτων, ανατρέξτε στην τεκμηρίωση.
+empty.repo=Μήπως ανεβάσατε ένα πακέτο, αλλά δεν εμφανίζεται εδώ; Πηγαίνετε στις ρυθμίσεις πακέτων και συνδέστε το σε αυτό το αποθετήριο.
filter.type=Τύπος
filter.type.all=Όλα
filter.no_result=Το φίλτρο δεν παρήγαγε αποτελέσματα.
@@ -3109,6 +3122,10 @@ npm.dependencies.development=Εξαρτήσεις Ανάπτυξης
npm.dependencies.peer=Εξαρτήσεις Ομότιμου
npm.dependencies.optional=Προαιρετικές Εξαρτήσεις
npm.details.tag=Σήμανση
+pub.install=Για να εγκαταστήσετε το πακέτο μέσω του Dart, εκτελέστε την ακόλουθη εντολή:
+pub.documentation=Για περισσότερες πληροφορίες σχετικά με το μητρώο Pub, ανατρέξτε στην τεκμηρίωση.
+pub.details.repository_site=Ιστοσελίδα Αποθετηρίου
+pub.details.documentation_site=Ιστοσελίδα Τεκμηρίωσης
pypi.requires=Απαιτεί Python
pypi.install=Για να εγκαταστήσετε το πακέτο χρησιμοποιώντας το pip, εκτελέστε την ακόλουθη εντολή:
pypi.documentation=Για περισσότερες πληροφορίες σχετικά με το μητρώο PyPI, ανατρέξτε στην τεκμηρίωση.
@@ -3119,6 +3136,8 @@ rubygems.dependencies.development=Εξαρτήσεις Ανάπτυξης
rubygems.required.ruby=Απαιτεί την έκδοση Ruby
rubygems.required.rubygems=Απαιτεί έκδοση RubyGem
rubygems.documentation=Για περισσότερες πληροφορίες σχετικά με το μητρώο RubyGems, ανατρέξτε στην τεκμηρίωση.
+vagrant.install=Για προσθήκη ενός κυτίου Vagrant, εκτελέστε την ακόλουθη εντολή:
+vagrant.documentation=Για περισσότερες πληροφορίες σχετικά με το μητρώο του Vagrant, ανατρέξτε στην τεκμηρίωση.
settings.link=Σύνδεση αυτού του πακέτου με ένα αποθετήριο
settings.link.description=Εάν συνδέσετε ένα πακέτο με ένα αποθετήριο, το πακέτο περιλαμβάνεται στη λίστα πακέτων του αποθετηρίου.
settings.link.select=Επιλογή Αποθετηρίου
diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini
index 566a7bd167..e5da074f64 100644
--- a/options/locale/locale_en-US.ini
+++ b/options/locale/locale_en-US.ini
@@ -9,7 +9,6 @@ sign_out = Sign Out
sign_up = Register
link_account = Link Account
register = Register
-website = Website
version = Version
powered_by = Powered by %s
page = Page
@@ -179,7 +178,8 @@ log_root_path_helper = Log files will be written to this directory.
optional_title = Optional Settings
email_title = Email Settings
-smtp_host = SMTP Host
+smtp_addr = SMTP Host
+smtp_port = SMTP Port
smtp_from = Send Email As
smtp_from_helper = Email address Gitea will use. Enter a plain email address or use the "Name" format.
mailer_user = SMTP Username
@@ -268,8 +268,11 @@ users = Users
organizations = Organizations
search = Search
code = Code
+search.type.tooltip = Search type
search.fuzzy = Fuzzy
+search.fuzzy.tooltip = Include results that also matches the search term closely
search.match = Match
+search.match.tooltip = Include only results that matches the exact search term
code_search_unavailable = Currently code search is not available. Please contact your site administrator.
repo_no_results = No matching repositories found.
user_no_results = No matching users found.
@@ -277,6 +280,9 @@ org_no_results = No matching organizations found.
code_no_results = No source code matching your search term found.
code_search_results = Search results for '%s'
code_last_indexed_at = Last indexed %s
+relevant_repositories_tooltip = Repositories that are forks or that have no topic, no icon, and no description are hidden.
+relevant_repositories = Only relevant repositories are being shown, show unfiltered results.
+
[auth]
create_new_account = Register Account
@@ -504,6 +510,7 @@ activity = Public Activity
followers = Followers
starred = Starred Repositories
watched = Watched Repositories
+code = Code
projects = Projects
following = Following
follow = Follow
@@ -799,6 +806,7 @@ email_notifications.enable = Enable Email Notifications
email_notifications.onmention = Only Email on Mention
email_notifications.disable = Disable Email Notifications
email_notifications.submit = Set Email Preference
+email_notifications.andyourown = And Your Own Notifications
visibility = User visibility
visibility.public = Public
@@ -1034,13 +1042,13 @@ file_view_rendered = View Rendered
file_view_raw = View Raw
file_permalink = Permalink
file_too_large = The file is too large to be shown.
-bidi_bad_header = `This file contains unexpected Bidirectional Unicode characters!`
-bidi_bad_description = `This file contains unexpected Bidirectional Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.`
-bidi_bad_description_escaped = `This file contains unexpected Bidirectional Unicode characters. Hidden unicode characters are escaped below. Use the Unescape button to show how they render.`
-unicode_header = `This file contains hidden Unicode characters!`
-unicode_description = `This file contains hidden Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.`
-unicode_description_escaped = `This file contains hidden Unicode characters. Hidden unicode characters are escaped below. Use the Unescape button to show how they render.`
-line_unicode = `This line has hidden unicode characters`
+invisible_runes_header = `This file contains invisible Unicode characters!`
+invisible_runes_description = `This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.`
+ambiguous_runes_header = `This file contains ambiguous Unicode characters!`
+ambiguous_runes_description = `This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.`
+invisible_runes_line = `This line has invisible unicode characters`
+ambiguous_runes_line = `This line has ambiguous unicode characters`
+ambiguous_character = `%[1]c [U+%04[1]X] is confusable with %[2]c [U+%04[2]X]`
escape_control_characters = Escape
unescape_control_characters = Unescape
@@ -1061,6 +1069,7 @@ normal_view = Normal View
line = line
lines = lines
+editor.add_file = Add File
editor.new_file = New File
editor.upload_file = Upload File
editor.edit_file = Edit File
@@ -1226,6 +1235,8 @@ issues.new.add_reviewer_title = Request review
issues.choose.get_started = Get Started
issues.choose.blank = Default
issues.choose.blank_about = Create an issue from default template.
+issues.choose.ignore_invalid_templates = Invalid templates have been ignored
+issues.choose.invalid_templates = %v invalid template(s) found
issues.no_ref = No Branch/Tag Specified
issues.create = Create Issue
issues.new_label = New Label
@@ -1266,6 +1277,8 @@ issues.filter_milestone = Milestone
issues.filter_milestone_no_select = All milestones
issues.filter_assignee = Assignee
issues.filter_assginee_no_select = All assignees
+issues.filter_poster = Author
+issues.filter_poster_no_select = All authors
issues.filter_type = Type
issues.filter_type.all_issues = All issues
issues.filter_type.assigned_to_you = Assigned to you
@@ -1754,8 +1767,11 @@ activity.git_stats_deletion_n = %d deletions
search = Search
search.search_repo = Search repository
+search.type.tooltip = Search type
search.fuzzy = Fuzzy
+search.fuzzy.tooltip = Include results that also matches the search term closely
search.match = Match
+search.match.tooltip = Include only results that matches the exact search term
search.results = Search results for "%s" in %s
search.code_no_results = No source code matching your search term found.
search.code_search_unavailable = Currently code search is not available. Please contact your site administrator.
@@ -1784,10 +1800,6 @@ settings.mirror_settings.push_mirror.remote_url = Git Remote Repository URL
settings.mirror_settings.push_mirror.add = Add Push Mirror
settings.sync_mirror = Synchronize Now
settings.mirror_sync_in_progress = Mirror synchronization is in progress. Check back in a minute.
-settings.email_notifications.enable = Enable Email Notifications
-settings.email_notifications.onmention = Only Email on Mention
-settings.email_notifications.disable = Disable Email Notifications
-settings.email_notifications.submit = Set Email Preference
settings.site = Website
settings.update_settings = Update Settings
settings.branches.update_default_branch = Update Default Branch
@@ -1893,6 +1905,7 @@ settings.confirm_delete = Delete Repository
settings.add_collaborator = Add Collaborator
settings.add_collaborator_success = The collaborator has been added.
settings.add_collaborator_inactive_user = Can not add an inactive user as a collaborator.
+settings.add_collaborator_owner = Can not add an owner as a collaborator.
settings.add_collaborator_duplicate = The collaborator is already added to this repository.
settings.delete_collaborator = Remove
settings.collaborator_deletion = Remove Collaborator
@@ -1951,6 +1964,8 @@ settings.event_delete = Delete
settings.event_delete_desc = Branch or tag deleted.
settings.event_fork = Fork
settings.event_fork_desc = Repository forked.
+settings.event_wiki = Wiki
+settings.event_wiki_desc = Wiki page created, renamed, edited or deleted.
settings.event_release = Release
settings.event_release_desc = Release published, updated or deleted in a repository.
settings.event_push = Push
@@ -2302,6 +2317,7 @@ create_org = Create Organization
repo_updated = Updated
people = People
teams = Teams
+code = Code
lower_members = members
lower_repositories = repositories
create_new_team = New Team
@@ -2798,16 +2814,19 @@ config.queue_length = Queue Length
config.deliver_timeout = Deliver Timeout
config.skip_tls_verify = Skip TLS Verification
-config.mailer_config = SMTP Mailer Configuration
+config.mailer_config = Mailer Configuration
config.mailer_enabled = Enabled
-config.mailer_disable_helo = Disable HELO
+config.mailer_enable_helo = Enable HELO
config.mailer_name = Name
-config.mailer_host = Host
+config.mailer_protocol = Protocol
+config.mailer_smtp_addr = SMTP Addr
+config.mailer_smtp_port = SMTP Port
config.mailer_user = User
config.mailer_use_sendmail = Use Sendmail
config.mailer_sendmail_path = Sendmail Path
config.mailer_sendmail_args = Extra Arguments to Sendmail
config.mailer_sendmail_timeout = Sendmail Timeout
+config.mailer_use_dummy = Dummy
config.test_email_placeholder = Email (e.g. test@example.com)
config.send_test_mail = Send Testing Email
config.test_mail_failed = Failed to send a testing email to '%s': %v
@@ -2860,6 +2879,9 @@ config.access_log_template = Template
config.xorm_log_mode = XORM Log Mode
config.xorm_log_sql = Log SQL
+config.get_setting_failed = Get setting %s failed
+config.set_setting_failed = Set setting %s failed
+
monitor.cron = Cron Tasks
monitor.name = Name
monitor.schedule = Schedule
@@ -2892,6 +2914,7 @@ monitor.queue.nopool.title = No Worker Pool
monitor.queue.nopool.desc = This queue wraps other queues and does not itself have a worker pool.
monitor.queue.wrapped.desc = A wrapped queue wraps a slow starting queue, buffering queued requests in a channel. It does not have a worker pool itself.
monitor.queue.persistable-channel.desc = A persistable-channel wraps two queues, a channel queue that has its own worker pool and a level queue for persisted requests from previous shutdowns. It does not have a worker pool itself.
+monitor.queue.flush = Flush worker
monitor.queue.pool.timeout = Timeout
monitor.queue.pool.addworkers.title = Add Workers
monitor.queue.pool.addworkers.submit = Add Workers
@@ -3022,6 +3045,9 @@ pin = Pin notification
mark_as_read = Mark as read
mark_as_unread = Mark as unread
mark_all_as_read = Mark all as read
+subscriptions = Subscriptions
+watching = Watching
+no_subscriptions = No subscriptions
[gpg]
default_key=Signed with default key
@@ -3044,6 +3070,7 @@ title = Packages
desc = Manage repository packages.
empty = There are no packages yet.
empty.documentation = For more information on the package registry, see the documentation.
+empty.repo = Did you upload a package, but it's not shown here? Go to package settings and link it to this repo.
filter.type = Type
filter.type.all = All
filter.no_result = Your filter produced no results.
@@ -3080,6 +3107,7 @@ container.details.platform = Platform
container.details.repository_site = Repository Site
container.details.documentation_site = Documentation Site
container.pull = Pull the image from the command line:
+container.digest = Digest:
container.documentation = For more information on the Container registry, see the documentation.
container.multi_arch = OS / Arch
container.layers = Image Layers
@@ -3109,6 +3137,10 @@ npm.dependencies.development = Development Dependencies
npm.dependencies.peer = Peer Dependencies
npm.dependencies.optional = Optional Dependencies
npm.details.tag = Tag
+pub.install = To install the package using Dart, run the following command:
+pub.documentation = For more information on the Pub registry, see the documentation.
+pub.details.repository_site = Repository Site
+pub.details.documentation_site = Documentation Site
pypi.requires = Requires Python
pypi.install = To install the package using pip, run the following command:
pypi.documentation = For more information on the PyPI registry, see the documentation.
@@ -3119,6 +3151,8 @@ rubygems.dependencies.development = Development Dependencies
rubygems.required.ruby = Requires Ruby version
rubygems.required.rubygems = Requires RubyGem version
rubygems.documentation = For more information on the RubyGems registry, see the documentation.
+vagrant.install = To add a Vagrant box, run the following command:
+vagrant.documentation = For more information on the Vagrant registry, see the documentation.
settings.link = Link this package to a repository
settings.link.description = If you link a package with a repository, the package is listed in the repository's package list.
settings.link.select = Select Repository
diff --git a/options/locale/locale_es-ES.ini b/options/locale/locale_es-ES.ini
index d2f5695ec2..7588d53f1f 100644
--- a/options/locale/locale_es-ES.ini
+++ b/options/locale/locale_es-ES.ini
@@ -9,7 +9,6 @@ sign_out=Cerrar sesión
sign_up=Registrarse
link_account=Vincular cuenta
register=Registro
-website=Página web
version=Versión
powered_by=Impulsado por %s
page=Página
@@ -37,9 +36,9 @@ passcode=Código de acceso
webauthn_insert_key=Introduzca su clave de seguridad
webauthn_sign_in=Presione el botón en su clave de seguridad. Si su clave de seguridad no tiene ningún botón, vuelva a insertarla.
-webauthn_press_button=Por favor, preione el botón en su clave de seguridad…
+webauthn_press_button=Por favor, presione el botón de su llave de seguridad…
webauthn_use_twofa=Utilice un código de doble factor desde su teléfono móvil
-webauthn_error=No se pudo leer la clave de seguridad.
+webauthn_error=No se pudo leer su llave de seguridad.
webauthn_unsupported_browser=Su navegador no soporta actualmente WebAuthn.
webauthn_error_unknown=Ha ocurrido un error desconocido. Por favor, inténtelo de nuevo.
webauthn_error_insecure=WebAuthn sólo soporta conexiones seguras. Para probar sobre HTTP, puede utilizar el origen "localhost" o "127.0.0.1"
@@ -179,7 +178,8 @@ log_root_path_helper=Archivos de registro se escribirán en este directorio.
optional_title=Configuración opcional
email_title=Configuración de Correo
-smtp_host=Servidor SMTP
+smtp_addr=Servidor SMTP
+smtp_port=Puerto SMTP
smtp_from=Enviar correos electrónicos como
smtp_from_helper=Dirección de correo electrónico que utilizará Gitea. Introduzca una dirección de correo electrónico normal o utilice el formato "Nombre" .
mailer_user=Nombre de usuario SMTP
@@ -268,8 +268,10 @@ users=Usuarios
organizations=Organizaciones
search=Buscar
code=Código
+search.type.tooltip=Tipo de búsqueda
search.fuzzy=Parcial
search.match=Coincidir
+search.match.tooltip=Incluye sólo los resultados que coincidan con el término de búsqueda exacto
code_search_unavailable=Actualmente la búsqueda de código no está disponible. Póngase en contacto con el administrador de su sitio.
repo_no_results=No se ha encontrado ningún repositorio coincidente.
user_no_results=No se ha encontrado ningún usuario coincidente.
@@ -277,6 +279,9 @@ org_no_results=No se ha encontrado ninguna organización coincidente.
code_no_results=No se ha encontrado código de fuente que coincida con su término de búsqueda.
code_search_results=Resultados de búsqueda para '%s'
code_last_indexed_at=Indexado por última vez %s
+relevant_repositories_tooltip=Repositorios que son bifurcaciones o que no tienen ningún tema, ningún icono, y ninguna descripción están ocultos.
+relevant_repositories=Solo se muestran repositorios relevantes, mostrar resultados sin filtrar.
+
[auth]
create_new_account=Registrar una cuenta
@@ -504,6 +509,7 @@ activity=Actividad pública
followers=Seguidores
starred=Repositorios Favoritos
watched=Repositorios seguidos
+code=Código
projects=Proyectos
following=Siguiendo
follow=Seguir
@@ -533,7 +539,7 @@ twofa=Autenticación de doble factor
account_link=Cuentas vinculadas
organization=Organizaciones
uid=UUID
-webauthn=Claves Seguridades
+webauthn=Llaves de Seguridad
public_profile=Perfil público
biography_placeholder=Cuéntenos un poco más sobre usted
@@ -764,7 +770,7 @@ twofa_disable_note=Puede deshabilitar la autenticación de doble factor si lo ne
twofa_disable_desc=Deshabilitar la autenticación de doble factor hará su cuenta menos segura. ¿Continuar?
regenerate_scratch_token_desc=Si extravió su código de respaldo, o ya lo usó para iniciar sesión, puede restablecerlo aquí.
twofa_disabled=La autenticación de doble factor ha sido deshabilitada.
-scan_this_image=Analiza esta imagen con la aplicación de autenticación:
+scan_this_image=Escanee esta imagen con su aplicación de autenticación:
or_enter_secret=O introduzca el secreto: %s
then_enter_passcode=E introduzca el código de acceso mostrado en la aplicación:
passcode_invalid=El código de acceso es incorrecto. Vuelva a intentarlo.
@@ -775,7 +781,7 @@ webauthn_desc=Las claves de seguridad son dispositivos hardware que contienen cl
webauthn_register_key=Añadir clave de seguridad
webauthn_nickname=Apodo
webauthn_delete_key=Eliminar clave de seguridad
-webauthn_delete_key_desc=Si elimina una clave de seguridad no podrá utilizarla para registrarte con ella. ¿Continuar?
+webauthn_delete_key_desc=Si elimina una llave de seguridad ya no podrá utilizarla para iniciar sesión con ella. ¿Continuar?
manage_account_links=Administrar cuentas vinculadas
manage_account_links_desc=Estas cuentas externas están vinculadas a su cuenta de Gitea.
@@ -799,6 +805,7 @@ email_notifications.enable=Habilitar notificaciones por correo electrónico
email_notifications.onmention=Enviar correo sólo al ser mencionado
email_notifications.disable=Deshabilitar las notificaciones por correo electrónico
email_notifications.submit=Establecer preferencias de correo electrónico
+email_notifications.andyourown=Y sus propias notificaciones
visibility=Visibilidad del usuario
visibility.public=Público
@@ -932,6 +939,7 @@ form.name_pattern_not_allowed=El patrón '%s' no está permitido en un nombre de
need_auth=Autorización
migrate_options=Opciones de migración
migrate_service=Servicio de Migración
+migrate_options_mirror_helper=Este repositorio será una réplica
migrate_options_lfs=Migrar archivos LFS
migrate_options_lfs_endpoint.label=Punto final de LFS
migrate_options_lfs_endpoint.description=Migración intentará usar su mando Git para determinar el servidor LFS. También puede especificar un punto final personalizado si los datos LFS del repositorio se almacenan en otro lugar.
@@ -976,7 +984,7 @@ migrate.migrating_topics=Migrando Temas
migrate.migrating_milestones=Migrando Hitos
migrate.migrating_labels=Migrando etiquetas
migrate.migrating_releases=Migrando Lanzamientos
-migrate.migrating_issues=Migrando Incidencías
+migrate.migrating_issues=Migrando incidencias
migrate.migrating_pulls=Migrando Pull Requests
mirror_from=réplica de
@@ -1033,13 +1041,13 @@ file_view_rendered=Ver procesado
file_view_raw=Ver original
file_permalink=Enlace permanente
file_too_large=El archivo es demasiado grande para ser mostrado.
-bidi_bad_header=`¡Este archivo contiene caracteres Unicode bidireccional inesperados!`
-bidi_bad_description=`Este archivo contiene caracteres Bidirectional Unicode inesperados que pueden ser procesados de forma diferente a lo que aparece a continuación. Si su caso de uso es intencional y legítimo, puede ignorar esta advertencia. Use el botón de Escape para revelar caracteres ocultos.`
-bidi_bad_description_escaped=`Este archivo contiene caracteres Unicode bidireccionales inesperados. Los caracteres unicode ocultos se escapan debajo. Utilice el botón Unescape para mostrar cómo se renderizan.`
-unicode_header=`¡Este archivo contiene caracteres Unicode ocultos!`
-unicode_description=`Este archivo contiene caracteres Unicode ocultos que pueden ser procesados de forma diferente a lo que aparece a continuación. Si su caso de uso es intencional y legítimo, puede ignorar esta advertencia. Use el botón de Escape para revelar caracteres ocultos.`
-unicode_description_escaped=`Este archivo contiene caracteres Unicode ocultos. Los caracteres unicode ocultos se escapan debajo. Utilice el botón Unescape para mostrar cómo renderizan.`
-line_unicode=`Esta línea tiene caracteres unicode ocultos`
+invisible_runes_header=`¡Este archivo contiene caracteres Unicode invisibles!`
+invisible_runes_description=`Este archivo contiene caracteres Unicode invisibles que pueden ser procesados de forma diferente a lo que aparece a continuación. Si su caso de uso es intencional y legítimo, puede ignorar esta advertencia. Use el botón de Escape para revelar caracteres ocultos.`
+ambiguous_runes_header=`¡Este archivo contiene caracteres Unicode ambiguos!`
+ambiguous_runes_description=`Este archivo contiene caracteres Unicode ambiguos que pueden confundirse con otros en tu idioma actual. Si tu caso de uso es intencional y legítimo, puedes ignorar esta advertencia. Usa el botón de Escape para resaltar estos caracteres.`
+invisible_runes_line=`Esta línea tiene caracteres unicode invisibles`
+ambiguous_runes_line=`Esta línea tiene caracteres unicode ambiguos`
+ambiguous_character=`%[1]c [U+%04[1]X] es confusable con %[2]c [U+%04[2]X]`
escape_control_characters=Escapar
unescape_control_characters=No Escapar
@@ -1060,6 +1068,7 @@ normal_view=Vista normal
line=línea
lines=líneas
+editor.add_file=Añadir archivo
editor.new_file=Nuevo Archivo
editor.upload_file=Subir archivo
editor.edit_file=Editar Archivo
@@ -1225,6 +1234,8 @@ issues.new.add_reviewer_title=Solicitar revisión
issues.choose.get_started=Comenzar
issues.choose.blank=Predeterminado
issues.choose.blank_about=Crear una incidencia a partir de la plantilla predeterminada.
+issues.choose.ignore_invalid_templates=Las plantillas no válidas han sido ignoradas
+issues.choose.invalid_templates=%v plantilla(s) no válida(s) encontradas
issues.no_ref=Ninguna Rama/Etiqueta especificada
issues.create=Crear incidencia
issues.new_label=Nueva Etiqueta
@@ -1232,9 +1243,9 @@ issues.new_label_placeholder=Nombre etiqueta
issues.new_label_desc_placeholder=Descripción
issues.create_label=Crear etiqueta
issues.label_templates.title=Carga un conjunto predefinido de etiquetas
-issues.label_templates.info=No hay etiquetas existentes todavía. Crea una etiqueta con "Nueva Etiqueta" o use la etiqueta predefinida:
+issues.label_templates.info=Todavía no existen etiquetas. Cree una etiqueta con "Nueva Etiqueta" o use un conjunto predefinido de etiquetas:
issues.label_templates.helper=Seleccionar un conjunto de etiquetas
-issues.label_templates.use=Utilice la etiqueta
+issues.label_templates.use=Usar este conjunto de etiquetas
issues.label_templates.fail_to_load_file=Error al cargar el archivo de plantilla de etiqueta '%s': %v
issues.add_label=añadió la etiqueta %s %s
issues.add_labels=añadió las etiquetas %s %s
@@ -1265,6 +1276,8 @@ issues.filter_milestone=Milestone
issues.filter_milestone_no_select=Todos los hitos
issues.filter_assignee=Asignada a
issues.filter_assginee_no_select=Todos los asignados
+issues.filter_poster=Autor
+issues.filter_poster_no_select=Todos los autores
issues.filter_type=Tipo
issues.filter_type.all_issues=Todas las incidencias
issues.filter_type.assigned_to_you=Asignadas a ti
@@ -1302,6 +1315,7 @@ issues.previous=Página Anterior
issues.next=Página Siguiente
issues.open_title=Abierta
issues.closed_title=Cerrada
+issues.draft_title=Borrador
issues.num_comments=%d comentarios
issues.commented_at=`comentado %s`
issues.delete_comment_confirm=¿Seguro que deseas eliminar este comentario?
@@ -1418,6 +1432,7 @@ issues.due_date_form_remove=Eliminar
issues.due_date_not_writer=Necesita acceso de escritura al repositorio para actualizar la fecha de vencimiento de un issue.
issues.due_date_not_set=Sin fecha de vencimiento.
issues.due_date_added=añadió la fecha de vencimiento %s %s
+issues.due_date_modified=modificó la fecha de vencimiento de %[2]s a %[1]s %[3]s
issues.due_date_remove=eliminó la fecha de vencimiento %s %s
issues.due_date_overdue=Vencido
issues.due_date_invalid=La fecha de vencimiento es inválida o está fuera de rango. Por favor utilice el formato 'aaaa-mm-dd'.
@@ -1529,6 +1544,8 @@ pulls.remove_prefix=Eliminar prefijo %s
pulls.data_broken=Este pull request está rota debido a que falta información del fork.
pulls.files_conflicted=Este pull request tiene cambios en conflicto con la rama de destino.
pulls.is_checking=La comprobación de conflicto de fusión está en progreso. Inténtalo de nuevo en unos momentos.
+pulls.is_ancestor=Esta rama ya está incluida en la rama de destino. No hay nada que fusionar.
+pulls.is_empty=Los cambios en esta rama ya están en la rama de destino. Esto será un commit vacío.
pulls.required_status_check_failed=Algunos controles requeridos no han tenido éxito.
pulls.required_status_check_missing=Faltan algunos controles necesarios.
pulls.required_status_check_administrator=Como administrador, aún puede fusionar este Pull Request.
@@ -1749,8 +1766,10 @@ activity.git_stats_deletion_n=%d eliminaciones
search=Buscar
search.search_repo=Buscar repositorio
+search.type.tooltip=Tipo de búsqueda
search.fuzzy=Parcial
search.match=Coincidir
+search.match.tooltip=Incluye sólo los resultados que coincidan con el término de búsqueda exacto
search.results=Resultados de la búsqueda para "%s" en %s
search.code_no_results=No se ha encontrado código de fuente que coincida con su término de búsqueda.
search.code_search_unavailable=Actualmente la búsqueda de código no está disponible. Póngase en contacto con el administrador de su sitio.
@@ -1779,10 +1798,6 @@ settings.mirror_settings.push_mirror.remote_url=URL del repositorio remoto de Gi
settings.mirror_settings.push_mirror.add=Añadir Réplica de Push
settings.sync_mirror=Sincronizar ahora
settings.mirror_sync_in_progress=La sincronización del repositorio replicado está en curso. Vuelva a intentarlo más tarde.
-settings.email_notifications.enable=Habilitar las notificaciones por correo electrónico
-settings.email_notifications.onmention=Enviar correo sólo al mencionar
-settings.email_notifications.disable=Deshabilitar las notificaciones por correo electrónico
-settings.email_notifications.submit=Establecer Preferencia de correo electrónico
settings.site=Sitio web
settings.update_settings=Actualizar configuración
settings.branches.update_default_branch=Actualizar rama por defecto
@@ -1888,6 +1903,7 @@ settings.confirm_delete=Eliminar este repositorio
settings.add_collaborator=Añadir colaborador
settings.add_collaborator_success=El nuevo colaborador ha sido añadido.
settings.add_collaborator_inactive_user=No se puede añadir un usuario inactivo como colaborador.
+settings.add_collaborator_owner=No se puede añadir un propietario como colaborador.
settings.add_collaborator_duplicate=El colaborador ya está añadido a este repositorio.
settings.delete_collaborator=Eliminar
settings.collaborator_deletion=Eliminar colaborador
@@ -1946,6 +1962,8 @@ settings.event_delete=Eliminar
settings.event_delete_desc=Rama o etiqueta eliminada.
settings.event_fork=Fork
settings.event_fork_desc=Repositorio forkeado.
+settings.event_wiki=Wiki
+settings.event_wiki_desc=Página de la Wiki creada, renombrada, editada o eliminada.
settings.event_release=Lanzamiento
settings.event_release_desc=Lanzamiento publicado, actualizado o eliminado en un repositorio.
settings.event_push=Push
@@ -2200,7 +2218,7 @@ releases.desc=Seguir las versiones y descargas del proyecto.
release.releases=Lanzamientos
release.detail=Detalles de lanzamiento
release.tags=Etiquetas
-release.new_release=Nueva Release
+release.new_release=Nuevo lanzamiento
release.draft=Borrador
release.prerelease=Pre-lanzamiento
release.stable=Estable
@@ -2219,7 +2237,7 @@ release.content=Contenido
release.prerelease_desc=Marcar como Pre-Lanzamiento
release.prerelease_helper=Marcar este lanzamiento como no es adecuada para usar en producción.
release.cancel=Cancelar
-release.publish=Publicar Release
+release.publish=Publicar lanzamiento
release.save_draft=Guardar borrador
release.edit_release=Actualizar Lanzamiento
release.delete_release=Eliminar Lanzamiento
@@ -2297,6 +2315,7 @@ create_org=Crear Organización
repo_updated=Actualizado
people=Personas
teams=Equipos
+code=Código
lower_members=miembros
lower_repositories=repositorios
create_new_team=Nuevo equipo
@@ -2535,6 +2554,8 @@ users.delete_account=Eliminar Cuenta de Usuario
users.cannot_delete_self=No puedes eliminarte a ti mismo
users.still_own_repo=Este usuario todavía posee uno o más depósitos. Eliminar o transferir estos repositorios primero.
users.still_has_org=Este usuario es un miembro de una organización. Primero retire el usuario de cualquier organización.
+users.purge=Borrar usuario
+users.purge_help=Borrar forzosamente el usuario y cualquier repositorio, organización y paquete propiedad del usuario. Todos los comentarios también serán borrados.
users.still_own_packages=Este usuario todavía posee uno o más paquetes. Elimine estos paquetes primero.
users.deletion_success=La cuenta de usuario ha sido eliminada.
users.reset_2fa=Reiniciar 2FA
@@ -2793,14 +2814,17 @@ config.skip_tls_verify=Saltar verificación TLS
config.mailer_config=Configuración del servidor de correo
config.mailer_enabled=Activado
-config.mailer_disable_helo=Desactivar HELO
+config.mailer_enable_helo=Habilitar HELO
config.mailer_name=Nombre
-config.mailer_host=Servidor
+config.mailer_protocol=Protocolo
+config.mailer_smtp_addr=Dirección SMTP
+config.mailer_smtp_port=Puerto SMTP
config.mailer_user=Usuario
config.mailer_use_sendmail=Usar Sendmail
config.mailer_sendmail_path=Ruta de Sendmail
config.mailer_sendmail_args=Argumentos adicionales por Sendmail
config.mailer_sendmail_timeout=Tiempo de espera de Sendmail
+config.mailer_use_dummy=Dummy
config.test_email_placeholder=Correo electrónico (ej. test@ejemplo.com)
config.send_test_mail=Enviar prueba de correo
config.test_mail_failed=Fallo al enviar correo electrónico de prueba a '%s': %v
@@ -2853,6 +2877,7 @@ config.access_log_template=Plantilla
config.xorm_log_mode=Modo de registro XORM
config.xorm_log_sql=Registrar SQL
+
monitor.cron=Tareas de Cron
monitor.name=Nombre
monitor.schedule=Agenda
@@ -2885,6 +2910,7 @@ monitor.queue.nopool.title=No existe un grupo de trabajadores
monitor.queue.nopool.desc=Esta cola envuelve otras colas y no tiene grupos de trabajadores.
monitor.queue.wrapped.desc=Una cola de tipo envuelto envuelve una cola de inicio lenta, búfer de peticiones en un canal. No tiene grupos de trabajadores en si misma.
monitor.queue.persistable-channel.desc=Una cola de tipo canal persistente envuelve dos colas, una cola de canales que tiene sus propios grupos de trabajadores y una cola de niveles para peticiones persistentes de apagones anteriores. No tiene grupos de trabajadores en sí misma.
+monitor.queue.flush=Vaciar trabajador
monitor.queue.pool.timeout=Tiempo de espera
monitor.queue.pool.addworkers.title=Añadir trabajadores
monitor.queue.pool.addworkers.submit=Añadir trabajadores
@@ -3015,6 +3041,9 @@ pin=Fijar notificación
mark_as_read=Marcar como leído
mark_as_unread=Marcar como no leído
mark_all_as_read=Marcar todo como leído
+subscriptions=Suscripciones
+watching=Siguiendo
+no_subscriptions=Sin suscripciones
[gpg]
default_key=Firmado con clave predeterminada
@@ -3037,6 +3066,7 @@ title=Paquetes
desc=Administrar paquetes del repositorio.
empty=Todavía no hay paquetes.
empty.documentation=Para más información sobre el registro de paquetes, consulte la documentación.
+empty.repo=¿Has subido un paquete, pero no se muestra aquí? Ve a la configuración del paquete y añade el link a este repositorio.
filter.type=Tipo
filter.type.all=Todo
filter.no_result=El filtro no produjo ningún resultado.
@@ -3073,6 +3103,7 @@ container.details.platform=Plataforma
container.details.repository_site=Sitio del repositorio
container.details.documentation_site=Sitio de documentación
container.pull=Arrastra la imagen desde la línea de comandos:
+container.digest=Resumen:
container.documentation=Para más información sobre el registro de Container, consulte la documentación.
container.multi_arch=SO / Arquitectura
container.layers=Capas de imagen
@@ -3102,6 +3133,10 @@ npm.dependencies.development=Dependencias de desarrollo
npm.dependencies.peer=Dependencias de pares
npm.dependencies.optional=Dependencias opcionales
npm.details.tag=Etiqueta
+pub.install=Para instalar el paquete usando Dart, ejecute el siguiente comando:
+pub.documentation=Para obtener más información sobre el registro de Pub, consulte la documentación.
+pub.details.repository_site=Sitio del repositorio
+pub.details.documentation_site=Sitio de documentación
pypi.requires=Requiere Python
pypi.install=Para instalar el paquete usando pip, ejecute el siguiente comando:
pypi.documentation=Para obtener más información sobre el registro PyPI, consulte la documentación.
@@ -3112,6 +3147,8 @@ rubygems.dependencies.development=Dependencias de desarrollo
rubygems.required.ruby=Requiere versión Ruby
rubygems.required.rubygems=Requiere la versión de RubyGem
rubygems.documentation=Para obtener más información sobre el registro de RubyGems, consulte la documentación.
+vagrant.install=Para añadir un paquete Vagrant, ejecuta el siguiente comando:
+vagrant.documentation=Para más información sobre el registro de paquetes Vagrant, revisa la documentación.
settings.link=Vincular este paquete a un repositorio
settings.link.description=Si enlaza un paquete con un repositorio, el paquete se enumera en la lista de paquetes del repositorio.
settings.link.select=Seleccionar repositorio
diff --git a/options/locale/locale_fa-IR.ini b/options/locale/locale_fa-IR.ini
index d97d3eead1..9e012ff557 100644
--- a/options/locale/locale_fa-IR.ini
+++ b/options/locale/locale_fa-IR.ini
@@ -8,7 +8,6 @@ sign_out=خروج
sign_up=ثبت نام
link_account=پیوند به حساب
register=ثبت نام
-website=وبسایت
version=نسخه
powered_by=قدرت از %s
page=صفحه
@@ -158,7 +157,6 @@ log_root_path_helper=فایلهای گزارش روی این مسیر ذخی
optional_title=تنظیمات اختیاری
email_title=تنظیمات ایمیل
-smtp_host=میزبان SMTP
smtp_from=ارسال ایمیل به عنوان
smtp_from_helper=آدرس ایمیلی که گیتی استفاده میکند. یک ایمیل وارد کنید یا به "Name" شکل استفاده کنید.
mailer_user=نام کاربری SMTP
@@ -256,6 +254,7 @@ code_no_results=کد منبعی مطابق با جستجوی شما یافت ن
code_search_results=نتایج جستجو برای '%s '
code_last_indexed_at=آخرین به روزرسانی در %s
+
[auth]
create_new_account=نامنویسی حساب کاربری
register_helper_msg=قبلا ثبت نام کردید؟ از اینجا وارد شوید!
@@ -1642,10 +1641,6 @@ settings.mirror_settings.push_mirror.remote_url=Git Remote Repository URL
settings.mirror_settings.push_mirror.add=اضافه کردن Push Mirror
settings.sync_mirror=همگام سازی کن
settings.mirror_sync_in_progress=همگام سازی قرینه در حالت پردازش است. یک دقیقه دیگر مجددا بررسی کنید.
-settings.email_notifications.enable=فعالسازی اعلانهای ایمیل
-settings.email_notifications.onmention=تنها یادوآری از طریق ایمیل
-settings.email_notifications.disable=غیرفعال کردن اعلانهای ایمیل
-settings.email_notifications.submit=ثبت اولویت ایمیل
settings.site=تارنما
settings.update_settings=به روزرسانی تنظیمات
settings.branches.update_default_branch=بروزرسانی شاخه پیش فرض
@@ -2580,11 +2575,8 @@ config.queue_length=طول صف
config.deliver_timeout=مهلت تحویل
config.skip_tls_verify=صرف نظر از اعتبارسنجی TLS
-config.mailer_config=پیکربندی سامانه ایمیلی SMTP
config.mailer_enabled=فعال شده
-config.mailer_disable_helo=غیر فعال کردن HELO
config.mailer_name=نام
-config.mailer_host=میزبان
config.mailer_user=کاربر
config.mailer_use_sendmail=استفاده از ارسال رایانامه (ایمیل) مستقیم
config.mailer_sendmail_path=مسیر ارسال ایمیل مستقیم
@@ -2642,6 +2634,7 @@ config.access_log_template=الگو
config.xorm_log_mode=شیوه ثبت رخداد XORM
config.xorm_log_sql=ثبت رخداد SQL
+
monitor.cron=وظایف Cron
monitor.name=نام
monitor.schedule=زمان بندی
diff --git a/options/locale/locale_fi-FI.ini b/options/locale/locale_fi-FI.ini
index 2699598ed9..faac82b0cf 100644
--- a/options/locale/locale_fi-FI.ini
+++ b/options/locale/locale_fi-FI.ini
@@ -2,13 +2,13 @@ home=Etusivu
dashboard=Kojelauta
explore=Tutki
help=Apua
+logo=Logo
sign_in=Kirjaudu sisään
sign_in_with=Kirjaudu sisään tunnuksilla
sign_out=Kirjaudu ulos
sign_up=Rekisteröidy
link_account=Yhdistä tili
register=Rekisteröidy
-website=Nettisivut
version=Versio
powered_by=Voimanlähteenä %s
page=Sivu
@@ -21,16 +21,31 @@ signed_in_as=Kirjautuneena käyttäjänä
enable_javascript=Tämä sivusto toimii paremmin JavaScriptillä.
toc=Sisällysluettelo
licenses=Lisenssit
+return_to_gitea=Palaa Giteaan
username=Käyttäjätunnus
email=Sähköpostiosoite
password=Salasana
+access_token=Pääsymerkki
re_type=Kirjoita salasana uudelleen
captcha=CAPTCHA
twofa=Kaksivaiheinen todennus
twofa_scratch=Kaksivaiheinen kertakäyttöinen koodi
passcode=Tunnuskoodi
+webauthn_insert_key=Aseta turva-avaimesi
+webauthn_sign_in=Paina turva-avaimesi painiketta. Jos turva-avaimessasi ei ole painiketta, irroita se ja aseta uudelleen.
+webauthn_press_button=Paina turva-avaimesi painiketta…
+webauthn_use_twofa=Käytä kaksivaihesta vahvistusta puhelimestasi
+webauthn_error=Turva-avainta ei voitu lukea.
+webauthn_unsupported_browser=Selaimesi ei tällä hetkellä tue WebAuthnia.
+webauthn_error_unknown=Tuntematon virhe. Yritä uudelleen.
+webauthn_error_insecure=WebAuthn tukee vain suojattuja yhteyksiä. Testaukseen HTTP:n yli, voit käyttää osoitetta "localhost" tai "127.0.0.1"
+webauthn_error_unable_to_process=Palvelin ei pystynyt toteuttamaan kutsua.
+webauthn_error_duplicated=Turva-avainta ei ole sallittu tässä pyynnössä. Varmista, ettei avainta ole jo rekisteröity.
+webauthn_error_empty=Sinun täytyy asettaa nimi tälle avaimelle.
+webauthn_error_timeout=Aikakatkaisu saavutettu ennenkuin avaintasi on voitu lukea. Lataa tämä sivu uudelleen ja yritä uudelleen.
+webauthn_reload=Päivitä
repository=Repo
organization=Organisaatio
@@ -40,6 +55,7 @@ new_migrate=Uusi migraatio
new_mirror=Uusi peilaus
new_fork=Uusi repositorio
new_org=Uusi organisaatio
+new_project=Uusi projekti
manage_org=Ylläpidä organisaatioita
admin_panel=Sivuston ylläpito
account_settings=Tilin asetukset
@@ -59,28 +75,47 @@ pull_requests=Pull requestit
issues=Ongelmat
milestones=Merkkipaalut
+ok=OK
cancel=Peruuta
save=Tallenna
add=Lisää
add_all=Lisää kaikki
remove=Poista
remove_all=Poista kaikki
+edit=Muokkaa
+copy=Kopioi
+copy_url=Kopioi osoite
+copy_branch=Kopioi haaran nimi
+copy_success=Kopioitu!
+copy_error=Kopiointi epäonnistui
write=Kirjoita
preview=Esikatselu
loading=Ladataan…
+step1=Vaihe 1:
+step2=Vaihe 2:
+error=Virhe
error404=Sivu, jota yrität nähdä, joko ei löydy tai et ole oikeutettu katsomaan sitä.
+never=Ei koskaan
+rss_feed=RSS-syöte
[error]
+occurred=Virhe tapahtui
+report_message=Jos olet varma, että tämä on ongelma Giteassa, etsi ongelmaa GitHubista tai avaa uusi ongelma tarvittaessa.
+missing_csrf=Virheellinen pyyntö: CSRF-tunnusta ei ole olemassa
+invalid_csrf=Virheellinen pyyntö: Virheellinen CSRF-tunniste
+not_found=Kohdetta ei löytynyt.
+network_error=Verkkovirhe
[startpage]
app_desc=Kivuton, itsehostattu Git-palvelu
install=Helppo asentaa
+install_desc=Yksinkertaisesti aja binääri alustallasi, toimita se Dockerilla, tai saa se pakettina.
platform=Alustariippumaton
platform_desc=Gitea käy missä tahansa alustassa, johon Go kykenee kääntämään. Windows, macOS, Linux, ARM, jne. Valitse omasi!
lightweight=Kevyt
@@ -92,6 +127,7 @@ license_desc=Mene osoitteeseen ohjeet ennen minkään asetuksen muuttamista.
+require_db_desc=Gitea tarvitsee toimiakseen MySQL, PostgreSQL, MSSQL, SQLite3 tai TiDB (MySQL protokolla) tietokannan.
db_title=Tietokanta asetukset
db_type=Tietokanta tyyppi
host=Isäntä
@@ -99,10 +135,16 @@ user=Käyttäjätunnus
password=Salasana
db_name=Tietokannan nimi
db_helper=Huomautus MySQL-käyttäjille: käytä InnoDB-tallennusmoottoria, ja jos käytät "utf8mb4" merkistöä, InnoDB-version on oltava yli 5.6.
+db_schema=Skeema
ssl_mode=SSL
charset=Merkistö
path=Polku
sqlite_helper=SQLite3-tietokannan tiedostopolku.
Syötä absoluuttinen polku, jos ajat Giteaa palveluna.
+reinstall_error=Yrität asentaa olemassa olevaan Gitea tietokantaan
+reinstall_confirm_message=Asentaminen uudelleen olemassa olevalla Gitea-tietokannalla voi aiheuttaa useita ongelmia. Useimmissa tapauksissa sinun pitäisi käyttää olemassa olevia "app.ini" asetuksia Gitean käyttöön. Jos tiedät mitä teet, vahvista seuraavat seikat:
+reinstall_confirm_check_1=Tiedot, jotka on salattu SECRET_KEY:llä app.ini:ssä saatetaan menettää: käyttäjät eivät ehkä voi kirjautua sisään 2FA/OTP:lla ja peilit eivät välttämättä toimi oikein. Ruksaamalla tämän vahvistat, että nykyinen app.ini -tiedosto sisältää oikean SECRET_KEY:n.
+reinstall_confirm_check_2=Repot ja asetukset saattaa olla tarpeen uudelleensynkronoida. Valitsemalla tämän vahvistat, että uudelleensynkronoit repojen koukut ja authorized_keys -tiedoston manuaalisesti. Varmistat, että repon ja peilin asetukset ovat oikeat.
+reinstall_confirm_check_3=Vahvistat, että olet täysin varma siitä, että tämä Gitea toimii oikealla app.ini sijainnilla ja että olet varma, että sinun täytyy asentaa uudelleen. Vahvistat, että tunnustat edellä mainitut riskit.
err_empty_db_path=SQLite3-tietokannan polku ei voi olla tyhjä.
no_admin_and_disable_registration=Et voi kytkeä rekisteröintiä pois luomatta sitä ennen ylläpitotiliä.
err_empty_admin_password=Ylläpitäjän salasana ei voi olla tyhjä.
@@ -119,6 +161,7 @@ lfs_path=Git LFS -juuripolku
lfs_path_helper=Git LFS:n ylläpitämät tiedostot tullaan tallentamaan tähän hakemistoon. Jätä tyhjäksi kytkeäksesi toiminnon pois.
run_user=Aja käyttäjänä
run_user_helper=Anna käyttäjätunnus, jona Giteaa ajetaan. Käyttäjällä on oltava oikeudet repositorioiden juuripolkuun.
+domain=Palvelimen verkkotunnus
ssh_port=SSH-palvelimen portti
ssh_port_helper=Porttinumero, jossa SSH-palvelimesi kuuntelee. Jätä tyhjäksi kytkeäksesi pois.
http_port=Gitean HTTP-kuunteluportti
@@ -130,7 +173,6 @@ log_root_path_helper=Lokitiedostot kirjoitetaan tähän kansioon.
optional_title=Valinnaiset asetukset
email_title=Sähköpostiasetukset
-smtp_host=SMTP isäntä
smtp_from=Lähetä sähköpostit osoitteella
smtp_from_helper=Sähköpostiosoite, jota Gitea käyttää. Kirjoita osoite ”nimi” -muodossa.
mailer_user=SMTP-käyttäjätunnus
@@ -151,6 +193,7 @@ openid_signin=Ota OpenID kirjautuminen käyttöön
openid_signin_popup=Ota käyttöön kirjautuminen OpenID:n kautta.
openid_signup=Ota käyttöön OpenID itse-rekisteröinti
openid_signup_popup=Ota käyttöön OpenID-pohjainen käyttäjän itse-rekisteröinti.
+enable_captcha=Ota käyttöön CAPTCHA rekisteröityessä
enable_captcha_popup=Pakollinen captcha käyttäjän itse rekisteröityessä.
require_sign_in_view=Vaadi sisäänkirjautuminen sivujen näkemiseksi
require_sign_in_view_popup=Rajoita pääsy vain kirjautuneille käyttäjille. Vierailijat näkevät vain 'kirjaudu sisään' ja rekisteröidy -sivut.
@@ -165,22 +208,43 @@ test_git_failed=Epäonnistui testata 'git' komentoa: %v
sqlite3_not_available=Tämä Gitea versio ei tue SQLite3. Lataa virallinen binääriversio kohteesta %s (ei 'gobuild' versio).
invalid_db_setting=Tietokanta-asetukset ovat väärin: %v
invalid_repo_path=Repojen juuri polku on virheellinen: %v
+invalid_app_data_path=Sovelluksen datapolku on virheellinen: %v
+internal_token_failed=Sisäisen pääsymerkin luonti epäonnistui: %v
save_config_failed=Asetusten tallentaminen epäonnistui: %v
install_success=Tervetuloa! Kiitos kun valitsit Gitean. Pidä hauskaa!
+default_keep_email_private=Piilota sähköpostiosoitteet oletuksena
+default_keep_email_private_popup=Piilota oletusarvoisesti uusien käyttäjätilien sähköpostiosoitteet.
+default_enable_timetracking=Ota ajan seuranta oletusarvoisesti käyttöön
+default_enable_timetracking_popup=Ota käyttöön uusien repojen aikaseuranta oletusarvoisesti.
+no_reply_address=Piilotettu sähköpostin verkkotunnus
+no_reply_address_helper=Verkkotunnuksen nimi käyttäjille, joilla on piilotettu sähköpostiosoite. Esimerkiksi käyttäjätunnus 'joe' kirjataan Git nimellä 'joe@noreply.example.org' jos piilotettu sähköpostiosoite on asetettu 'noreply.example.org'.
+password_algorithm=Salasanan hajautusalgoritmi
+password_algorithm_helper=Aseta salasanan hajautusalgoritmi. Algoritmeillä on eri vaatimukset ja vahvuudet. `argon2`, vaikka sillä on hyvät ominaisuudet, käyttää paljon muistia ja voi olla sopimaton pienille järjestelmille.
[home]
uname_holder=Käyttäjätunnus tai sähköpostiosoite
password_holder=Salasana
switch_dashboard_context=Vaihda kojelaudan kontekstia
my_repos=Repot
+show_more_repos=Näytä lisää repoja…
collaborative_repos=Yhteistyö repot
my_orgs=Organisaationi
my_mirrors=Peilini
view_home=Näytä %s
search_repos=Etsi repo…
+filter=Muut suodattimet
+filter_by_team_repositories=Suodata tiimin repojen mukaan
+feed_of=Syöte "%s"
+show_archived=Arkistoidut
+show_both_archived_unarchived=Näytetään arkistoidut ja arkistoimattomat
+show_only_archived=Näytetään vain arkistoidut
+show_only_unarchived=Näytetään vain arkistoimattomat
show_private=Yksityinen
+show_both_private_public=Näytetään sekä julkiset että yksityiset
+show_only_private=Näytetään vain yksityiset
+show_only_public=Näytetään vain julkiset
issues.in_your_repos=Repoissasi
@@ -190,6 +254,7 @@ users=Käyttäjät
organizations=Organisaatiot
search=Hae
code=Koodi
+search.match=Osuma
repo_no_results=Vastaavia repoja ei löydy.
user_no_results=Vastaavia käyttäjiä ei löytynyt.
org_no_results=Ei löytynyt vastaavia organisaatioita.
@@ -197,12 +262,14 @@ code_no_results=Hakuehtoasi vastaavaa lähdekoodia ei löytynyt.
code_search_results=Hakutulokset: '%s '
code_last_indexed_at=Viimeksi indeksoitu %s
+
[auth]
create_new_account=Rekisteröi tili
register_helper_msg=On jo tili? Kirjaudu sisään nyt!
social_register_helper_msg=Onko sinulla jo tili? Linkitä se nyt!
disable_register_prompt=Rekisteröinti on estetty. Ota yhteys ylläpitäjääsi.
disable_register_mail=Sähköpostivahvistus rekisteröinnille on estetty.
+remember_me=Muista tämä laite
forgot_password_title=Unohtuiko salasana
forgot_password=Unohtuiko salasana?
sign_up_now=Tarvitsetko tilin? Rekisteröidy nyt.
@@ -210,6 +277,7 @@ sign_up_successful=Tilin luonti onnistui.
confirmation_mail_sent_prompt=Uusi varmistussähköposti on lähetetty osoitteeseen %s, ole hyvä ja tarkista saapuneet seuraavan %s tunnin sisällä saadaksesi rekisteröintiprosessin valmiiksi.
must_change_password=Vaihda salasanasi
allow_password_change=Vaadi käyttäjää vaihtamaan salasanansa (suositeltava)
+reset_password_mail_sent_prompt=Varmistussähköposti on lähetetty osoitteeseen %s. Tarkista saapuneet seuraavan %s tunnin sisällä saadaksesi tilin palauttamisen valmiiksi.
active_your_account=Aktivoi tilisi
account_activated=Tili on aktivoitu
prohibit_login=Kirjautuminen estetty
@@ -218,9 +286,11 @@ resent_limit_prompt=Olet jo tilannut aktivointisähköpostin hetki sitten. Ole h
has_unconfirmed_mail=Hei %s, sinulla on varmistamaton sähköposti osoite (%s). Jos et ole saanut varmistus sähköpostia tai tarvitset uudelleenlähetyksen, ole hyvä ja klikkaa allaolevaa painiketta.
resend_mail=Klikkaa tästä uudelleenlähettääksesi aktivointi sähköpostisi
email_not_associate=Tätä sähköpostiosoitetta ei ole liitetty mihinkään tiliin.
+send_reset_mail=Lähetä tilin palautussähköposti
reset_password=Tilin palautus
invalid_code=Vahvistusavain on virheellinen tai vanhentunut.
reset_password_helper=Palauta käyttäjätili
+reset_password_wrong_user=Olet kirjautunut sisään nimellä %s, mutta tilin palautuslinkki on tarkoitettu kohteelle %s
password_too_short=Salasanan pituus ei voi olla vähemmän kuin %d merkkiä.
non_local_account=Ei-lokaalit käyttäjät eivät voi päivittää salasanojaan Gitean web-käyttöliittymän kautta.
verify=Vahvista
@@ -232,10 +302,12 @@ twofa_scratch_token_incorrect=Kertakäyttökoodisi on virheellinen.
login_userpass=Kirjaudu sisään
login_openid=OpenID
oauth_signup_tab=Rekisteröi uusi tili
+oauth_signup_title=Viimeistele tili
oauth_signup_submit=Viimeistele tili
oauth_signin_tab=Linkitä olemassa olevaan tiliin
oauth_signin_title=Kirjaudu sisään valtuuttaaksesi linkitetyn tilin
oauth_signin_submit=Yhdistä tiliin
+oauth.signin.error.access_denied=Valtuutuspyyntö on evätty.
openid_connect_submit=Connect
openid_connect_title=Yhdistä olemassaolevaan tiliin
openid_connect_desc=Valittu OpenID-osoite on tuntematon. Liitä se uuteen tiliin täällä.
@@ -251,22 +323,39 @@ authorize_title=Valtuutatko "%s" pääsemään tilillesi?
authorization_failed=Käyttöoikeuden varmistus epäonnistui
authorization_failed_desc=Käyttöoikeuden varmistus epäonnistui virheellisen pyynnön takia. Ota yhteyttä sovelluksen ylläpitäjään, jonka olet yrittänyt valtuuttaa.
sspi_auth_failed=SSPI todennus epäonnistui
+password_pwned=Valitsemasi salasana on varastettujen salasanojen luettelossa, joka on aiemmin paljastunut julkisissa tietorikkomuksissa. Yritä uudelleen toisella salasanalla.
[mail]
+view_it_on=Näytä %s
+link_not_working_do_paste=Eikö toimi? Yritä kopioida ja liittää se selaimeesi.
+hi_user_x=Hei %s,
activate_account=Ole hyvä ja aktivoi tilisi
activate_email=Vahvista sähköpostiosoitteesi
+activate_email.title=%s, vahvista sähköpostiosoitteesi
register_notify=Tervetuloa Giteaan
+register_notify.text_2=Voit nyt kirjautua käyttäjätunnuksella: %s.
reset_password=Palauta käyttäjätili
+reset_password.title=%s, olet pyytänyt tilisi palauttamista
register_success=Rekisteröinti onnistui
+issue.x_mentioned_you=@%s mainitsi sinut:
+issue.action.push_1=@%[1]s työnsi %[3]d commitin kohteeseen %[2]s
+issue.action.push_n=@%[1]s työnsi %[3]d committia kohteeseen %[2]s
+issue.action.reject=@%[1]s pyysi muutoksia tässä vetopyynnössä.
+release.title=Otsikko: %s
+release.note=Huomautus:
+release.downloads=Lataukset:
+release.download.zip=Lähdekoodi (ZIP)
+release.download.targz=Lähdekoodi (TAR.GZ)
+repo.transfer.to_you=sinä
[modal]
@@ -287,6 +376,7 @@ AuthName=Luvan nimi
AdminEmail=Ylläpito sähköposti
NewBranchName=Uuden haaran nimi
+CommitSummary=Commitin yhteenveto
CommitMessage=Commitin viesti
CommitChoice=Commitin valinta
TreeName=Tiedostopolku
@@ -302,17 +392,24 @@ max_size_error=` täytyy sisältää enintään %s merkkiä.`
email_error=` ei ole kelvollinen sähköpostiosoite.`
include_error=` täytyy sisältää tekstiosa '%s'.`
unknown_error=Tuntematon virhe:
+captcha_incorrect=CAPTCHA koodi on virheellinen.
password_not_match=Salasanat eivät täsmää.
+lang_select_error=Valitse kieli listalta.
username_been_taken=Käyttäjätunnus on jo varattu.
+repo_name_been_taken=Repon nimi on jo käytössä.
+repository_force_private=Pakotettu yksityisyys käytössä: yksityisiä repoja ei voida muuttaa julkisiksi.
org_name_been_taken=Organisaation nimi on jo käytössä.
team_name_been_taken=Tiimin nimi on jo varattu.
email_been_used=Sähköpostiosoite on jo käytössä.
+email_invalid=Sähköpostiosoite on virheellinen.
+openid_been_used=OpenID-osoite '%s' on jo käytössä.
username_password_incorrect=Käyttäjätunnus tai salasana on virheellinen.
password_lowercase_one=Ainakin yksi pieni kirjan
password_uppercase_one=Ainakin yksi iso kirjain
password_digit_one=Ainakin yksi numero
password_special_one=Ainakin yksi erikoismerkki (välimerkki, sulut, lainausmerkit, jne.)
+enterred_invalid_org_name=Antamasi organisaation nimi on virheellinen.
enterred_invalid_password=Syöttämäsi salasana oli väärä.
user_not_exist=Käyttäjää ei ole olemassa.
team_not_exist=Tiimiä ei ole olemassa.
@@ -333,16 +430,20 @@ repositories=Repot
activity=Julkinen toiminta
followers=Seuraajat
starred=Tähdelliset repot
+projects=Projektit
following=Seurataan
follow=Seuraa
unfollow=Lopeta seuraaminen
+heatmap.loading=Ladataan lämpökarttaa…
user_bio=Elämäkerta
form.name_reserved=Käyttäjätunnus '%s' on varattu.
+form.name_chars_not_allowed=Käyttäjänimi '%s' sisältää virheellisiä merkkejä.
[settings]
profile=Profiili
account=Tili
+appearance=Ulkoasu
password=Salasana
security=Turvallisuus
avatar=Profiilikuva
@@ -356,8 +457,10 @@ twofa=Kaksivaiheinen todennus
account_link=Linkitetyt tilit
organization=Organisaatiot
uid=Käyttäjä ID
+webauthn=Turva-avaimet
public_profile=Julkinen profiili
+biography_placeholder=Kerro itsestäsi
profile_desc=Sähköpostiosoitettasi käytetään ilmoituksiin ja muihin toimintoihin.
password_username_disabled=Ei-paikalliset käyttäjät eivät voi muuttaa käyttäjätunnustaan. Ole hyvä ja ota yhteyttä sivuston ylläpitäjään saadaksesi lisätietoa.
full_name=Kokonimi
@@ -365,6 +468,9 @@ website=Nettisivut
location=Sijainti
update_theme=Päivitä teema
update_profile=Päivitä profiili
+update_language=Päivitä kieli
+update_language_not_found=Kieli '%s' ei ole käytettävissä.
+update_language_success=Kieli on päivitetty.
update_profile_success=Profiilisi on päivitetty.
change_username=Käyttäjätunnuksesi on muutettu.
change_username_prompt=Huomio: käyttäjätunnuksen muutos muuttaa myös tilisi URL:n.
@@ -372,6 +478,24 @@ continue=Jatka
cancel=Peruuta
language=Kieli
ui=Teema
+hidden_comment_types=Piilotetut kommenttityypit
+comment_type_group_reference=Viittaus
+comment_type_group_label=Tunniste
+comment_type_group_milestone=Merkkipaalu
+comment_type_group_assignee=Osoitettu henkilölle
+comment_type_group_title=Otsikko
+comment_type_group_branch=Haara
+comment_type_group_time_tracking=Ajan seuranta
+comment_type_group_deadline=Määräaika
+comment_type_group_dependency=Riippuvuus
+comment_type_group_lock=Lukituksen tila
+comment_type_group_review_request=Arviointipyyntö
+comment_type_group_pull_request_push=Lisätyt commitit
+comment_type_group_project=Projekti
+saved_successfully=Asetuksesi tallennettiin onnistuneesti.
+privacy=Yksityisyys
+keep_activity_private=Piilota toiminta profiilisivulta
+keep_activity_private_popup=Tekee toiminnon näkyvän vain sinulle ja ylläpitäjille
lookup_avatar_by_mail=Hae profiilikuva sähköpostin perusteella
federated_avatar_lookup=Ulkopuolinen profiilikuvan haku
@@ -398,17 +522,23 @@ primary=Ensisijainen
activated=Aktivoitu
requires_activation=Vaatii aktivoinnin
primary_email=Tee ensisijainen
+activate_email=Lähetä aktivointi
+activations_pending=Odottaa aktivointia
delete_email=Poista
email_deletion=Poista sähköpostiosoite
email_deletion_desc=Sähköpostiosoite ja siihen liittyvät tiedot poistetaan tililtäsi. Kyseisen sähköpostiosoitteen sisältävät commitit pysyvät muuttumattomia. Jatketaanko?
email_deletion_success=Sähköpostiosoite on poistettu.
theme_update_success=Teemasi on päivitetty.
theme_update_error=Valittua teemaa ei löydy.
+openid_deletion=Poista OpenID-osoite
+openid_deletion_success=OpenID-osoite on poistettu.
add_new_email=Lisää uusi sähköpostiosoite
add_new_openid=Lisää uusi OpenID URI
add_email=Lisää sähköpostiosoite
add_openid=Lisää OpenID URI
add_email_success=Uusi sähköpostiosoite on lisätty.
+email_preference_set_success=Sähköpostin asetukset on asetettu onnistuneesti.
+add_openid_success=Uusi OpenID-osoite on lisätty.
keep_email_private=Piilota sähköpostiosoite
keep_email_private_popup=Sähköpostiosoitteesi on piilotettu muilta käyttäjiltä.
openid_desc=OpenID mahdollistaa todentamisen delegoinnin ulkopuoliselle palvelun tarjoajalle.
@@ -422,10 +552,35 @@ ssh_helper=Tarvitsetko apua? Tutustu GitHubin oppaaseen Tarvitsetko apua? Katso GitHubin opas GPG:stä.
add_new_key=Lisää SSH avain
add_new_gpg_key=Lisää GPG-avain
+key_content_ssh_placeholder=Alkaa sanoilla 'ssh-ed25519', 'ssh-rsa', 'ecdsa-sha2-nistp256', 'ecdsa-sha2-nistp384', 'ecdsa-sha2-nistp521', 'sk-ecdsa-sha2-nistp256@openssh.com', tai 'sk-ssh-ed25519@openssh.com'
+key_content_gpg_placeholder=Alkaa sanoilla '-----BEGIN PGP PUBLIC KEY BLOCK-----'
+ssh_key_name_used=Samanniminen SSH avain on jo olemassa tililläsi.
+gpg_key_id_used=Julkinen GPG-avain samalla tunnuksella on jo olemassa.
+gpg_no_key_email_found=Tämä GPG-avain ei vastaa mitään tiliisi liitettyä aktivoitua sähköpostiosoitetta. Se voidaan silti lisätä, jos allekirjoitat annetun pääsymerkin.
+gpg_key_verified=Vahvistettu avain
+gpg_key_verified_long=Avain on vahvistettu pääsymerkillä ja sitä voidaan käyttää todentamaan commitit, jotka vastaavat tämän käyttäjän aktivoituja sähköpostiosoitteita tämän avaimen kaikkien vastaavien identiteettien lisäksi.
+gpg_key_verify=Vahvista
+gpg_token_required=Sinun täytyy antaa allekirjoitus alla olevalle pääsymerkille
+gpg_token=Pääsymerkki
+gpg_token_help=Voit luoda allekirjoituksen käyttäen:
+gpg_token_code=echo "%s" | gpg -a --default-key %s --detach-sig
+gpg_token_signature=Panssaroitu GPG-allekirjoitus
+key_signature_gpg_placeholder=Alkaa sanoilla '-----BEGIN PGP SIGNATURE-----'
+verify_gpg_key_success=GPG-avain '%s' on vahvistettu.
+ssh_key_verified=Vahvistettu avain
+ssh_key_verified_long=Avain on vahvistettu pääsymerkillä ja sitä voidaan käyttää todentamaan commitit, jotka vastaavat tämän käyttäjän aktivoituja sähköpostiosoitteita.
+ssh_key_verify=Vahvista
+ssh_token_required=Sinun täytyy antaa allekirjoitus alla olevalle pääsymerkille
+ssh_token=Pääsymerkki
+ssh_token_help=Voit luoda allekirjoituksen käyttäen:
+ssh_token_signature=Panssaroitu SSH-allekirjoitus
+key_signature_ssh_placeholder=Alkaa sanoilla '-----BEGIN SSH SIGNATURE-----'
+verify_ssh_key_success=SSH avain '%s' on vahvistettu.
subkeys=Aliavaimet
key_id=Avain ID
key_name=Avaimen nimi
key_content=Sisältö
+principal_content=Sisältö
add_key_success=SSH-avain '%s' on lisätty.
add_gpg_key_success=GPG-avain '%s' lisättiin.
delete_key=Poista
@@ -435,15 +590,26 @@ gpg_key_deletion_desc=GPG-avaimen poistaminen peruuttaa sillä allekirjoitettuje
gpg_key_deletion_success=GPG-avain on poistettu.
add_on=Lisätty
valid_until=Vanhenee
+valid_forever=Voimassa ikuisesti
last_used=Käytetty viimeksi
no_activity=Ei viimeaikaista toimintaa
+can_read_info=Luku
+can_write_info=Kirjoitus
+show_openid=Näytä profiilissa
+hide_openid=Piilota profiilista
+ssh_disabled=SSH pois käytöstä
manage_social=Hallitse liitettyjä sosiaalisia tilejä
+manage_access_token=Hallitse pääsymerkkejä
generate_new_token=Luo uusi pääsymerkki
+new_token_desc=Pääsymerkkiä käyttävillä sovelluksilla on täysi pääsy tiliisi.
token_name=Pääsymerkin nimi
generate_token=Luo pääsymerkki
+generate_token_success=Uusi pääsymerkkisi on nyt luotu. Kopioi se nyt, koska sitä ei näytetä enää uudelleen.
delete_token=Poista
access_token_deletion=Poista pääsymerkki
+access_token_deletion_cancel_action=Peruuta
+access_token_deletion_confirm_action=Poista
edit_oauth2_application=Muokkaa OAuth2 sovellusta
remove_oauth2_application=Poista OAuth2 sovellus
@@ -459,8 +625,15 @@ oauth2_application_edit=Muokkaa
twofa_desc=Kaksivaiheinen todennus parantaa tilisi turvallisuutta.
+twofa_is_enrolled=Tilisi käyttää kaksivaiheista vahvistusta.
+twofa_not_enrolled=Tilisi ei tällä hetkellä käytä kaksivaiheista vahvistusta.
+twofa_enroll=Ota kaksivaiheinen vahvistus käyttöön
twofa_disabled=Kaksivaiheinen todennus on otettu pois käytöstä.
+scan_this_image=Skannaa tämä kuva tunnistautumissovelluksellasi:
+or_enter_secret=Tai kirjoita salainen avain: %s
+twofa_enrolled=Tiliisi on otettu käyttöön kaksivaiheinen vahvistus. Ota palautustunnus (%s) talteen turvalliseen paikkaan, sillä se näytetään vain kerran!
+webauthn_nickname=Nimimerkki
manage_account_links=Hallitse linkitettyjä tilejä
manage_account_links_desc=Nämä ulkoiset tilit on linkitetty Gitea tiliisi.
@@ -476,37 +649,62 @@ delete_prompt=Tämä toiminto poistaa käyttäjätilisi pysyvästi. Toimintoa Voit siirtää repon.
owner=Omistaja
+owner_helper=Jotkin organisaatiot eivät välttämättä näy pudotusvalikossa, koska repojen maksimimäärää on rajoitettu.
repo_name=Repon nimi
repo_name_helper=Hyvä repon nimi on lyhyt, mieleenpainuva ja yksilöllinen.
+repo_size=Repon koko
template=Malli
template_select=Valitse malli.
+template_helper=Tee reposta mallipohja
visibility=Näkyvyys
visibility_description=Vain omistaja tai organisaation jäsenet, jos heillä on oikeudet, voivat nähdä sen.
visibility_helper=Tee reposta yksityinen
+visibility_helper_forced=Sivuston ylläpitäjä pakottaa uudet repot olemaan yksityisiä.
fork_repo=Forkkaa repo
fork_from=Forkkaa lähteestä
fork_visibility_helper=Forkatun repon näkyvyyttä ei voi muuttaa.
+clone_in_vsc=Kloonaa VS Codessa
+download_zip=Lataa ZIP
+download_tar=Lataa TAR.GZ
repo_desc=Kuvaus
repo_lang=Kieli
repo_gitignore_helper=Valitse .gitignore mallit.
issue_labels=Ongelmien tunnisteet
+issue_labels_helper=Valitse pohja ongelmien nimilapuille.
license=Lisenssi
license_helper=Valitse lisenssitiedosto.
readme=README
auto_init=Alusta repo (Luo .gitignore, License ja README)
create_repo=Luo repo
default_branch=Oletus branch
+mirror_prune=Karsi
watchers=Tarkkailijat
stargazers=Tähtiharrastajat
forks=Haarat
pick_reaction=Valitse reaktiosi
+delete_preexisting_label=Poista
+desc.private=Yksityinen
+desc.public=Julkinen
+desc.private_template=Yksityinen malli
+desc.public_template=Malli
+desc.internal=Sisäinen
+template.git_hooks=Git-koukut
+template.webhooks=Webkoukut
template.topics=Aiheet
template.avatar=Profiilikuva
template.issue_labels=Ongelmien tunnisteet
@@ -522,16 +720,22 @@ migrate_items_pullrequests=Vetopyynnöt
migrate_items_releases=Julkaisut
migrate_repo=Siirrä repo
migrate.clone_address=Migraation / Kloonaa URL osoitteesta
+migrate.github_token_desc=Voit laittaa yhden tai useamman pääsymerkin pilkulla erotellen tähän nopeuttaaksesi migraatiota GitHub APIn vauhtirajojen takia. VAROITUS: Tämän ominaisuuden väärinkäyttö voi rikkoa palveluntarjoajan ehtoja ja johtaa tilin estämiseen.
migrate.permission_denied=Sinun ei sallita tuovan paikallisia repoja.
migrate.failed=Siirto epäonnistui: %v
+migrate.migrate_items_options=Pääsymerkki vaaditaan lisäkohteiden siirtämiseen
+migrate.migrating=Tuodaan kohteesta %s ...
+migrate.migrating_failed=Tuonti kohteesta %s epäonnistui.
+migrate.migrating_failed.error=Virhe: %s
+migrate.migrating_git=Tuodaan Git-tietoja
mirror_from=peilaus alkaen
forked_from=forkattu lähteestä
unwatch=Lopeta tarkkailu
watch=Tarkkaile
-unstar=Peru ääni
-star=Äänestä
-download_archive=Lataa varasto
+unstar=Poista tähti
+star=Tähti
+download_archive=Lataa repo
no_desc=Ei kuvausta
quick_guide=Pikaopas
@@ -550,7 +754,10 @@ labels=Tunnisteet
milestones=Merkkipaalut
commits=Commitit
+commit=Commit
releases=Julkaisut
+tag=Tagi
+released_this=julkaisi tämän
file_raw=Raaka
file_history=Historia
file_view_raw=Näytä raaka
@@ -558,6 +765,8 @@ file_permalink=Pysyvä linkki
video_not_supported_in_browser=Selaimesi ei tue HTML5 video-tagia.
audio_not_supported_in_browser=Selaimesi ei tue HTML5 audio-tagia.
+blame=Selitys
+download_file=Lataa tiedosto
normal_view=Normaali näkymä
line=rivi
lines=rivejä
@@ -586,6 +795,7 @@ editor.create_new_branch_np=Luo uusi haara tälle commitille.
editor.cancel=Peruuta
editor.filename_cannot_be_empty=Tiedostonimi ei voi olla tyhjä.
editor.filename_is_invalid=Tiedostonnimi on epäkelpo: '%s'.
+editor.branch_already_exists=Haara '%s' on jo olemassa tässä repossa.
editor.no_changes_to_show=Ei muutoksia näytettäväksi.
editor.add_subdir=Lisää hakemisto…
editor.unable_to_upload_files=Tiedostojen lataaminen kohteeseen '%s' epäonnistui virheellä: %v
@@ -593,6 +803,7 @@ editor.upload_files_to_dir=Lataa tiedostot kohteeseen '%s'
editor.require_signed_commit=Haara vaatii vahvistetun commitin
commits.commits=Commitit
+commits.nothing_to_compare=Nämä haarat vastaavat toisiaan.
commits.find=Haku
commits.search_all=Kaikki haarat
commits.author=Tekijä
@@ -601,16 +812,42 @@ commits.date=Päivämäärä
commits.older=Vanhemmat
commits.newer=Uudemmat
commits.signed_by=Allekirjoittanut
+commits.gpg_key_id=GPG avaimen ID
+commits.ssh_key_fingerprint=SSH avaimen sormenjälki
+projects=Projektit
+projects.description_placeholder=Kuvaus
+projects.create=Luo projekti
+projects.title=Otsikko
+projects.new=Uusi projekti
+projects.create_success=Projekti '%s' on luotu.
+projects.deletion=Poista projekti
+projects.deletion_success=Projekti on poistettu.
+projects.edit=Muokkaa projektia
+projects.modify=Päivitä projekti
+projects.edit_success=Projekti '%s' on päivitetty.
+projects.type.basic_kanban=Yksinkertainen Kanban
+projects.type.uncategorized=Luokittelematon
+projects.board.edit=Muokkaa luetteloa
+projects.board.new_submit=Lähetä
+projects.board.new=Uusi taulu
+projects.board.set_default=Aseta oletukseksi
+projects.board.delete=Poista taulu
+projects.board.color=Väri
+projects.open=Avaa
+projects.close=Sulje
issues.desc=Ongelmien, tehtävien ja merkkipaalujen hallinta.
+issues.filter_assignees=Suodata käyttäjiä
issues.filter_milestones=Suodata merkkipaalu
issues.new=Uusi ongelma
issues.new.labels=Tunnisteet
+issues.new.add_labels_title=Aseta tunniste
issues.new.no_label=Ei tunnistetta
issues.new.clear_labels=Tyhjennä tunnisteet
+issues.new.no_items=Ei kohteita
issues.new.milestone=Merkkipaalu
issues.new.add_milestone_title=Aseta merkkipaalu
issues.new.no_milestone=Ei merkkipaalua
@@ -618,17 +855,24 @@ issues.new.clear_milestone=Tyhjennä merkkipaalu
issues.new.open_milestone=Avoimet merkkipaalut
issues.new.closed_milestone=Suljetut merkkipaalut
issues.new.assignees=Käsittelijä
+issues.new.add_assignees_title=Osoita käyttäjille
issues.new.clear_assignees=Tyhjennä käsittelijä
issues.new.no_assignees=Ei käsittelijää
+issues.choose.blank=Oletus
issues.no_ref=Haaraa/tagia ei määritelty
issues.create=Ilmoita ongelma
issues.new_label=Uusi tunniste
issues.new_label_placeholder=Tunnisteen nimi
issues.new_label_desc_placeholder=Kuvaus
issues.create_label=Luo tunniste
+issues.label_templates.helper=Valitse tunnistejoukko
issues.add_milestone_at=`lisäsi tämän merkkipaaluun %s %s`
+issues.change_milestone_at=`vaihtoi merkkipaalun %s merkkipaaluun %s %s`
+issues.remove_milestone_at=`poisti tämän %s merkkipaalusta %s`
+issues.remove_project_at=`poisti tämän %s projektista %s`
issues.deleted_milestone=`(poistettu)`
issues.self_assign_at=`itse otti tämän käsittelyyn %s`
+issues.change_title_at=`muutti otsikon %s otsikoksi %s %s`
issues.delete_branch_at=`poisti haaran %s %s`
issues.filter_label=Tunniste
issues.filter_label_exclude=`Käytä alt
+ klikkaus/rivinvaihto
poissulkeaksesi tunnisteita`
@@ -641,6 +885,7 @@ issues.filter_type.all_issues=Kaikki ongelmat
issues.filter_type.assigned_to_you=Osoitettu sinulle
issues.filter_type.created_by_you=Ilmoittamasi
issues.filter_type.mentioning_you=Jotka mainitsee sinut
+issues.filter_type.review_requested=Arvostelua pyydetty
issues.filter_sort=Lajittele
issues.filter_sort.latest=Uusin
issues.filter_sort.oldest=Vanhin
@@ -658,6 +903,7 @@ issues.action_open=Avaa
issues.action_close=Sulje
issues.action_label=Tunniste
issues.action_milestone=Merkkipaalu
+issues.action_milestone_no_select=Ei merkkipaalua
issues.opened_by=%[1]s avasi %[3]s
issues.previous=Edellinen
issues.next=Seuraava
@@ -668,6 +914,7 @@ issues.commented_at=`kommentoi %s`
issues.delete_comment_confirm=Haluatko varmasti poistaa tämän kommentin?
issues.context.copy_link=Kopioi linkki
issues.context.quote_reply=Vastaa lainaamalla
+issues.context.reference_issue=Viittaa uudesa ongelmassa
issues.context.edit=Muokkaa
issues.context.delete=Poista
issues.no_content=Sisältöä ei vielä ole.
@@ -712,9 +959,11 @@ issues.lock.reason=Lukitsemisen syy
issues.lock.title=Lukitse keskustelu tästä ongelmasta.
issues.unlock.title=Avaa keskustelu tästä ongelmasta.
issues.tracker=Ajan seuranta
+issues.start_tracking_short=Aloita ajanotto
issues.start_tracking=Aloita ajan seuranta
issues.start_tracking_history=`aloitti työskentelyn %s`
issues.tracker_auto_close=Ajan seuranta pysähtyy automaattisesti kun tämä ongelma on suljettu
+issues.stop_tracking=Pysäytä ajanotto
issues.stop_tracking_history=`lopetti työskentelyn %s`
issues.add_time=Lisää aika käsin
issues.add_time_short=Lisää aika
@@ -725,22 +974,44 @@ issues.add_time_minutes=Minuuttia
issues.add_time_sum_to_small=Aikaa ei syötetty.
issues.time_spent_from_all_authors=`Käytetty kokonaisaika: %s`
issues.due_date=Määräpäivä
+issues.push_commit_1=lisäsi %d commitin %s
+issues.push_commits_n=lisäsi %d committia %s
+issues.due_date_form=vvvv-kk-pp
issues.due_date_form_edit=Muokkaa
issues.due_date_form_remove=Poista
issues.due_date_not_set=Määräpäivää ei asetettu.
+issues.due_date_overdue=Myöhässä
issues.dependency.title=Riippuvuudet
+issues.dependency.issue_no_dependencies=Riippuvuuksia ei asetettu.
+issues.dependency.pr_no_dependencies=Riippuvuuksia ei asetettu.
issues.dependency.add=Lisää riippuvuus…
issues.dependency.cancel=Peru
issues.dependency.remove=Poista
issues.dependency.remove_info=Poistä tämä riippuvuus
issues.review.self.approval=Et voi hyväksyä omia vetopyyntöjä.
+issues.review.self.rejection=Et voi pyytää muutoksia omaan vetopyyntöön.
issues.review.approve=hyväksyi nämä muutokset %s
+issues.review.left_comment=jätti kommentin
+issues.review.pending=Odottaa
+issues.review.pending.tooltip=Tämä kommentti ei tällä hetkellä näy muille käyttäjille. Lähettääksesi odottavat kommentit, valitse '%s' -> '%s/%s/%s' sivun yläreunassa.
+issues.review.show_resolved=Näytä ratkaisu
+issues.review.hide_resolved=Piilota ratkaisu
+issues.reference_issue.body=Kuvaus
+issues.content_history.deleted=poistettu
+issues.content_history.edited=muokattu
+issues.content_history.created=luotu
-pulls.new=Uusi pull pyyntö
+pulls.new=Uusi vetopyyntö
+pulls.compare_changes=Uusi vetopyyntö
+pulls.has_viewed_file=Katsottu
+pulls.viewed_files_label=%[1]d / %[2]d tiedostoa katsottu
+pulls.compare_compare=vedä kohteesta
pulls.filter_branch=Suodata branch
pulls.no_results=Tuloksia ei löytynyt.
-pulls.nothing_to_compare=Nämä haarat ovat samanlaisia. Ei ole tarvetta luoda vetopyyntöä.
+pulls.nothing_to_compare=Nämä haarat vastaavat toisiaan. Ei ole tarvetta luoda vetopyyntöä.
+pulls.nothing_to_compare_and_allow_empty_pr=Nämä haarat vastaavat toisiaan. Vetopyyntö tulee olemaan tyhjä.
+pulls.has_pull_request=`Vetopyyntö haarojen välillä on jo olemassa: %[2]s#%[3]d`
pulls.create=Luo Pull-pyyntö
pulls.title_desc=haluaa yhdistää %[1]d committia lähteestä %[2]s
kohteeseen %[3]s
pulls.merged_title_desc=yhdistetty %[1]d committia lähteestä %[2]s
kohteeseen %[3]s
%[4]s
@@ -749,6 +1020,9 @@ pulls.tab_commits=Commitit
pulls.tab_files=Muuttuneet tiedostot
pulls.merged=Yhdistetty
pulls.has_merged=Vetopyyntö on yhdistetty.
+pulls.title_wip_desc=`Aloita otsikko sanalla %s estääksesi vetopyynnön yhdistämisen vahingossa.`
+pulls.add_prefix=Lisää %s etuliite
+pulls.remove_prefix=Poista %s etuliite
pulls.can_auto_merge_desc=Tämä pull-pyyntö voidaan yhdistää automaattisesti.
@@ -776,12 +1050,17 @@ milestones.edit_success=Merkkipaalu '%s' on päivitetty.
milestones.filter_sort.most_issues=Eniten ongelmia
milestones.filter_sort.least_issues=Vähiten ongelmia
+signing.wont_sign.always=Commitit ovat aina allekirjoitettuja
wiki=Wiki
+wiki.welcome=Tervetuloa Wikiin.
+wiki.welcome_desc=Wikissä voit kirjoittaa ja jakaa dokumentaatiota käyttäjien kesken.
+wiki.create_first_page=Luo ensimmäinen sivu
wiki.page=Sivu
wiki.filter_page=Suodatin sivu
wiki.new_page=Sivu
+wiki.default_commit_message=Kirjoita muistiinpano tästä päivityksestä (valinnainen).
wiki.save_page=Tallenna sivu
wiki.last_commit_info=%s muokkasi tätä sivua %s
wiki.edit_page_button=Muokkaa
@@ -814,6 +1093,7 @@ activity.new_issues_count_n=Uutta ongelmaa
activity.new_issue_label=Avoinna
activity.unresolved_conv_label=Auki
activity.published_release_label=Julkaistu
+activity.git_stats_pushed_1=on työntänyt
activity.git_stats_file_1=%d tiedosto
activity.git_stats_file_n=%d tiedostoa
activity.git_stats_addition_1=%d lisäys
@@ -850,6 +1130,7 @@ settings.danger_zone=Vaaravyöhyke
settings.new_owner_has_same_repo=Uudella omistajalla on jo samanniminen repo. Ole hyvä ja valitse toinen nimi.
settings.transfer=Siirrä omistajuus
settings.transfer_form_title=Syötä repon nimi vahvistuksena:
+settings.transfer_notices_3=- Jos arkisto on yksityinen ja se siirretään yksittäiselle käyttäjälle, tämä toiminto varmistaa, että käyttäjällä on ainakin lukuoikeudet (ja muuttaa käyttöoikeuksia tarvittaessa).
settings.transfer_owner=Uusi omistaja
settings.wiki_delete=Poista Wiki data
settings.wiki_delete_desc=Repon wikin data poistaminen on pysyvä eikä voi peruuttaa.
@@ -874,51 +1155,132 @@ settings.githook_edit_desc=Jos koukku ei ole käytössä, esitellään esimerkki
settings.githook_name=Koukun nimi
settings.githook_content=Koukun sisältö
settings.update_githook=Päivitys koukku
+settings.payload_url=Kohde URL
settings.http_method=HTTP-menetelmä
settings.secret=Salaus
settings.slack_username=Käyttäjätunnus
settings.slack_icon_url=Kuvakkeen URL
settings.discord_username=Käyttäjätunnus
+settings.event_desc=Triggeröi:
+settings.event_send_everything=Kaikki tapahtumat
+settings.event_choose=Mukautetut tapahtumat…
+settings.event_header_repository=Repon tapahtumat
settings.event_create=Luo
+settings.event_create_desc=Haara tai tagi luotu.
settings.event_delete=Poista
+settings.event_delete_desc=Haara tai tagi poistettu.
settings.event_release_desc=Julkaisu julkaistu, päivitetty tai poistettu varastosta.
+settings.event_push=Työnnä
+settings.event_push_desc=Git push repoon.
settings.event_repository=Repo
+settings.event_repository_desc=Repo luotu tai poistettu.
+settings.event_header_issue=Ongelmien tapahtumat
+settings.event_issues_desc=Ongelma avattu, suljettu, avattu uudelleen tai muokattu.
+settings.event_issue_assign=Ongelma määritetty
+settings.event_issue_assign_desc=Ongelma osoitettu tai osoitus poistettu.
+settings.event_issue_label_desc=Ongelman tunnisteet päivitetty tai tyhjennetty.
+settings.event_issue_milestone_desc=Ongelma merkkipaaluteettu tai merkkipaalu-osoitus poistettu.
settings.event_issue_comment_desc=Ongelman kommentti luotu, muokattu tai poistettu.
+settings.event_header_pull_request=Vetopyyntöjen tapahtumat
settings.event_pull_request=Vetopyyntö
+settings.event_package_desc=Paketti on luotu tai poistettu repossa.
+settings.active_helper=Tiedot käynnistetyistä tapahtumista lähetetään tähän webkoukun URL-osoitteeseen.
+settings.add_hook_success=Uusi webkoukku on lisätty.
settings.update_webhook=Päivitä webkoukku
+settings.delete_webhook=Poista webkoukku
settings.recent_deliveries=Viimeisimmät toimitukset
settings.hook_type=Koukkutyyppi
settings.slack_token=Pääsymerkki
settings.slack_domain=Verkkotunnus
settings.slack_channel=Kanava
-settings.deploy_keys=Deploy avaimet
-settings.add_deploy_key=Lisää deploy avain
+settings.add_web_hook_desc=Integroi %s repoon.
+settings.web_hook_name_gitea=Gitea
+settings.web_hook_name_gogs=Gogs
+settings.web_hook_name_slack=Slack
+settings.web_hook_name_discord=Discord
+settings.web_hook_name_dingtalk=DingTalk
+settings.web_hook_name_telegram=Telegram
+settings.web_hook_name_matrix=Matrix
+settings.web_hook_name_feishu=Feishu
+settings.web_hook_name_larksuite=Lark Suite
+settings.web_hook_name_packagist=Packagist
+settings.deploy_keys=Julkaisuavaimet
+settings.add_deploy_key=Lisää julkaisuavain
+settings.deploy_key_desc=Julkaisuavaimilla on vain-luku oikeudet repoon.
+settings.is_writable_info=Salli tämän julkaisuavaimen puskea repoon.
+settings.no_deploy_keys=Julkaisuavaimia ei ole käytössä vielä.
settings.title=Otsikko
settings.deploy_key_content=Sisältö
+settings.key_been_used=Julkaisuavain identtisellä sisällöllä on jo käytössä.
+settings.key_name_used=Julkaisuavain samalla nimellä on jo olemassa.
+settings.add_key_success=Julkaisuavain '%s' on lisätty.
+settings.deploy_key_deletion=Poista julkaisuavain
+settings.deploy_key_deletion_desc=Julkaisuavaimen poistaminen kumoaa sen pääsyn tähän repoon. Jatketaanko?
+settings.deploy_key_deletion_success=Julkaisuavain on poistettu.
settings.branches=Haarat
settings.protected_branch=Haaran suojaus
settings.branch_protection=Haaran '%s' suojaus
settings.protect_this_branch=Ota haaran suojaus käyttöön
+settings.protect_whitelist_deploy_keys=Lisää julkaisuavaimet sallittujen listalle mahdollistaaksesi repohin kirjoituksen.
settings.protect_whitelist_users=Lista käyttäjistä joilla työntö oikeus:
settings.protect_whitelist_search_users=Etsi käyttäjiä…
settings.protect_merge_whitelist_committers_desc=Salli vain listaan merkittyjen käyttäjien ja tiimien yhdistää vetopyynnöt tähän haaraan.
settings.protect_merge_whitelist_users=Lista käyttäjistä joilla yhdistämis-oikeus:
settings.protect_required_approvals=Vaadittavat hyväksynnät:
settings.protect_approvals_whitelist_users=Sallittujen tarkastajien lista:
+settings.protect_protected_file_patterns_desc=Suojatut tiedostot, joita ei voi muuttaa suoraan, vaikka käyttäjällä olisi oikeudet lisätä, muokata tai poistaa tiedostoja tässä haarassa. Useita malleja voidaan erottaa puolipisteellä ('\;'). Katso github.com/gobwas/glob dokumentaatio mallisyntaksille. Esimerkkejä: .drone.yml
, /docs/**/*.txt
.
+settings.protect_unprotected_file_patterns_desc=Suojaamattomat tiedostot, joita voidaan muuttaa suoraan, jos käyttäjällä on kirjoitusoikeudet, ohittaen push-rajoituksen. Useita kuvioita voidaan erottaa puolipisteellä ('\;'). Katso github.com/gobwas/glob dokumentaatio kuviosyntaksille. Esimerkkejä: .drone.yml
, /docs/**/*.txt
.
settings.update_protect_branch_success=Haaran '%s' suojaus on päivitetty.
settings.remove_protected_branch_success=Haaran '%s' suojaus on poistettu käytöstä.
settings.choose_branch=Valitse haara…
settings.no_protected_branch=Suojattuja haaroja ei ole.
settings.edit_protected_branch=Muokkaa
settings.protected_branch_required_approvals_min=Vaadittavat hyväksynnät ei voi olla negatiivinen.
+settings.tags=Tagit
+settings.tags.protection=Tagien suojaaminen
+settings.tags.protection.pattern=Tagin kuvio
+settings.tags.protection.allowed=Sallitut
+settings.tags.protection.allowed.users=Sallitut käyttäjät
+settings.tags.protection.allowed.teams=Sallitut tiimit
+settings.tags.protection.allowed.noone=Ei kukaan
+settings.tags.protection.create=Suojaa tagi
+settings.tags.protection.none=Suojattuja tageja ei ole.
+settings.tags.protection.pattern.description=Voit käyttää yhtä nimeä tai glob-kuviota tai säännöllistä lauseketta, joka täsmää useisiin tageihin. Lue lisää suojatut tagit oppaasta.
+settings.bot_token=Botti pääsymerkki
+settings.matrix.homeserver_url=Kotipalvelimen URL
+settings.matrix.access_token=Pääsymerkki
settings.archive.button=Arkistoi repo
settings.archive.header=Arkistoi tämä repo
+settings.archive.tagsettings_unavailable=Tagien asetukset eivät ole saatavilla, jos repo on arkistoitu.
settings.lfs=LFS
+settings.lfs_filelist=LFS-tiedostot tallennettu tähän repoon
+settings.lfs_no_lfs_files=LFS-tiedostoja ei ole tallennettu tähän repoon.
+settings.lfs_findcommits=Etsi commitit
+settings.lfs_lfs_file_no_commits=LFS-tiedostolle ei löytynyt committeja
+settings.lfs_noattribute=Tällä polulla ei ole lukittavaa attribuuttia oletushaarassa
+settings.lfs_delete=Poista LFS-tiedosto OID:lla %s
+settings.lfs_delete_warning=LFS-tiedoston poistaminen voi aiheuttaa 'object does not exists'-virheitä checkouttattaessa. Oletko varma?
+settings.lfs_findpointerfiles=Etsi osoitintiedostoja
+settings.lfs_locks=Lukot
+settings.lfs_invalid_locking_path=Virheellinen polku: %s
+settings.lfs_invalid_lock_directory=Hakemistoa ei voida lukita: %s
+settings.lfs_lock_already_exists=Lukitus on jo olemassa: %s
+settings.lfs_lock_path=Lukittavan tiedostopolku...
+settings.lfs_locks_no_locks=Ei lukkoja
+settings.lfs_lock_file_no_exist=Lukittua tiedostoa ei ole olemassa oletushaarassa
+settings.lfs_force_unlock=Pakota lukituksen avaus
+settings.lfs_pointers.found=Löytyi %d blob osoitinta - %d yhdistettyö, %d yhdistämätöntä (%d puuttuu varastosta)
+settings.lfs_pointers.sha=Blob SHA
settings.lfs_pointers.oid=OID
+settings.lfs_pointers.inRepo=Repossa
+settings.lfs_pointers.exists=Löytyy varastosta
+settings.lfs_pointers.accessible=Saatavilla käyttäjälle
diff.browse_source=Selaa lähdekoodia
diff.parent=vanhempi
+diff.commit=commit
diff.git-notes=Muistiinpanot
+diff.options_button=Vertailun asetukset
diff.show_split_view=Jaettu näkymä
diff.show_unified_view=Yhdistetty näkymä
diff.whitespace_button=Tyhjämerkki
@@ -932,12 +1294,18 @@ diff.view_file=Näytä tiedosto
diff.file_image_width=Leveys
diff.file_image_height=Korkeus
diff.file_byte_size=Koko
+diff.comment.markdown_info=Muotoilu markdownilla tuettu.
+diff.comment.add_single_comment=Lisää yksittäinen kommentti
diff.comment.add_review_comment=Lisää kommentti
+diff.comment.start_review=Aloita tarkistus
diff.comment.reply=Vastaa
+diff.review.header=Lähetä arvio
diff.review.comment=Kommentoi
diff.review.approve=Hyväksy
+diff.review.reject=Pyydä muutoksia
release.releases=Julkaisut
+release.tags=Tagit
release.new_release=Uusi julkaisu
release.draft=Työversio
release.prerelease=Esiversio
@@ -958,12 +1326,16 @@ release.publish=Julkaise versio
release.save_draft=Tallenna luonnos
release.edit_release=Päivitä julkaisu
release.delete_release=Poista julkaisu
+release.delete_tag=Poista tagi
+release.deletion_tag_desc=Poistetaanko tämä tagi reposta? Repon sisältö ja historia pysyvät muuttumattomina. Jatketaanko?
+release.deletion_tag_success=Tagi on poistettu.
release.tag_name_invalid=Tagin nimi ei ole kelvollinen.
release.downloads=Lataukset
branch.name=Haaran nimi
branch.delete_head=Poista
branch.delete=Poista haara '%s'
+branch.create_branch=Luo haara %s
@@ -1003,12 +1375,16 @@ settings.permission=Käyttöoikeudet
settings.visibility=Näkyvyys
settings.visibility.public=Julkinen
settings.visibility.limited=Rajoitettu (näkyvä vain kirjautuneille käyttäjille)
+settings.visibility.limited_shortname=Rajattu
settings.visibility.private=Yksityinen (näkyvä vain organisaation jäsenille)
+settings.visibility.private_shortname=Yksityinen
settings.update_settings=Päivitä asetukset
settings.delete=Poista organisaatio
settings.delete_account=Poista tämä organisaatio
+settings.delete_prompt=Organisaatio poistetaan pysyvästi, ja tätä EI VOI peruuttaa myöhemmin!
settings.confirm_delete_account=Vahvista poisto
+settings.hooks_desc=Lisää webkoukkuja, jotka suoritetaan kaikissa repoissa tässä organisaatiossa.
members.membership_visibility=Jäsenyyden näkyvyys:
@@ -1054,6 +1430,7 @@ users=Käyttäjätilit
organizations=Organisaatiot
repositories=Repot
authentication=Todennuslähteet
+emails=Käyttäjien sähköpostit
config=Asetukset
notices=Järjestelmän ilmoitukset
monitor=Valvonta
@@ -1062,10 +1439,13 @@ last_page=Viimeisin
total=Yhteensä: %d
dashboard.statistic=Yhteenveto
+dashboard.operations=Huoltotoimet
dashboard.system_status=Järjestelmän tila
dashboard.operation_name=Toiminnon nimi
dashboard.operation_switch=Vaihda
dashboard.operation_run=Suorita
+dashboard.delete_inactive_accounts=Poista kaikki aktivoimattomat käyttäjät
+dashboard.delete_repo_archives=Poista kaikki repojen arkistot (ZIP, TAR.GZ, jne..)
dashboard.server_uptime=Palvelimen Uptime
dashboard.current_goroutine=Nykyiset Goroutinet
dashboard.current_memory_usage=Nykyinen muistinkäyttö
@@ -1100,23 +1480,48 @@ users.name=Käyttäjätunnus
users.full_name=Kokonimi
users.activated=Aktivoitu
users.admin=Ylläpito
+users.restricted=Rajoitettu
+users.2fa=2FA
users.repos=Repot
users.created=Luotu
users.last_login=Viimeksi kirjautunut
+users.never_login=Ei koskaan kirjautunut
users.edit=Muokkaa
users.auth_source=Todennuslähde
users.local=Paikallinen
users.password_helper=Jätä salasanakenttä tyhjäksi jos haluat pitää sen muuttamattomana.
users.update_profile_success=Käyttäjän tili on päivitetty.
users.edit_account=Muokkaa käyttäjän tiliä
+users.max_repo_creation_desc=(Aseta -1 käyttääksesi globaalia oletusrajaa.)
+users.is_activated=Käyttäjätili on aktivoitu
+users.prohibit_login=Ota sisäänkirjautuminen pois käytöstä
+users.is_admin=Ylläpitäjä
+users.is_restricted=Rajoitettu tili
+users.allow_git_hook=Voi luoda Git koukkuja
+users.allow_create_organization=Voi luoda organisaatioita
users.update_profile=Päivitä käyttäjän tili
users.delete_account=Poista käyttäjän tili
+users.list_status_filter.menu_text=Suodata
+users.list_status_filter.reset=Tyhjennä
+users.list_status_filter.is_active=Aktiivinen
+users.list_status_filter.not_active=Ei-aktiivinen
+users.list_status_filter.is_admin=Ylläpitäjä
+users.list_status_filter.not_admin=Ei ylläpitäjä
+users.list_status_filter.is_restricted=Rajoitettu
+users.list_status_filter.not_restricted=Ei rajoitettu
+users.list_status_filter.is_prohibit_login=Kirjautuminen estetty
+users.list_status_filter.not_prohibit_login=Kirjautuminen sallittu
+users.list_status_filter.is_2fa_enabled=2FA käytössä
+users.list_status_filter.not_2fa_enabled=2FA ei käytössä
emails.email_manage_panel=Käyttäjien sähköpostien hallinta
emails.primary=Ensisijainen
emails.activated=Aktivoitu
emails.filter_sort.email=Sähköposti
+emails.filter_sort.email_reverse=Sähköposti (käänteinen)
emails.filter_sort.name=Käyttäjänimi
+emails.filter_sort.name_reverse=Käyttäjänimi (käänteinen)
+emails.duplicate_active=Tämä sähköpostiosoite on jo käytössä toisella käyttäjällä.
orgs.org_manage_panel=Organisaatioiden hallinta
orgs.name=Nimi
@@ -1129,11 +1534,12 @@ repos.owner=Omistaja
repos.name=Nimi
repos.private=Yksityinen
repos.watches=Tarkkailijat
-repos.stars=Äänet
+repos.stars=Tähdet
repos.forks=Haarat
repos.issues=Ongelmat
repos.size=Koko
+packages.owner=Omistaja
@@ -1155,12 +1561,14 @@ auths.user_dn=Käyttäjä DN
auths.search_page_size=Sivukoko
auths.filter=Käyttäjäsuodatin
auths.admin_filter=Ylläpitosuodatin
+auths.restricted_filter=Rajoitettu suodatin
auths.smtp_auth=SMTP todennustyyppi
auths.smtphost=SMTP isäntä
auths.smtpport=SMTP portti
auths.allowed_domains=Sallitut verkkotunnukset
auths.skip_tls_verify=Ohita TLS tarkistaminen
auths.pam_service_name=PAM palvelun nimi
+auths.oauth2_tokenURL=Pääsymerkki URL
auths.enable_auto_register=Ota käyttöön automaattinen rekisteröinti
auths.tips=Vinkit
auths.tips.oauth2.general=OAuth2-autentikointi
@@ -1210,6 +1618,7 @@ config.show_registration_button=Näytä rekisteröidy painike
config.disable_key_size_check=Poista käytöstä avaimen vähimmäiskoko tarkistus
config.enable_captcha=Ota CAPTCHA käyttöön
config.active_code_lives=Aktiivinen koodi elämät ennen vanhenemista
+config.default_keep_email_private=Piilota sähköpostiosoitteet oletuksena
config.default_visibility_organization=Uuden organisaation oletusnäkyvyys
config.webhook_config=Webkoukku asetukset
@@ -1217,9 +1626,7 @@ config.queue_length=Jonon pituus
config.deliver_timeout=Toimitus aikakatkaisu
config.mailer_enabled=Käytössä
-config.mailer_disable_helo=Poista käytöstä HELO
config.mailer_name=Nimi
-config.mailer_host=Isäntä
config.mailer_user=Käyttäjä
config.oauth_config=OAuth asetukset
@@ -1253,6 +1660,7 @@ config.log_mode=Loki tila
config.disabled_logger=Pois käytöstä
config.access_log_template=Malli
+
monitor.cron=Cron tehtävät
monitor.name=Nimi
monitor.schedule=Aikataulu
@@ -1266,6 +1674,7 @@ monitor.queues=Jonot
monitor.queue=Jono: %s
monitor.queue.name=Nimi
monitor.queue.type=Tyyppi
+monitor.queue.pool.addworkers.desc=Lisää käsittelijöitä tähän pooliin aikakatkaisulla tai ilman. Jos asetat aikakatkaisun, nämä käsittelijät poistetaan poolista kun aikakatkaisu on päättynyt.
@@ -1285,6 +1694,10 @@ notices.op=Toiminta
create_repo=luotu repo %s
rename_repo=uudelleennimetty repo %[1]s
nimelle %[3]s
transfer_repo=siirretty repo %s
kohteeseen %s
+push_tag=työnsi tagin %[3]s kohteeseen %[4]s
+delete_tag=poisti tagin %[2]s kohteesta %[3]s
+compare_commits_general=Vertaa committeja
+create_branch=loi haaran %[3]s repossa %[4]s
[tool]
ago=%s sitten
@@ -1324,8 +1737,29 @@ mark_as_unread=Merkitse lukemattomaksi
mark_all_as_read=Merkitse kaikki luetuiksi
[gpg]
+error.no_committer_account=Committaajan sähköpostiosoitteeseen ei ole linkitetty tiliä
+error.not_signed_commit=Ei allekirjoitettu committi
[units]
[packages]
+title=Paketit
+desc=Hallitse repon paketteja.
+empty=Täällä ei vielä ole paketteja.
+empty.documentation=Lisätietoa pakettirekisteristä löydät dokumentaatiosta.
+filter.type=Tyyppi
+filter.type.all=Kaikki
+filter.no_result=Suodattimesi ei tuottanut tuloksia.
+installation=Asennus
+details.author=Tekijä
+composer.documentation=Lisätietoa Composer-rekisteristä löydät dokumentaatiosta.
+conan.documentation=Lisätietoa Conan-rekisteristä löydät dokumentaatiosta.
+container.documentation=Lisätietoa Container-rekisteristä löydätdokumentaatiosta.
+generic.documentation=Lisätietoa yleisestä pakettirekisteristä löydät dokumentaatiosta.
+helm.documentation=Lisätietoa Helm-rekisteristä löydät dokumentaatiosta.
+maven.documentation=Lisätietoa Maven-rekisteristä löydät dokumentaatiosta.
+nuget.documentation=Lisätietoa NuGet-rekisteristä löydät dokumentaatiosta.
+npm.documentation=Lisätietoa npm-rekisteristä löydät dokumentaatiosta.
+pypi.documentation=Lisätietoa PyPI-rekisteristä löydät dokumentaatiosta.
+rubygems.documentation=Lisätietoa RubyGems-rekisteristä löydät dokumentaatiosta.
diff --git a/options/locale/locale_fr-FR.ini b/options/locale/locale_fr-FR.ini
index c1f7f6e0b2..600f43049f 100644
--- a/options/locale/locale_fr-FR.ini
+++ b/options/locale/locale_fr-FR.ini
@@ -2,13 +2,13 @@ home=Accueil
dashboard=Tableau de bord
explore=Explorateur
help=Aide
+logo=Logo
sign_in=Connexion
sign_in_with=Se connecter avec
sign_out=Déconnexion
sign_up=S'inscrire
link_account=Lier un Compte
register=S'inscrire
-website=Site web
version=Version
powered_by=Propulsé par %s
page=Page
@@ -35,6 +35,18 @@ twofa_scratch=Code de secours pour l'authentification à deux facteurs
passcode=Code d'accès
webauthn_insert_key=Insérez votre clé de sécurité
+webauthn_sign_in=Appuyez sur le bouton de votre clé de sécurité. Si votre clé de sécurité n'a pas de bouton, réinsérez-la.
+webauthn_press_button=Veuillez appuyer sur le bouton de votre clé de sécurité…
+webauthn_use_twofa=Utilisez l'authentification à deux facteurs avec votre téléphone
+webauthn_error=Impossible de lire votre clé de sécurité.
+webauthn_unsupported_browser=Votre navigateur ne prend actuellement pas en charge WebAuthn.
+webauthn_error_unknown=Une erreur indéterminée s'est produite. Veuillez réessayer.
+webauthn_error_insecure=WebAuthn ne prend en charge que les connexions sécurisées. Pour les tests via HTTP, vous pouvez utiliser l'origine "localhost" ou "127.0.0.1"
+webauthn_error_unable_to_process=Le serveur n'a pas pu traiter votre demande.
+webauthn_error_duplicated=La clé de sécurité n'est pas autorisée pour cette demande. Veuillez vous assurer que la clé n'est pas déjà enregistrée.
+webauthn_error_empty=Vous devez définir un nom pour cette clé.
+webauthn_error_timeout=Le délai d'attente imparti a été atteint avant que votre clé ne puisse être lue. Veuillez recharger la page pour réessayer.
+webauthn_reload=Recharger
repository=Dépôt
organization=Organisation
@@ -95,7 +107,12 @@ never=Jamais
rss_feed=Flux RSS
[error]
+occurred=Une erreur s’est produite
+report_message=Si vous êtes sûr qu'il s'agit d'un bug de Gitea, cherchez s’il existe des tickets sur GitHub ou ouvrez-en un nouveau si nécessaire.
missing_csrf=Requête incorrecte: aucun jeton CSRF présent
+invalid_csrf=Requête incorrecte : jeton CSRF invalide
+not_found=La cible n'a pu être trouvée.
+network_error=Erreur réseau
[startpage]
app_desc=Un service Git auto-hébergé sans prise de tête
@@ -156,12 +173,13 @@ http_port=Port d'écoute HTTP de Gitea
http_port_helper=Port sur lequel le serveur web Gitea attendra des requêtes.
app_url=URL de base de Gitea
app_url_helper=Adresse HTTP(S) de base pour les clones git et les notifications par e-mail.
-log_root_path=Chemin des fichiers log
+log_root_path=Chemin des journaux
log_root_path_helper=Les fichiers de journalisation seront écrits dans ce répertoire.
optional_title=Paramètres facultatifs
email_title=Paramètres E-mail
-smtp_host=Hôte SMTP
+smtp_addr=Hôte SMTP
+smtp_port=Port SMTP
smtp_from=Envoyer les e-mails en tant que
smtp_from_helper=Adresse e-mail utilisée par Gitea. Veuillez entrer votre e-mail directement ou sous la forme .
mailer_user=Utilisateur SMTP
@@ -183,7 +201,7 @@ openid_signin_popup=Activer l'authentification via OpenID.
openid_signup=Activer l'inscription OpenID
openid_signup_popup=Activer l'inscription avec OpenID.
enable_captcha=Activer le CAPTCHA d'inscription
-enable_captcha_popup=Demander un Captcha à l'inscription.
+enable_captcha_popup=Demander un CAPTCHA à l'inscription.
require_sign_in_view=Exiger la connexion à un compte pour afficher les pages
require_sign_in_view_popup=Limiter l'accès aux pages aux utilisateurs connectés. Les visiteurs ne verront que les pages de connexion et d'inscription.
admin_setting_desc=La création d'un compte administrateur est facultative. Le premier utilisateur enregistré deviendra automatiquement un administrateur le cas échéant.
@@ -250,14 +268,21 @@ users=Utilisateurs
organizations=Organisations
search=Rechercher
code=Code
+search.type.tooltip=Type de recherche
search.fuzzy=Approximative
+search.fuzzy.tooltip=Inclure également les résultats proches de la recherche
search.match=Exacte
+search.match.tooltip=Inclure uniquement les résultats exacts
+code_search_unavailable=Actuellement, la recherche de code n'est pas disponible. Veuillez contacter l'administrateur de votre site.
repo_no_results=Aucun dépôt correspondant n'a été trouvé.
user_no_results=Aucun utilisateur correspondant n'a été trouvé.
org_no_results=Aucune organisation correspondante n'a été trouvée.
code_no_results=Aucun code source correspondant à votre terme de recherche n'a été trouvé.
code_search_results=Résultats de recherche pour "%s"
code_last_indexed_at=Dernière indexation %s
+relevant_repositories_tooltip=Les dépôts qui sont des forks ou qui n'ont aucun sujet, aucune icône et aucune description sont cachés.
+relevant_repositories=Seuls les dépôts pertinents sont affichés, afficher les résultats non filtrés.
+
[auth]
create_new_account=Créer un compte
@@ -265,6 +290,7 @@ register_helper_msg=Déjà enregistré ? Connectez-vous !
social_register_helper_msg=Déjà inscrit ? Connectez-vous !
disable_register_prompt=Les inscriptions sont désactivées. Veuillez contacter l'administrateur du site.
disable_register_mail=La confirmation par e-mail à l'inscription est désactivée.
+manual_activation_only=Contactez l'administrateur de votre site pour terminer l'activation.
remember_me=Mémoriser cet appareil
forgot_password_title=Mot de passe oublié
forgot_password=Mot de passe oublié ?
@@ -303,6 +329,9 @@ oauth_signup_submit=Finaliser la création du compte
oauth_signin_tab=Lier à un compte existant
oauth_signin_title=Connectez-vous pour autoriser le compte lié
oauth_signin_submit=Lier un compte
+oauth.signin.error=Une erreur s'est produite lors du traitement de la demande d'autorisation. Si cette erreur persiste, veuillez contacter l'administrateur du site.
+oauth.signin.error.access_denied=La demande d'autorisation a été refusée.
+oauth.signin.error.temporarily_unavailable=L'autorisation a échoué car le serveur d'authentification est temporairement indisponible. Veuillez réessayer plus tard.
openid_connect_submit=Se connecter
openid_connect_title=Se connecter à un compte existant
openid_connect_desc=L'URI OpenID choisie est inconnue. Associez-le à un nouveau compte ici.
@@ -349,14 +378,22 @@ reset_password.text=Veuillez cliquer sur le lien suivant pour récupérer votre
register_success=Inscription réussie
+issue_assigned.pull=@%[1]s vous a assigné à la demande d’ajout %[2]s dans le dépôt %[3]s.
issue_assigned.issue=@%[1]s vous a assigné le ticket %[2]s dans le dépôt %[3]s.
issue.x_mentioned_you=@%s vous a mentionné:
+issue.action.force_push=%[1]s a forcé la mise à jour de %[2]s depuis %[3]s vers %[4]s.
+issue.action.push_1=@%[1]s a mis à jour le commit %[3]d vers %[2]s
+issue.action.push_n=@%[1]s a poussé les révisions %[3]d vers %[2]s
+issue.action.close=@%[1]s a fermé #%[2]d.
+issue.action.reopen=@%[1]s a réouvert #%[2]d.
+issue.action.merge=@%[1]s a fusionné de #%[2]d vers %[3]s.
issue.action.approve=@%[1]s a approuvé cette demande d'ajout.
issue.action.reject=@%[1]s a demandé des modifications sur cette demande d'ajout.
issue.action.review=@%[1]s a commenté sur cette demande d'ajout.
issue.action.review_dismissed=@%[1]s a rejeté la dernière révision de %[2]s pour cette demande d'ajout.
issue.action.ready_for_review=@%[1]s a marqué cette demande d'ajout prête à être revue.
+issue.action.new=@%[1]s a créé #%[2]d.
issue.in_tree_path=Dans %s:
release.new.subject=%s publiée dans %s
@@ -411,8 +448,10 @@ size_error=` doit être à la taille de %s.`
min_size_error=` %s caractères minimum `
max_size_error=` %s caractères maximum `
email_error=` adresse e-mail invalide `
+url_error=`'%s' n'est pas une URL valide.`
include_error=`doit contenir la sous-chaîne '%s'.`
glob_pattern_error=` le motif de développement est invalide : %s.`
+regex_pattern_error=` le motif regex est invalide : %s.`
unknown_error=Erreur inconnue :
captcha_incorrect=Le code CAPTCHA est incorrect.
password_not_match=Les mots de passe ne correspondent pas.
@@ -421,6 +460,7 @@ lang_select_error=Sélectionnez une langue dans la liste.
username_been_taken=Le nom d'utilisateur est déjà pris.
username_change_not_local_user=Les utilisateurs non-locaux n'ont pas le droit de modifier leur nom d'utilisateur.
repo_name_been_taken=Ce nom de dépôt est déjà utilisé.
+repository_force_private=Force Private est activé : les dépôts privés ne peuvent pas être rendus publics.
repository_files_already_exist=Les fichiers existent déjà pour ce dépôt. Contactez l'administrateur système.
repository_files_already_exist.adopt=Des fichiers existent déjà pour ce dépôt et peuvent seulement être adoptés.
repository_files_already_exist.delete=Des fichiers existent déjà pour ce dépôt. Vous devez les supprimer.
@@ -456,7 +496,9 @@ auth_failed=Échec d'authentification : %v
still_own_repo=Ce compte possède toujours un ou plusieurs dépôts, vous devez d'abord les supprimer ou les transférer.
still_has_org=Votre compte est un membre d’une ou plusieurs organisations, veuillez d'abord les quitter.
+still_own_packages=Votre compte possède toujours un ou plusieurs paquets, vous devez d'abord les supprimer ou les transférer.
org_still_own_repo=Cette organisation possède encore un ou plusieurs dépôts. Vous devez d'abord les supprimer ou les transférer.
+org_still_own_packages=Cette organisation possède encore un ou plusieurs paquets. Vous devez d'abord les supprimer.
target_branch_not_exist=La branche cible n'existe pas.
@@ -468,6 +510,7 @@ activity=Activité publique
followers=abonnés
starred=Dépôts favoris
watched=Dépôts surveillés
+code=Code
projects=Projets
following=Abonnements
follow=Suivre
@@ -497,6 +540,7 @@ twofa=Authentification à deux facteurs
account_link=Comptes associés
organization=Organisations
uid=ID d'Utilisateur
+webauthn=Clés de sécurité
public_profile=Profil public
biography_placeholder=Parlez-nous un peu de vous
@@ -518,6 +562,7 @@ continue=Continuer
cancel=Annuler
language=Langues
ui=Thème
+hidden_comment_types=Texte de commentaires caché
comment_type_group_reference=Référence
comment_type_group_label=Étiquette
comment_type_group_milestone=Jalon
@@ -607,7 +652,12 @@ ssh_key_been_used=Cette clef SSH a déjà été ajoutée au serveur.
ssh_key_name_used=Une clé SSH avec le même nom existe déjà sur votre compte.
ssh_principal_been_used=Ce principal a déjà été ajouté au serveur.
gpg_key_id_used=Une clef GPG publique avec le même identifiant existe déjà.
+gpg_no_key_email_found=Cette clé GPG ne correspond à aucune adresse e-mail activée et associée avec votre compte. Elle peut toujours être ajoutée si vous signez le jeton fourni.
+gpg_key_matched_identities=Identités correspondantes :
+gpg_key_matched_identities_long=Les identités embarquées dans cette clé correspondent à l'adresse courriel activée ci-après pour cet utilisateur. Les révisions correspondantes à cette adresse courriel peuvent être vérifiés avec cette clé.
gpg_key_verified=Clé vérifiée
+gpg_key_verified_long=La clé a été vérifiée avec un jeton et peut être utilisée pour vérifier les commits correspondant à toutes les adresses courriel pour cet utilisateur en plus de toutes les identités pour cette clé.
+gpg_key_verify=Vérifier
gpg_invalid_token_signature=La clé GPG fournie, la signature et le jeton ne correspondent pas ou le jeton n'est pas à jour.
gpg_token_required=Vous devez fournir une signature pour le jeton ci-dessous
gpg_token=Jeton
@@ -615,10 +665,17 @@ gpg_token_help=Vous pouvez générer une signature en utilisant :
gpg_token_code=echo "%s" | gpg -a --default-key %s --detach-sig
gpg_token_signature=Signature GPG renforcée
key_signature_gpg_placeholder=Commence par '-----BEGIN PGP SIGNATURE-----'
+verify_gpg_key_success=La clef GPG '%s' a été vérifiée.
+ssh_key_verified=Clé vérifiée
+ssh_key_verified_long=La clé a été vérifiée avec un jeton et peut être utilisée pour vérifier les commits correspondant à toutes les adresses courriel activées pour cet utilisateur.
+ssh_key_verify=Vérifier
+ssh_invalid_token_signature=La clé SSH, la signature ou le jeton fournis ne correspondent pas ou le jeton n'est pas à jour.
ssh_token_required=Vous devez fournir une signature pour le jeton ci-dessous
ssh_token=Jeton
ssh_token_help=Vous pouvez générer une signature en utilisant :
ssh_token_signature=Signature SSH renforcée
+key_signature_ssh_placeholder=Commence par '-----BEGIN PGP SIGNATURE-----'
+verify_ssh_key_success=La clef SSH '%s' a été vérifiée.
subkeys=Sous-clés
key_id=Clé ID
key_name=Nom de la Clé
@@ -666,6 +723,9 @@ generate_token_success=Votre nouveau jeton a été généré. Copiez-le maintena
generate_token_name_duplicate=%s a déjà été utilisé comme nom d'application. Veuillez en utiliser un nouveau.
delete_token=Supprimer
access_token_deletion=Suppression de jetons d'accès
+access_token_deletion_cancel_action=Annuler
+access_token_deletion_confirm_action=Supprimer
+access_token_deletion_desc=Supprimer un jeton révoquera l'accès à votre compte pour toutes les applications l'utilisant. Cette action est irréversible. Continuer ?
delete_token_success=Ce jeton a été supprimé. Les applications l'utilisant n'ont plus accès à votre compte.
manage_oauth2_applications=Gérer les applications OAuth2
@@ -718,10 +778,16 @@ passcode_invalid=Le mot de passe est invalide. Réessayez.
twofa_enrolled=L'authentification à deux facteurs a été activée pour votre compte. Gardez votre jeton de secours (%s) en lieu sûr car il ne vous sera montré qu'une seule fois !
twofa_failed_get_secret=Impossible d'obtenir le secret.
+webauthn_desc=Les clefs de sécurité sont des dispositifs matériels contenant des clefs cryptographiques. Elles peuvent être utilisées pour l'authentification à deux facteurs. La clef de sécurité doit supporter le standard WebAuthn Authenticator.
+webauthn_register_key=Ajouter une clé de sécurité
+webauthn_nickname=Pseudonyme
+webauthn_delete_key=Supprimer la clé de sécurité
+webauthn_delete_key_desc=Si vous retirez une clé de sécurité vous ne pourrez plus l'utiliser pour vous connecter. Continuer ?
manage_account_links=Gérer les comptes liés
manage_account_links_desc=Ces comptes externes sont liés à votre compte Gitea.
account_links_not_available=Il n'y a pour l'instant pas de compte externe connecté à votre compte Gitea.
+link_account=Lier un Compte
remove_account_link=Supprimer un compte lié
remove_account_link_desc=Supprimer un compte lié révoquera son accès à votre compte Gitea. Continuer ?
remove_account_link_success=Le compte lié a été supprimé.
@@ -740,7 +806,9 @@ email_notifications.enable=Activer les notifications par e-mail
email_notifications.onmention=N'envoyer un e-mail que si vous êtes mentionné
email_notifications.disable=Désactiver les notifications par email
email_notifications.submit=Définir la préférence e-mail
+email_notifications.andyourown=Et vos propres notifications
+visibility=Visibilité de l'utilisateur
visibility.public=Publique
visibility.public_tooltip=Visible par tous les utilisateurs
visibility.limited=Limitée
@@ -767,6 +835,8 @@ visibility_fork_helper=(Changer ceci affectera toutes les bifurcations.)
clone_helper=Besoin d'aide pour dupliquer ? Visitez l'aide.
fork_repo=Créer une bifurcation du dépôt
fork_from=Bifurquer depuis
+already_forked=Vous avez déjà forké %s
+fork_to_different_account=Créer un embranchement vers un autre compte
fork_visibility_helper=La visibilité d'un dépôt bifurqué ne peut pas être modifiée.
use_template=Utiliser ce modèle
clone_in_vsc=Cloner dans VS Code
@@ -799,7 +869,9 @@ default_branch=Branche par défaut
default_branch_helper=La branche par défaut est la branche de base pour les demandes d'ajout et les révisions de code.
mirror_prune=Purger
mirror_prune_desc=Supprimer les références externes obsolètes
+mirror_interval=Intervalle de synchronisation (les unités de temps valides sont 'h', 'm' et 's'). 0 pour désactiver la synchronisation automatique. (Intervalle minimum : %s)
mirror_interval_invalid=L'intervalle de synchronisation est invalide.
+mirror_sync_on_commit=Synchroniser quand les commits sont poussés
mirror_address=Cloner depuis une URL
mirror_address_desc=Insérez tous les identifiants requis dans la section Autorisation.
mirror_address_url_invalid=L'url fournie est invalide. Vous devez échapper tous les composants de l'url correctement.
@@ -848,6 +920,7 @@ desc.archived=Archivé
template.items=Élément du modèle
template.git_content=Contenu Git (branche par défaut)
template.git_hooks=Déclencheurs Git
+template.git_hooks_tooltip=Vous ne pouvez actuellement pas modifier ou supprimer les déclencheurs Git ajoutés. Sélectionnez cette option uniquement si vous faites confiance au modèle de dépôt.
template.webhooks=Déclencheurs Web
template.topics=Sujets
template.avatar=Avatar
@@ -867,6 +940,7 @@ form.name_pattern_not_allowed="%s" n'est pas autorisé dans un nom de dépôt.
need_auth=Autorisation
migrate_options=Options de migration
migrate_service=Service de migration
+migrate_options_mirror_helper=Ce dépôt sera un miroir
migrate_options_lfs=Migrer les fichiers LFS
migrate_options_lfs_endpoint.label=Point d'accès LFS
migrate_options_lfs_endpoint.description=La migration va tenter d'utiliser votre dépôt Git distant pour déterminer le serveur LFS. Vous pouvez également spécifier un point d'accès personnalisé si les données LFS du dépôt sont stockées ailleurs.
@@ -883,8 +957,10 @@ migrate_items_releases=Versions
migrate_repo=Migrer le dépôt
migrate.clone_address=Migrer/Cloner depuis une URL
migrate.clone_address_desc=L'URL HTTP(S) ou Git "clone" d'un dépôt existant
+migrate.github_token_desc=Vous pouvez mettre un ou plusieurs jetons séparés par des virgules ici pour rendre la migration plus rapide en raison de la limite de débit de l'API GitHub. ATTENTION : Abuser de cette fonctionnalité peut enfreindre la politique du fournisseur de services et entraîner un blocage de compte.
migrate.clone_local_path=ou un chemin serveur local
migrate.permission_denied=Vous n'êtes pas autorisé à importer des dépôts locaux.
+migrate.permission_denied_blocked=Vous ne pouvez pas importer depuis des hôtes interdits, veuillez demander à l'administrateur de vérifier les paramètres ALLOWED_DOMAINS/ALLOW_LOCALNETWORKS/BLOCKED_DOMAINS.
migrate.invalid_local_path=Chemin local non valide, non existant ou n'étant pas un dossier.
migrate.invalid_lfs_endpoint=Le point d'accès LFS n'est pas valide.
migrate.failed=Echec de migration: %v
@@ -895,11 +971,15 @@ migrate.migrate=Migrer depuis %s
migrate.migrating=Migration de %s ...
migrate.migrating_failed=La migration de %s a échoué.
migrate.migrating_failed.error=Erreur: %s
+migrate.migrating_failed_no_addr=Échec de la migration.
+migrate.github.description=Migrer les données depuis github.com ou d’autres instances de GitHub.
migrate.git.description=Migrer uniquement un dépôt depuis n’importe quel service Git.
migrate.gitlab.description=Migrer les données depuis gitlab.com ou d’autres instances de GitLab.
migrate.gitea.description=Migrer les données depuis gitea.com ou d’autres instances de Gitea.
migrate.gogs.description=Migrer les données depuis notabug.org ou d’autres instances de Gogs.
migrate.onedev.description=Migrer les données depuis code.onedev.io ou d’autre instance de OneDev.
+migrate.codebase.description=Migrer les données depuis codebasehq.com.
+migrate.gitbucket.description=Migrer les données depuis des instances GitBucket.
migrate.migrating_git=Migration des données Git
migrate.migrating_topics=Migration des sujets
migrate.migrating_milestones=Migration des jalons
@@ -928,6 +1008,7 @@ clone_this_repo=Cloner ce dépôt
create_new_repo_command=Création d'un nouveau dépôt en ligne de commande
push_exist_repo=Soumission d'un dépôt existant par ligne de commande
empty_message=Ce dépôt n'a pas de contenu.
+broken_message=Les données git de ce dépôt ne peuvent pas être lues. Contactez l'administrateur de cette instance ou supprimez ce dépôt.
code=Code
code.desc=Accéder au code source, fichiers, révisions et branches.
@@ -941,6 +1022,7 @@ tags=Tags
issues=Tickets
pulls=Demandes d'ajout
project_board=Projets
+packages=Paquets
labels=Étiquettes
org_labels_desc=Les étiquettes de niveau d'une organisation peuvent être utilisés avec tous les dépôts de cette organisation
org_labels_desc_manage=gérer
@@ -960,7 +1042,15 @@ file_view_rendered=Voir le rendu
file_view_raw=Voir le Raw
file_permalink=Lien permanent
file_too_large=Le fichier est trop gros pour être affiché.
+invisible_runes_header=`Ce fichier contient des caractères Unicode invisibles !`
+invisible_runes_description=`Ce fichier contient des caractères Unicode invisibles qui pourraient être affichés différemment de ce qui apparaît ci-dessous. Si votre cas d'utilisation est intentionnel et légitime, vous pouvez ignorer en toute sécurité cet avertissement. Utilisez le bouton Échapper pour mettre en évidence ces caractères invisbles.`
+ambiguous_runes_header=`Ce fichier contient des caractères Unicode ambigus !`
+invisible_runes_line=`Cette ligne contient des caractères Unicode invisibles`
+ambiguous_runes_line=`Cette ligne contient des caractères Unicode ambigus`
+ambiguous_character=`%[1]c [U+%04[1]X] peut être confondu avec %[2]c [U+%04[2]X]`
+escape_control_characters=Échapper
+unescape_control_characters=Annuler l'échappement
file_copy_permalink=Copier le lien permanent
video_not_supported_in_browser=Votre navigateur ne supporte pas le tag HTML5 "video".
audio_not_supported_in_browser=Votre navigateur ne supporte pas la balise « audio » HTML5.
@@ -977,6 +1067,7 @@ normal_view=Vue normale
line=ligne
lines=lignes
+editor.add_file=Ajouter un fichier
editor.new_file=Nouveau fichier
editor.upload_file=Téléverser un fichier
editor.edit_file=Modifier le fichier
@@ -1000,6 +1091,10 @@ editor.add_tmpl=Ajouter ''
editor.add=Ajouter '%s'
editor.update=Mise à jour de '%s'
editor.delete=Supprimer '%s'
+editor.patch=Appliquer le correctif
+editor.patching=Correction:
+editor.fail_to_apply_patch=Impossible d'appliquer le correctif '%s'
+editor.new_patch=Nouveau correctif
editor.commit_message_desc=Ajouter une description détaillée facultative…
editor.signoff_desc=Ajout d'un trailer Signed-off-by par le committeur à la fin du message du journal de commit.
editor.commit_directly_to_this_branch=Soumettre directement dans la branche %s.
@@ -1024,6 +1119,8 @@ editor.commit_empty_file_text=Le fichier que vous allez commiter est vide. Conti
editor.no_changes_to_show=Il n’y a aucun changement à afficher.
editor.fail_to_update_file=Impossible de mettre à jour/créer le fichier '%s'.
editor.fail_to_update_file_summary=Message d'erreur :
+editor.push_rejected_no_message=La modification a été rejetée par le serveur sans message. Veuillez vérifier les Git Hooks.
+editor.push_rejected=La modification a été rejetée par le serveur. Veuillez vérifier vos Git Hooks.
editor.push_rejected_summary=Message de rejet complet :
editor.add_subdir=Ajouter un dossier…
editor.unable_to_upload_files=Échec lors de l'envoie du fichier '%s' avec l’erreur : %v
@@ -1033,6 +1130,8 @@ editor.cannot_commit_to_protected_branch=Impossible de créer une révision sur
editor.no_commit_to_branch=Impossible d'enregistrer la révisions directement sur la branche parce que :
editor.user_no_push_to_branch=L'utilisateur ne peut pas pousser vers la branche
editor.require_signed_commit=Cette branche nécessite une révision signée
+editor.cherry_pick=Picorer %s vers:
+editor.revert=Rétablir %s sur:
commits.desc=Naviguer dans l'historique des modifications.
commits.commits=Révisions
@@ -1051,7 +1150,15 @@ commits.signed_by=Signé par
commits.signed_by_untrusted_user=Signé par un utilisateur non approuvé
commits.signed_by_untrusted_user_unmatched=Signé par un utilisateur non fiable qui ne correspond pas au validateur
commits.gpg_key_id=ID de la clé GPG
+commits.ssh_key_fingerprint=Empreinte numérique de la clé SSH
+commit.actions=Actions
+commit.revert=Rétablir
+commit.revert-header=Rétablir : %s
+commit.revert-content=Sélectionnez la branche sur laquelle revenir :
+commit.cherry-pick=Picorer
+commit.cherry-pick-header=Picorer : %s
+commit.cherry-pick-content=Sélectionner la branche à picorer :
ext_issues.desc=Lien vers un gestionnaire de tickets externe.
@@ -1119,12 +1226,12 @@ issues.new.assignees=Affecté à
issues.new.add_assignees_title=Assigner des utilisateurs
issues.new.clear_assignees=Supprimer les affectations
issues.new.no_assignees=Pas d'assignataires
-issues.new.no_reviewers=Pas de relecteur
+issues.new.no_reviewers=Aucune évaluation
issues.new.add_reviewer_title=Demander une revue
issues.choose.get_started=Démarrons
issues.choose.blank=Par défaut
issues.choose.blank_about=Créer un ticket à partir du modèle par défaut.
-issues.no_ref=Aucune branche/tag spécifiés
+issues.no_ref=Aucune branche/étiquette spécifiées
issues.create=Créer un ticket
issues.new_label=Nouvelle étiquette
issues.new_label_placeholder=Nom de l'étiquette
@@ -1161,6 +1268,8 @@ issues.filter_milestone=Jalon
issues.filter_milestone_no_select=Tous les jalons
issues.filter_assignee=Assigné
issues.filter_assginee_no_select=Toutes les affectations
+issues.filter_poster=Auteur
+issues.filter_poster_no_select=Tous les auteurs
issues.filter_type=Type
issues.filter_type.all_issues=Tous les tickets
issues.filter_type.assigned_to_you=Qui vous sont assignés
@@ -1219,7 +1328,7 @@ issues.ref_reopening_from=`a référencé une pull request %[4]s
issues.ref_closed_from=`a fermé ce ticket %[4]s %[2]s`
issues.ref_reopened_from=`a réouvert ce ticket %[4]s %[2]s`
issues.ref_from=`de %[1]s`
-issues.poster=Publier
+issues.poster=Éditeur
issues.collaborator=Collaborateur
issues.owner=Propriétaire
issues.re_request_review=Redemander la revue
@@ -1371,6 +1480,7 @@ compare.compare_head=comparer
pulls.desc=Activer les demandes de fusion et la revue de code.
pulls.new=Nouvelle demande d'ajout
+pulls.view=Voir la demande d'ajout
pulls.compare_changes=Nouvelle demande de fusion
pulls.compare_changes_desc=Sélectionnez la branche dans laquelle fusionner et la branche depuis laquelle tirer les modifications.
pulls.compare_base=fusionner dans
@@ -1380,6 +1490,7 @@ pulls.filter_branch=Filtre de branche
pulls.no_results=Aucun résultat trouvé.
pulls.nothing_to_compare=Ces branches sont identiques. Il n'y a pas besoin de créer une demande de fusion.
pulls.nothing_to_compare_and_allow_empty_pr=Ces branches sont égales. Cette demande d'ajout sera vide.
+pulls.has_pull_request='Il existe déjà une demande d'ajout entre ces deux branches : %[2]s#%[3]d'
pulls.create=Créer une demande d'ajout
pulls.title_desc=veut fusionner %[1]d révision(s) depuis %[2]s
vers %[3]s
pulls.merged_title_desc=a fusionné %[1]d révision(s) à partir de %[2]s
vers %[3]s
%[4]s
@@ -1430,6 +1541,10 @@ pulls.no_merge_helper=Activez des options de fusion dans les paramètres du dép
pulls.no_merge_wip=Cette demande d'ajout ne peut pas être fusionnée car elle est marquée comme en cours de chantier.
pulls.no_merge_not_ready=Cette demande d'ajout n'est pas prête à être fusionnée, vérifiez l'état de la revue et les vérifications.
pulls.no_merge_access=Vous n'êtes pas autorisé⋅e à fusionner cette demande d'ajout.
+pulls.merge_pull_request=Créer une révision de fusion
+pulls.rebase_merge_pull_request=Rebaser puis avancer rapidement
+pulls.rebase_merge_commit_pull_request=Rebaser puis créer une révision de fusion
+pulls.squash_merge_pull_request=Créer une révision de concaténation
pulls.merge_manually=Fusionné manuellement
pulls.merge_commit_id=L'ID de la révision de fusion
pulls.require_signed_wont_sign=La branche nécessite des révisions signées mais cette fusion ne sera pas signée
@@ -1443,6 +1558,7 @@ pulls.rebase_conflict_summary=Message d'erreur
pulls.unrelated_histories=Échec de la fusion: La tête de fusion et la base ne partagent pas d'historique commun. Indice : Essayez une stratégie différente
pulls.merge_out_of_date=Échec de la fusion: La base a été mise à jour en cours de fusion. Indice : Réessayez.
pulls.push_rejected_summary=Message de rejet complet
+pulls.push_rejected_no_message=Échec de la fusion : La poussée a été rejetée mais il n'y avait pas de message distant.
Revoyez les Git Hooks pour ce dépot
pulls.open_unmerged_pull_exists=`Vous ne pouvez pas ré-ouvrir cette demande de fusion car il y a une demande de fusion (#%d) en attente avec des propriétés identiques.`
pulls.status_checking=Certains contrôles sont en attente
pulls.status_checks_success=Tous les contrôles ont réussi
@@ -1460,9 +1576,17 @@ pulls.merge_instruction_hint=`Vous pouvez également voir guide des Webhooks.
settings.webhook_deletion=Retirer le Webhook
settings.webhook_deletion_desc=Supprimer un webhook supprime ses paramètres et son historique. Continuer ?
settings.webhook_deletion_success=Le webhook a été supprimé.
-settings.webhook.test_delivery=Tester la version
+settings.webhook.test_delivery=Tester l'envoi
settings.webhook.test_delivery_desc=Testez ce webhook avec un faux événement.
settings.webhook.request=Requête
settings.webhook.response=Réponse
@@ -1761,7 +1882,7 @@ settings.add_webhook_desc=Gitea enverra à l'URL cible des requêtes POST<
settings.payload_url=URL cible
settings.http_method=Méthode HTTP
settings.content_type=Type de contenu POST
-settings.secret=Confidentiel
+settings.secret=Secret
settings.slack_username=Nom d'utilisateur
settings.slack_icon_url=URL de l'icône
settings.discord_username=Nom d'utilisateur
@@ -1771,19 +1892,19 @@ settings.event_push_only=Événements de poussée
settings.event_send_everything=Tous les événements
settings.event_choose=Événements personnalisés…
settings.event_header_repository=Événements du dépôt
-settings.event_create=Créer
-settings.event_create_desc=Branche ou Tag créé.
-settings.event_delete=Supprimer
-settings.event_delete_desc=Branche ou étiquette supprimé.
+settings.event_create=Création
+settings.event_create_desc=Branche ou étiquette créé.
+settings.event_delete=Suppression
+settings.event_delete_desc=Branche ou étiquette supprimée.
settings.event_fork=Bifurcation
settings.event_fork_desc=Dépôt bifurqué.
settings.event_release=Version
settings.event_release_desc=Version publiée, mise à jour ou supprimée dans un dépôt.
-settings.event_push=Pousser
+settings.event_push=Poussée
settings.event_push_desc=Git push vers un dépôt.
settings.event_repository=Dépôt
settings.event_repository_desc=Dépôt créé ou supprimé.
-settings.event_header_issue=Événements du ticket
+settings.event_header_issue=Événements des tickets
settings.event_issues=Tickets
settings.event_issues_desc=Ticket ouvert, fermé, ré-ouvert ou modifié.
settings.event_issue_assign=Ticket assigné
@@ -1791,7 +1912,7 @@ settings.event_issue_assign_desc=Ticket assigné ou non assigné.
settings.event_issue_label=Étiquettes des tickets
settings.event_issue_label_desc=Étiquettes de ticket mises à jour ou effacées.
settings.event_issue_milestone=Ticket jalonnée
-settings.event_issue_milestone_desc=Ticket jalonnée ou dé-jalonnée.
+settings.event_issue_milestone_desc=Ticket jalonné ou dé-jalonné.
settings.event_issue_comment=Commentaire du ticket
settings.event_issue_comment_desc=Commentaire du ticket créé, modifié, ou supprimé.
settings.event_header_pull_request=Événements de demande d'ajout
@@ -1997,7 +2118,7 @@ release.new_subheader=Les versions organisent les versions publiées du projet.
release.edit_subheader=Les versions organisent les versions publiées du projet.
release.tag_name=Nom du tag
release.target=Cible
-release.tag_helper=Choisissez un tag existant ou créez un nouveau tag.
+release.tag_helper=Choisissez une étiquette existante ou créez une nouvelle étiquette.
release.title=Titre
release.content=Contenu
release.prerelease_desc=Marquer comme pré-version
@@ -2007,12 +2128,12 @@ release.publish=Publier
release.save_draft=Sauvegarder le Brouillon
release.edit_release=Modifier la version
release.delete_release=Supprimer cette version
-release.delete_tag=Supprimer le tag
+release.delete_tag=Supprimer l'étiquette
release.deletion=Supprimer cette version
release.deletion_success=Cette livraison a été supprimée.
release.deletion_tag_success=L'étiquette a été supprimée.
-release.tag_name_already_exist=Une version avec ce nom de balise existe déjà.
-release.tag_name_invalid=Le nom de balise est invalide.
+release.tag_name_already_exist=Une version avec ce nom d'étiquette existe déjà.
+release.tag_name_invalid=Le nom de l'étiquette est invalide.
release.tag_already_exist=Ce nom d'étiquette existe déjà.
release.downloads=Téléchargements
release.download_count=Télécharger: %s
@@ -2032,7 +2153,7 @@ branch.create_from=de '%s'
branch.create_success=La branche "%s" a été crée.
branch.branch_already_exists=La branche '%s' existe déjà dans ce dépôt.
branch.branch_name_conflict=Le nom de la branche « %s » est en conflit avec une branche existante « %s ».
-branch.tag_collision=La branche '%s' ne peut être créée comme une balise car un nom identique existe déjà dans le dépôt.
+branch.tag_collision=La branche '%s' ne peut être créée comme une étiquette avec un nom identique existe déjà dans le dépôt.
branch.deleted_by=Supprimée par %s
branch.restore_success=La branche "%s" a été restaurée.
branch.restore_failed=La restauration de la branche '%s' a échoué.
@@ -2107,7 +2228,7 @@ settings.delete_prompt=Cette organisation sera supprimée définitivement. Cette
settings.confirm_delete_account=Confirmez la suppression
settings.delete_org_title=Supprimer l'organisation
settings.delete_org_desc=Cette organisation sera supprimée définitivement. Voulez-vous continuer ?
-settings.hooks_desc=Vous pouvez ajouter des déclencheurs qui seront activés pour tous les dépôts de cette organisation.
+settings.hooks_desc=Vous pouvez ajouter des webhooks qui seront activés pour tous les dépôts de cette organisation.
settings.labels_desc=Ajouter des étiquettes qui peuvent être utilisées sur les tickets pour tous les dépôts dans cette organisation.
@@ -2316,6 +2437,7 @@ repos.forks=Bifurcations
repos.issues=Tickets
repos.size=Taille
+packages.published=Publiés
systemhooks=Rappels système
@@ -2485,11 +2607,8 @@ config.queue_length=Longueur de la file d'attente
config.deliver_timeout=Expiration d'Envoi
config.skip_tls_verify=Passer la vérification TLS
-config.mailer_config=Configuration du service SMTP
config.mailer_enabled=Activé
-config.mailer_disable_helo=Désactiver HELO
config.mailer_name=Nom
-config.mailer_host=Hôte
config.mailer_user=Utilisateur
config.mailer_use_sendmail=Utiliser Sendmail
config.mailer_sendmail_path=Chemin d’accès à Sendmail
@@ -2547,6 +2666,7 @@ config.access_log_template=Modèle
config.xorm_log_mode=Mode de journalisation de XORM
config.xorm_log_sql=Activer la journalisation SQL
+
monitor.cron=Tâches récurrentes
monitor.name=Nom
monitor.schedule=Planification
@@ -2617,9 +2737,9 @@ notices.system_notice_list=Informations
notices.view_detail_header=Voir les détails de l'information système
notices.actions=Actions
notices.select_all=Tout Sélectionner
-notices.deselect_all=Tous déselectionner
+notices.deselect_all=Tout désélectionner
notices.inverse_selection=Inverser la sélection
-notices.delete_selected=Supprimé les éléments sélectionnés
+notices.delete_selected=Supprimer les éléments sélectionnés
notices.delete_all=Supprimer toutes les notifications
notices.type=Type
notices.type_1=Dépôt
@@ -2631,6 +2751,11 @@ notices.delete_success=Les informations systèmes ont été supprimées.
[action]
create_repo=a créé le dépôt %s
rename_repo=a rebaptisé le dépôt de %[1]s
vers %[3]s
+create_pull_request=`a créé la demande d'ajout %[3]s#%[2]s`
+close_pull_request=`a fermé la demande d'ajout %[3]s#%[2]s`
+reopen_pull_request=`a réouvert la demande d'ajout %[3]s#%[2]s`
+comment_pull=`a commenté la demande d'ajout %[3]s#%[2]s`
+merge_pull_request=`a fusionné la demande d'ajout %[3]s#%[2]s`
transfer_repo=a transféré le dépôt %s
à %s
delete_tag=étiquette supprimée %[2]s de %[3]s
delete_branch=branche %[2]s supprimée de %[3]s
@@ -2638,6 +2763,9 @@ compare_branch=Comparer
compare_commits=Comparer %d révisions
compare_commits_general=Comparer les révisions
mirror_sync_delete=a synchronisé puis supprimé la nouvelle référence %[2]s
vers %[3]s depuis le miroir
+approve_pull_request=`a approuvé %[3]s#%[2]s`
+reject_pull_request=`a suggérés des changements pour %[3]s#%[2]s`
+publish_release=`a publié "%[4]s" à %[3]s`
review_dismissed_reason=Raison :
[tool]
@@ -2695,4 +2823,8 @@ error.no_unit_allowed_repo=Vous n'êtes pas autorisé à accéder à n'importe q
error.unit_not_allowed=Vous n'êtes pas autorisé à accéder à cette section du dépôt.
[packages]
+empty.repo=Avez-vous téléchargé un paquet, mais il n'est pas affiché ici? Allez dans les paramètres du paquet et liez le à ce dépôt.
+published_by_in=%[1]s publié par %[3]s en %[5]s
+rubygems.required.ruby=Nécessite la version de Ruby
+rubygems.required.rubygems=Nécessite la version de RubyGem
diff --git a/options/locale/locale_hu-HU.ini b/options/locale/locale_hu-HU.ini
index 43bae7e10c..d3d31f30ef 100644
--- a/options/locale/locale_hu-HU.ini
+++ b/options/locale/locale_hu-HU.ini
@@ -8,7 +8,6 @@ sign_out=Kijelentkezés
sign_up=Regisztrálás
link_account=Fiók kapcsolása
register=Regisztráció
-website=Webhely
version=Verzió
powered_by=Biztosítja: %s
page=Oldal
@@ -135,7 +134,6 @@ log_root_path_helper=A naplófájlok ebbe a mappába fognak íródni.
optional_title=További beállítások
email_title=E-mail beállítások
-smtp_host=SMTP kiszolgáló
smtp_from=E-mail küldése mint
smtp_from_helper=Az E-mail cím a mit a Gitea használni fog. Megadhatja sima email címként vagy "Név" formátumban.
mailer_user=SMTP-felhasználónév
@@ -218,6 +216,7 @@ code_no_results=Nincs találat a keresési kifejezésedre.
code_search_results=Keresési találatok "%s"
code_last_indexed_at=Utoljára indexelve: %s
+
[auth]
create_new_account=Regisztráció
register_helper_msg=Van már felhasználói fiókja? Jelentkezzen be!
@@ -1104,10 +1103,6 @@ settings.basic_settings=Alap beállítások
settings.mirror_settings=Tükrözési beállítások
settings.sync_mirror=Szinkronizálás most
settings.mirror_sync_in_progress=Tükör szinkronizálása folyamatban. Kérem várjon.
-settings.email_notifications.enable=Email értesítések engedélyezése
-settings.email_notifications.onmention=Email küldése csak megjelölés esetén
-settings.email_notifications.disable=Email értesítés kikapcsolása
-settings.email_notifications.submit=E-mail beállítások megadása
settings.site=Weboldal
settings.update_settings=Beállítások frissítése
settings.advanced_settings=Haladó beállítások
@@ -1629,11 +1624,8 @@ config.queue_length=Várakozási Sor Hossza
config.deliver_timeout=Kézbesítési Időtúllépés
config.skip_tls_verify=A TLS Hitelesítés Kihagyása
-config.mailer_config=SMTP levelező Beállítások
config.mailer_enabled=Engedélyezett
-config.mailer_disable_helo=HELO Letiltása
config.mailer_name=Név
-config.mailer_host=Kiszolgáló
config.mailer_user=Felhasználó
config.mailer_use_sendmail=Sendmail Használata
config.mailer_sendmail_path=Sendmail Elérési Útja
@@ -1680,6 +1672,7 @@ config.disabled_logger=Letiltva
config.access_log_template=Sablon
config.xorm_log_sql=SQL naplózása
+
monitor.cron=Ütemezett Feladatok
monitor.name=Név
monitor.schedule=Ütemezés
diff --git a/options/locale/locale_id-ID.ini b/options/locale/locale_id-ID.ini
index 34c7db6b23..53d6006c96 100644
--- a/options/locale/locale_id-ID.ini
+++ b/options/locale/locale_id-ID.ini
@@ -8,7 +8,6 @@ sign_out=Keluar
sign_up=Daftar
link_account=Tautan Akun
register=Daftar
-website=Situs Web
version=Versi
powered_by=Diberdayakan oleh %s
page=Halaman
@@ -131,7 +130,6 @@ log_root_path_helper=Berkas log akan ditulis ke direktori ini.
optional_title=Pengaturan Opsional
email_title=Pengaturan Surel
-smtp_host=Host SMTP
smtp_from=Kirim Surel sebagai
smtp_from_helper=Alamat surel Gitea akan digunakan. Masukkan alamat surel atau gunakan fomat "Nama" .
mailer_user=Nama Pengguna SMTP
@@ -208,6 +206,7 @@ org_no_results=Tidak ada organisasi yang cocok ditemukan.
code_no_results=Tidak ada kode sumber yang cocok dengan istilah yang anda cari.
code_search_results=Hasil pencarian untuk '%s'
+
[auth]
create_new_account=Daftar Akun
register_helper_msg=Sudah memiliki akun? Masuk sekarang!
@@ -1235,11 +1234,8 @@ config.queue_length=Panjang antrian
config.deliver_timeout=Berikan waktu habis
config.skip_tls_verify=Melewatkan verifikasi TLS
-config.mailer_config=Pengaturan SMTP Mailer
config.mailer_enabled=Diaktifkan
-config.mailer_disable_helo=Nonaktifkan HELO
config.mailer_name=Nama
-config.mailer_host=Host
config.mailer_user=Pengguna
config.mailer_use_sendmail=Menggunakan Sendmail
config.mailer_sendmail_path=Jalur Sendmail
@@ -1287,6 +1283,7 @@ config.log_mode=Mode catat
config.disabled_logger=Nonaktif
config.xorm_log_sql=Catatan SQL
+
monitor.cron=Tugas Cron
monitor.name=Nama
monitor.schedule=Jadwal
diff --git a/options/locale/locale_is-IS.ini b/options/locale/locale_is-IS.ini
index c56b14f907..c6b747826a 100644
--- a/options/locale/locale_is-IS.ini
+++ b/options/locale/locale_is-IS.ini
@@ -8,7 +8,6 @@ sign_out=Skrá Út
sign_up=Nýskráning
link_account=Tengja Notanda
register=Nýskráning
-website=Vefsíða
version=Útgáfa
powered_by=Keyrt af %s
page=Síða
@@ -177,7 +176,6 @@ log_root_path_helper=Annálaskrár verða skrifaðar í þessa möppu.
optional_title=Valfrjálsar Stillingar
email_title=Tölvupóstsstillingar
-smtp_host=SMTP Hýsill
smtp_from=Senda Tölvupóst Sem
smtp_from_helper=Netfang sem Gitea mun nota. Sláðu inn venjulegt netfang eða notaðu „Nafn“ sniðið.
mailer_user=SMTP Notandanafn
@@ -240,6 +238,7 @@ org_no_results=Engar samsvarandi stofnanir fundust.
code_no_results=Enginn samsvarandi frumkóði fannst eftur þínum leitarorðum.
code_search_results=Leitarniðurstöður fyrir „%s“
+
[auth]
create_new_account=Skrá Notanda
register_helper_msg=Ertu nú þegar með notanda? Skráðu þig inn núna!
@@ -1026,10 +1025,6 @@ settings.mirror_settings.direction.pull=Pull
settings.mirror_settings.direction.push=Push
settings.mirror_settings.last_update=Síðasta uppfærsla
settings.mirror_settings.push_mirror.remote_url=Vefslóð Git Fjarhugbúnaðarsafns
-settings.email_notifications.enable=Virkja Tölvupósttilkynningar
-settings.email_notifications.onmention=Aðeins Tölvupóst Þegar Minnst Er á Mig
-settings.email_notifications.disable=Óvirkja Tölvupósttilkynningar
-settings.email_notifications.submit=Stilla Val á Tölvupósti
settings.site=Vefsíða
settings.update_settings=Uppfæra Stillingar
settings.branches.update_default_branch=Uppfæra Sjálfgefna Grein
@@ -1269,7 +1264,6 @@ config.db_path=Slóð
config.mailer_name=Heiti
-config.mailer_host=Hýsill
config.mailer_user=Notandi
@@ -1279,6 +1273,7 @@ config.https_only=Aðeins HTTPS
+
monitor.name=Heiti
monitor.goroutines=%d Górútínur
monitor.desc=Lýsing
diff --git a/options/locale/locale_it-IT.ini b/options/locale/locale_it-IT.ini
index 5b397773f9..f002c534d7 100644
--- a/options/locale/locale_it-IT.ini
+++ b/options/locale/locale_it-IT.ini
@@ -2,13 +2,13 @@ home=Home
dashboard=Pannello di controllo
explore=Esplora
help=Aiuto
+logo=Logo
sign_in=Accedi
sign_in_with=Accedi con
sign_out=Esci
sign_up=Registrati
link_account=Collega account
register=Registrati
-website=Sito Web
version=Versione
powered_by=Gestito da %s
page=Pagina
@@ -34,6 +34,19 @@ twofa=Verifica in due passaggi
twofa_scratch=Codice di recupero per la verifica in due passaggi
passcode=Codice di sicurezza
+webauthn_insert_key=Inserisci la tua chiave di sicurezza
+webauthn_sign_in=Premere il pulsante sul tasto di sicurezza. Se il tasto di sicurezza non ha pulsante, reinseriscilo.
+webauthn_press_button=Si prega di premere il pulsante sul tasto di sicurezza…
+webauthn_use_twofa=Usa un codice a due fattori dal tuo telefono
+webauthn_error=Impossibile leggere la tua chiave di sicurezza.
+webauthn_unsupported_browser=Il tuo browser al momento non supporta WebAuthn.
+webauthn_error_unknown=Si è verificato un errore sconosciuto. Riprova.
+webauthn_error_insecure=WebAuthn supporta solo connessioni sicure. Per il test su HTTP, è possibile utilizzare l'origine "localhost" o "127.0.0.1"
+webauthn_error_unable_to_process=Il server non può elaborare la richiesta.
+webauthn_error_duplicated=La chiave di sicurezza non è consentita per questa richiesta. Assicurati che la chiave non sia già registrata.
+webauthn_error_empty=Devi impostare un nome per questa chiave.
+webauthn_error_timeout=Timeout raggiunto prima che la tua chiave possa essere letta. Ricarica la pagina e riprova.
+webauthn_reload=Ricarica
repository=Repository
organization=Organizzazione
@@ -71,7 +84,13 @@ add=Aggiungi
add_all=Aggiungi tutti
remove=Rimuovi
remove_all=Rimuovi tutti
+edit=Modifica
+copy=Copia
+copy_url=Copia URL
+copy_branch=Copia nome del ramo
+copy_success=Copiato!
+copy_error=Copia fallita
write=Scrivi
preview=Anteprima
@@ -80,11 +99,20 @@ loading=Caricamento…
step1=Passo 1:
step2=Passo 2:
+error=Errore
error404=La pagina che stai cercando di raggiungere non esiste oppure non sei autorizzato a visualizzarla.
+never=Mai
+rss_feed=Feed RSS
[error]
+occurred=Si è verificato un errore
+report_message=Se sei sicuro che questo sia un bug Gitea, cerca i problemi su GitHub o apri un nuovo problema se necessario.
+missing_csrf=Richiesta errata: nessun token CSRF presente
+invalid_csrf=Richiesta errata: token CSRF non valido
+not_found=Il bersaglio non è stato trovato.
+network_error=Errore di rete
[startpage]
app_desc=Un servizio auto-ospitato per Git pronto all'uso
@@ -101,6 +129,7 @@ license_desc=Ottieni la documentazione prima di cambiare qualsiasi impostazione.
+require_db_desc=Gitea requires MySQL, PostgreSQL, MSSQL, SQLite3 or TiDB (MySQL protocol).
db_title=Impostazioni Database
db_type=Tipo di database
host=Host
@@ -114,6 +143,11 @@ ssl_mode=SSL
charset=Charset
path=Percorso
sqlite_helper=Percorso file del database SQLite3.
Inserisci un percorso assoluto se stai usando Gitea come servizio.
+reinstall_error=Stai cercando di installare in un database Gitea esistente
+reinstall_confirm_message=La reinstallazione con un database Gitea esistente può causare problemi multipli. Nella maggior parte dei casi, dovresti usare il tuo "app.ini" esistente per eseguire Gitea. Se sai cosa stai facendo, confermi quanto segue:
+reinstall_confirm_check_1=I dati crittografati da SECRET_KEY nell'app. ni potrebbe essere perso: gli utenti potrebbero non essere in grado di accedere con 2FA/OTP & mirror potrebbe non funzionare correttamente. Selezionando questa casella confermi che il file attuale app.ini contiene il corretto SECRET_KEY.
+reinstall_confirm_check_2=I repository e le impostazioni potrebbero avere bisogno di essere ri-sincronizzati. Selezionando questa casella confermi che potrai risincronizzare manualmente gli hook per i repository e il file authorized_keys. Confermi che assicurerai che le impostazioni del repository e del mirror siano corrette.
+reinstall_confirm_check_3=Confermi di essere assolutamente sicuro che questo Gitea è in esecuzione con l'app corretta. ni posizione e che sei sicuro di dover reinstallare. Confermi di aver riconosciuto i rischi di cui sopra.
err_empty_db_path=Il percorso del database SQLite3 non può essere vuoto.
no_admin_and_disable_registration=Non puoi disabilitare l'auto-registrazione degli utenti senza creare un account amministratore.
err_empty_admin_password=La password dell'amministratore non può essere vuota.
@@ -131,6 +165,8 @@ lfs_path=Percorso radice di Git LFS
lfs_path_helper=I file trovati da Git LFS saranno salvati in questa directory. Lasciare vuoto per disattivare.
run_user=Esegui come Nome utente
run_user_helper=Inserisci il nome utente del sistema operativo su cui Gitea viene eseguito. Nota che l'utente deve avere accesso al percorso radice dei repository.
+domain=Dominio Server
+domain_helper=Dominio o indirizzo host per il server.
ssh_port=Porta Server SSH
ssh_port_helper=Numero di porta in ascolto sul server SSH. Lasciare vuoto per disattivare.
http_port=Porta in ascolto HTTP Gitea
@@ -142,7 +178,8 @@ log_root_path_helper=I file di log saranno scritti in questa directory.
optional_title=Impostazioni Facoltative
email_title=Impostazioni Email
-smtp_host=Host SMTP
+smtp_addr=Host SMTP
+smtp_port=Porta SMTP
smtp_from=Invia Email come
smtp_from_helper=Indirizzo Email che Gitea utilizzerà. Inserisci un indirizzo email o usa il formato "Name" .
mailer_user=Nome utente SMTP
@@ -177,8 +214,12 @@ install_btn_confirm=Installare Gitea
test_git_failed=Fallito il test del comando git: %v
sqlite3_not_available=Questa versione di Gitea non supporta SQLite3. Si prega di scaricare la versione binaria ufficiale da %s (not the 'gobuild' version).
invalid_db_setting=Le impostazioni del database sono invalide: %v
+invalid_db_table=La tabella del database '%s' non è valida: %v
invalid_repo_path=Il percorso radice del Repository è invalido: %v
+invalid_app_data_path=Il percorso dati dell'app non è valido: %v
run_user_not_match=Il nome utente 'esegui come' non è il nome utente attuale: %s -> %s
+internal_token_failed=Generazione del token interno non riuscita: %v
+secret_key_failed=Generazione della chiave segreta non riuscita: %v
save_config_failed=Salvataggio della configurazione non riuscito: %v
invalid_admin_setting=Le impostazioni dell'account amministratore sono invalide: %v
install_success=Benvenuto! Grazie per aver scelto Gitea. Attenzione e buon divertimento!
@@ -207,6 +248,7 @@ view_home=Vedi %s
search_repos=Trova un repository…
filter=Altro filtri
filter_by_team_repositories=Filtra per repository del team
+feed_of=Feed di "%s"
show_archived=Archiviato
show_both_archived_unarchived=Mostra sia gli archiviati che i non archiviati
@@ -228,6 +270,7 @@ search=Cerca
code=Codice
search.fuzzy=Fuzzy
search.match=Corrispondenze
+code_search_unavailable=Attualmente la ricerca di codice non è disponibile. Contatta l'amministratore del sito.
repo_no_results=Nessuna repository corrispondente.
user_no_results=Nessun utente corrispondente.
org_no_results=Nessun'organizzazione corrispondente trovata.
@@ -235,12 +278,14 @@ code_no_results=Nessun codice sorgente corrispondente ai termini di ricerca.
code_search_results=Risultati di ricerca per '%s'
code_last_indexed_at=Ultimo indicizzato %s
+
[auth]
create_new_account=Registra un account
register_helper_msg=Hai già un account? Accedi ora!
social_register_helper_msg=Hai già un account? Accedi ora!
disable_register_prompt=La registrazione è disabilitata. Si prega di contattare l'amministratore del sito.
disable_register_mail=Email di conferma per la registrazione disabilitata.
+manual_activation_only=Contatta l'amministratore del sito per completare l'attivazione.
remember_me=Ricorda questo dispositivo
forgot_password_title=Password Dimenticata
forgot_password=Password dimenticata?
@@ -279,12 +324,17 @@ oauth_signup_submit=Completa l'Account
oauth_signin_tab=Collegamento ad un Account Esistente
oauth_signin_title=Accedi per autorizzare l' Account collegato
oauth_signin_submit=Collega Account
+oauth.signin.error=Si è verificato un errore nell'elaborazione della richiesta di autorizzazione. Se questo errore persiste, si prega di contattare l'amministratore del sito.
+oauth.signin.error.access_denied=La richiesta di autorizzazione è stata negata.
+oauth.signin.error.temporarily_unavailable=Autorizzazione non riuscita perché il server di autenticazione non è temporaneamente disponibile. Riprova più tardi.
openid_connect_submit=Connetti
openid_connect_title=Connetti a una conta esistente
openid_connect_desc=L'URI OpenID scelto è sconosciuto. Qui puoi associarlo a un nuovo account.
openid_register_title=Crea Nuovo Account
openid_register_desc=L'URI OpenID scelto è sconosciuto. Qui puoi associarlo a un nuovo account.
openid_signin_desc=Inserisci il tuo URI OpenID. Ad esempio: https://anne.me, bob.openid.org.cn o gnusocial.net/carry.
+disable_forgot_password_mail=Il recupero dell'account è disabilitato perché non è stata impostata alcuna email. Contatta l'amministratore del sito.
+disable_forgot_password_mail_admin=Il recupero dell'account è disponibile solo quando l'email è impostata. Si prega di impostare un'email per abilitare il recupero dell'account.
email_domain_blacklisted=Non è possibile registrarsi con il proprio indirizzo email.
authorize_application=Autorizza applicazione
authorize_redirect_notice=Verrai reindirizzato a %s se autorizzi questa applicazione.
@@ -298,21 +348,64 @@ password_pwned=La password che hai scelto è in una lista %s,
activate_account=Per favore attiva il tuo account
+activate_account.title=%s, si prega di attivare il tuo account
+activate_account.text_1=Ciao %[1]s, grazie per essersi registrato al %[2]s!
+activate_account.text_2=Clicca sul seguente link per attivare il tuo account entro %s:
activate_email=Verifica il tuo indirizzo e-mail
+activate_email.title=%s, verifica il tuo indirizzo e-mail
+activate_email.text=Clicca sul seguente link per verificare il tuo indirizzo email entro %s:
register_notify=Benvenuto su Gitea
+register_notify.title=%[1]s, benvenuto in %[2]s
+register_notify.text_1=questa è la tua email di conferma di registrazione per %s!
+register_notify.text_2=Ora è possibile accedere tramite nome utente: %s.
+register_notify.text_3=Se questo account è stato creato per te, per favore imposta prima la tua password.
reset_password=Recupera il tuo account
+reset_password.title=%s, hai richiesto di recuperare il tuo account
+reset_password.text=Clicca sul seguente link per recuperare il tuo account entro %s:
register_success=Registrazione completata con successo
+issue_assigned.pull=@%[1]s ti ha assegnato il Problema %[2]s nel repository %[3]s.
+issue_assigned.issue=@%[1]s ti ha assegnato il Problema %[2]s nel repository %[3]s.
+issue.x_mentioned_you=@%s ti ha menzionato:
+issue.action.force_push=%[1]s force-pushed il %[2]s da %[3]s a %[4]s.
+issue.action.push_1=@%[1]s ha spinto %[3]d commit a %[2]s
+issue.action.push_n=@%[1]s ha spinto %[3]d commit a %[2]s
+issue.action.close=@%[1]s chiuso #%[2]d.
+issue.action.reopen=@%[1]s riaperto #%[2]d.
+issue.action.merge=@%[1]s unito #%[2]d in %[3]s.
+issue.action.approve=@%[1]s ha approvato questa pull request.
+issue.action.reject=@%[1]s ha richiesto modifiche su questa pull request.
+issue.action.review=@%[1]s ha commentato questa pull request.
+issue.action.review_dismissed=@%[1]s ha respinto l'ultima recensione da %[2]s per questa pull request.
+issue.action.ready_for_review=@%[1]s ha contrassegnato questa pull request pronta per la revisione.
+issue.action.new=@%[1]s creato #%[2]d.
+issue.in_tree_path=In %s:
+release.new.subject=%s in %s rilasciato
+release.new.text=@%[1]s rilasciato %[2]s in %[3]s
+release.title=Titolo: %s
+release.note=Nota:
+release.downloads=Scaricamenti:
+release.download.zip=Codice Sorgente (Zip)
+release.download.targz=Codice Sorgente (Tar.Gz)
+repo.transfer.subject_to=%s vorrebbe trasferire "%s" a %s
+repo.transfer.subject_to_you=%s vorrebbe trasferire "%s" a te
+repo.transfer.to_you=tu
+repo.transfer.body=Per accettare o respingerla visita %s o semplicemente ignorarla.
+repo.collaborator.added.subject=%s ti ha aggiunto a %s
+repo.collaborator.added.text=Sei stato aggiunto come collaboratore del repository:
[modal]
yes=Sì
@@ -350,8 +443,10 @@ size_error='deve essere %s.'
min_size_error=` deve contenere almeno %s caratteri.`
max_size_error=` deve contenere massimo %s caratteri.`
email_error=` non è un indirizzo e-mail valido.`
+url_error=%s" non è un URL valido.
include_error=` deve contenere la stringa '%s'.`
glob_pattern_error=` il pattern glob non è valido: %s.`
+regex_pattern_error=` modello regex non valido: %s.`
unknown_error=Errore sconosciuto:
captcha_incorrect=Il codice CAPTCHA non è corretto.
password_not_match=Le password non corrispondono.
@@ -360,6 +455,7 @@ lang_select_error=Selezionare una lingua dall'elenco.
username_been_taken=Il Nome utente esiste già.
username_change_not_local_user=Gli utenti non locali non sono autorizzati a modificare il proprio nome utente.
repo_name_been_taken=Il nome del repository esiste già.
+repository_force_private=Force Private è abilitato: i repository privati non possono essere resi pubblici.
repository_files_already_exist=File già esistenti per questo repository. Contatta l'amministratore di sistema.
repository_files_already_exist.adopt=I file esistono già per questo repository e possono essere solo Adottati.
repository_files_already_exist.delete=I file esistono già per questo repository. È necessario eliminarli.
@@ -389,12 +485,15 @@ cannot_add_org_to_team=Un'organizzazione non può essere aggiunto come membro de
invalid_ssh_key=Impossibile verificare la tua chiave SSH: %s
invalid_gpg_key=Impossibile verificare la tua chiave GPG: %s
+invalid_ssh_principal=Principal non valido: %s
unable_verify_ssh_key=Impossibile verificare la tua chiave SSH; si prega di ricontrollarla per verificare eventuali errori.
auth_failed=Autenticazione non riuscita: %v
still_own_repo=Il tuo account possiede una o più repositories; rimuovile o trasferiscile per proseguire.
still_has_org=Il tuo account è un membro di una o più organizzazioni; abbandonali prima di proseguire.
+still_own_packages=Il tuo account possiede uno o più pacchetti; eliminali prima.
org_still_own_repo=Questa organizzazione possiede ancora una o più repositories, rimuoverle o trasferirle per continuare.
+org_still_own_packages=Questa organizzazione possiede ancora uno o più pacchetti; eliminarli prima.
target_branch_not_exist=Il ramo (branch) di destinazione non esiste.
@@ -405,6 +504,7 @@ repositories=Repository
activity=Attività pubblica
followers=Seguaci
starred=Repositories votate
+watched=Repository Osservate
projects=Progetti
following=Seguiti
follow=Segui
@@ -420,6 +520,7 @@ form.name_chars_not_allowed=Il nome utente '%s' contiene caratteri non validi.
[settings]
profile=Profilo
account=Account
+appearance=Aspetto
password=Password
security=Sicurezza
avatar=Avatar
@@ -433,6 +534,7 @@ twofa=Verifica in due passaggi
account_link=Account collegati
organization=Organizzazioni
uid=Uid
+webauthn=Chiavi Di Sicurezza
public_profile=Profilo pubblico
biography_placeholder=Raccontaci un po' di te
@@ -443,7 +545,9 @@ website=Sito web
location=Posizione
update_theme=Aggiorna tema
update_profile=Aggiorna Profilo
+update_language=Aggiorna Lingua
update_language_not_found=La lingua '%s' non è disponibile.
+update_language_success=La lingua è stata aggiornata.
update_profile_success=Il tuo profilo è stato aggiornato.
change_username=Il tuo nome utente è stato modificato.
change_username_prompt=Nota: i cambiamenti al nome utente vanno a modificare anche l'URL del tuo account.
@@ -452,6 +556,22 @@ continue=Continua
cancel=Annulla
language=Lingua
ui=Tema
+hidden_comment_types=Tipi di commenti nascosti
+comment_type_group_reference=Riferimento
+comment_type_group_label=Etichetta
+comment_type_group_milestone=Traguardo
+comment_type_group_assignee=Assegnatario
+comment_type_group_title=Titolo
+comment_type_group_branch=Ramo
+comment_type_group_time_tracking=Cronografo
+comment_type_group_deadline=Scadenza
+comment_type_group_dependency=Dipendenza
+comment_type_group_lock=Stato Blocco
+comment_type_group_review_request=Richiesta di revisione
+comment_type_group_pull_request_push=Aggiunti commit
+comment_type_group_project=Progetto
+comment_type_group_issue_ref=Riferimento del problema
+saved_successfully=Le impostazioni sono state salvate correttamente.
privacy=Privacy
keep_activity_private=Nascondi l'attività dal profilo
keep_activity_private_popup=Rendi l'attività visibile solo da te e dagli amministratori
@@ -465,6 +585,7 @@ delete_current_avatar=Elimina Avatar attuale
uploaded_avatar_not_a_image=Il file caricato non è un'immagine.
uploaded_avatar_is_too_big=Il file inviato eccede le dimensioni massime.
update_avatar_success=Il tuo avatar è stato aggiornato.
+update_user_avatar_success=L'avatar dell'utente è stato aggiornato.
change_password=Aggiorna Password
old_password=Password attuale
@@ -508,6 +629,7 @@ keep_email_private_popup=Il tuo indirizzo email sarà nascosto agli altri utenti
openid_desc=OpenID consente di delegare l'autenticazione ad un provider esterno.
manage_ssh_keys=Gestisci chiavi SSH
+manage_ssh_principals=Gestisci i Certificati SSH
manage_gpg_keys=Gestisci Chiavi GPG
add_key=Aggiungi Chiave
ssh_desc=Queste chiavi SSH pubbliche sono associate con il tuo account. Le corrispondenti chiavi private consentono l'accesso completo alle tue repositories.
@@ -519,10 +641,35 @@ add_new_key=Aggiungi Chiave SSH
add_new_gpg_key=Aggiungi Chiave GPG
key_content_ssh_placeholder=Inizia con 'ssh-ed25519', 'ssh-rsa', 'ecdsa-sha2-nistp256', 'ecdsa-sha2-nistp384', 'ecdsa-sha2-nistp521', 'sk-ecdsa-sha2-nistp256@openssh.com', o 'sk-ssh-ed25519@openssh.com'
key_content_gpg_placeholder=Comincia con '-----BEGIN PGP PUBLIC KEY BLOCK-----'
+add_new_principal=Aggiungi Principal
ssh_key_been_used=Questa chiave SSH è già stata aggiunta al server.
ssh_key_name_used=Una chiave SSH con lo stesso nome esiste già sul tuo account.
ssh_principal_been_used=Questa chiave SSH è già stata aggiunta al server.
gpg_key_id_used=Esiste già una chiave GPG pubblica con lo stesso ID.
+gpg_no_key_email_found=Questa chiave GPG non corrisponde a nessun indirizzo email attivato associato al tuo account. Potrebbe essere ancora aggiunto se firmi il token fornito.
+gpg_key_matched_identities=Identità Corrispondenti:
+gpg_key_matched_identities_long=Le identità incorporate in questa chiave corrispondono ai seguenti indirizzi email attivati per questo utente. I commit che corrispondono a questi indirizzi email possono essere verificati con questa chiave.
+gpg_key_verified=Chiave Verificata
+gpg_key_verified_long=La chiave è stata verificata con un token e può essere utilizzata per verificare che i commit corrispondano a tutti gli indirizzi email attivati per questo utente oltre a qualsiasi identità corrispondente per questa chiave.
+gpg_key_verify=Verifica
+gpg_invalid_token_signature=La chiave GPG fornita, la firma e il token non corrispondono o il token è obsoleto.
+gpg_token_required=Devi fornire una firma per il token sottostante
+gpg_token=Token
+gpg_token_help=È possibile generare una firma utilizzando:
+gpg_token_code=echo "%s" | gpg -a --default-key %s --detach-sig
+gpg_token_signature=Firma GPG corazzata
+key_signature_gpg_placeholder=Comincia con '-----BEGIN PGP SIGNATURE-----'
+verify_gpg_key_success=La chiave GPG '%s' è stata verificata.
+ssh_key_verified=Chiave Verificata
+ssh_key_verified_long=La chiave è stata verificata con un token e può essere utilizzata per verificare che i commit corrispondano a tutti gli indirizzi email attivati per questo utente.
+ssh_key_verify=Verifica
+ssh_invalid_token_signature=La chiave SSH fornita, la firma o il token non corrispondono o il token è obsoleto.
+ssh_token_required=Devi fornire una firma per il token sottostante
+ssh_token=Token
+ssh_token_help=È possibile generare una firma utilizzando:
+ssh_token_signature=Firma SSH corazzata
+key_signature_ssh_placeholder=Comincia con '-----BEGIN SSH SIGNATURE-----'
+verify_ssh_key_success=La chiave SSH '%s' è stata verificata.
subkeys=Sottochiavi
key_id=ID chiave
key_name=Nome della Chiave
@@ -537,8 +684,10 @@ gpg_key_deletion=Rimuovi chiave GPG
ssh_principal_deletion=Rimuovi certificato SSH principale
ssh_key_deletion_desc=Rimuovere una chiave SSH ne revoca l'accesso al tuo account. Continuare?
gpg_key_deletion_desc=La rimozione di una chiave GPG invalida i commits firmati da essa. Continuare?
+ssh_principal_deletion_desc=Rimuovere un Certificato Utente SSH ne revoca l'accesso al tuo account. Continuare?
ssh_key_deletion_success=La chiave SSH è stata rimossa.
gpg_key_deletion_success=La chiave GPG è stata rimossa.
+ssh_principal_deletion_success=Il principale è stato rimosso.
add_on=Aggiunto il
valid_until=Valido fino al
valid_forever=Valido per sempre
@@ -548,6 +697,7 @@ can_read_info=Letto
can_write_info=Scrivere
key_state_desc=Questa chiave è stata utilizzata negli ultimi 7 giorni
token_state_desc=Questo token è stato utilizzato negli ultimi 7 giorni
+principal_state_desc=Questo principal è stato utilizzato negli ultimi 7 giorni
show_openid=Mostra nel profilo
hide_openid=Nascondi dal profilo
ssh_disabled=SSH disabilitato
@@ -567,6 +717,9 @@ generate_token_success=Il nuovo token è stato generato. Copia ora in quanto non
generate_token_name_duplicate=%s è già stato utilizzato come nome dell'applicazione. Si prega di usarne uno nuovo.
delete_token=Elimina
access_token_deletion=Elimina token di accesso
+access_token_deletion_cancel_action=Annulla
+access_token_deletion_confirm_action=Elimina
+access_token_deletion_desc=L'eliminazione di un token annullerà l'accesso al tuo account per le applicazioni che lo utilizzano. Questo non può essere annullato. Continuare?
delete_token_success=Il token è stato eliminato. Le applicazioni che lo utilizzavano non hanno più accesso al tuo account.
manage_oauth2_applications=Gestisci applicazioni OAuth2
@@ -619,10 +772,16 @@ passcode_invalid=Il codice di accesso non è corretto. Riprova.
twofa_enrolled=Il tuo account è stato registrato alla verifica in due passaggi. Conserva il token di sicurezza (%s) in un luogo sicuro in quanto viene visualizzato sono una volta!
twofa_failed_get_secret=Impossibile ottenere il segreto.
+webauthn_desc=Le chiavi di sicurezza sono dispositivi hardware contenenti chiavi crittografiche. Possono essere utilizzate per l'autenticazione a due fattori. Le chiavi di sicurezza devono supportare lo standard WebAuthenticator di WebAuthn.
+webauthn_register_key=Aggiungi Chiave Di Sicurezza
+webauthn_nickname=Soprannome
+webauthn_delete_key=Rimuovi Chiave Di Sicurezza
+webauthn_delete_key_desc=Se si rimuove una chiave di sicurezza non è più possibile accedere con esso. Continuare?
manage_account_links=Gestisci gli account collegati
manage_account_links_desc=Questi account esterni sono collegati al tuo account Gitea.
account_links_not_available=Attualmente non è collegato alcun account esterno al tuo account Gitea.
+link_account=Collega Account
remove_account_link=Rimuovi account collegato
remove_account_link_desc=Rimuovere un account collegato ne revoca l'accesso al tuo account Gitea. Continuare?
remove_account_link_success=L'account collegato è stato rimosso.
@@ -632,6 +791,7 @@ repos_none=Non possiedi alcun repository
delete_account=Elimina Account
delete_prompt=Questa operazione eliminerà permanentemente il tuo account utente. NON PUÒ essere annullata.
+delete_with_all_comments=Il tuo account è più recente di %s giorni. Per evitare commenti fantasma, tutti i commenti relativi a issue/PR verranno eliminati con esso.
confirm_delete_account=Conferma Eliminazione
delete_account_title=Elimina account utente
delete_account_desc=Sei sicuro di voler rimuovere questo account utente permanentemente?
@@ -640,10 +800,20 @@ email_notifications.enable=Abilita Notifiche Email
email_notifications.onmention=Solo email su Menzione
email_notifications.disable=Disabilita notifiche email
email_notifications.submit=Imposta Preferenze Email
+email_notifications.andyourown=E Le Tue Notifiche
+visibility=Visibilità utente
+visibility.public=Pubblico
+visibility.public_tooltip=Visibile a tutti gli utenti
+visibility.limited=Limitato
+visibility.limited_tooltip=Visibile solo agli utenti registrati
+visibility.private=Privato
+visibility.private_tooltip=Visibile solo ai membri dell'organizzazione
[repo]
+new_repo_helper=Un repository contiene tutti i file del progetto, inclusa la cronologia delle revisioni. Lo hai già altrove? Migrare il repository.
owner=Proprietario
+owner_helper=Alcune organizzazioni potrebbero non essere visualizzate nel menu a discesa a causa di un limite massimo al numero di repository.
repo_name=Nome Repository
repo_name_helper=Un buon nome per un repository è costituito da parole chiave corte, facili da ricordare e uniche.
repo_size=Dimensione repository
@@ -659,33 +829,55 @@ visibility_fork_helper=(Questa modifica avrà effetto su tutti i fork)
clone_helper=Hai bisogno di aiuto per la clonazione? Visita Help.
fork_repo=Forka Repository
fork_from=Forka da
+already_forked=Hai già fatto il fork di %s
+fork_to_different_account=Fai Fork a un account diverso
fork_visibility_helper=La visibilità di un repository forkato non può essere modificata.
use_template=Usa questo modello
+clone_in_vsc=Clona nel codice VS
+download_zip=Scarica ZIP
+download_tar=Scarica TAR.GZ
+download_bundle=Scarica BUNDLE
generate_repo=Genera repository
generate_from=Genera da
repo_desc=Descrizione
repo_desc_helper=Inserisci una breve descrizione (opzionale)
repo_lang=Lingua
repo_gitignore_helper=Seleziona i template di .gitignore.
+repo_gitignore_helper_desc=Scegli di quali file non tenere traccia da un elenco di modelli per le lingue comuni. Gli artefatti tipici generati dagli strumenti di build di ogni lingua sono inclusi su .gitignore per impostazione predefinita.
issue_labels=Etichette Issue
issue_labels_helper=Seleziona un set di etichette per problemi.
license=Licenza
license_helper=Seleziona un file di licenza.
+license_helper_desc=Una licenza governa ciò che gli altri possono e non possono fare con il tuo codice. Non sei sicuro di chi è giusto per il tuo progetto? Vedi Scegli una licenza.
readme=LEGGIMI
readme_helper=Seleziona un template per il file LEGGIMI.
readme_helper_desc=Qui puoi scrivere una descrizione completa del progetto.
auto_init=Inizializza Repository (Aggiungi .gitignore, Licenza e LEGGIMI)
trust_model_helper=Seleziona il modello di fiducia per la verifica della firma. Le opzioni possibili sono:
trust_model_helper_collaborator=Collaboratore: Fidati delle firme da parte dei collaboratori
+trust_model_helper_committer=Committer: Fidati delle Firme che corrispondono ai committenti
+trust_model_helper_collaborator_committer=Collaboratore+Committer: Fidati delle firme da parte dei collaboratori che corrispondono al committer
+trust_model_helper_default=Predefinito: utilizzare il modello di trust predefinito per questa installazione
create_repo=Crea Repository
default_branch=Ramo (Branch) predefinito
+default_branch_helper=Il ramo predefinito è il ramo base per le richieste di pull e i commit di codice.
mirror_prune=Rimuovi
mirror_prune_desc=Rimuovi i riferimenti di puntamento-remoto obsoleti
+mirror_interval=Intervallo di specchio (le unità di tempo valide sono 'h', 'm', 's'). 0 per disabilitare la sincronizzazione periodica. (Intervallo minimo: %s)
mirror_interval_invalid=L'intervallo di aggiornamento dei mirror non è valido.
+mirror_sync_on_commit=Sincronizzazione quando i commit vengono premuti
mirror_address=Clona da URL
+mirror_address_desc=Metti tutte le credenziali richieste nella sezione Autorizzazione.
mirror_address_url_invalid=L'url fornito non è valido. Devi effettuare l'escape completo tutti i componenti dell'Url.
mirror_address_protocol_invalid=L'url fornito non è valido. Solo dai link http(s):// o git:// possono essere replicate.
+mirror_lfs=Large File Storage (LFS)
+mirror_lfs_desc=Attiva il mirroring dei dati LFS.
+mirror_lfs_endpoint=Punto d'accesso LFS
+mirror_lfs_endpoint_desc=La sincronizzazione tenterà di utilizzare l'url clone per determinare il server LFS. È inoltre possibile specificare un endpoint personalizzato se il repository dati LFS è memorizzato da qualche altra parte.
mirror_last_synced=Ultima sincronizzazione
+mirror_password_placeholder=(Inmodificato)
+mirror_password_blank_placeholder=(Disattivato)
+mirror_password_help=Cambia il nome utente per cancellare una password memorizzata.
watchers=Osservatori
stargazers=Fan
forks=Fork
@@ -702,12 +894,14 @@ delete_preexisting_label=Elimina
delete_preexisting=Elimina file preesistenti
delete_preexisting_content=Elimina file in %s
delete_preexisting_success=Eliminato file non adottati in %s
+blame_prior=Visualizza la colpa prima di questa modifica
transfer.accept=Accetta trasferimento
transfer.accept_desc=Trasferisci a "%s"
transfer.reject=Rifiuta trasferimento
transfer.reject_desc=Annulla il trasferimento a "%s"
transfer.no_permission_to_accept=Non hai i permessi per accettare
+transfer.no_permission_to_reject=Non hai i permessi per rifiutare
desc.private=Privato
desc.public=Pubblico
@@ -720,6 +914,7 @@ desc.archived=Archiviato
template.items=Elementi del modello
template.git_content=Contenuto di Git (Ramo predefinito)
template.git_hooks=Git Hooks
+template.git_hooks_tooltip=Al momento non sei in grado di modificare o rimuovere Git Hooks una volta aggiunto. Selezionare questa opzione solo se ti fidi del template repository.
template.webhooks=Webhooks
template.topics=Argomenti
template.avatar=Avatar
@@ -731,11 +926,20 @@ archive.title=Questo repository è archiviato. Puoi vedere i file e clonarli, ma
archive.issue.nocomment=Questo repository è archiviato. Non puoi commentare i problemi.
archive.pull.nocomment=Questo repository è archiviato. Non puoi commentare le richieste di pull.
+form.reach_limit_of_creation_1=Hai già raggiunto il tuo limite di %d repository.
+form.reach_limit_of_creation_n=Hai già raggiunto il tuo limite di %d repository.
form.name_reserved=Il nome repository '%s' è riservato.
form.name_pattern_not_allowed=Il modello '%s' non è consentito come nome di un repository.
+need_auth=Autorizzazione
migrate_options=Opzioni di migrazione
migrate_service=Servizio migrazione
+migrate_options_mirror_helper=Questo repository sarà un mirror
+migrate_options_lfs=Migra file LFS
+migrate_options_lfs_endpoint.label=Punto d'accesso LFS
+migrate_options_lfs_endpoint.description=La migrazione tenterà di utilizzare il tuo Git remote per determinare il server LFS. È inoltre possibile specificare un endpoint personalizzato se il repository dati LFS è memorizzato da qualche altra parte.
+migrate_options_lfs_endpoint.description.local=È supportato anche un percorso server locale.
+migrate_options_lfs_endpoint.placeholder=Lascia vuoto per derivare dall'URL della clonazione
migrate_items=Elementi di migrazione
migrate_items_wiki=Wiki
migrate_items_milestones=Milestone
@@ -747,9 +951,12 @@ migrate_items_releases=Rilasci
migrate_repo=Migra Repository
migrate.clone_address=Migra / Clona da URL
migrate.clone_address_desc=URL HTTP (S) o Git 'clone' di un repository esistente
+migrate.github_token_desc=È possibile mettere uno o più token con virgola separati qui per rendere la migrazione più veloce a causa del limite di velocità API GitHub. ATTENZIONE: L'abuso di questa funzione potrebbe violare la politica del fornitore di servizi e portare al blocco dell'account.
migrate.clone_local_path=o un percorso del server locale
migrate.permission_denied=Non è consentito importare repository locali.
+migrate.permission_denied_blocked=Non è possibile importare da host non consentiti, si prega di chiedere all'amministratore di controllare ALLOWED_DOMAINS/ALLOW_LOCALNETWORKS/BLOCKED_DOMAINS impostazioni.
migrate.invalid_local_path=Percorso locale non valido, non esiste o non è una cartella.
+migrate.invalid_lfs_endpoint=Il punto d'accesso LFS non è valido.
migrate.failed=Migrazione non riuscita: %v
migrate.migrate_items_options=Il Token di accesso è richiesto per migrare elementi aggiuntivi
migrated_from=Migrato da %[2]s
@@ -757,6 +964,23 @@ migrated_from_fake=Migrato da %[1]s
migrate.migrate=Migra da %s
migrate.migrating=Migrazione da %s...
migrate.migrating_failed=Migrazione da %s fallita.
+migrate.migrating_failed.error=Errore: %s
+migrate.migrating_failed_no_addr=Migrazione non riuscita.
+migrate.github.description=Migrare i dati da github.com o da altre istanze di GitHub.
+migrate.git.description=Migra un repository solo da qualsiasi servizio Git.
+migrate.gitlab.description=Migrare i dati da gitlab.com o da altre istanze di GitLab.
+migrate.gitea.description=Migrare i dati da gitea.com o altre istanze di Gitea.
+migrate.gogs.description=Migrare i dati da notabug.org o da altre istanze Gogs.
+migrate.onedev.description=Migrare i dati da code.onedev.io o da altre istanze OneDev.
+migrate.codebase.description=Migrare i dati da codebasehq.com.
+migrate.gitbucket.description=Migra i dati dalle istanze di GitBucket.
+migrate.migrating_git=Migrazione dei Dati Git
+migrate.migrating_topics=Migrazione dei topic
+migrate.migrating_milestones=Migrazione dei traguardi
+migrate.migrating_labels=Migrazione delle etichette
+migrate.migrating_releases=Migrazione delle uscite
+migrate.migrating_issues=Migrazione dei problemi
+migrate.migrating_pulls=Migrazione delle Pull Request
mirror_from=mirror da
forked_from=forkato da
@@ -778,6 +1002,7 @@ clone_this_repo=Clona questo repository
create_new_repo_command=Creazione di un nuovo repository da riga di comando
push_exist_repo=Push di un repository esistente da riga di comando
empty_message=Questo repository non contiene alcun contenuto.
+broken_message=I dati Git sottostanti a questo repository non possono essere letti. Contattare l'amministratore di questa istanza o eliminare questo repository.
code=Codice
code.desc=Accedi al codice sorgente, file, commits e branches.
@@ -785,11 +1010,13 @@ branch=Ramo (Branch)
tree=Albero (Tree)
clear_ref=`Cancella il riferimento corrente`
filter_branch_and_tag=Filtra per branch o tag
+find_tag=Trova etichetta
branches=Rami (Branch)
tags=Tag
issues=Problemi
pulls=Pull Requests
project_board=Progetti
+packages=Pacchetti
labels=Etichette
org_labels_desc=Etichette a livello di organizzazione che possono essere utilizzate con tutti i repository sotto questa organizzazione
org_labels_desc_manage=gestisci
@@ -800,25 +1027,43 @@ commit=Commit
release=Rilascio
releases=Rilasci
tag=Etichetta
+released_this=ha rilasciato questo
+file.title=%s a %s
file_raw=Originale
file_history=Cronologia
file_view_source=Visualizza sorgente
+file_view_rendered=Visualizza Renderizzato
file_view_raw=Vedi originale
file_permalink=Permalink
file_too_large=Il file è troppo grande per essere visualizzato.
+invisible_runes_header=`Questo file contiene caratteri Unicode invisibili!`
+invisible_runes_description=`Questo file contiene caratteri Unicode invisibili che possono essere elaborati in modo diverso da quello che appare di seguito. Se il tuo caso di utilizzo è intenzionale e legittimo, puoi tranquillamente ignorare questo avviso. Usa il pulsante Escape per rivelare caratteri nascosti.`
+ambiguous_runes_header=`Questo file contiene caratteri Unicode ambigui!`
+ambiguous_runes_description=`Questo file contiene caratteri Unicode ambigui che possono essere confusi con altri nella tua localizzazione attuale. Se il tuo caso di utilizzo è intenzionale e legittimo, puoi tranquillamente ignorare questo avviso. Usa il pulsante Escape per evidenziare questi caratteri.`
+invisible_runes_line=`Questa riga ha caratteri unicode invisibili`
+ambiguous_runes_line=`Questa riga ha caratteri unicode ambigui`
+ambiguous_character=`%[1]c [U+%04[1]X] è confondibile con %[2]c [U+%04[2]X]`
+escape_control_characters=Fuga
+unescape_control_characters=Unescape
+file_copy_permalink=Copia Permalink
+view_git_blame=Visualizza Git Blame
video_not_supported_in_browser=Il tuo browser non supporta i tag "video" di HTML5.
audio_not_supported_in_browser=Il tuo browser non supporta il tag "video" di HTML5.
stored_lfs=Memorizzati con Git LFS
symbolic_link=Link Simbolico
commit_graph=Grafico dei commit
commit_graph.select=Seleziona rami
+commit_graph.hide_pr_refs=Nascondi Pull Requests
commit_graph.monochrome=Mono
+commit_graph.color=Colore
blame=Blame
+download_file=Scarica file
normal_view=Vista normale
line=riga
lines=righe
+editor.add_file=Aggiungi file
editor.new_file=Nuovo file
editor.upload_file=Carica File
editor.edit_file=Modifica File
@@ -842,7 +1087,12 @@ editor.add_tmpl=Aggiungi ''
editor.add=Aggiungi '%s'
editor.update=Aggiornare '%s'
editor.delete=Eliminare '%s'
+editor.patch=Applica Patch
+editor.patching=Patching:
+editor.fail_to_apply_patch=Impossibile applicare la patch '%s'
+editor.new_patch=Nuova Patch
editor.commit_message_desc=Aggiungi una descrizione estesa facoltativa…
+editor.signoff_desc=Aggiungi "firmato da" dal committer alla fine del messaggio di log di commit.
editor.commit_directly_to_this_branch=Impegnarsi direttamente con il %s branch.
editor.create_new_branch=Creare un nuovo branch per questo commit e inizia una pull request.
editor.create_new_branch_np=Crea un nuovo ramo per questo commit.
@@ -863,7 +1113,11 @@ editor.file_already_exists=Un file di nome '%s' esiste già in questo repository
editor.commit_empty_file_header=Commit di un file vuoto
editor.commit_empty_file_text=Il file che stai per effettuare il commit è vuoto. Procedere?
editor.no_changes_to_show=Non ci sono cambiamenti da mostrare.
+editor.fail_to_update_file=Impossibile aggiornare/creare il file '%s'.
editor.fail_to_update_file_summary=Messaggio d'errore:
+editor.push_rejected_no_message=La modifica è stata rifiutata dal server senza un messaggio. Controlla Git Hooks.
+editor.push_rejected=La modifica è stata rifiutata dal server. Controlla Git Hooks.
+editor.push_rejected_summary=Messaggio Di Rifiuto Completo:
editor.add_subdir=Aggiungi una directory…
editor.unable_to_upload_files=Impossibile caricare i file su '%s' con errore:%v
editor.upload_file_is_locked=Il file '%s' è bloccato da %s.
@@ -872,10 +1126,13 @@ editor.cannot_commit_to_protected_branch=Impossibile eseguire un commit sul bran
editor.no_commit_to_branch=Impossibile effettuare il commit direttamente sul branch perché:
editor.user_no_push_to_branch=L'utente non può effettuare il push sul branch
editor.require_signed_commit=Il branch richiede un commit firmato
+editor.cherry_pick=Cherry-pick %s suto:
+editor.revert=Ripristina %s su:
commits.desc=Sfoglia la cronologia di modifiche del codice rogente.
commits.commits=Commit
commits.no_commits=Nessun commit in comune. '%s' e '%s' hanno storie completamente diverse.
+commits.nothing_to_compare=Questi rami sono uguali.
commits.search=Ricerca commits…
commits.search.tooltip=Puoi anteporre le parole chiave con "author:", "committer:", "after:", o "before:", o "revert author:Alice before:2019-04-01".
commits.find=Cerca
@@ -889,11 +1146,21 @@ commits.signed_by=Firmato da
commits.signed_by_untrusted_user=Firmato da un utente non attendibile
commits.signed_by_untrusted_user_unmatched=Firmato da un utente non attendibile che non corrisponde al committer
commits.gpg_key_id=ID Chiave GPG
+commits.ssh_key_fingerprint=Impronta Digitale Chiave SSH
+commit.actions=Azioni
+commit.revert=Ripristina
+commit.revert-header=Ripristina: %s
+commit.revert-content=Selezionare il ramo su cui ripristinare:
+commit.cherry-pick=Cherry-pick
+commit.cherry-pick-header=Cherry-pick: %s
+commit.cherry-pick-content=Seleziona il ramo su cui scegliere:
+ext_issues=Accesso ai Problemi Esterni
ext_issues.desc=Collegamento al puntatore di una issue esterna.
projects=Progetti
+projects.desc=Gestisci problemi e pull nelle schede di progetto.
projects.description=Descrizione (opzionale)
projects.description_placeholder=Descrizione
projects.create=Crea un progetto
@@ -920,10 +1187,13 @@ projects.board.new_title=Nuovo Nome Della Scheda
projects.board.new_submit=Invia
projects.board.new=Nuova Scheda
projects.board.set_default=Imposta come predefinito
+projects.board.set_default_desc=Imposta questa scheda come predefinita per problemi non categorizzati e pull
projects.board.delete=Elimina Scheda
projects.board.deletion_desc=L'eliminazione di una scheda di progetto sposta tutti i problemi correlati a 'Uncategorized'. Continuare?
+projects.board.color=Colore
projects.open=Apri
projects.close=Chiudi
+projects.board.assigned_to=Assegnato a
issues.desc=Organizza le segnalazioni di bug, attività e pietre miliari.
issues.filter_assignees=Filtra assegnatario
@@ -970,6 +1240,11 @@ issues.label_templates.info=Non esistono etichette. Crea una etichetta con 'Nuov
issues.label_templates.helper=Scegli un set di etichette
issues.label_templates.use=Usa Set Etichette
issues.label_templates.fail_to_load_file=Impossibile caricare il file template di etichetta '%s': %v
+issues.add_label=ha aggiunto l'etichetta %s %s
+issues.add_labels=ha aggiunto le %s etichette %s
+issues.remove_label=rimosso l'etichetta %s %s
+issues.remove_labels=rimosso le %s etichette %s
+issues.add_remove_labels=aggiunto %s e rimosso %s etichette %s
issues.add_milestone_at=`aggiunta alle pietre miliari %s %s`
issues.add_project_at=`aggiunto questo al progetto %s %s`
issues.change_milestone_at=`pietra miliare modificata da %s a %s %s`
@@ -983,6 +1258,9 @@ issues.add_assignee_at=`è stato assegnato da %s %s`
issues.remove_assignee_at=`è stato rimosso da %s %s`
issues.remove_self_assignment=`Rimosso il loro incarico %s`
issues.change_title_at=`Titolo modificato da %s a %s %s`
+issues.change_ref_at=`ha cambiato il riferimento da %s a %s %s`
+issues.remove_ref_at=`riferimento rimosso %s %s`
+issues.add_ref_at=`aggiunto riferimento %s %s`
issues.delete_branch_at=`branch %s eliminato %s`
issues.filter_label=Etichetta
issues.filter_label_exclude=`Usa alt
+ click/enter
per escludere le etichette`
@@ -991,6 +1269,8 @@ issues.filter_milestone=Traguardo
issues.filter_milestone_no_select=Tutte le pietre miliari
issues.filter_assignee=Assegnatario
issues.filter_assginee_no_select=Tutte le assegnazioni
+issues.filter_poster=Autore
+issues.filter_poster_no_select=Tutti gli autori
issues.filter_type=Tipo
issues.filter_type.all_issues=Tutti i problemi
issues.filter_type.assigned_to_you=Assegnati a te
@@ -1010,6 +1290,7 @@ issues.filter_sort.moststars=Più favoriti
issues.filter_sort.feweststars=Meno favoriti
issues.filter_sort.mostforks=Maggior numero di fork
issues.filter_sort.fewestforks=Minor numero di fork
+issues.keyword_search_unavailable=Attualmente la ricerca per parola chiave non è disponibile. Contatta l'amministratore del sito.
issues.action_open=Aperto
issues.action_close=Chiuso
issues.action_label=Etichetta
@@ -1018,19 +1299,28 @@ issues.action_milestone_no_select=Nessuna pietra miliare
issues.action_assignee=Assegnatario
issues.action_assignee_no_select=Nessun assegnatario
issues.opened_by=aperto %[1]s da %[3]s
+pulls.merged_by=di %[3]s è stato fuso %[1]s
+pulls.merged_by_fake=di %[2]s è stato fuso %[1]s
+issues.closed_by=di %[3]s è stato chiuso %[1]s
+issues.opened_by_fake=aperto %[1]s da %[2]s
+issues.closed_by_fake=di %[2]s è stato chiuso %[1]s
issues.previous=Pagina precedente
issues.next=Pagina successiva
issues.open_title=Aperto
issues.closed_title=Chiuso
+issues.draft_title=Bozza
issues.num_comments=%d commenti
issues.commented_at=`%s ha commentato`
issues.delete_comment_confirm=Sei sicuro/a di voler eliminare questo commento?
issues.context.copy_link=Copia link
issues.context.quote_reply=Quota risposta
+issues.context.reference_issue=Fai riferimento in un nuovo problema
issues.context.edit=Modifica
issues.context.delete=Elimina
issues.no_content=Non ci sono ancora contenuti.
issues.close_issue=Chiudi
+issues.pull_merged_at=`merged commit %[2]s
in %[3]s %[4]s`
+issues.manually_pull_merged_at=`merged commit %[2]s
in %[3]s manualmente %[4]s`
issues.close_comment_issue=Commenta e Chiudi
issues.reopen_issue=Riapri
issues.reopen_comment_issue=Commenta e Riapri
@@ -1052,6 +1342,8 @@ issues.re_request_review=Revisione ri-richiesta
issues.is_stale=Ci sono stati cambiamenti a questa PR da questa revisione
issues.remove_request_review=Elimina richiesta revisione
issues.remove_request_review_block=Impossibile rimuovere la richiesta di revisione
+issues.dismiss_review=Respingi Recensione
+issues.dismiss_review_warning=Sei sicuro di voler respingere questa recensione?
issues.sign_in_require_desc=Effettua l'accesso per partecipare alla conversazione.
issues.edit=Modifica
issues.cancel=Annulla
@@ -1095,13 +1387,21 @@ issues.lock.reason=Motivo per il blocco
issues.lock.title=Blocca la conversazione su questa issue.
issues.unlock.title=Sblocca la conversazione su questa issue.
issues.comment_on_locked=Non puoi commentare un problema bloccato.
+issues.delete=Elimina
+issues.delete.title=Eliminare questo problema?
+issues.delete.text=Vuoi davvero eliminare questo problema? (Questo rimuoverà permanentemente tutti i contenuti. Considera invece di chiuderlo, se vuoi tenerlo archiviato)
issues.tracker=Cronografo
+issues.start_tracking_short=Avvia timer
issues.start_tracking=Avvia cronografo
issues.start_tracking_history='ha iniziato a lavorare %s`
issues.tracker_auto_close=Il timer verrà interrotto automaticamente una volta che il problema verrá chiuso
+issues.tracking_already_started=`Hai già avviato il monitoraggio del tempo su un altro problema!`
+issues.stop_tracking=Ferma timer
issues.stop_tracking_history=`ha smesso di funzionare %s`
+issues.cancel_tracking=Scarta
issues.cancel_tracking_history=`ha cancellato il cronografo %s`
issues.add_time=Aggiungi Tempo manualmente
+issues.del_time=Elimina questo registro di tempo
issues.add_time_short=Aggiungi tempo
issues.add_time_cancel=Annulla
issues.add_time_history=`aggiunto tempo trascorso %s`
@@ -1117,6 +1417,7 @@ issues.error_modifying_due_date=Impossibile modificare la data di scadenza.
issues.error_removing_due_date=Impossibile rimuovere la data di scadenza.
issues.push_commit_1=aggiunto %d commit %s
issues.push_commits_n=aggiunto %d commit %s
+issues.force_push_codes=`force-pushed %[1]s from %[2]s
to %[4]s
%[6]s`
issues.due_date_form=yyyy-mm-dd
issues.due_date_form_add=Aggiungi data di scadenza
issues.due_date_form_edit=Modifica
@@ -1124,16 +1425,21 @@ issues.due_date_form_remove=Rimuovi
issues.due_date_not_writer=E' necessario l'accesso di scrittura del repository per aggiornare la data di una sua issue.
issues.due_date_not_set=Nessuna data di scadenza impostata.
issues.due_date_added=la data di scadenza %s è stata aggiunta %s
+issues.due_date_modified=ha modificato la data di scadenza da %[2]s a %[1]s %[3]s s
issues.due_date_remove=rimossa la data di scadenza %s %s
issues.due_date_overdue=Scaduto
issues.due_date_invalid=La data di scadenza non è valida o fuori intervallo. Si prega di utilizzare il formato 'aaaa-mm-dd'.
issues.dependency.title=Dipendenze
+issues.dependency.issue_no_dependencies=Nessuna dipendenza impostata.
+issues.dependency.pr_no_dependencies=Nessuna dipendenza impostata.
issues.dependency.add=Aggiungi dipendenza…
issues.dependency.cancel=Annulla
issues.dependency.remove=Rimuovi
issues.dependency.remove_info=Rimuovi questa dipendenza
issues.dependency.added_dependency=`ha aggiunto una nuova dipendenza %s`
issues.dependency.removed_dependency=`ha rimosso una dipendenza %s`
+issues.dependency.pr_closing_blockedby=La chiusura di questa pull request è bloccata dai seguenti problemi
+issues.dependency.issue_closing_blockedby=La chiusura di questo problema è bloccata dai seguenti problemi
issues.dependency.issue_close_blocks=Questo problema impedisce la chiusura dei seguenti problemi
issues.dependency.pr_close_blocks=Questa richiesta di pull impedisce la chiusura dei seguenti problemi
issues.dependency.issue_close_blocked=Devi chiudere tutte le anomalie che bloiccano questo problema prima di chiudelo.
@@ -1154,6 +1460,8 @@ issues.review.self.approval=Non puoi approvare la tua pull request.
issues.review.self.rejection=Non puoi richiedere modifiche sulla tua pull request.
issues.review.approve=hanno approvato queste modifiche %s
issues.review.comment=revisionato %s
+issues.review.dismissed=recensione %s di %s respinta
+issues.review.dismissed_label=Respinta
issues.review.left_comment=lascia un commento
issues.review.content.empty=Devi lasciare un commento che indichi la modifica richiesta.
issues.review.reject=richieste modifiche %s
@@ -1162,8 +1470,10 @@ issues.review.add_review_request=recensione richiesta da %s %s
issues.review.remove_review_request=ha rimosso la richiesta di revisione per %s %s
issues.review.remove_review_request_self=ha rifiutato di rivedere %s
issues.review.pending=In sospeso
+issues.review.pending.tooltip=Questo commento non è attualmente visibile ad altri utenti. Per inviare i tuoi commenti in sospeso, seleziona '%s' -> '%s/%s/%s' nella parte superiore della pagina.
issues.review.review=Revisiona
issues.review.reviewers=Revisori
+issues.review.outdated=Scaduto
issues.review.show_outdated=Visualizza obsoleti
issues.review.hide_outdated=Nascondere obsoleti
issues.review.show_resolved=Mostra risolti
@@ -1172,17 +1482,38 @@ issues.review.resolve_conversation=Risolvi la conversazione
issues.review.un_resolve_conversation=Segnala la conversazione come non risolta
issues.review.resolved_by=ha contrassegnato questa conversazione come risolta
issues.assignee.error=Non tutte le assegnazioni sono state aggiunte a causa di un errore imprevisto.
+issues.reference_issue.body=Corpo
+issues.content_history.deleted=eliminato
+issues.content_history.edited=modificato
+issues.content_history.created=creato
+issues.content_history.delete_from_history=Elimina dalla cronologia
+issues.content_history.delete_from_history_confirm=Eliminare dalla cronologia?
+issues.content_history.options=Opzioni
+issues.reference_link=Riferimento: %s
+compare.compare_base=base
+compare.compare_head=confronta
pulls.desc=Attiva pull request e revisioni di codice.
pulls.new=Nuova Pull Request
+pulls.view=Visualizza Pull Request
pulls.compare_changes=Nuova Pull Request
+pulls.allow_edits_from_maintainers=Consenti modifiche dai manutentori
+pulls.allow_edits_from_maintainers_desc=Gli utenti con accesso in scrittura al ramo base possono anche inviare a questo ramo
+pulls.allow_edits_from_maintainers_err=Aggiornamento non riuscito
pulls.compare_changes_desc=Selezione il branch su cui eseguire il merge e il branch da cui eseguire il pull.
+pulls.has_viewed_file=Visualizzato
+pulls.has_changed_since_last_review=Modificato dalla tua ultima recensione
+pulls.viewed_files_label=%[1]d / %[2]d file visti
pulls.compare_base=unisci a
pulls.compare_compare=esegui un pull da
+pulls.switch_comparison_type=Cambia tipo di confronto
+pulls.switch_head_and_base=Testa e base di commutazione
pulls.filter_branch=Filtra branch
pulls.no_results=Nessun risultato trovato.
pulls.nothing_to_compare=Questi rami sono uguali. Non c'è alcuna necessità di creare una pull request.
+pulls.nothing_to_compare_and_allow_empty_pr=Questi rami sono uguali. Questa PR sarà vuota.
+pulls.has_pull_request=`Una pull request tra questi rami esiste già: %[2]s#%[3]d`
pulls.create=Crea Pull Request
pulls.title_desc=vorrebbe unire %[1]d commit da %[2]s
a %[3]s
pulls.merged_title_desc=ha unito %[1]d commit da %[2]s
a %[3]s
%[4]s
@@ -1195,18 +1526,28 @@ pulls.cant_reopen_deleted_branch=Questa pull request non può essere riaperta pe
pulls.merged=Unito
pulls.merged_as=La pull request è stata unita come %[2]s
.
pulls.manually_merged=Unito manualmente
+pulls.manually_merged_as=La pull request è stata unita manualmente come %[2]s
.
pulls.is_closed=La pull request è stata chiusa.
pulls.has_merged=La pull request è stata unita.
pulls.title_wip_desc=`Inizia il titolo con %s per evitare che la pull request venga unita accidentalmente.`
+pulls.cannot_merge_work_in_progress=Questa pull request è contrassegnata come un lavoro in corso.
+pulls.still_in_progress=Ancora in corso?
+pulls.add_prefix=Aggiungi prefisso %s
+pulls.remove_prefix=Rimuovi il prefisso %s
pulls.data_broken=Questa pull request è rovinata a causa di informazioni mancanti del fork.
pulls.files_conflicted=Questa pull request ha modifiche in conflitto con il branch di destinazione.
pulls.is_checking=Verifica dei conflitti di merge in corso. Riprova tra qualche istante.
+pulls.is_ancestor=Questo ramo è già incluso nel ramo di destinazione. Non c'è nulla da unire.
+pulls.is_empty=Le modifiche di questo ramo sono già nel ramo di destinazione. Questo sarà un commit vuoto.
pulls.required_status_check_failed=Alcuni controlli richiesti non hanno avuto successo.
pulls.required_status_check_missing=Mancano alcuni controlli richiesti.
pulls.required_status_check_administrator=Come amministratore, puoi ancora unire questa pull request.
pulls.blocked_by_approvals=La richiesta Pull non ha abbastanza approvazioni. %d di %d approvazioni concesse.
pulls.blocked_by_rejection=Questa Pull Request ha delle modifiche richieste da un revisore.
+pulls.blocked_by_official_review_requests=Questa richiesta Pull ha richieste di recensione ufficiale.
pulls.blocked_by_outdated_branch=Questa Pull Request è bloccata perché obsoleta.
+pulls.blocked_by_changed_protected_files_1=Questa richiesta Pull è bloccata perché modifica un file protetto:
+pulls.blocked_by_changed_protected_files_n=Questa richiesta Pull è bloccata perché modifica file protetti:
pulls.can_auto_merge_desc=La pull request può essere unita automaticamente.
pulls.cannot_auto_merge_desc=Questa pull request non può essere unita automaticamente a causa di conflitti.
pulls.cannot_auto_merge_helper=Unire manualmente per risolvere i conflitti.
@@ -1218,19 +1559,33 @@ pulls.reject_count_1=%d richiesta di cambiamento
pulls.reject_count_n=%d richieste di cambiamento
pulls.waiting_count_1=%d in attesa di revisione
pulls.waiting_count_n=%d in attesa di revisione
+pulls.wrong_commit_id=l'id del commit deve essere un id del commit nel branch di destinazione
pulls.no_merge_desc=Questa pull request non può essere unita perché tutte le opzioni di merge del repository sono disattivate.
pulls.no_merge_helper=Attiva le opzioni di merge nelle impostazioni del repository o unisci la pull request manualmente.
pulls.no_merge_wip=Questa pull request non può essere unita perché è contrassegnata come un lavoro in corso.
pulls.no_merge_not_ready=Questa pull request non è pronta per il merge, controlla lo stato della revisione e i controlli di stato.
pulls.no_merge_access=Non sei autorizzato ad effettuare il merge su questa pull request.
+pulls.merge_pull_request=Crea commit unito
+pulls.rebase_merge_pull_request=Ricostruisci poi manda avanti
+pulls.rebase_merge_commit_pull_request=Ricostruisci quindi crea commit unito
+pulls.squash_merge_pull_request=Crea commit mescolato
+pulls.merge_manually=Unito manualmente
+pulls.merge_commit_id=L'ID del commit di merge
pulls.require_signed_wont_sign=Il branch richiede commit firmati ma questo merge non verrà firmato
pulls.invalid_merge_option=Non puoi utilizzare questa opzione di merge per questa pull request.
+pulls.merge_conflict=Unione non riuscita: C'è stato un conflitto durante l'operazione. Suggerimento: Prova una strategia diversa
pulls.merge_conflict_summary=Messaggio d'errore
+pulls.rebase_conflict=Merge non riuscito: c'è stato un conflitto durante il rebase dell'commit: %[1]s. Suggerimento: Prova una strategia diversa
+pulls.rebase_conflict_summary=Messaggio d'Errore
; %[2]s
%[3]s
pulls.unrelated_histories=Unione fallita: gli Head del ramo da unire e la base non condividono una storia cronologica in comune. Suggerimento: prova una strategia diversa
pulls.merge_out_of_date=Unione fallita: Durante la generazione del merge, la base è stata aggiornata. Suggerimento: Riprova.
+pulls.head_out_of_date=Unione non riuscita: durante la generazione della fusione, la testa è stata aggiornata. Suggerimento: Riprova.
+pulls.push_rejected=Unisci non riuscito: il push è stato rifiutato. Rivedi gli Hooks Git per questo repository.
+pulls.push_rejected_summary=Messaggio Di Rifiuto Completo
+pulls.push_rejected_no_message=Unione non riuscita: il push è stato rifiutato ma non c'è stato un messaggio remoto.
Controlla gli Hooks di Git per questo repository
pulls.open_unmerged_pull_exists=`Non è possibile riaprire questa pull request perché ne esiste un'altra (#%d) con proprietà identiche.`
pulls.status_checking=Alcuni controlli sono in sospeso
pulls.status_checks_success=Tutti i controlli sono stati effettuati con successo
@@ -1239,13 +1594,31 @@ pulls.status_checks_failure=Alcuni controlli sono falliti
pulls.status_checks_error=Alcuni controlli hanno segnalato errori
pulls.status_checks_requested=Richiesto
pulls.status_checks_details=Dettagli
+pulls.update_branch=Aggiorna il ramo tramite merge
+pulls.update_branch_rebase=Aggiorna il ramo per cambio base
pulls.update_branch_success=Brench aggiornato con successo
pulls.update_not_allowed=Non sei abilitato ad aggiornare il branch
pulls.outdated_with_base_branch=Questo brench non è aggiornato con il branch di base
+pulls.closed_at=`chiusa questa pull request %[2]s`
+pulls.reopened_at=`riaperta questa pull request %[2]s`
+pulls.merge_instruction_hint=`Puoi anche visualizzare le istruzioni da riga di comando.`
+pulls.merge_instruction_step1_desc=Dal repository del tuo progetto, fai il check out di un nuovo branch e verifica le modifiche.
+pulls.merge_instruction_step2_desc=Fai il merge delle modifiche e aggiorna su Gitea.
+pulls.auto_merge_button_when_succeed=(Quando i controlli sono superati)
+pulls.auto_merge_when_succeed=Unione automatica quando tutti i controlli sono superati
+pulls.auto_merge_newly_scheduled=La pull request era programmata per unire quando tutti i controlli sono superati.
+pulls.auto_merge_has_pending_schedule=%[1]s ha programmato questa pull request per unire automaticamente quando tutti i controlli hanno successo %[2]s.
+pulls.auto_merge_cancel_schedule=Annulla fusione automatica
+pulls.auto_merge_not_scheduled=Questa pull request non è programmata per la fusione automarica.
+pulls.auto_merge_canceled_schedule=L'unione automatica è stata annullata per questa richiesta di pull.
+pulls.auto_merge_newly_scheduled_comment=`ha programmato questa pull request per unire automaticamente quando tutti i controlli sono superati %[1]s`
+pulls.auto_merge_canceled_schedule_comment=`cancella l'auto-merging di questa pull request quando tutti i testi sono superati %[1]s`
+pulls.delete.title=Eliminare questa pull request?
+pulls.delete.text=Vuoi davvero eliminare questo problema? (Questo rimuoverà permanentemente tutti i contenuti. Considera invece di chiuderlo, se vuoi tenerlo archiviato)
milestones.new=Nuova Milestone
milestones.closed=Chiuso %s
@@ -1291,6 +1664,7 @@ signing.wont_sign.commitssigned=Questo merge non sarà firmato poiché i commit
signing.wont_sign.approved=Il merge non sarà firmato poiché il PR non è approvato
signing.wont_sign.not_signed_in=Non hai effettuato l'accesso
+ext_wiki=Accesso al Wiki esterno
ext_wiki.desc=Collegamento a una wiki esterna.
wiki=Wiki
@@ -1315,6 +1689,7 @@ wiki.page_already_exists=Esiste già una pagina Wiki con questo stesso nome.
wiki.reserved_page=Il nome della pagina wiki '%s' è riservato.
wiki.pages=Pagine
wiki.last_updated=Ultimo aggiornamento: %s
+wiki.page_name_desc=Inserisci un nome per questa pagina Wiki. Alcuni nomi speciali sono: 'Home', '_Sidebar' e '_Footer'.
activity=Attività
activity.period.filter_label=Periodo:
@@ -1385,7 +1760,10 @@ activity.git_stats_deletion_n=%d cancellazioni
search=Ricerca
search.search_repo=Ricerca repository
search.fuzzy=Fuzzy
+search.match=Corrispondenze
search.results=Risultati della ricerca per "%s" in %s
+search.code_no_results=Nessun codice sorgente corrispondente al termine di ricerca trovato.
+search.code_search_unavailable=Attualmente la ricerca di codice non è disponibile. Contatta l'amministratore del sito.
settings=Impostazioni
settings.desc=Impostazioni ti permette di gestire le impostazioni del repository
@@ -1400,14 +1778,20 @@ settings.hooks=Webhooks
settings.githooks=Git Hooks
settings.basic_settings=Impostazioni di Base
settings.mirror_settings=Impostazioni di mirror
+settings.mirror_settings.docs=Configura il tuo progetto per inviare e/o ritirare automaticamente le modifiche a/da un altro repository. I rami, i tag e i commit verranno sincronizzati automaticamente. Come faccio i repository mirror?
+settings.mirror_settings.mirrored_repository=Repository replicata
+settings.mirror_settings.direction=Direzione
+settings.mirror_settings.direction.pull=Tira
+settings.mirror_settings.direction.push=Push
+settings.mirror_settings.last_update=Ultimo aggiornamento
+settings.mirror_settings.push_mirror.none=Nessun mirror push configurato
+settings.mirror_settings.push_mirror.remote_url=Url Del Repository Remoto Git
+settings.mirror_settings.push_mirror.add=Aggiungi Push Mirror
settings.sync_mirror=Sincronizza ora
settings.mirror_sync_in_progress=Sincronizzazione del mirror in corso. Torna tra qualche minuto.
-settings.email_notifications.enable=Abilita Notifiche Email
-settings.email_notifications.onmention=Solo email su Menzione
-settings.email_notifications.disable=Disabilita notifiche email
-settings.email_notifications.submit=Imposta Preferenze Email
settings.site=Sito web
settings.update_settings=Aggiorna Impostazioni
+settings.branches.update_default_branch=Aggiorna Ramo Predefinito
settings.advanced_settings=Opzioni avanzate
settings.wiki_desc=Abilita Wiki Repository
settings.use_internal_wiki=Utilizza la wiki incorporata
@@ -1426,6 +1810,9 @@ settings.tracker_url_format_error=L'URL del tracker di problemi esterno non è u
settings.tracker_issue_style=Formato numerico del tracciatore di issue esterno
settings.tracker_issue_style.numeric=Numerico
settings.tracker_issue_style.alphanumeric=Alfanumerico
+settings.tracker_issue_style.regexp=Espressione Regolare
+settings.tracker_issue_style.regexp_pattern=Motivo Espressione Regolare
+settings.tracker_issue_style.regexp_pattern_desc=Il primo gruppo catturato verrà utilizzato al posto di {index}
.
settings.tracker_url_format_desc=Usa i segnaposto {user}
, {repo}
e {index}
per il nome utente, il nome del repository e l'indice delle issue.
settings.enable_timetracker=Abilita il cronografo
settings.allow_only_contributors_to_track_time=Consenti soltanto ai contributori di utilizzare il cronografo
@@ -1435,8 +1822,20 @@ settings.pulls.allow_merge_commits=Abilita il merging dei commit
settings.pulls.allow_rebase_merge=Abilita l'unione dei commit mediante riassegnazione
settings.pulls.allow_rebase_merge_commit=Abilita il rebase con commit ad unione esplicita (--no-ff)
settings.pulls.allow_squash_commits=Abilita lo Squashing per unire i commits via merge
+settings.pulls.allow_manual_merge=Abilita Mark PR come unito manualmente
+settings.pulls.enable_autodetect_manual_merge=Abilita il rilevamento automatico della fusione manuale (Nota: in alcuni casi speciali possono verificarsi errori)
+settings.pulls.allow_rebase_update=Abilita l'aggiornamento del ramo pull request per rebase
+settings.pulls.default_delete_branch_after_merge=Elimina il ramo pull request dopo la fusione per impostazione predefinita
+settings.packages_desc=Abilita Il Registro Dei Pacchetti Repository
+settings.projects_desc=Abilita Progetti Repository
settings.admin_settings=Impostazioni amministratore
settings.admin_enable_health_check=Abilita verifica dell'integrità del repository (git fsck)
+settings.admin_code_indexer=Indicizzatore del codice
+settings.admin_stats_indexer=Indicizzatore di statistiche del codice
+settings.admin_indexer_commit_sha=Hash SHA dell'ultimo commit indicizzato
+settings.admin_indexer_unindexed=Non indicizzato
+settings.reindex_button=Aggiungi alla coda di re-indicizzazione
+settings.reindex_requested=Re-indicizzazione richiesta
settings.admin_enable_close_issues_via_commit_in_any_branch=Chiudi un issue tramite un commit eseguito in un branch non predefinito
settings.danger_zone=Zona Pericolosa
settings.new_owner_has_same_repo=Il nuovo proprietario ha già un repository con lo stesso nome. Per favore scegli un altro nome.
@@ -1451,11 +1850,20 @@ settings.convert_fork_notices_1=Questa operazione convertirà il fork in un norm
settings.convert_fork_confirm=Converti Repository
settings.convert_fork_succeed=Il fork è stato convertito in un repository regolare.
settings.transfer=Trasferisci proprietà
+settings.transfer.rejected=Il trasferimento del repository è stato rifiutato.
+settings.transfer.success=Il trasferimento del repository è andato a buon fine.
+settings.transfer_abort=Annulla trasferimento
+settings.transfer_abort_invalid=Non è possibile annullare un trasferimento di repository non esistente.
+settings.transfer_abort_success=Il trasferimento del repository a %s è stato annullato con successo.
settings.transfer_desc=Trasferisci questo repository a un altro utente o a un'organizzazione nella quale hai diritti d'amministratore.
settings.transfer_form_title=Inserisci il nome del repository come conferma:
+settings.transfer_in_progress=Al momento c'è un trasferimento in corso. Si prega di annullarlo se si desidera trasferire questo repository a un altro utente.
settings.transfer_notices_1=-Si perderà l'accesso al repository se lo si trasferisce ad un utente singolo.
settings.transfer_notices_2=-Si manterrà l'accesso al repository se si trasferisce in un'organizzazione che possiedi (o condividi con qualcun'altro).
+settings.transfer_notices_3=- Se il repository è privato e viene trasferito a un singolo utente, questa azione si assicura che l'utente abbia almeno i permessi di lettura (e le modifiche se necessario).
settings.transfer_owner=Nuovo Proprietario
+settings.transfer_perform=Esegui trasferimento
+settings.transfer_started=Questo repository è stato contrassegnato per il trasferimento e attende conferma da "%s"
settings.transfer_succeed=Il repository è stato trasferito.
settings.signing_settings=Impostazioni Verifica Firma
settings.trust_model=Modello di Fiducia per la Firma
@@ -1465,6 +1873,11 @@ settings.trust_model.collaborator=Collaboratore
settings.trust_model.collaborator.long=Collaboratore: Firme di fiducia da parte dei collaboratori
settings.trust_model.collaborator.desc=Le firme valide da parte dei collaboratori di questo repository saranno contrassegnate con "trusted" (sia che corrispondano al committer o meno). Altrimenti, le firme valide saranno contrassegnate con "untrusted" se la firma corrisponde al committer e "unmatched" se non.
settings.trust_model.committer=Committer
+settings.trust_model.committer.long=Committer: firme affidabili che corrispondono ai committer (questo corrisponde a GitHub e costringerà i commit firmati di Gitea ad avere Gitea come committer)
+settings.trust_model.committer.desc=Le firme valide saranno contrassegnate come "fidate" se corrispondono al committente, altrimenti saranno contrassegnate come "non corrispondono". Questo costringerà Gitea ad essere il committer dei commit firmati con l'effettivo committer contrassegnato come Co-Authored-By: e Co-Committed-By: nel commit. La chiave Gitea predefinita deve corrispondere a un utente nel database.
+settings.trust_model.collaboratorcommitter=Collaboratore+Committer
+settings.trust_model.collaboratorcommitter.long=Collaboratore+Committer: Firme di fiducia da parte dei collaboratori che corrispondono al committer
+settings.trust_model.collaboratorcommitter.desc=Le firme valide da parte dei collaboratori di questa repository saranno contrassegnate "fidate" se corrispondono al committer. Altrimenti le firme saranno contrassegnate con "untrusted" se la firma corrisponde al committer non corrisponde. Questo costringerà Gitea a essere contrassegnato come committer su impegni firmati con l'effettivo committer contrassegnato come Co-Authored-By: e Co-Committed-By: nel commit. La chiave Gitea predefinita deve corrispondere a un utente nel database.
settings.wiki_delete=Elimina dati Wiki
settings.wiki_delete_desc=L'eliminazione dei dati della wiki del repository è permanente e non può essere annullata.
settings.wiki_delete_notices_1=-Questa operazione eliminerà permanentemente e disabiliterà la wiki repository per %s.
@@ -1495,6 +1908,8 @@ settings.add_team=Aggiungi Squadra
settings.add_team_duplicate=Il team ha già il repository
settings.add_team_success=Il team ha ora accesso al repository.
settings.search_team=Cerca Squadra…
+settings.change_team_permission_tip=Il permesso del team è impostato sulla pagina delle impostazioni del team e non può essere modificato per repository
+settings.delete_team_tip=Questo team ha accesso a tutte le repository e non può essere rimosso
settings.remove_team_success=L'accesso del team al repository è stato rimosso.
settings.add_webhook=Aggiungi Webhook
settings.add_webhook.invalid_channel_name=Il canale Webhook non può essere vuoto e contenere solo un # carattere.
@@ -1509,6 +1924,9 @@ settings.webhook.response=Risposta
settings.webhook.headers=Intestazioni
settings.webhook.payload=Contenuto
settings.webhook.body=Corpo
+settings.webhook.replay.description=Riproduci questo webhook.
+settings.webhook.delivery.success=Un evento è stato aggiunto alla coda di consegna. Potrebbe volerci qualche secondo prima che venga visualizzato nella cronologia delle consegne.
+settings.githooks_desc=Git Hooks è alimentato da Git stesso. È possibile modificare i file hook qui sotto per impostare operazioni personalizzate.
settings.githook_edit_desc=Se l'hook è inattivo, sarà presentato un contenuto esempio. Lasciando il contenuto vuoto disattiverai questo hook.
settings.githook_name=Nome hook
settings.githook_content=Contenuto hook
@@ -1520,6 +1938,7 @@ settings.content_type=Tipo di contenuto POST
settings.secret=Segreto
settings.slack_username=Nome utente
settings.slack_icon_url=URL icona
+settings.slack_color=Colore
settings.discord_username=Nome utente
settings.discord_icon_url=URL icona
settings.event_desc=Attivato su:
@@ -1539,11 +1958,15 @@ settings.event_push=Push
settings.event_push_desc=Git push in un repository.
settings.event_repository=Repository
settings.event_repository_desc=Repository creato o eliminato.
+settings.event_header_issue=Eventi dei Problemi
settings.event_issues=Issues
settings.event_issues_desc=Issue aperto, chiuso, riaperto o modificato.
settings.event_issue_assign=Issue Assegnato
settings.event_issue_assign_desc=Issue assegnata o non assegnata.
settings.event_issue_label=Issue etichettato
+settings.event_issue_label_desc=Etichette dei Problemi aggiornate o cancellate.
+settings.event_issue_milestone=Obiettivo Raggiunto
+settings.event_issue_milestone_desc=Obiettivo raggiunto o abbandonato.
settings.event_issue_comment=Commento Issue
settings.event_issue_comment_desc=Commento issue creato, modificato o rimosso.
settings.event_header_pull_request=Eventi di Pull Request
@@ -1553,8 +1976,18 @@ settings.event_pull_request_assign=Pull Request assegnata
settings.event_pull_request_assign_desc=Pull request assegnata o non assegnata.
settings.event_pull_request_label=Pull Request etichettata
settings.event_pull_request_label_desc=Etichette Pull request aggiornate o cancellate.
+settings.event_pull_request_milestone=Pull Request raggiunta
+settings.event_pull_request_milestone_desc=Pull request raggiunto o abbandonato.
+settings.event_pull_request_comment=Commento su questa richiesta di pull
+settings.event_pull_request_comment_desc=Commento della Pull request creato, modificato o cancellato.
+settings.event_pull_request_review=Pull Request Revisionata
+settings.event_pull_request_review_desc=Pull request approvata, respinta o recensione commento.
+settings.event_pull_request_sync=Richiesta Pull Sincronizzata
settings.event_pull_request_sync_desc=Pull request sincronizzata.
+settings.event_package=Pacchetto
+settings.event_package_desc=Pacchetto creato o eliminato in un repository.
settings.branch_filter=Filtro branch
+settings.branch_filter_desc=Whitelist dei rami per gli eventi di spinta, creazione dei rami e cancellazione dei rami, specificati come modello globo. Se vuoto o *
, gli eventi per tutti i rami sono segnalati. Vedi la documentazione github.com/gobwas/glob per la sintassi. Esempi: master
, {master,release*}
.
settings.active=Attivo
settings.active_helper=Le informazioni sugli eventi innescati saranno inviate a questo URL del webhook.
settings.add_hook_success=Il webhook è stato aggiunto.
@@ -1566,6 +1999,23 @@ settings.hook_type=Tipo di Hook
settings.slack_token=Gettone
settings.slack_domain=Dominio
settings.slack_channel=Canale
+settings.add_web_hook_desc=Integra %s nel tuo repository.
+settings.web_hook_name_gitea=Gitea
+settings.web_hook_name_gogs=Gogs
+settings.web_hook_name_slack=Slack
+settings.web_hook_name_discord=Discord
+settings.web_hook_name_dingtalk=DingTalk
+settings.web_hook_name_telegram=Telegram
+settings.web_hook_name_matrix=Matrix
+settings.web_hook_name_msteams=Microsoft Teams
+settings.web_hook_name_feishu_or_larksuite=Feishu / Lark Suite
+settings.web_hook_name_feishu=Feishu
+settings.web_hook_name_larksuite=Lark Suite
+settings.web_hook_name_wechatwork=WeCom (Wechat Work)
+settings.web_hook_name_packagist=Packagist
+settings.packagist_username=Nome utente Packagist
+settings.packagist_api_token=API token
+settings.packagist_package_url=Url pacchetto pacchetti
settings.deploy_keys=Dispiega Chiavi
settings.add_deploy_key=Aggiungi Deploy Key
settings.deploy_key_desc=Le deploy key possiedono l'accesso solamente alla lettura di un repository.
@@ -1604,6 +2054,7 @@ settings.protect_merge_whitelist_committers_desc=Consentire soltanto agli utenti
settings.protect_merge_whitelist_users=Utenti nella whitelist per il merging:
settings.protect_merge_whitelist_teams=Team nella whitelist per il merging:
settings.protect_check_status_contexts=Abilita Controllo Stato
+settings.protect_check_status_contexts_desc=Richiedi il superamento di controlli di stato prima dell'unione di due rami. Scegliere quali controlli di stato devono passare prima che i rami possano essere uniti in un ramo che corrisponde a questa regola. Se abilitato, i commit devono prima essere inviati a un altro ramo, quindi uniti o pushati direttamente a un ramo che corrisponde a questa regola dopo aver superato i controlli di stato. Se non viene selezionato alcuna regola, l'ultimo commit avrá successo indipendentemente dal contesto.
settings.protect_check_status_contexts_list=Controlli di stato trovati nell'ultima settimana per questo repository
settings.protect_required_approvals=Approvazioni richieste:
settings.protect_required_approvals_desc=Permetti solo di unire la richiesta pull con abbastanza recensioni positive.
@@ -1614,6 +2065,11 @@ settings.protect_approvals_whitelist_teams=Team nella whitelist per le revisioni
settings.dismiss_stale_approvals=Ignora impostazione vecchie
settings.dismiss_stale_approvals_desc=Quando i nuovi commit che cambiano il contenuto della pull request vengono pushati nel branch, le vecchie approvazioni verranno eliminate.
settings.require_signed_commits=Richiede commit firmati
+settings.require_signed_commits_desc=Rifiuta i push a questo ramo se non sono firmati o verificabili.
+settings.protect_protected_file_patterns=Modelli di file protetti (separati da punto e virgola '\;'):
+settings.protect_protected_file_patterns_desc=File protetti che non possono essere modificati direttamente anche se l'utente ha i diritti di aggiungere, modificare o eliminare file in questo ramo. I modelli multipli possono essere separati usando il punto e virgola ('\;'). Vedi la documentazione github.com/gobwas/glob per la sintassi del modello. Esempi: .drone.yml
, /docs/**/*.txt
.
+settings.protect_unprotected_file_patterns=Modelli di file non protetti (separati da punto e virgola '\;'):
+settings.protect_unprotected_file_patterns_desc=File non protetti che possono essere modificati direttamente se l'utente ha accesso in scrittura, bypassando la restrizione push. Più modelli possono essere separati usando il punto e virgola ('\;'). Vedi la documentazione github.com/gobwas/glob per la sintassi del modello. Esempi: .drone.yml
, /docs/**/*.txt
.
settings.add_protected_branch=Attiva protezione
settings.delete_protected_branch=Disattiva protezione
settings.update_protect_branch_success=La protezione branch per il branch '%s' è stata aggiornata.
@@ -1622,11 +2078,26 @@ settings.protected_branch_deletion=Disattiva protezione branch
settings.protected_branch_deletion_desc=Disattivare la protezione branch permette agli utenti con permesso di scrittura di pushare sul branch. Continuare?
settings.block_rejected_reviews=Blocca il merge di revisioni rifiutate
settings.block_rejected_reviews_desc=Il merge non sarà possibile quando sono richiesti cambiamenti da revisori, anche se ci sono sufficienti approvazioni.
+settings.block_on_official_review_requests=Blocca il merge sulle richieste ufficiali di revisione
+settings.block_on_official_review_requests_desc=Il merge non sarà possibile quando sono richiesti cambiamenti da revisori, anche se ci sono sufficienti approvazioni.
+settings.block_outdated_branch=Blocca il merge se la pull request è obsoleta
+settings.block_outdated_branch_desc=Il merging non sarà possibile quando il ramo testa è dietro il ramo base.
settings.default_branch_desc=Seleziona un branch del repository predefinito per le pull request ed i commit di codice:
+settings.default_merge_style_desc=Modalità di merge predefinita per le richieste di pull:
settings.choose_branch=Scegli un branch…
settings.no_protected_branch=Non ci sono branch protetti.
settings.edit_protected_branch=Modifica
settings.protected_branch_required_approvals_min=Le autorizzazioni richieste non possono essere negative.
+settings.tags=Etichette
+settings.tags.protection=Protezione Etichetta
+settings.tags.protection.pattern=Sequenza Etichetta
+settings.tags.protection.allowed=Consentito
+settings.tags.protection.allowed.users=Utenti ammessi
+settings.tags.protection.allowed.teams=Squadre ammesse
+settings.tags.protection.allowed.noone=Nessuno
+settings.tags.protection.create=Proteggi Etichetta
+settings.tags.protection.none=Non ci sono etichette protette.
+settings.tags.protection.pattern.description=È possibile utilizzare un singolo nome o un modello globo o un'espressione regolare per abbinare più tag. Leggi di più nella guida per i tag protetti.
settings.bot_token=Token del Bot
settings.chat_id=ID chat
settings.matrix.homeserver_url=URL Homeserver
@@ -1640,6 +2111,7 @@ settings.archive.success=Il repo è stato archiviato con successo.
settings.archive.error=Si è verificato un errore durante il tentativo di archiviare il repo. Vedi il log per maggiori dettagli.
settings.archive.error_ismirror=Non puoi archiviare un mirror repo.
settings.archive.branchsettings_unavailable=Le impostazioni dei branch non sono disponibili se il repo è archiviato.
+settings.archive.tagsettings_unavailable=Le impostazioni delle etichette non sono disponibili se il repo è archiviato.
settings.unarchive.button=Non archiviare Repo
settings.unarchive.header=Non archiviare questo Repo
settings.unarchive.text=Dis-Archiviare la repository ripristinerà la sua capacità di ricevere commit e push, così come la creazione di nuovi problemi e richieste di pull.
@@ -1671,6 +2143,12 @@ settings.lfs_pointers.inRepo=Nel repo
settings.lfs_pointers.exists=Esiste nel negozio
settings.lfs_pointers.accessible=Accessibile all'utente
settings.lfs_pointers.associateAccessible=Associa %d OID accessibili
+settings.rename_branch_failed_exist=Impossibile rinominare il ramo perché il ramo di destinazione %s esiste.
+settings.rename_branch_failed_not_exist=Impossibile rinominare il ramo %s perché non esiste.
+settings.rename_branch_success=Il ramo %s è stato rinominato con successo in %s.
+settings.rename_branch_from=vecchio nome del ramo
+settings.rename_branch_to=nuovo nome del ramo
+settings.rename_branch=Rinomina ramo
diff.browse_source=Sfoglia il codice sorgente
diff.parent=parent
@@ -1699,6 +2177,12 @@ diff.file_image_width=Larghezza
diff.file_image_height=Altezza
diff.file_byte_size=Dimensione
diff.file_suppressed=File diff soppresso perché troppo grande
+diff.file_suppressed_line_too_long=File diff soppresso perché una o più righe sono troppo lunghe
+diff.too_many_files=Alcuni file non sono stati mostrati perché troppi file sono cambiati in questo diff
+diff.show_more=Mostra Altro
+diff.load=Carica Diff
+diff.generated=generato
+diff.vendored=esterno
diff.comment.placeholder=Lascia un commento
diff.comment.markdown_info=Lo stile con markdown è supportato.
diff.comment.add_single_comment=Aggiungi un commento singolo
@@ -1713,16 +2197,23 @@ diff.review.approve=Approva
diff.review.reject=Richiedi cambiamenti
diff.committed_by=committato da
diff.protected=Protetto
+diff.image.side_by_side=A fianco
+diff.image.swipe=Scorri
+diff.image.overlay=Sovrapposta
+diff.has_escaped=Questa riga ha caratteri Unicode nascosti
releases.desc=Tenere traccia di versioni e download del progetto.
release.releases=Rilasci
release.detail=Dettagli rilascio
+release.tags=Etichette
release.new_release=Nuovo Rilascio
release.draft=Bozza
release.prerelease=Pre-Rilascio
release.stable=Stabile
release.compare=Confronta
release.edit=modifica
+release.ahead.commits=%d commit
+release.ahead.target=a %s da questa uscita
release.source_code=Codice Sorgente
release.new_subheader=Le release organizzano le versioni del progetto.
release.edit_subheader=Le release organizzano le versioni del progetto.
@@ -1738,13 +2229,20 @@ release.publish=Pubblica Rilascio
release.save_draft=Salva Bozza
release.edit_release=Aggiorna release
release.delete_release=Elimina release
+release.delete_tag=Elimina Etichetta
release.deletion=Elimina release
+release.deletion_desc=L'eliminazione di una release lo rimuove solo da Gitea. Il tag Git, i contenuti del repository e la cronologia rimangono invariati. Continuare?
release.deletion_success=La release è stata eliminata.
+release.deletion_tag_desc=Eliminerà questo tag dal repository. I contenuti del repository e la cronologia rimangono invariati. Continuare?
release.deletion_tag_success=L'etichetta è stata eliminata.
release.tag_name_already_exist=Una release con questo nome tag esiste già.
release.tag_name_invalid=Il nome tag non è valido.
+release.tag_name_protected=Il nome dell'etichetta è protetto.
+release.tag_already_exist=Questo nome tag esiste già.
release.downloads=Download
release.download_count=Scarica: %s
+release.add_tag_msg=Utilizzare il titolo e il contenuto del rilascio come messaggio di tag.
+release.add_tag=Crea Solo Branch
branch.name=Nome branch
branch.search=Cerca branch
@@ -1766,19 +2264,36 @@ branch.deleted_by=Eliminato da %s
branch.restore_success=Il branch '%s' è stato ripristinato.
branch.restore_failed=Impossibile ripristinare il branch '%s '.
branch.protected_deletion_failed=Il branch '%s' è protetto. Non può essere eliminato.
+branch.default_deletion_failed=Il branch '%s' è protetto. Non può essere eliminato.
branch.restore=Ripristina Branch '%s'
branch.download=Scarica Branch '%s'
branch.included_desc=Questo ramo fa parte del ramo predefinito
branch.included=Incluso
+branch.create_new_branch=Crea un ramo dal ramo:
+branch.confirm_create_branch=Crea ramo
+branch.create_branch_operation=Crea ramo
+branch.new_branch=Crea nuovo ramo
+branch.new_branch_from=Crea un nuovo ramo da '%s'
+branch.renamed=Il ramo %s è stato rinominato in %s.
+tag.create_tag=Crea branch %s
+tag.create_tag_operation=Crea etichetta
+tag.confirm_create_tag=Crea etichetta
+tag.create_tag_from=Crea un nuovo tag da '%s'
+tag.create_success=Il branch '%s' è stato creato.
topic.manage_topics=Gestisci argomenti
topic.done=Fatto
topic.count_prompt=Non puoi selezionare più di 25 argomenti
topic.format_prompt=Gli argomenti devono iniziare con una lettera o un numero, possono includere trattini ('-') e possono essere lunghi fino a 35 caratteri.
+find_file.go_to_file=Vai al file
+find_file.no_matching=Nessun file corrispondente trovato
+error.csv.too_large=Impossibile visualizzare questo file perché è troppo grande.
+error.csv.unexpected=Impossibile visualizzare questo file perché contiene un carattere inatteso alla riga %d e alla colonna %d.
+error.csv.invalid_field_count=Impossibile visualizzare questo file perché ha un numero errato di campi alla riga %d.
[org]
org_name_holder=Nome dell'Organizzazione
@@ -1823,6 +2338,7 @@ settings.visibility.private_shortname=Privato
settings.update_settings=Aggiorna Impostazioni
settings.update_setting_success=Le impostazioni dell'organizzazione sono state aggiornate.
settings.change_orgname_prompt=Nota: cambiare il nome dell'organizzazione cambia anche il relativo URL.
+settings.change_orgname_redirect_prompt=Il vecchio nome reindirizzerà fino a quando non sarà richiesto.
settings.update_avatar_success=L'avatar dell'organizzazione è stato aggiornato.
settings.delete=Elimina organizzazione
settings.delete_account=Elimina questa organizzazione
@@ -1832,6 +2348,7 @@ settings.delete_org_title=Elimina organizzazione
settings.delete_org_desc=Questa organizzazione verrà eliminata definitivamente. Continuare?
settings.hooks_desc=Aggiungi i webhooks che verranno attivati per tutti i repository sotto questa organizzazione.
+settings.labels_desc=Aggiungi i webhooks che verranno attivati per tutti i repository sotto questa organizzazione.
members.membership_visibility=Visibilità appartenenza:
members.public=Visibile
@@ -1842,15 +2359,24 @@ members.member_role=Ruolo del membro:
members.owner=Proprietario
members.member=Membro
members.remove=Rimuovi
+members.remove.detail=Rimuovere %[1]s dalla %[2]s?
members.leave=Abbandona
+members.leave.detail=Lasciare %s?
members.invite_desc=Aggiungi un nuovo membro a %s:
members.invite_now=Invita ora
teams.join=Iscriviti
teams.leave=Abbandona
+teams.leave.detail=Lasciare %s?
teams.can_create_org_repo=Crea repository
teams.can_create_org_repo_helper=I membri possono creare nuovi repository nell'organizzazione. Il creatore otterrà l'accesso di amministratore alla nuova repository.
+teams.none_access=Nessun Accesso
+teams.none_access_helper=I membri non possono visualizzare o fare altre azioni su questa unità.
+teams.general_access=Accesso Generale
+teams.general_access_helper=I permessi dei membri saranno decisi dalla seguente tabella dei permessi.
+teams.read_access=Lettura
teams.read_access_helper=I membri possono visualizzare e clonare i repository del team.
+teams.write_access=Scrittura
teams.write_access_helper=I membri possono leggere e pushare sui repository del team.
teams.admin_access=Accesso amministratore
teams.admin_access_helper=I membri possono pullare e pushare sulle repository del team e anche aggiungere collaboratori.
@@ -1891,6 +2417,7 @@ dashboard=Pannello di Controllo
users=Account utenti
organizations=Organizzazioni
repositories=Repository
+hooks=Webhooks
authentication=Fonti di autenticazione
emails=Email Utente
config=Configurazione
@@ -1900,9 +2427,11 @@ first_page=Prima
last_page=Ultima
total=Totale: %d
+dashboard.new_version_hint=Gitea %s è ora disponibile, stai eseguendo %s. Controlla il blog per maggiori dettagli.
dashboard.statistic=Riepilogo
dashboard.operations=Operazioni di manutenzione
dashboard.system_status=Stato del sistema
+dashboard.statistic_info=Il database Gitea contiene %d utenti, %d organizzazioni, %d chiavi pubbliche, %d depositi, %d orologi, %d stelle, ~%d azioni, %d accessi, %d problemi, %d commenti, %d conti sociali, %d seguono, %d specchi, %d rilasci, %d fonti di autenticazione, %d webhooks, %d pietre miliari, %d etichette, %d attività di aggancio, %d squadre, %d attività di aggiornamento, %d allegati.
dashboard.operation_name=Nome Operazione
dashboard.operation_switch=Cambia
dashboard.operation_run=Esegui
@@ -1914,24 +2443,34 @@ dashboard.task.cancelled=Compito: %[1]s annullato: %[3]s
dashboard.task.error=Errore in Attività: %[1]s: %[3]s
dashboard.task.finished=Compito: %[1]s iniziato da %[2]s ha finito
dashboard.task.unknown=Attività sconosciuta: %[1]s
+dashboard.cron.started=Cron Avviato: %[1]s
dashboard.cron.process=Cron: %[1]s
dashboard.cron.cancelled=Cron: %s cancellato: %[3]s
dashboard.cron.error=Errore in Cron: %s: %[3]s
dashboard.cron.finished=Cron: %[1]s ha finito
dashboard.delete_inactive_accounts=Elimina tutti gli account non attivati
dashboard.delete_inactive_accounts.started=Attività di eliminazione degli account non attivati iniziata.
+dashboard.delete_repo_archives=Elimina tutti gli archivi dei repository (ZIP, TAR.GZ, etc..)
dashboard.delete_repo_archives.started=Attività di eliminazione degli archivi del repository iniziata.
dashboard.delete_missing_repos=Elimina tutti i repository mancanti dei loro file Git
dashboard.delete_missing_repos.started=Elimina tutti i repository mancanti dei loro file Git.
dashboard.delete_generated_repository_avatars=Elimina gli avatar generati nelle repository
dashboard.update_mirrors=Aggiorna Mirror
dashboard.repo_health_check=Controlla integrità di tutti i repository
+dashboard.check_repo_stats=Controlla tutte le statistiche del repository
dashboard.archive_cleanup=Elimina vecchi archivi del repository
dashboard.deleted_branches_cleanup=Pulisci branch eliminati
+dashboard.update_migration_poster_id=Aggiorna gli ID del poster di migrazione
dashboard.git_gc_repos=Esegui la garbage collection su tutti i repository
+dashboard.resync_all_sshkeys=Aggiornare il file '.ssh/authorized_keys' con le chiavi SSH Gitea.
+dashboard.resync_all_sshkeys.desc=(Non necessario per il server SSH integrato.)
+dashboard.resync_all_sshprincipals=Aggiornare il file '.ssh/authorized_keys' con le chiavi SSH Gitea.
+dashboard.resync_all_sshprincipals.desc=(Non necessario per il server SSH integrato.)
dashboard.resync_all_hooks=Sincronizza nuovamente gli hook di pre-ricezione, di aggiornamento e di post-ricezione di tutti i repository.
dashboard.reinit_missing_repos=Reinizializza tutti i repository Git mancanti per i quali esistono cambiamenti registrati esistenti
dashboard.sync_external_users=Sincronizza dati utente esterno
+dashboard.cleanup_hook_task_table=Pulisci tabella hook_task
+dashboard.cleanup_packages=Pulizia pacchetti scaduti
dashboard.server_uptime=Tempo in Attività del Server
dashboard.current_goroutine=Goroutine Correnti
dashboard.current_memory_usage=Utilizzo di Memoria Corrente
@@ -1961,6 +2500,10 @@ dashboard.total_gc_time=Pausa Totale della GC
dashboard.total_gc_pause=Pausa Totale della GC
dashboard.last_gc_pause=Ultima pausa della GC
dashboard.gc_times=Esecuzioni GC
+dashboard.delete_old_actions=Elimina tutte le vecchie azioni dal database
+dashboard.delete_old_actions.started=Elimina tutte le vecchie azioni dal database iniziate.
+dashboard.update_checker=Controllore dell'aggiornamento
+dashboard.delete_old_system_notices=Elimina tutte le vecchie notifiche di sistema dal database
users.user_manage_panel=Gestione account utente
users.new_account=Crea account utente
@@ -1995,9 +2538,26 @@ users.allow_import_local=Può importare repository locali
users.allow_create_organization=Può creare organizzazioni
users.update_profile=Aggiorna account utente
users.delete_account=Elimina account utente
+users.cannot_delete_self=Non puoi eliminare te stesso
users.still_own_repo=Questo utente possiede ancora una o più repository. Eliminare o trasferire questi repository prima di continuare.
users.still_has_org=Questo utente è membro di un'organizzazione. Rimuovi l'utente da tutte le organizzazioni prima di proseguire.
+users.purge=Elimina Utente
+users.purge_help=Eliminare forzatamente l'utente e tutti i depositi, le organizzazioni e i pacchetti di proprietà dell'utente. Tutti i commenti verranno eliminati troppo.
+users.still_own_packages=Questo utente possiede ancora uno o più pacchetti. Elimina prima questi pacchetti.
users.deletion_success=L'account utente è stato eliminato.
+users.reset_2fa=Resetta 2FA
+users.list_status_filter.menu_text=Filtro
+users.list_status_filter.reset=Ripristina
+users.list_status_filter.is_active=Attivo
+users.list_status_filter.not_active=Inattivo
+users.list_status_filter.is_admin=Amministratore
+users.list_status_filter.not_admin=Non Amministratore
+users.list_status_filter.is_restricted=Limitato
+users.list_status_filter.not_restricted=Non Limitato
+users.list_status_filter.is_prohibit_login=Divieto Di Login
+users.list_status_filter.not_prohibit_login=Consenti Login
+users.list_status_filter.is_2fa_enabled=2FA Abilitato
+users.list_status_filter.not_2fa_enabled=2FA Disabilitato
emails.email_manage_panel=Gestione delle Email Utente
emails.primary=Primario
@@ -2019,6 +2579,8 @@ orgs.members=Membri
orgs.new_orga=Nuova Organizzazione
repos.repo_manage_panel=Gestione Repository
+repos.unadopted=Depositi Non Adottati
+repos.unadopted.no_more=Nessun repository non adottato trovato
repos.owner=Proprietario
repos.name=Nome
repos.private=Privati
@@ -2028,9 +2590,24 @@ repos.forks=Fork
repos.issues=Problemi
repos.size=Dimensione
+packages.package_manage_panel=Gestione Pacchetti
+packages.total_size=Dimensione totale: %s
+packages.owner=Proprietario
+packages.creator=Creatore
+packages.name=Nome
+packages.version=Versione
+packages.type=Tipo
+packages.repository=Repository
+packages.size=Dimensione
+packages.published=Pubblicata
+defaulthooks=Webhook predefiniti
+defaulthooks.desc=I Webhooks effettuano automaticamente richieste HTTP POST ad un server quando si verificano determinati eventi Gitea. I Webhooks definiti qui sono predefiniti e verranno copiati in tutti i nuovi repository. Per saperne di più leggi la guida ai webhooks.
+defaulthooks.add_webhook=Aggiungi Webhook predefinito
+defaulthooks.update_webhook=Aggiorna Webhook predefinito
systemhooks=Webhooks di Sistema
+systemhooks.desc=I Webhooks effettuano automaticamente richieste HTTP POST ad un server quando si verificano determinati eventi Gitea. I Webhooks definiti qui agiranno su tutti i repository del sistema, quindi considera le eventuali implicazioni sulle performance che potrebbero avere. Per saperne di più leggi la guida ai webhooks.
systemhooks.add_webhook=Aggiungi Webhook di Sistema
systemhooks.update_webhook=Aggiorna Webhook di Sistema
@@ -2057,6 +2634,7 @@ auths.attribute_name=Attributo nome
auths.attribute_surname=Attributo cognome
auths.attribute_mail=Attributo email
auths.attribute_ssh_public_key=Attributo chiave SSH pubblica
+auths.attribute_avatar=Attributo Avatar
auths.attributes_in_bind=Estrai Attributi dal Contesto Bind DN
auths.allow_deactivate_all=Consenti un risultato di ricerca vuoto per disattivare tutti gli utenti
auths.use_paged_search=Utilizza ricerca per pagina
@@ -2065,9 +2643,13 @@ auths.filter=Fitro utente
auths.admin_filter=Filtro Amministratore
auths.restricted_filter=Filtro riservato
auths.restricted_filter_helper=Lasciare vuoto per non impostare alcun utente come limitato. Utilizzare un asterisco ('*') per impostare tutti gli utenti che non corrispondono al filtro amministratore.
+auths.verify_group_membership=Verifica l'appartenenza al gruppo in LDAP (lascia vuoto il filtro per saltare)
auths.group_search_base=Ricerca Gruppo Base DN
auths.group_attribute_list_users=Gruppo Attributo Contenente Elenco Utenti
auths.user_attribute_in_group=Attributo Utente Elencato nel Gruppo
+auths.map_group_to_team=Mappa i gruppi LDAP alle squadre dell'organizzazione (lasciare vuoto il campo per saltare)
+auths.map_group_to_team_removal=Rimuovi gli utenti dai team sincronizzati se l'utente non appartiene al gruppo LDAP corrispondente
+auths.enable_ldap_groups=Abilita gruppi LDAP
auths.ms_ad_sa=Attributi di ricerca AD MS
auths.smtp_auth=Tipo di autenticazione SMTP
auths.smtphost=Host SMTP
@@ -2075,7 +2657,13 @@ auths.smtpport=Porta SMTP
auths.allowed_domains=Domini consentiti
auths.allowed_domains_helper=Lasciare vuoto per ammettere tutti i domini. Separare più domini con una virgola (',').
auths.skip_tls_verify=Salta verifica TLS
+auths.force_smtps=Forza SMTPS
+auths.force_smtps_helper=SMTPS è sempre utilizzato sulla porta 465. Impostalo per forzare SMTPS su altre porte. (Otherwise STARTTLS sarà utilizzato su altre porte se è supportato dall'host.)
+auths.helo_hostname=HELO nome dell'host
+auths.helo_hostname_helper=Nome host inviato con HELO. Lasciare vuoto per inviare il nome host corrente.
+auths.disable_helo=Disattiva HELO
auths.pam_service_name=Nome del Servizio PAM
+auths.pam_email_domain=Dominio Email PAM (opzionale)
auths.oauth2_provider=OAuth2 Provider
auths.oauth2_icon_url=URL icona
auths.oauth2_clientID=ID Client (Chiave)
@@ -2086,6 +2674,17 @@ auths.oauth2_tokenURL=URL token
auths.oauth2_authURL=Autorizza URL
auths.oauth2_profileURL=URL profilo
auths.oauth2_emailURL=URL email
+auths.skip_local_two_fa=Salta 2FA locale
+auths.skip_local_two_fa_helper=Lasciare l'azzeramento significa che gli utenti locali con il set 2FA dovranno ancora passare 2FA per accedere
+auths.oauth2_tenant=Comproprietà
+auths.oauth2_scopes=Ambiti Aggiuntivi
+auths.oauth2_required_claim_name=Nome Richiesto
+auths.oauth2_required_claim_name_helper=Imposta questo nome per limitare il login da questa fonte agli utenti con un reclamo con questo nome
+auths.oauth2_required_claim_value=Valore Richiesto
+auths.oauth2_required_claim_value_helper=Imposta questo valore per limitare il login da questa fonte agli utenti con un reclamo con questo nome e valore
+auths.oauth2_group_claim_name=Riscatta nome che fornisce nomi di gruppo per questa fonte (facoltativo)
+auths.oauth2_admin_group=Valore del reclamo di gruppo per gli utenti amministratori. (Opzionale - richiede il nome della richiesta sopra)
+auths.oauth2_restricted_group=Valore di reclamo di gruppo per utenti ristretti. (Facoltativo - richiede il nome di reclamo sopra)
auths.enable_auto_register=Abilitare Registrazione Automatica
auths.sspi_auto_create_users=Crea automaticamente gli utenti
auths.sspi_auto_create_users_helper=Permetti al metodo di autenticazione SSPI di creare automaticamente nuovi account per gli utenti che accedono per la prima volta
@@ -2102,6 +2701,7 @@ auths.tips.oauth2.general=Autenticazione OAuth2
auths.tips.oauth2.general.tip="Quando si registra una nuova autenticazione OAuth2, l'URL di callback/reindirizzamento deve essere:/user/oauth2//callback
auths.tip.oauth2_provider=OAuth2 Provider
auths.tip.bitbucket=Registra un nuovo cliente OAuth su https://bitbucket.org/account/user//oauth-consumers/new e aggiungi il permesso 'Account' - 'Read'
+auths.tip.nextcloud=Registra un nuovo OAuth sulla tua istanza utilizzando il seguente menu "Impostazioni -> Sicurezza -> OAuth 2.0 client"
auths.tip.dropbox=Crea una nuova applicazione su https://www.dropbox.com/developers/apps
auths.tip.facebook=Registra una nuova applicazione su https://developers.facebook.com/apps e aggiungi il prodotto "Facebook Login"
auths.tip.github=Registra una nuova applicazione OAuth su https://github.com/settings/applications/new
@@ -2111,6 +2711,8 @@ auths.tip.openid_connect=Utilizza l'OpenID Connect Discovery URL (/.well
auths.tip.twitter=Vai su https://dev.twitter.com/apps, crea una applicazione e assicurati che l'opzione "Allow this application to be used to Sign In with Twitter" sia abilitata
auths.tip.discord=Registra una nuova applicazione su https://discordapp.com/developers/applications/me
auths.tip.gitea=Registra una nuova applicazione OAuth2. La guida può essere trovata a https://docs.gitea.io/en-us/oauth2-provider/
+auths.tip.yandex=Crea una nuova applicazione su https://oauth.yandex.com/client/new. Seleziona i seguenti permessi da "Yandex. assport API": "Access to email address", "Access to user avatar" e "Access to username, name and surname, gender"
+auths.tip.mastodon=Inserisci un URL di istanza personalizzato per l'istanza mastodon con cui vuoi autenticarti (o usa quella predefinita)
auths.edit=Modifica fonte di autenticazione
auths.activated=Questa fonte di autenticazione è attiva
auths.new_success=L'autenticazione '%s' è stata aggiunta.
@@ -2130,6 +2732,7 @@ config.app_ver=Versione Gitea
config.app_url=URL di base di Gitea
config.custom_conf=Percorso file di configurazione
config.custom_file_root_path=Percorso Root File Personalizzato
+config.domain=Dominio Server
config.offline_mode=Modalità locale
config.disable_router_log=Disattivare Log del Router
config.run_user=Esegui come Nome utente
@@ -2145,6 +2748,7 @@ config.reverse_auth_user=Autenticazione Utente Inversa
config.ssh_config=Configurazione SSH
config.ssh_enabled=Attivo
config.ssh_start_builtin_server=Usa il server integrato
+config.ssh_domain=Dominio Server Ssh
config.ssh_port=Porta
config.ssh_listen_port=Porta in ascolto
config.ssh_root_path=Percorso Root
@@ -2170,6 +2774,7 @@ config.db_path=Percorso
config.service_config=Configurazione Servizio
config.register_email_confirm=Richiedere la conferma Email per registrarsi
config.disable_register=Disattiva Self-Registration
+config.allow_only_internal_registration=Consenti la registrazione solo tramite Gitea stessa
config.allow_only_external_registration=Attiva la registrazione solo tramite servizi esterni
config.enable_openid_signup=Attiva OpenID Self-Registration
config.enable_openid_signin=Attiva l'accesso tramite OpenID
@@ -2194,15 +2799,19 @@ config.queue_length=Lunghezza della coda
config.deliver_timeout=Tempo Limite di Consegna
config.skip_tls_verify=Salta autenticazione TLS
-config.mailer_config=Configurazione Mailer SMTP
+config.mailer_config=Configurazione Mailer
config.mailer_enabled=Attivo
-config.mailer_disable_helo=Disattiva HELO
+config.mailer_enable_helo=Abilita HELO
config.mailer_name=Nome
-config.mailer_host=Host
+config.mailer_protocol=Protocollo
+config.mailer_smtp_addr=Indirizzo SMTP
+config.mailer_smtp_port=Porta SMTP
config.mailer_user=Utente
config.mailer_use_sendmail=Utilizza Sendmail
config.mailer_sendmail_path=Percorso Sendmail
config.mailer_sendmail_args=Argomenti aggiuntivi per Sendmail
+config.mailer_sendmail_timeout=Timeout Sendmail
+config.mailer_use_dummy=Dummy
config.test_email_placeholder=Email (es. test@example.com)
config.send_test_mail=Invia email di prova
config.test_mail_failed=Impossibile inviare mail di prova a '%s': %v
@@ -2255,6 +2864,7 @@ config.access_log_template=Template
config.xorm_log_mode=Modalità log XORM
config.xorm_log_sql=Log SQL
+
monitor.cron=Incarichi Cron
monitor.name=Nome
monitor.schedule=Agenda
@@ -2262,12 +2872,16 @@ monitor.next=La Prossima Volta
monitor.previous=La Scorsa Volta
monitor.execute_times=Esecuzioni
monitor.process=Processi in Esecuzione
+monitor.stacktrace=Stacktraces
+monitor.goroutines=%d Goroutines
monitor.desc=Descrizione
monitor.start=Orario Avvio
monitor.execute_time=Tempo di Esecuzione
+monitor.last_execution_result=Risultato
monitor.process.cancel=Annulla processo
monitor.process.cancel_desc=L'annullamento di un processo potrebbe causare la perdita di dati
monitor.process.cancel_notices=Annulla: %s?
+monitor.process.children=Figli
monitor.queues=Code
monitor.queue=Coda: %s
monitor.queue.name=Nome
@@ -2275,11 +2889,15 @@ monitor.queue.type=Tipo
monitor.queue.exemplar=Tipo di esemplare
monitor.queue.numberworkers=Numero di workers
monitor.queue.maxnumberworkers=Massimo numero di Workers
+monitor.queue.numberinqueue=Numero in coda
monitor.queue.review=Rivedi configurazione
monitor.queue.review_add=Rivedi/aggiungi Workers
monitor.queue.configuration=Configurazione iniziale
monitor.queue.nopool.title=Nessun pool di Workers
monitor.queue.nopool.desc=Questa coda racchiude altre code al suo interno e non ha un proprio pool.
+monitor.queue.wrapped.desc=Una coda a capo avvolge una coda iniziale lenta, le richieste in coda di buffering in un canale. Non ha un pool di lavoratori stesso.
+monitor.queue.persistable-channel.desc=Un canale persistibile avvolge due code, una coda di canale che ha un proprio pool di operatori e una coda di livello per le richieste persistenti dagli arresti precedenti. Non ha un pool di operai.
+monitor.queue.flush=Flush worker
monitor.queue.pool.timeout=Timeout
monitor.queue.pool.addworkers.title=Aggiungi Workers
monitor.queue.pool.addworkers.submit=Aggiungi Workers
@@ -2289,7 +2907,15 @@ monitor.queue.pool.addworkers.timeout.placeholder=Imposta 0 per non avere timeou
monitor.queue.pool.addworkers.mustnumbergreaterzero=Il numero di Workers da aggiungere deve essere maggiore di zero
monitor.queue.pool.addworkers.musttimeoutduration=Il timeout deve essere una durata golang, per esempio 5m o 0
monitor.queue.pool.flush.title=Pulisci Coda
+monitor.queue.pool.flush.desc=Flush aggiungerà un worker che terminerà una volta che la coda sarà vuota, o il tempo sarà esaurito.
monitor.queue.pool.flush.submit=Aggiungi un Flush Worker
+monitor.queue.pool.flush.added=Flush Worker aggiunto per %[1]s
+monitor.queue.pool.pause.title=Coda Di Pausa
+monitor.queue.pool.pause.desc=La Pausa di una Coda impedirà all'elaborazione dei dati
+monitor.queue.pool.pause.submit=Coda Di Pausa
+monitor.queue.pool.resume.title=Riprendi Coda
+monitor.queue.pool.resume.desc=Imposta questa coda per riprendere il lavoro
+monitor.queue.pool.resume.submit=Riprendi Coda
monitor.queue.settings.title=Impostazioni pool
monitor.queue.settings.desc=I gruppi crescono dinamicamente con un boost in risposta al loro blocco delle code dei worker. Queste modifiche non influenzeranno i gruppi di worker attuali.
@@ -2335,14 +2961,34 @@ notices.delete_success=Gli avvisi di sistema sono stati eliminati.
[action]
create_repo=ha creato il repository %s
rename_repo=repository rinominato da %[1]s
a [3]s
+commit_repo=a inviato a %[3]s di %[4]s
+create_issue=`ha aperto il problema %[3]s#%[2]s`
+close_issue=`ha chiuso il problema %[3]s#%[2]s`
+reopen_issue=`ha riaperto il problema %[3]s#%[2]s`
+create_pull_request=`ha creato la pull request %[3]s#%[2]s`
+close_pull_request=`ha chiuso la pull request %[3]s#%[2]s`
+reopen_pull_request=`ha riaperto la pull request %[3]s#%[2]s`
+comment_issue=`ha commentato sul problema %[3]s#%[2]s`
+comment_pull=`ha commentato su pull request %[3]s#%[2]s`
+merge_pull_request=`ha unito il pull request %[3]s#%[2]s`
transfer_repo=repository %s
trasferito in %s
+push_tag=ha inviato il tag %[3]s su %[4]s
delete_tag=tag eliminato %[2]s da %[3]s
delete_branch=branch eliminato %[2]s da %[3]s
compare_branch=Confronta
compare_commits=Confronta %d commits
compare_commits_general=Confronta commit
+mirror_sync_push=ha sincronizzato i commit a %[3]s di %[4]s dal mirror
+mirror_sync_create=ha sincronizzato un nuovo riferimento %[3]s su %[4]s dal mirror
mirror_sync_delete=riferimento sincronizzato ed eliminato %[2]s
a %[3]s dal mirror
+approve_pull_request=`ha approvato %[3]s#%[2]s`
+reject_pull_request=`ha suggerito modifiche per %[3]s#%[2]s`
+publish_release=`ha rilasciato "%[4]s" su %[3]s`
+review_dismissed=`respinta la recensione da %[4]s per %[3]s#%[2]s`
review_dismissed_reason=Motivo:
+create_branch=ha creato il ramo %[3]s in %[4]s
+starred_repo=ha salvato come preferito %[2]s
+watched_repo=ha iniziato a guardare %[2]s
[tool]
ago=%s fa
@@ -2395,8 +3041,103 @@ error.probable_bad_signature=ATTENZIONE! Anche se esiste una chiave con questo I
error.probable_bad_default_signature=ATTENZIONE! Anche se la chiave predefinita ha questo ID essa non verifica questo commit! Questo commit è SOSPETTO.
[units]
+unit=Unità
error.no_unit_allowed_repo=Non possiedi il permesso di accedere ad alcuna sezione di questo repository.
error.unit_not_allowed=Non possiedi il permesso di accedere a questa sezione di repository.
[packages]
+title=Pacchetti
+desc=Gestisci pacchetti repository.
+empty=Non ci sono ancora pacchetti.
+empty.documentation=Per ulteriori informazioni sul registro dei pacchetti, consultare la documentazione.
+empty.repo=Hai caricato un pacchetto, ma non è mostrato qui? Vai alle impostazioni del pacchetto e collegalo a questo repo.
+filter.type=Tipo
+filter.type.all=Tutti
+filter.no_result=Il filtro non ha prodotto risultati.
+filter.container.tagged=Etichettato
+filter.container.untagged=Nont etichettato
+published_by=Pubblicato %[1]s di %[3]s
+published_by_in=Pubblicato %[1]s di %[3]s in %[5]s
+installation=Installazione
+about=Informazioni su questo pacchetto
+requirements=Requisiti
+dependencies=Dipendenze
+keywords=Parole Chiave
+details=Dettagli
+details.author=Autore
+details.project_site=Sito Del Progetto
+details.license=Licenza
+assets=Asset
+versions=Versioni
+versions.on=su
+versions.view_all=Vedi tutti
+dependency.id=ID
+dependency.version=Versione
+composer.registry=Imposta questo registro nel tuo file ~/.composer/config.json
:
+composer.install=Per installare il pacchetto utilizzando Composer, eseguire il seguente comando:
+composer.documentation=Per ulteriori informazioni sul registro dei compositori, consultare la documentazione.
+composer.dependencies=Dipendenze
+composer.dependencies.development=Dipendenze Di Sviluppo
+conan.details.repository=Repository
+conan.registry=Configura questo registro dalla riga di comando:
+conan.install=Per installare il pacchetto usando Conan, eseguire il seguente comando:
+conan.documentation=Per ulteriori informazioni sul registro di Conan, consultare la documentazione.
+container.details.type=Tipo Immagine
+container.details.platform=Piattaforma
+container.details.repository_site=Sito Repository
+container.details.documentation_site=Sito Documentazione
+container.pull=Tirare l'immagine dalla riga di comando:
+container.documentation=Per ulteriori informazioni sul registro Container, vedere la documentazione.
+container.multi_arch=OS / Arch
+container.layers=Livelli Immagine
+container.labels=Etichette
+container.labels.key=Chiave
+container.labels.value=Valore
+generic.download=Scarica il pacchetto dalla riga di comando:
+generic.documentation=Per ulteriori informazioni sul registro generico, consultare la documentazione.
+helm.registry=Configura questo registro dalla riga di comando:
+helm.install=Per installare il pacchetto, eseguire il seguente comando:
+helm.documentation=Per ulteriori informazioni sul registro Helm, consultare la documentazione.
+maven.registry=Configura questo registro nel file pom.xml
del tuo progetto:
+maven.install=Per utilizzare il pacchetto includere i seguenti nel blocco dipendenze
nel file pom.xml
:
+maven.install2=Esegui tramite riga di comando:
+maven.download=Per scaricare la dipendenza, eseguire tramite riga di comando:
+maven.documentation=Per ulteriori informazioni sul registro Maven, consultare la documentazione.
+nuget.registry=Configura questo registro dalla riga di comando:
+nuget.install=Per installare il pacchetto utilizzando NuGet, eseguire il seguente comando:
+nuget.documentation=Per ulteriori informazioni sul registro di NuGest, consultare la documentazione.
+nuget.dependency.framework=Target Framework
+npm.registry=Impostare questo registro nel file del progetto .npmrc
:
+npm.install=Per installare il pacchetto usando npm, eseguire il seguente comando:
+npm.install2=o aggiungerlo al file package.json:
+npm.documentation=Per ulteriori informazioni sul registro npm, vedere la documentazione.
+npm.dependencies=Dipendenze
+npm.dependencies.development=Dipendenze Di Sviluppo
+npm.dependencies.peer=Dipendenze Peer
+npm.dependencies.optional=Dipendenze Opzionali
+npm.details.tag=Tag
+pub.install=Per installare il pacchetto utilizzando NuGet, eseguire il seguente comando:
+pub.documentation=Per ulteriori informazioni sul registro Pub, consultare la documentazione.
+pub.details.repository_site=Sito Repository
+pypi.requires=Richiede Python
+pypi.install=Per installare il pacchetto usando pip, eseguire il seguente comando:
+pypi.documentation=Per ulteriori informazioni sul registro PyPI, consultare la documentazione.
+rubygems.install=Per installare il pacchetto usando gem, eseguire il seguente comando:
+rubygems.install2=o aggiungerlo al file Gem:
+rubygems.dependencies.runtime=Dipendenze Runtime
+rubygems.dependencies.development=Dipendenze Di Sviluppo
+rubygems.required.ruby=Richiede la versione di Ruby
+rubygems.required.rubygems=Richiede la versione RubyGem
+rubygems.documentation=Per ulteriori informazioni sul registro di RubyGems, vedere la documentazione.
+settings.link=Collega questo pacchetto a un repository
+settings.link.description=Se si collega un pacchetto a un repository, il pacchetto è elencato nell'elenco dei pacchetti del repository.
+settings.link.select=Seleziona Repository
+settings.link.button=Aggiorna Collegamento Repository
+settings.link.success=Il link del repository è stato aggiornato correttamente.
+settings.link.error=Impossibile aggiornare il link del repository.
+settings.delete=Elimina pacchetto
+settings.delete.description=L'eliminazione di un pacchetto è permanente e non può essere annullata.
+settings.delete.notice=Stai per eliminare %s (%s). Questa operazione è irreversibile, sei sicuro?
+settings.delete.success=Il pacchetto è stato eliminato.
+settings.delete.error=Impossibile eliminare il pacchetto.
diff --git a/options/locale/locale_ja-JP.ini b/options/locale/locale_ja-JP.ini
index 065c828afa..3b39236545 100644
--- a/options/locale/locale_ja-JP.ini
+++ b/options/locale/locale_ja-JP.ini
@@ -9,7 +9,6 @@ sign_out=サインアウト
sign_up=登録
link_account=アカウント連携
register=登録
-website=Webサイト
version=バージョン
powered_by=Powered by %s
page=ページ
@@ -179,7 +178,8 @@ log_root_path_helper=ログファイルがこのディレクトリに書き込
optional_title=オプション設定
email_title=メール設定
-smtp_host=SMTPホスト
+smtp_addr=SMTPホスト
+smtp_port=SMTPポート
smtp_from=メール送信者
smtp_from_helper=Giteaが使用するメールアドレス。 メールアドレスのみ、または、 "名前" の形式で入力してください。
mailer_user=SMTPユーザー名
@@ -268,8 +268,11 @@ users=ユーザー
organizations=組織
search=検索
code=コード
+search.type.tooltip=検索タイプ
search.fuzzy=あいまい
+search.fuzzy.tooltip=検索ワードにおおよそ一致している結果も含めます
search.match=一致
+search.match.tooltip=検索ワードに一致する結果だけを含めます
code_search_unavailable=現在コード検索は利用できません。 サイト管理者にお問い合わせください。
repo_no_results=一致するリポジトリが見つかりません。
user_no_results=一致するユーザーが見つかりません。
@@ -277,6 +280,9 @@ org_no_results=一致する組織が見つかりません。
code_no_results=検索ワードに一致するソースコードが見つかりません。
code_search_results='%s' の検索結果
code_last_indexed_at=最終取得 %s
+relevant_repositories_tooltip=フォークリポジトリや、トピック、アイコン、説明のいずれも無いリポジトリは表示されません。
+relevant_repositories=妥当と思われるリポジトリのみを表示しています。 フィルタリングしない結果を表示。
+
[auth]
create_new_account=アカウントを登録
@@ -504,6 +510,7 @@ activity=公開アクティビティ
followers=フォロワー
starred=スター付きリポジトリ
watched=ウォッチ中リポジトリ
+code=コード
projects=プロジェクト
following=フォロー中
follow=フォロー
@@ -799,6 +806,7 @@ email_notifications.enable=メール通知有効
email_notifications.onmention=メンションのみメール通知
email_notifications.disable=メール通知無効
email_notifications.submit=メール設定を保存
+email_notifications.andyourown=自分に関する通知も含める
visibility=ユーザーの公開範囲
visibility.public=パブリック
@@ -1034,13 +1042,13 @@ file_view_rendered=レンダリング表示
file_view_raw=Rawデータを見る
file_permalink=パーマリンク
file_too_large=このファイルは大きすぎるため、表示できません。
-bidi_bad_header=`このファイルには予期しない双方向Unicode文字が含まれています!`
-bidi_bad_description=`このファイルには予期しない双方向Unicode文字が含まれており、下に見えているものとは異なる処理が行われる可能性があります。 あなたのユースケースが意図的かつ正当な場合はこの警告を無視して構いません。 不可視文字を表示するにはエスケープボタンを使用します。`
-bidi_bad_description_escaped=`このファイルには予期しない双方向Unicode文字が含まれています。 下の表示では、不可視Unicode文字はエスケープされています。 どのようにレンダリングされるかを表示するにはエスケープ解除ボタンを使用します。`
-unicode_header=`このファイルには不可視Unicode文字が含まれています!`
-unicode_description=`このファイルには不可視Unicode文字が含まれており、下に見えているものとは異なる処理が行われる可能性があります。 あなたのユースケースが意図的かつ正当な場合はこの警告を無視して構いません。 不可視文字を表示するにはエスケープボタンを使用します。`
-unicode_description_escaped=`このファイルには不可視Unicode文字が含まれています。 下の表示では、不可視Unicode文字はエスケープされています。 どのようにレンダリングされるかを表示するにはエスケープ解除ボタンを使用します。`
-line_unicode=`この行には不可視Unicode文字があります`
+invisible_runes_header=`このファイルには不可視のUnicode文字が含まれています!`
+invisible_runes_description=`このファイルには不可視Unicode文字が含まれており、下に見えているものとは異なる処理が行われる可能性があります。 あなたのユースケースが意図的かつ正当な場合はこの警告を無視して構いません。 不可視文字を表示するにはエスケープボタンを使用します。`
+ambiguous_runes_header=`このファイルには曖昧(ambiguous)なUnicode文字が含まれています!`
+ambiguous_runes_description=`このファイルには曖昧(ambiguous)なUnicode文字が含まれており、あなたが使用しているロケールにおいて他の文字と混同する可能性があります。 あなたのユースケースが意図的かつ正当な場合はこの警告を無視して構いません。 それらの文字をハイライトするにはエスケープボタンを使用します。`
+invisible_runes_line=`この行には不可視のUnicode文字があります`
+ambiguous_runes_line=`この行には曖昧(ambiguous)なUnicode文字があります`
+ambiguous_character=`%[1]c [U+%04[1]X] は %[2]c [U+%04[2]X] と混同するおそれがあります`
escape_control_characters=エスケープ
unescape_control_characters=エスケープ解除
@@ -1061,6 +1069,7 @@ normal_view=通常表示
line=行
lines=行
+editor.add_file=ファイル追加
editor.new_file=新規ファイル
editor.upload_file=ファイルをアップロード
editor.edit_file=ファイルを編集
@@ -1226,6 +1235,8 @@ issues.new.add_reviewer_title=レビュー依頼
issues.choose.get_started=始める
issues.choose.blank=デフォルト
issues.choose.blank_about=デフォルトのテンプレートからイシューを作成。
+issues.choose.ignore_invalid_templates=無効なテンプレートが無視されました
+issues.choose.invalid_templates=無効なテンプレートが%v 件見つかりました
issues.no_ref=ブランチ/タグ指定なし
issues.create=イシューを作成
issues.new_label=新しいラベル
@@ -1266,6 +1277,8 @@ issues.filter_milestone=マイルストーン
issues.filter_milestone_no_select=すべてのマイルストーン
issues.filter_assignee=担当者
issues.filter_assginee_no_select=すべての担当者
+issues.filter_poster=作成者
+issues.filter_poster_no_select=すべての作成者
issues.filter_type=タイプ
issues.filter_type.all_issues=すべてのイシュー
issues.filter_type.assigned_to_you=自分が担当
@@ -1420,6 +1433,7 @@ issues.due_date_form_remove=削除
issues.due_date_not_writer=イシューの期日を変更するには、リポジトリへの書き込み権限が必要です。
issues.due_date_not_set=期日は未設定です。
issues.due_date_added=が期日 %s を追加 %s
+issues.due_date_modified=が期日を %[2]s から %[1]s に変更 %[3]s
issues.due_date_remove=が期日 %s を削除 %s
issues.due_date_overdue=期日は過ぎています
issues.due_date_invalid=期日が正しくないか範囲を超えています。 'yyyy-mm-dd' の形式で入力してください。
@@ -1753,8 +1767,11 @@ activity.git_stats_deletion_n=%d行削除
search=検索
search.search_repo=リポジトリを検索
+search.type.tooltip=検索タイプ
search.fuzzy=あいまい
+search.fuzzy.tooltip=検索ワードにおおよそ一致している結果も含めます
search.match=一致
+search.match.tooltip=検索ワードに一致する結果だけを含めます
search.results=%[3]s 内での "%[1]s" の検索結果
search.code_no_results=検索ワードに一致するソースコードが見つかりません。
search.code_search_unavailable=現在コード検索は利用できません。 サイト管理者にお問い合わせください。
@@ -1783,10 +1800,6 @@ settings.mirror_settings.push_mirror.remote_url=リモートGitリポジトリ
settings.mirror_settings.push_mirror.add=プッシュミラーを追加
settings.sync_mirror=今すぐ同期
settings.mirror_sync_in_progress=ミラー同期を実行しています。 しばらくあとでまた確認してください。
-settings.email_notifications.enable=メール通知有効
-settings.email_notifications.onmention=メンションのみメール通知
-settings.email_notifications.disable=メール通知無効
-settings.email_notifications.submit=メール設定を保存
settings.site=Webサイト
settings.update_settings=設定を更新
settings.branches.update_default_branch=デフォルトブランチを更新
@@ -1892,6 +1905,7 @@ settings.confirm_delete=リポジトリを削除
settings.add_collaborator=共同作業者を追加
settings.add_collaborator_success=共同作業者を追加しました。
settings.add_collaborator_inactive_user=アクティベートされていないユーザーを共同作業者として追加することはできません。
+settings.add_collaborator_owner=共同作業者としてオーナーを追加することはできません。
settings.add_collaborator_duplicate=共同作業者として既にこのリポジトリに追加されています。
settings.delete_collaborator=削除
settings.collaborator_deletion=共同作業者の削除
@@ -1950,6 +1964,8 @@ settings.event_delete=削除
settings.event_delete_desc=ブランチやタグが削除されたとき。
settings.event_fork=フォーク
settings.event_fork_desc=リポジトリがフォークされたとき。
+settings.event_wiki=Wiki
+settings.event_wiki_desc=Wikiページが作成・名前変更・編集・削除されたとき。
settings.event_release=リリース
settings.event_release_desc=リポジトリでリリースが作成・更新・削除されたとき。
settings.event_push=プッシュ
@@ -2301,6 +2317,7 @@ create_org=組織を作成
repo_updated=最終更新
people=メンバー
teams=チーム
+code=コード
lower_members=メンバー
lower_repositories=リポジトリ
create_new_team=新しいチーム
@@ -2797,16 +2814,19 @@ config.queue_length=キューの長さ
config.deliver_timeout=送信タイムアウト
config.skip_tls_verify=TLS検証を省略
-config.mailer_config=SMTPメーラーの設定
+config.mailer_config=メーラー設定
config.mailer_enabled=有効
-config.mailer_disable_helo=HELOコマンド無効
+config.mailer_enable_helo=HELO有効
config.mailer_name=名称
-config.mailer_host=ホスト
+config.mailer_protocol=プロトコル
+config.mailer_smtp_addr=SMTPアドレス
+config.mailer_smtp_port=SMTPポート
config.mailer_user=ユーザー
config.mailer_use_sendmail=Sendmailを使う
config.mailer_sendmail_path=Sendmailのパス
config.mailer_sendmail_args=Sendmailの追加引数
config.mailer_sendmail_timeout=Sendmail のタイムアウト
+config.mailer_use_dummy=Dummy
config.test_email_placeholder=Email (例 test@example.com)
config.send_test_mail=テストメールを送信
config.test_mail_failed='%s' へのテストメール送信に失敗しました: %v
@@ -2859,6 +2879,7 @@ config.access_log_template=テンプレート
config.xorm_log_mode=XORMログのモード
config.xorm_log_sql=SQLのログ出力
+
monitor.cron=Cronタスク
monitor.name=名称
monitor.schedule=スケジュール
@@ -2891,6 +2912,7 @@ monitor.queue.nopool.title=ワーカープールはありません
monitor.queue.nopool.desc=このキューは他のキューをラップし、これ自体にはワーカープールがありません。
monitor.queue.wrapped.desc=wrappedキューは、すぐに開始されないキューをラップし、入ってきたリクエストをチャンネルにバッファリングします。 これ自体にはワーカープールがありません。
monitor.queue.persistable-channel.desc=persistable-channelキューは二つのキューをラップします。 一つはchannelキューで、自分のワーカープールを持ちます。もう一つはlevelキューで、前回のシャットダウンからリクエストを引き継ぐためのものです。 これ自体にはワーカープールがありません。
+monitor.queue.flush=掃き出しワーカー
monitor.queue.pool.timeout=タイムアウト
monitor.queue.pool.addworkers.title=ワーカーの追加
monitor.queue.pool.addworkers.submit=ワーカーを追加
@@ -3021,6 +3043,9 @@ pin=通知をピン留め
mark_as_read=既読にする
mark_as_unread=未読にする
mark_all_as_read=すべて既読にする
+subscriptions=購読
+watching=ウォッチ中
+no_subscriptions=購読しているものはありません
[gpg]
default_key=デフォルト鍵で署名
@@ -3043,6 +3068,7 @@ title=パッケージ
desc=リポジトリ パッケージを管理します。
empty=パッケージはまだありません。
empty.documentation=パッケージレジストリの詳細については、 ドキュメント を参照してください。
+empty.repo=パッケージはアップロードしたけども、ここに表示されない? パッケージ設定を開いて、パッケージをこのリポジトリにリンクしてください。
filter.type=タイプ
filter.type.all=すべて
filter.no_result=フィルタの結果、空になりました。
@@ -3079,6 +3105,7 @@ container.details.platform=プラットフォーム
container.details.repository_site=リポジトリサイト
container.details.documentation_site=ドキュメントサイト
container.pull=コマンドラインでイメージを取得します:
+container.digest=ダイジェスト:
container.documentation=Container レジストリの詳細については、 ドキュメント を参照してください。
container.multi_arch=OS / アーキテクチャ
container.layers=イメージレイヤー
@@ -3108,6 +3135,10 @@ npm.dependencies.development=開発用依存関係
npm.dependencies.peer=Peer依存関係
npm.dependencies.optional=オプションの依存関係
npm.details.tag=タグ
+pub.install=Dart を使用してパッケージをインストールするには、次のコマンドを実行します:
+pub.documentation=Pub レジストリの詳細については、ドキュメント を参照してください。
+pub.details.repository_site=リポジトリサイト
+pub.details.documentation_site=ドキュメントサイト
pypi.requires=必要なPython
pypi.install=pip を使用してパッケージをインストールするには、次のコマンドを実行します:
pypi.documentation=PyPI レジストリの詳細については、ドキュメント を参照してください。
@@ -3118,6 +3149,8 @@ rubygems.dependencies.development=開発用依存関係
rubygems.required.ruby=必要なRubyバージョン
rubygems.required.rubygems=必要なRubyGemバージョン
rubygems.documentation=RubyGemsレジストリの詳細については、ドキュメント を参照してください。
+vagrant.install=Vagrant ボックスを追加するには、次のコマンドを実行します。
+vagrant.documentation=Vagrantレジストリの詳細については ドキュメントを参照してください。
settings.link=このパッケージをリポジトリにリンク
settings.link.description=パッケージをリポジトリにリンクすると、リポジトリのパッケージリストに表示されるようになります。
settings.link.select=リポジトリを選択
diff --git a/options/locale/locale_ko-KR.ini b/options/locale/locale_ko-KR.ini
index 6049c44313..f4f682cb7a 100644
--- a/options/locale/locale_ko-KR.ini
+++ b/options/locale/locale_ko-KR.ini
@@ -8,7 +8,6 @@ sign_out=로그아웃
sign_up=가입하기
link_account=계정 연결
register=가입하기
-website=웹 사이트
version=버전
powered_by=%s 제공
page=페이지
@@ -126,7 +125,6 @@ log_root_path_helper=로그파일은 이 디렉토리에 저장됩니다.
optional_title=추가설정
email_title=이메일 설정
-smtp_host=SMTP 호스트
smtp_from=이메일 발신인
smtp_from_helper=Gitea 가 사용할 이메일 주소. 이메일 주소 또는 "이름" 형식으로 입력하세요.
mailer_user=SMTP 사용자이름
@@ -203,6 +201,7 @@ org_no_results=일치하는 조직이 없습니다.
code_no_results=검색어와 일치하는 소스코드가 없습니다.
code_search_results='%s'에 대한 검색결과
+
[auth]
create_new_account=계정 등록
register_helper_msg=이미 계정을 가지고 계신가요? 로그인하세요!
@@ -970,8 +969,6 @@ settings.basic_settings=기본 설정
settings.mirror_settings=미러 설정
settings.sync_mirror=지금 동기화
settings.mirror_sync_in_progress=미러 동기화 진행중입니다. 잠시 후 다시 확인해주십시오.
-settings.email_notifications.enable=이메일 알림 켜기
-settings.email_notifications.disable=이메일 알림 끄기
settings.site=웹 사이트
settings.update_settings=설정 저장
settings.advanced_settings=고급 설정
@@ -1452,11 +1449,8 @@ config.queue_length=큐 길이
config.deliver_timeout=시간 제한 사용
config.skip_tls_verify=TLS 검증 건너뛰기
-config.mailer_config=SMTP 메일러 설정
config.mailer_enabled=활성화됨
-config.mailer_disable_helo=HELO 비활성화
config.mailer_name=이름
-config.mailer_host=호스트
config.mailer_user=사용자
config.mailer_use_sendmail=Sendmail 사용
config.mailer_sendmail_path=Sendmail 경로
@@ -1502,6 +1496,7 @@ config.git_gc_timeout=가비지 콜렉션 작업 시간 제한
config.log_config=로그 설정
config.log_mode=로그 모드
+
monitor.cron=Cron 작업
monitor.name=이름
monitor.schedule=스케줄
diff --git a/options/locale/locale_lv-LV.ini b/options/locale/locale_lv-LV.ini
index 5a43101b1e..e06ff0a7ff 100644
--- a/options/locale/locale_lv-LV.ini
+++ b/options/locale/locale_lv-LV.ini
@@ -9,7 +9,6 @@ sign_out=Izrakstīties
sign_up=Reģistrēties
link_account=Saistītie konti
register=Reģistrēties
-website=Mājas lapa
version=Versija
powered_by=Darbina %s
page=Lapa
@@ -179,7 +178,8 @@ log_root_path_helper=Žurnalizēšanas faili tiks rakstīti šajā direktorijā.
optional_title=Neobligātie iestatījumi
email_title=E-pastu iestatījumi
-smtp_host=SMTP resursdators
+smtp_addr=SMTP resursdators
+smtp_port=SMTP ports
smtp_from=Nosūtīt e-pastu kā
smtp_from_helper=E-pasta adrese, ko Gitea izmantos. Ievadiet tika e-pasta adrese vai izmantojiet "Vārds" formātu.
mailer_user=SMTP lietotāja vārds
@@ -278,6 +278,7 @@ code_no_results=Netika atrasts pirmkods, kas atbilstu kritērijiem.
code_search_results=Meklēšanas rezultāti '%s'
code_last_indexed_at=Pēdējo reizi indeksēts %s
+
[auth]
create_new_account=Reģistrēt kontu
register_helper_msg=Jau ir konts? Pieraksties tagad!
@@ -799,6 +800,7 @@ email_notifications.enable=Iespējot e-pasta paziņojumus
email_notifications.onmention=Tikai, ja esmu pieminēts
email_notifications.disable=Nesūtīt paziņojumus
email_notifications.submit=Saglabāt sūtīšanas iestatījumus
+email_notifications.andyourown=Iekļaut savus paziņojumus
visibility=Lietotāja redzamība
visibility.public=Publisks
@@ -861,7 +863,9 @@ default_branch=Noklusējuma atzars
default_branch_helper=Noklusētais atzars nosaka pamata atzaru uz kuru tiks veidoti izmaiņu pieprasījumi un koda revīziju iesūtīšana.
mirror_prune=Izmest
mirror_prune_desc=Izdzēst visas ārējās atsauces, kas ārējā repozitorijā vairs neeksistē
+mirror_interval=Spoguļošanas intervāls (derīgas laika vienības ir 'h', 'm', 's'). Norādiet 0, lai atslēgtu periodisku spoguļošanu. (Minimālais intervāls: %s)
mirror_interval_invalid=Nekorekts spoguļošanas intervāls.
+mirror_sync_on_commit=Sinhronizēt, kad revīzijas tiek iesūtītas
mirror_address=Spoguļa adrese
mirror_address_desc=Pieslēgšanās rekvizītus norādiet autorizācijas sadaļā.
mirror_address_url_invalid=Norādītais URL nav korekts. Norādiet visas URL daļas korekti.
@@ -930,6 +934,7 @@ form.name_pattern_not_allowed=Repozitorija nosaukums '%s' nav atļauts.
need_auth=Autorizācija
migrate_options=Migrācijas opcijas
migrate_service=Migrācijas serviss
+migrate_options_mirror_helper=Šis repozitorijs būs spogulis
migrate_options_lfs=Migrēt LFS failus
migrate_options_lfs_endpoint.label=LFS galapunkts
migrate_options_lfs_endpoint.description=Migrācija mēģinās izmantot attālināto URL, lai noteiktu LFS serveri. Var norādīt arī citu galapunktu, ja repozitorija LFS dati ir izvietoti citā vietā.
@@ -1031,13 +1036,6 @@ file_view_rendered=Skatīt rezultātu
file_view_raw=Rādīt neapstrādātu
file_permalink=Patstāvīgā saite
file_too_large=Šis fails ir par lielu, lai to parādītu.
-bidi_bad_header=`Šis fails satur neparedzētus virzienmaiņas unikoda simbolus!`
-bidi_bad_description=`Šis fails satur neparedzētus virzienmaiņas unikoda simbolus, kas var mainīt kā saturs tiek attēlots. Ja tie ir izmantoti ar pamatotu nodumu, tad varat ignorēt šo brīdinājumu. Izmantojiet Kodēt pogu, lai parādītu šos neredzamos simbolus.`
-bidi_bad_description_escaped=`Šis fails satur neparedzētus virzienmaiņas unikoda simbolus. Neredzamie unikoda simboli ir kodēti, lai būtu redzami. Izmantojiet Atkodēt pogu, lai redzētu kā tie tiek attēloti.`
-unicode_header=`Šis fails satur neredzamus unikoda simbolus!`
-unicode_description=`Šis fails satur neredzamus unikoda simbolus, kas var mainīt kā saturs tiek attēlots. Ja tie ir izmantoti ar pamatotu nodumu, tad varat ignorēt šo brīdinājumu. Izmantojiet Kodēt pogu, lai parādītu šos neredzamos simbolus.`
-unicode_description_escaped=`Šis fails satur neredzamus unikoda simbolus. Neredzamie unikoda simboli ir kodēti, lai būtu redzami. Izmantojiet Atkodēt pogu, lai redzētu kā tie tiek attēloti.`
-line_unicode=`Šajā līnijā ir paslēpti unikoda simboli`
escape_control_characters=Kodēt
unescape_control_characters=Atkodēt
@@ -1058,6 +1056,7 @@ normal_view=Parastais skats
line=rinda
lines=rindas
+editor.add_file=Pievienot
editor.new_file=Jauna datne
editor.upload_file=Augšupielādēt failu
editor.edit_file=Labot failu
@@ -1300,6 +1299,7 @@ issues.previous=Iepriekšējā
issues.next=Nākamā
issues.open_title=Atvērta
issues.closed_title=Slēgta
+issues.draft_title=Melnraksts
issues.num_comments=%d komentāri
issues.commented_at=` komentēja %s`
issues.delete_comment_confirm=Vai patiešām vēlaties dzēst šo komentāru?
@@ -1416,6 +1416,7 @@ issues.due_date_form_remove=Noņemt
issues.due_date_not_writer=Jums ir nepieciešamas rakstīšanas tiesības uz šo repozitoriju, lai mainītu izpildes termiņu.
issues.due_date_not_set=Izpildes termiņš nav uzstādīts.
issues.due_date_added=pievienoja izpildes termiņu %s %s
+issues.due_date_modified=mainīja termiņa datumu no %[2]s uz %[1]s %[3]s
issues.due_date_remove=noņēma izpildes termiņu %s %s
issues.due_date_overdue=Nokavēts
issues.due_date_invalid=Datums līdz nav korekts. Izmantojiet formātu 'gggg-mm-dd'.
@@ -1527,6 +1528,8 @@ pulls.remove_prefix=Noņemt %s prefiksu
pulls.data_broken=Izmaiņu pieprasījums ir bojāts, jo dzēsta informācija no atdalītā repozitorija.
pulls.files_conflicted=Šīs izmaiņu pieprasījuma izmaiņas konfliktē ar mērķa atzaru.
pulls.is_checking=Notiek konfliktu pārbaude, mirkli uzgaidiet un atjaunojiet lapu.
+pulls.is_ancestor=Atzars jau ir pilnībā iekļauts mērķā atzarā. Nav izmaiņu, ko sapludināt.
+pulls.is_empty=Mērķa atzars jau satur šī atzara izmaiņas. Šī revīzija būs tukša.
pulls.required_status_check_failed=Dažas no pārbaudēm nebija veiksmīgas.
pulls.required_status_check_missing=Trūkst dažu obligāto pārbaužu.
pulls.required_status_check_administrator=Kā administrators Jūs varat sapludināt šo izmaiņu pieprasījumu.
@@ -1605,6 +1608,8 @@ pulls.auto_merge_canceled_schedule=Automātiskā sapludināšana šim izmaiņu p
pulls.auto_merge_newly_scheduled_comment=`ieplānoja automātisko sapludināšanu šim izmaiņu pieprasījumam, kad visas pārbaudes būs veiksmīgas %[1]s`
pulls.auto_merge_canceled_schedule_comment=`atcēla automātisko sapludināšanu šim izmaiņu pieprasījumam %[1]s`
+pulls.delete.title=Dzēst šo izmaiņu pieprasījumu?
+pulls.delete.text=Vai patiešām vēlaties dzēst šo izmaiņu pieprasījumu? (Neatgriezeniski tiks izdzēsts viss saturs. Apsveriet iespēju to aizvērt, ja vēlaties informāciju saglabāt vēsturei)
milestones.new=Jauns atskaites punkts
milestones.closed=Aizvērts %s
@@ -1775,10 +1780,6 @@ settings.mirror_settings.push_mirror.remote_url=Git attālinātā repozitorija U
settings.mirror_settings.push_mirror.add=Pievienot iesūtīšanas spoguli
settings.sync_mirror=Sinhronizēt tagad
settings.mirror_sync_in_progress=Notiek spoguļa sinhronizācija. Atjaunojiet lapu, lai pārbaudītu atkārtoti, pēc brīža.
-settings.email_notifications.enable=Iespējot e-pasta paziņojumus
-settings.email_notifications.onmention=Tikai, ja esmu pieminēts
-settings.email_notifications.disable=Nesūtīt paziņojumus
-settings.email_notifications.submit=Saglabāt sūtīšanas iestatījumus
settings.site=Mājas lapa
settings.update_settings=Mainīt iestatījumus
settings.branches.update_default_branch=Atjaunot noklusēto atzaru
@@ -2531,6 +2532,8 @@ users.delete_account=Dzēst lietotāja kontu
users.cannot_delete_self=Nevar izdzēst sevi
users.still_own_repo=Lietotājam pieder repozitoriji, tos sākumā ir nepieciešams izdzēst vai mainīt to īpašnieku.
users.still_has_org=Šis lietotājs ir vienas vai vairāku organizāciju biedrs, lietotāju sākumā ir nepieciešams pamest šīs organizācijas vai viņu no tām ir jāizdzēš.
+users.purge=Attīrīt lietotu
+users.purge_help=Piespiedu dzēst lietotāju un visus tā repozitorijus, organizācijas un pakotnes. Arī visi lietotāja komentāri tiks dzēsti.
users.still_own_packages=Šim lietotājam pieder viena vai vairākas pakotnes. Tās nepieciešams izdzēst.
users.deletion_success=Lietotāja konts veiksmīgi izdzēsts.
users.reset_2fa=Noņemt 2FA
@@ -2787,16 +2790,19 @@ config.queue_length=Rindas garums
config.deliver_timeout=Piegādes noildze
config.skip_tls_verify=Izlaist TLS pārbaudi
-config.mailer_config=SMTP sūtītāja konfigurācija
+config.mailer_config=Pasta sūtītāja konfigurācija
config.mailer_enabled=Iespējota
-config.mailer_disable_helo=Atspējot HELO
+config.mailer_enable_helo=Iespējot HELO
config.mailer_name=Nosaukums
-config.mailer_host=Resursdators
+config.mailer_protocol=Protokols
+config.mailer_smtp_addr=SMTP adrese
+config.mailer_smtp_port=SMTP ports
config.mailer_user=Lietotājs
config.mailer_use_sendmail=Izmantot Sendmail
config.mailer_sendmail_path=Ceļš līdz sendmail programmai
config.mailer_sendmail_args=Papildus Sendmail komandrindas argumenti
config.mailer_sendmail_timeout=Sendmail noildze
+config.mailer_use_dummy=Tukšs
config.test_email_placeholder=E-pasts (piemēram, test@example.com)
config.send_test_mail=Nosūtīt pārbaudes e-pastu
config.test_mail_failed=Neizdevās nosūtīt pārbaudes e-pastu uz '%s': %v
@@ -2849,6 +2855,7 @@ config.access_log_template=Šablons
config.xorm_log_mode=XORM žurnalizēšanas veids
config.xorm_log_sql=SQL žurnalizēšana
+
monitor.cron=Cron uzdevumi
monitor.name=Nosaukums
monitor.schedule=Grafiks
@@ -3033,6 +3040,7 @@ title=Pakotnes
desc=Pārvaldīt repozitorija pakotnes.
empty=Pašlaik šeit nav nevienas pakotnes.
empty.documentation=Papildus informācija par pakotņu reģistru pieejama dokumentācijā.
+empty.repo=Neparādās augšupielādēta pakotne? Apmeklējiet pakotņu iestatījumus, lai sasaistītu ar repozitoriju.
filter.type=Veids
filter.type.all=Visas
filter.no_result=Pēc norādītajiem kritērijiem nekas netika atrasts.
@@ -3098,6 +3106,10 @@ npm.dependencies.development=Izstrādes atkarības
npm.dependencies.peer=Netiešās atkarības
npm.dependencies.optional=Neobligātās atkarības
npm.details.tag=Tags
+pub.install=Lai instalētu Dart pakotni, izpildiet sekojošu komandu:
+pub.documentation=Papildus informācija par Pub reģistru pieejama dokumentācijā.
+pub.details.repository_site=Repozitorija izmērs
+pub.details.documentation_site=Dokumentācijas lapa
pypi.requires=Nepieciešams Python
pypi.install=Lai instalētu pip pakotni, izpildiet sekojošu komandu:
pypi.documentation=Papildus informācija par PyPI reģistru pieejama dokumentācijā.
diff --git a/options/locale/locale_ml-IN.ini b/options/locale/locale_ml-IN.ini
index 27e4f11279..078b013ee1 100644
--- a/options/locale/locale_ml-IN.ini
+++ b/options/locale/locale_ml-IN.ini
@@ -8,7 +8,6 @@ sign_out=പുറത്തുകടക്കുക
sign_up=രജിസ്റ്റർ
link_account=അക്കൌണ്ട് ബന്ധിപ്പിയ്ക്കുക
register=രജിസ്റ്റർ
-website=വെബ് സൈറ്റ്
version=പതിപ്പ്
page=പേജ്
template=ടെംപ്ലേറ്റ്
@@ -112,7 +111,6 @@ log_root_path_helper=ലോഗ് ഫയലുകൾ ഈ ഡയറക്ടറ
optional_title=ഐച്ഛികമായ ക്രമീകരണങ്ങൾ
email_title=ഇമെയിൽ ക്രമീകരണങ്ങൾ
-smtp_host=SMTP ഹോസ്റ്റ്
smtp_from=ഈ വിലാസത്തില് ഇമെയിൽ അയയ്ക്കുക
smtp_from_helper=ഗിറ്റീ ഉപയോഗിയ്ക്കുന്ന ഇമെയില് വിലാസം. ഒരു സാധാ ഇമെയിൽ വിലാസം നൽകുക അല്ലെങ്കിൽ "പേര്" എന്ന ഘടന ഉപയോഗിക്കുക.
mailer_user=SMTP ഉപയോക്തൃനാമം
@@ -189,6 +187,7 @@ org_no_results=പൊരുത്തപ്പെടുന്ന സംഘടന
code_no_results=നിങ്ങളുടെ തിരയൽ പദവുമായി പൊരുത്തപ്പെടുന്ന സോഴ്സ് കോഡുകളൊന്നും കണ്ടെത്താനായില്ല.
code_search_results=%s എന്നതിനായുള്ള തിരയൽ ഫലങ്ങൾ
+
[auth]
create_new_account=അക്കൗണ്ട് രജിസ്റ്റർ ചെയ്യുക
register_helper_msg=ഇതിനകം ഒരു അക്കൗണ്ട് ഉണ്ടോ? ഇപ്പോൾ പ്രവേശിക്കുക!
@@ -786,6 +785,7 @@ repos.issues=ഇഷ്യൂകള്
+
[action]
diff --git a/options/locale/locale_nl-NL.ini b/options/locale/locale_nl-NL.ini
index 3e54ef3fd7..e3f10451f4 100644
--- a/options/locale/locale_nl-NL.ini
+++ b/options/locale/locale_nl-NL.ini
@@ -2,13 +2,13 @@ home=Beginscherm
dashboard=Overzicht
explore=Verkennen
help=Help
+logo=Logo
sign_in=Inloggen
sign_in_with=Inloggen met
sign_out=Uitloggen
sign_up=Registreren
link_account=Account Koppelen
register=Registreren
-website=Website
version=Versie
powered_by=Powered by %s
page=Pagina
@@ -25,7 +25,7 @@ licenses=Licenties
return_to_gitea=Terug naar Gitea
username=Gebruikersnaam
-email=E-mail adres
+email=E-mailadres
password=Wachtwoord
access_token=Toegangstoken
re_type=Typ uw wachtwoord opnieuw in
@@ -34,6 +34,19 @@ twofa=Twee factor authenticatie
twofa_scratch=Eenmalige twee factor authenticatie code
passcode=PIN
+webauthn_insert_key=Voer uw beveiligingssleutel in
+webauthn_sign_in=Druk op de knop van uw beveiligingssleutel. Als uw beveiligingssleutel geen knop heeft, voeg deze dan opnieuw in.
+webauthn_press_button=Druk alstublieft op de knop van uw beveiligingssleutel…
+webauthn_use_twofa=Gebruik een twee-factor code van uw telefoon
+webauthn_error=Kon uw beveiligingssleutel niet lezen.
+webauthn_unsupported_browser=Uw browser ondersteunt momenteel geen WebAuthn.
+webauthn_error_unknown=Er is een onbekende fout opgetreden. Probeer het opnieuw.
+webauthn_error_insecure=WebAuthn ondersteunt alleen beveiligde verbindingen. Om te testen via HTTP, kan je de oorsprong "localhost" of "127.0.0.1" gebruiken
+webauthn_error_unable_to_process=De server kon uw verzoek niet verwerken.
+webauthn_error_duplicated=De beveiligingssleutel is niet toegestaan voor dit verzoek. Zorg er alstublieft voor dat de sleutel niet al geregistreerd is.
+webauthn_error_empty=U moet een naam voor deze sleutel instellen.
+webauthn_error_timeout=Time-out bereikt voordat uw sleutel kon worden gelezen. Laad deze pagina opnieuw en probeer het opnieuw.
+webauthn_reload=Vernieuwen
repository=Repository
organization=Organisatie
@@ -55,7 +68,7 @@ your_settings=Instellingen
all=Alles
sources=Bronnen
-mirrors=Kopieën
+mirrors=Spiegels
collaborative=Samenwerkend
forks=Forks
@@ -74,6 +87,7 @@ remove_all=Alles verwijderen
edit=Bewerk
copy=Kopieer
+copy_url=Kopieer URL
copy_branch=Kopieer branchnaam
copy_success=Gekopieerd!
copy_error=Kopiëren mislukt
@@ -90,9 +104,15 @@ error404=De pagina die u probeert te bereiken bestaat niet of <
never=Nooit
+rss_feed=RSS Feed
[error]
+occurred=Er is een fout opgetreden
+report_message=Als je zeker weet dat dit een Gitea bug is, zoek dan naar problemen op GitHub of open een nieuw probleem indien nodig.
missing_csrf=Foutief verzoek: geen CSRF-token aanwezig
+invalid_csrf=Verkeerd verzoek: ongeldig CSRF-token
+not_found=Het doel kon niet worden gevonden.
+network_error=Netwerk fout
[startpage]
app_desc=Een eenvoudige, self-hosted Git service
@@ -109,6 +129,7 @@ license_desc=Alles staat op documentatie voordat je een instelling aanpast.
+require_db_desc=Gitea vereist MySQL, PostgreSQL, MSSQL, SQLite3 of TiDB (MySQL protocol).
db_title=Database-instellingen
db_type=Database-type
host=Server
@@ -122,10 +143,15 @@ ssl_mode=SSL
charset=Karakterset
path=Pad
sqlite_helper=Bestandspad voor de SQLite3-database.
Vul een volledig pad in als je GItea als een service uitvoert.
+reinstall_error=U probeert te installeren in een bestaande Gitea database
+reinstall_confirm_message=Herinstalleren met een bestaande Gitea-database kan meerdere problemen veroorzaken. In de meeste gevallen kun je het bestaande "app.ini" gebruiken om Gitea te laten draaien. Als je weet wat je aan het doen bent, bevestig dan het volgende:
+reinstall_confirm_check_1=De gegevens versleuteld door de SECRET_KEY in de app.ini kan verloren gaan: gebruikers kunnen mogelijk niet meer inloggen met 2FA/OTP & spiegels werken mogelijk niet meer. Door dit vakje aan te vinken bevestigt u dat het huidige app.ini bestand de juiste SECRET_KEY bevat.
+reinstall_confirm_check_2=De repositories en instellingen moeten mogelijk opnieuw worden gesynchroniseerd. Door dit vakje aan te vinken, bevestigt u dat u de hooks voor de repositories en authorized_keys bestand handmatig zult hersynchroniseren. U bevestigt dat u ervoor zult zorgen dat de instellingen van de repository en mirror correct zijn.
+reinstall_confirm_check_3=Je bevestigt dat je er absoluut zeker van bent dat deze Gitea draait met de juiste app. Geen locatie en dat je zeker weet dat je opnieuw moet installeren. Je bevestigt dat je de hierbovenstaande risico's erkent.
err_empty_db_path=SQLite3 database pad mag niet leeg zijn.
no_admin_and_disable_registration=U kunt zelf-registratie van de gebruiker niet uitschakelen zonder het maken van een administrator-account.
err_empty_admin_password=Het administrator-wachtwoord mag niet leeg zijn.
-err_empty_admin_email=De e-mail van de beheerder mag niet leeg zijn.
+err_empty_admin_email=Het e-mailadres van Het beheerder mag niet leeg zijn.
err_admin_name_is_reserved=Gebruikersnaam van beheerder is ongeldig, gebruikersnaam is gereserveerd
err_admin_name_pattern_not_allowed=Gebruikersnaam van beheerder is ongeldig, de gebruikersnaam is gereserveerd
err_admin_name_is_invalid=Gebruikersnaam van beheerder is ongeldig
@@ -152,7 +178,8 @@ log_root_path_helper=Logboekbestanden worden geschreven naar deze map.
optional_title=Optionele instellingen
email_title=E-mail instellingen
-smtp_host=SMTP host
+smtp_addr=SMTP Host
+smtp_port=SMTP Poort
smtp_from=E-mails versturen als
smtp_from_helper=E-mailadres dat Gitea gaat gebruiken. Voer een gewoon e-mailadres in of gebruik de "Naam" -indeling.
mailer_user=SMTP gebruikersnaam
@@ -182,13 +209,17 @@ admin_title=Instellingen beheerdersaccount
admin_name=Admin gebruikersnaam
admin_password=Wachtwoord
confirm_password=Verifieer wachtwoord
-admin_email=E-mail adres
+admin_email=E-mailadres
install_btn_confirm=Installeer Gitea
test_git_failed=Git test niet gelukt: 'git' commando %v
sqlite3_not_available=Deze Gitea-versie biedt geen ondersteuning voor SQLite3. Download de officiële build van %s (niet de versie van de 'gobuild').
invalid_db_setting=De database instelling zijn niet correct: %v
+invalid_db_table=De database tabel '%s' is ongeldig: %v
invalid_repo_path=Het pad van de hoofdmap van de repository is ongeldig: %v
+invalid_app_data_path=Ongeldig app-gegevenspad: %v
run_user_not_match=De 'uitvoeren als' gebruikersnaam is niet de huidige gebruikersnaam: %s -> %s
+internal_token_failed=Interne token genereren mislukt: %v
+secret_key_failed=Geheime sleutel genereren mislukt: %v
save_config_failed=Kan de configuratie niet opslaan: %v
invalid_admin_setting=Instelling van de administrator-account is ongeldig: %v
install_success=Welkom! Bedankt dat u voor Gitea heeft gekozen. Veel plezier en succes ermee!
@@ -202,6 +233,7 @@ default_enable_timetracking_popup=Tijdsregistratie voor nieuwe repositories stan
no_reply_address=Verborgen e-maildomein
no_reply_address_helper=Domeinnaam voor gebruikers met een verborgen e-mailadres. Bijvoorbeeld zal de gebruikersnaam 'joe' in Git worden geregistreerd als 'joe@noreply.example.org' als het verborgen email domein is ingesteld op 'noreply.example.org'.
password_algorithm=Wachtwoord Hash Algoritme
+password_algorithm_helper=Stel het wachtwoord hashing-algoritme in. Algoritmen hebben verschillende vereisten en sterkte. `argon2` heeft goede kenmerken, maar gebruikt veel geheugen en kan ongepast zijn voor kleinere systemen.
[home]
uname_holder=Gebruikersnaam of e-mailadres
@@ -211,7 +243,7 @@ my_repos=Repositories
show_more_repos=Toon meer repositories…
collaborative_repos=Gedeelde repositories
my_orgs=Mijn organisaties
-my_mirrors=Mijn kopieën
+my_mirrors=Mijn spiegels
view_home=Bekijk %s
search_repos=Zoek een repository…
filter=Andere filters
@@ -236,6 +268,9 @@ users=Gebruikers
organizations=Organisaties
search=Zoeken
code=Code
+search.fuzzy=Vergelijkbaar
+search.match=Overeenkomst
+code_search_unavailable=Er is momenteel geen code zoekfunctie beschikbaar. Neem contact op met uw sitebeheerder.
repo_no_results=Er zijn geen overeenkomende repositories gevonden.
user_no_results=Er zijn geen overeenkomende gebruikers gevonden.
org_no_results=Er zijn geen overeenkomende organisaties gevonden.
@@ -243,12 +278,15 @@ code_no_results=Geen broncode gevonden in overeenstemming met uw zoekterm.
code_search_results=Zoekresultaten voor ‘%s’
code_last_indexed_at=Laatst geïndexeerd %s
+
[auth]
create_new_account=Account registreren
register_helper_msg=Heeft u al een account? Klik hier om in te loggen
social_register_helper_msg=Heeft u al een account? Koppel deze nu!
disable_register_prompt=Registratie is uitgeschakeld. Neem alstublieft contact op met de pagina beheerder.
disable_register_mail=E-mailbevestiging voor registratie is uitgeschakeld.
+manual_activation_only=Neem contact op met uw sitebeheerder om de activering te voltooien.
+remember_me=Onthoud dit apparaat
forgot_password_title=Wachtwoord vergeten
forgot_password=Wachtwoord vergeten?
sign_up_now=Een account nodig? Meld u nu aan.
@@ -281,16 +319,22 @@ twofa_scratch_token_incorrect=Je eenmalige code is onjuist.
login_userpass=Inloggen
login_openid=OpenID
oauth_signup_tab=Registreer nieuw account
+oauth_signup_title=Voltooi nieuw account
oauth_signup_submit=Account voltooien
oauth_signin_tab=Bestaand account koppelen
oauth_signin_title=Inloggen om het gekoppelde account te machtigen
oauth_signin_submit=Account koppelen
+oauth.signin.error=Er is een fout opgetreden bij het verwerken van het autorisatieverzoek. Als deze fout zich blijft voordoen, neem dan contact op met de sitebeheerder.
+oauth.signin.error.access_denied=Het autorisatieverzoek is geweigerd.
+oauth.signin.error.temporarily_unavailable=Autorisatie mislukt omdat de verificatieserver tijdelijk niet beschikbaar is. Probeer het later opnieuw.
openid_connect_submit=Verbinden
openid_connect_title=Verbind met een bestaand account
openid_connect_desc=De gekozen OpenID-URI is onbekend. Koppel het aan een nieuw account hier.
openid_register_title=Nieuw account aanmaken
openid_register_desc=De gekozen OpenID-URI is onbekend. Koppel het aan een nieuw account hier.
openid_signin_desc=Geef uw OpenID-URI. Bijvoorbeeld: https://anne.me, bob.openid.org.cn of gnusocial.net/carry.
+disable_forgot_password_mail=Accountherstel is uitgeschakeld omdat er geen e-mailadres is ingesteld. Neem aub contact op met uw administrator.
+disable_forgot_password_mail_admin=Accountherstel is alleen beschikbaar wanneer een e-mailadres is ingesteld. Stel e-mailadres in om accountherstel te activeren.
email_domain_blacklisted=Je kan je niet registreren met dit e-mailadres.
authorize_application=Autoriseer applicatie
authorize_redirect_notice=U wordt doorgestuurd naar %s als u deze toepassing toestaat.
@@ -304,19 +348,47 @@ password_pwned=Het gekozen wachtwoord staat op een uw wachtwoord instellen.
reset_password=Account herstellen
+reset_password.title=%s, u heeft verzocht om uw account te herstellen
+reset_password.text=Klik op de volgende link om je account te herstellen binnen %s:
register_success=Registratie succesvol
+issue_assigned.pull=@%[1]s heeft u toegewezen aan de pull request %[2]s in repository %[3]s.
+issue_assigned.issue=@%[1]heeft u toegewezen aan issue %[2]s in repository %[3]s.
+issue.x_mentioned_you=@%s heeft u vermeld:
+issue.action.force_push=%[1]s heeft een force-push uitgevoerd %[2]s van %[3]s naar %[4]s.
+issue.action.push_1=@%[1]s heeft %[3]d commits gepusht naar %[2]s
+issue.action.push_n=@%[1]s heeft %[3]d commits gepusht naar %[2]s
+issue.action.close=@%[1]s sloot #%[2]d.
+issue.action.reopen=@%[1]s heropend #%[2]d.
+issue.action.merge=@%[1] heeft een merge uitgevoerd van #%[2]d naar %[3]s.
+issue.action.approve=@%[1]s heeft deze pull request goedgekeurd.
+issue.action.reject=@%[1]s vraagt om wijzigingen op deze pull request.
+issue.action.review=@%[1]s heeft gereageerd op deze pull request.
+issue.action.review_dismissed=@%[1]s wees de laatste review af van %[2]s voor deze pull request.
+issue.action.ready_for_review=@%[1]s markeerde deze pull request klaar voor beoordeling.
+issue.action.new=@%[1]s heeft #%[2]d aangemaakt.
issue.in_tree_path=In %s:
release.new.subject=%s in %s vrijgegeven
@@ -330,8 +402,10 @@ release.download.targz=Broncode (TAR.GZ)
repo.transfer.subject_to=%s zou "%s" willen overdragen aan %s
repo.transfer.subject_to_you=%s wil "%s" aan jou overdragen
repo.transfer.to_you=jij
+repo.transfer.body=Om het te accepteren of afwijzen, bezoek %s of negeer het gewoon.
repo.collaborator.added.subject=%s heeft jou toegevoegd aan %s
+repo.collaborator.added.text=U bent toegevoegd als een medewerker van de repository:
[modal]
yes=Ja
@@ -369,8 +443,10 @@ size_error=moet groter zijn dan %s
min_size_error=moet minimaal %s karakters bevatten.
max_size_error=mag maximaal %s karakters bevatten.
email_error=is niet een valide e-mail adres.
+url_error=`'%s' is niet een geldige URL.`
include_error=` moet substring '%s' bevatten.`
glob_pattern_error=` globpatroon is ongeldig: %s.`
+regex_pattern_error=` regex patroon is ongeldig: %s.`
unknown_error=Onbekende fout:
captcha_incorrect=De CAPTCHA-code is onjuist.
password_not_match=De wachtwoorden komen niet overeen.
@@ -379,6 +455,7 @@ lang_select_error=Selecteer een taal uit de lijst.
username_been_taken=Deze naam is al in gebruik.
username_change_not_local_user=Niet-lokale gebruikers mogen hun gebruikersnaam niet wijzigen.
repo_name_been_taken=De repository-naam wordt al gebruikt.
+repository_force_private=Forceer privé is ingeschakeld: privé repositories kunnen niet openbaar worden gemaakt.
repository_files_already_exist=Er bestaan al bestanden voor deze repository. Neem contact op met de systeembeheerder.
repository_files_already_exist.adopt=Bestanden bestaan al voor deze repository en kunnen alleen worden geadopteerd.
repository_files_already_exist.delete=Er bestaan al bestanden voor deze repository. U moet deze verwijderen.
@@ -389,6 +466,7 @@ org_name_been_taken=Naam van de organisatie wordt al gebruikt.
team_name_been_taken=De teamnaam is al in gebruik.
team_no_units_error=Toegang verlenen tot ten minste één repository sectie.
email_been_used=Het emailadres is al in gebruik.
+email_invalid=Het e-mailadres is ongeldig.
openid_been_used=OpenID adres '%s' reeds gebruikt.
username_password_incorrect=Gebruikersnaam of wachtwoord is onjuist.
password_complexity=Wachtwoord voldoet niet aan complexiteit eisen:
@@ -397,6 +475,7 @@ password_uppercase_one=Minstens één hoofdletter
password_digit_one=Minstens één cijfer
password_special_one=Minstens één speciaal teken (interpunctie, haakjes, aanhalingstekens, etc.)
enterred_invalid_repo_name=De repository-naam die u hebt ingevoerd is niet correct.
+enterred_invalid_org_name=De organizatienaam die u hebt ingevoerd is niet correct.
enterred_invalid_owner_name=De nieuwe eigenaarnaam is niet geldig.
enterred_invalid_password=Het ingevoerde wachtwoord is onjuist.
user_not_exist=De gebruiker bestaat niet.
@@ -412,7 +491,9 @@ auth_failed=Verificatie mislukt: %v
still_own_repo=Je account is nog eigenaar van één of meerdere repositories. Deze moeten eerst verwijderd of overgedragen worden.
still_has_org=Je account is lid van één of meerdere organisaties. Verlaat deze eerst.
+still_own_packages=Uw account bezit één of meer pakketten; verwijder deze eerst.
org_still_own_repo=Deze organisatie bezit minstens één repositories. Verwijder deze of draag deze eerst over.
+org_still_own_packages=Deze organisatie is nog eigenaar van één of meer pakketten; verwijder deze eerst.
target_branch_not_exist=Doel branch bestaat niet
@@ -423,6 +504,7 @@ repositories=repositories
activity=Openbare activiteit
followers=Volgers
starred=Repositories met ster
+watched=Gevolgde repositories
projects=Projecten
following=Volgt
follow=Volg
@@ -438,6 +520,7 @@ form.name_chars_not_allowed=Gebruikersnaam '%s' bevat ongeldige tekens.
[settings]
profile=Profiel
account=Account
+appearance=Vormgeving
password=Wachtwoord
security=Beveiliging
avatar=Profielfoto
@@ -451,6 +534,7 @@ twofa=Twee factor authenticatie
account_link=Gekoppelde Accounts
organization=Organisaties
uid=uid
+webauthn=Beveiligingssleutels
public_profile=Openbaar profiel
biography_placeholder=Vertel ons iets over jezelf
@@ -461,14 +545,33 @@ website=Website
location=Locatie
update_theme=Thema bijwerken
update_profile=Profiel bijwerken
+update_language=Taal wijzigen
update_language_not_found=De taal '%s' is niet beschikbaar.
+update_language_success=Taal is bijgewerkt.
update_profile_success=Je profiel is bijgewerkt.
change_username=Je gebruikersnaam is gewijzigd.
change_username_prompt=Let op: Als je je gebruikersnaam aanpast, verandert je account-URL ook.
+change_username_redirect_prompt=De oude gebruikersnaam wordt doorgestuurd tot deze wordt opgeëist.
continue=Doorgaan
cancel=Annuleren
language=Taal
ui=Thema
+hidden_comment_types=Verborgen commentaartypes
+comment_type_group_reference=Referentie
+comment_type_group_label=Label
+comment_type_group_milestone=Mijlpaal
+comment_type_group_assignee=Aangewezene
+comment_type_group_title=Titel
+comment_type_group_branch=Branch
+comment_type_group_time_tracking=Tijdregistratie
+comment_type_group_deadline=Deadline
+comment_type_group_dependency=Afhankelijkheid
+comment_type_group_lock=Vergrendel Status
+comment_type_group_review_request=Review aanvragen
+comment_type_group_pull_request_push=Commits toegevoegd
+comment_type_group_project=Project
+comment_type_group_issue_ref=Referentie issue
+saved_successfully=Uw instellingen zijn succesvol opgeslagen.
privacy=Privacy
keep_activity_private=De activiteit van de profielpagina verbergen
keep_activity_private_popup=Maakt de activiteit alleen zichtbaar voor jou en de admins
@@ -482,6 +585,7 @@ delete_current_avatar=Verwijder huidige avatar
uploaded_avatar_not_a_image=Het geüploade bestand is geen afbeelding.
uploaded_avatar_is_too_big=Het geüploade bestand heeft de maximale grootte overschreden.
update_avatar_success=Je avatar is bijgewerkt.
+update_user_avatar_success=De avatar van de gebruiker is bijgewerkt.
change_password=Wachtwoord bijwerken
old_password=Huidige wachtwoord
@@ -535,13 +639,37 @@ ssh_helper=Weet u niet hoe? Lees dan onze handleiding voor het
gpg_helper=Hulp nodig? Neem een kijkje op de GitHub handleiding over GPG.
add_new_key=SSH sleutel toevoegen
add_new_gpg_key=GPG sleutel toevoegen
+key_content_ssh_placeholder=Begint met 'ssh-ed25519', 'ssh-rsa', 'ecdsa-sha2-nistp256', 'ecdsa-sha2-nistp384', 'ecdsa-sha2-nistp521', 'sk-ecdsa-sha2-nistp256@openssh.com', of 'sk-ssh-ed25519@openssh.com'
key_content_gpg_placeholder=Begint met '-----BEGIN PGP PUBLIC KEY BLOCK-----'
add_new_principal=Verantwoordelijke toevoegen
ssh_key_been_used=Deze SSH-sleutel is al toegevoegd aan de server.
ssh_key_name_used=Er bestaat al een SSH sleutel met dezelfde naam in uw account.
ssh_principal_been_used=Deze verantwoordelijke is al toegevoegd aan de server.
gpg_key_id_used=Een publieke GPG-sleutel met dit ID bestaat al.
+gpg_no_key_email_found=Deze GPG-sleutel komt met geen enkele geactiveerd e-mailadres dat aan uw account is gekoppeld overeen. Het kan nog steeds worden toegevoegd als u de opgegeven token tekent.
+gpg_key_matched_identities=Overeenkomende identiteiten:
+gpg_key_matched_identities_long=De ingesloten identiteiten in deze sleutel komen overeen met de geactiveerde e-mailadressen voor deze gebruiker. Commits die overeenkomen met deze e-mailadressen kunnen worden geverifieerd met deze sleutel.
+gpg_key_verified=Geverifieerde sleutel
+gpg_key_verified_long=Sleutel is geverifieerd met een token en kan worden gebruikt om commits te verifiëren die overeenkomen met alle geactiveerde e-mailadressen voor deze gebruiker naast de bijbehorende identiteiten voor deze sleutel.
+gpg_key_verify=Verifiëren
+gpg_invalid_token_signature=De opgegeven GPG-sleutel, handtekening en token komen niet overeen of de token is verouderd.
+gpg_token_required=U moet een handtekening opgeven voor de onderstaande token
gpg_token=Token
+gpg_token_help=U kunt een handtekening genereren met:
+gpg_token_code=echo "%s" | gpg -a --default-key %s --detach-sig
+gpg_token_signature=Gepantserde GPG-handtekening
+key_signature_gpg_placeholder=Begint met '-----BEGIN PGP SIGNATURE-----'
+verify_gpg_key_success=GPG-sleutel '%s' is geverifieerd.
+ssh_key_verified=Geverifieerde sleutel
+ssh_key_verified_long=Sleutel is geverifieerd met een token en kan worden gebruikt om commits te verifiëren die overeenkomen met alle geactiveerde e-mailadressen voor deze gebruiker.
+ssh_key_verify=Verifiëren
+ssh_invalid_token_signature=De verstrekte SSH-sleutel, handtekening of token komen niet overeen of de token is verouderd.
+ssh_token_required=U moet een handtekening opgeven voor het onderstaande token
+ssh_token=Token
+ssh_token_help=U kunt een handtekening genereren door het volgende:
+ssh_token_signature=Gepantserde SSH handtekening
+key_signature_ssh_placeholder=Begint met '-----BEGIN SSH SIGNATURE-----'
+verify_ssh_key_success=SSH sleutel '%s' is geverifieerd.
subkeys=Subkeys
key_id=Key-ID
key_name=Sleutel naam
@@ -589,6 +717,9 @@ generate_token_success=Je nieuwe token is gegenereerd. Kopieer hem nu, want hij
generate_token_name_duplicate=%s is al gebruikt als een applicatienaam. Gebruik een nieuwe.
delete_token=Verwijderen
access_token_deletion=Verwijder toegangstoken
+access_token_deletion_cancel_action=Annuleren
+access_token_deletion_confirm_action=Verwijderen
+access_token_deletion_desc=Als je een token verwijdert, heeft de applicatie die het gebruikt geen toegang meer tot je account. Doorgaan?
delete_token_success=De token is verwijderd. Applicaties die hem gebruiken, verliezen toegang tot je account.
manage_oauth2_applications=Beheer OAuth2-applicaties
@@ -639,11 +770,18 @@ or_enter_secret=Of voer deze geheime code in: %s
then_enter_passcode=En vul de toegangscode, die in de applicatie weergegeven wordt, in:
passcode_invalid=De code is niet correct. Probeer het nogmaals.
twofa_enrolled=Tweefactorsauthenticatie is geactiveerd voor dit account. Bewaar je token (%s) op een veilige plek, omdat hij maar één keer wordt weergegeven!
+twofa_failed_get_secret=Kon geheim niet ophalen.
+webauthn_desc=Beveiligingssleutels zijn hardware apparaten die cryptografische sleutels bevatten. Ze kunnen worden gebruikt voor tweestapsverificatie. Beveiligingssleutels moeten de WebAuthn Authenticator standaard ondersteunen.
+webauthn_register_key=Voeg beveiligingssleutel toe
+webauthn_nickname=Bijnaam
+webauthn_delete_key=Verwijder beveiligingssleutel
+webauthn_delete_key_desc=Als u een beveiligingssleutel verwijdert, kunt u er niet meer mee inloggen. Doorgaan?
manage_account_links=Gekoppelde accounts beheren
manage_account_links_desc=Deze externe accounts zijn gekoppeld aan je Gitea-account.
account_links_not_available=Er zijn momenteel geen externe accounts aan je Gitea-account gelinkt.
+link_account=Account koppelen
remove_account_link=Gekoppeld account verwijderen
remove_account_link_desc=Als je een gekoppeld account verwijdert, verliest dit account toegang tot je Gitea-account. Doorgaan?
remove_account_link_success=Het gekoppelde account is verwijderd.
@@ -653,6 +791,7 @@ repos_none=U bezit geen repositories
delete_account=Verwijder uw account
delete_prompt=Als je doorgaat, wordt je gebruikersaccount permanent verwijderd. Dit KAN NIET ongedaan gemaakt worden.
+delete_with_all_comments=Uw account is jonger dan %s. Om spook opmerkingen te vermijden, worden alle issue/PR reacties er samen mee verwijderd.
confirm_delete_account=Bevestig verwijdering
delete_account_title=Verwijder gebruikers account
delete_account_desc=Weet je zeker dat je dit gebruikersaccount permanent wil verwijderen?
@@ -661,12 +800,18 @@ email_notifications.enable=E-mailnotificaties inschakelen
email_notifications.onmention=Alleen e-mail op vermelding
email_notifications.disable=E-mailnotificaties uitschakelen
email_notifications.submit=E-mailvoorkeur instellen
+email_notifications.andyourown=En je eigen notificaties
+visibility=Gebruiker zichtbaarheid
visibility.public=Openbaar
+visibility.public_tooltip=Zichtbaar voor alle gebruikers
visibility.limited=Beperkt
+visibility.limited_tooltip=Alleen zichtbaar voor ingelogde gebruikers
visibility.private=Privé
+visibility.private_tooltip=Enkel zichtbaar voor organisatieleden
[repo]
+new_repo_helper=Een repository bevat alle projectbestanden, inclusief de revisiegeschiedenis. Heeft u het ergens anders al? Migreer repository.
owner=Eigenaar
owner_helper=Sommige organisaties kunnen niet worden weergegeven in de dropdown vanwege een limiet op het maximale aantal repositories.
repo_name=Naam van repository
@@ -684,31 +829,55 @@ visibility_fork_helper=(Verandering van deze waarde zal van invloed zijn op alle
clone_helper=Heb je hulp nodig om te clonen? Bekijk dan de handleiding.
fork_repo=Repository forken
fork_from=Afsplitsing van
+already_forked=Je hebt %s al geforked
+fork_to_different_account=Fork naar een ander account
fork_visibility_helper=De zichtbaarheid van een geforkte repository kan niet worden veranderd.
use_template=Gebruik dit sjabloon
+clone_in_vsc=Kloon in VS Code
+download_zip=ZIP downloaden
+download_tar=TAR.GZ downloaden
+download_bundle=BUNDLE downloaden
generate_repo=Repository genereren
generate_from=Genereer van
repo_desc=Omschrijving
repo_desc_helper=Voer korte beschrijving in (optioneel)
repo_lang=Taal
repo_gitignore_helper=Selecteer .gitignore templates.
+repo_gitignore_helper_desc=Kies welke bestanden niet bij te houden vanuit een lijst met sjablonen voor alledaagse talen. Gebruikelijke artefacten gegenereerd door de build tools van elke taal zijn standaard inbegrepen met .gitignore.
issue_labels=Issuelabels
issue_labels_helper=Selecteer een issuelabelset.
license=Licentie
license_helper=Selecteer een licentie bestand.
+license_helper_desc=Een licentie bepaalt wat anderen wel en niet met je code kunnen doen. Niet zeker welke juist is voor jouw project? Zie Kies een licentie.
readme=README
readme_helper=Selecteer een README-bestandssjabloon.
+readme_helper_desc=Dit is de plek waar je een volledige beschrijving van je project kunt schrijven.
auto_init=Initialiseer repository (voegt .gitignore, License en README toe)
+trust_model_helper=Selecteer het vertrouwensmodel voor handtekeningverificatie. Mogelijke opties zijn:
+trust_model_helper_collaborator=Medewerker: Vertrouw handtekeningen door medewerkers
+trust_model_helper_committer=Committer: Vertrouw handtekeningen die overeenkomen met de committers
+trust_model_helper_collaborator_committer=Medewerker+Committer: Vertrouw handtekeningen door medewerkers die overeenkomen met de committer
+trust_model_helper_default=Standaard: Gebruik het standaard vertrouwemsmodel voor deze installatie
create_repo=Nieuwe repository
default_branch=Standaard branch
+default_branch_helper=De standaard branch is de basis branch voor pull requests en code commits.
mirror_prune=Opschonen
mirror_prune_desc=Verwijder verouderde remote-tracking-referenties
+mirror_interval=Spiegel Interval (geldige tijdseenheden zijn 'h', 'm', 's'). 0 om automatische synchronisatie uit te schakelen (Minimum interval: %s)
mirror_interval_invalid=Kloon-interval is niet geldig.
+mirror_sync_on_commit=Synchroniseer wanneer commits gepusht worden
mirror_address=Klonen van URL
+mirror_address_desc=Voeg alle vereiste inloggegevens toe in de autorisatie sectie.
mirror_address_url_invalid=De opgegeven url is ongeldig. U dient alle componenten van de url correct te escapen.
mirror_address_protocol_invalid=De opgegeven url is ongeldig. Alleen http(s):// of git:// locaties kunnen worden gemirrord.
+mirror_lfs=Grote bestandsopslag (LFS)
+mirror_lfs_desc=Activeer spiegelen van LFS-gegevens.
+mirror_lfs_endpoint=LFS Eindpunt
+mirror_lfs_endpoint_desc=Synchronisatie zal proberen de kloon-url te gebruiken om de LFS-serverte bepalen. Je kan ook een aangepast eindpunt opgeven als de LFS-gegevens ergens anders zijn opgeslagen.
mirror_last_synced=Laatst gesynchroniseerd
+mirror_password_placeholder=(Ongewijzigd)
mirror_password_blank_placeholder=(Niet ingesteld)
+mirror_password_help=Wijzig de gebruikersnaam om een opgeslagen wachtwoord te wissen.
watchers=Volgers
stargazers=Stargazers
forks=Forks
@@ -725,7 +894,14 @@ delete_preexisting_label=Verwijderen
delete_preexisting=Verwijder reeds bestaande bestanden
delete_preexisting_content=Verwijder bestanden in %s
delete_preexisting_success=Niet-geadopteerde bestanden verwijderd in %s
+blame_prior=Bekijk de schuld voorafgaand aan deze verandering
+transfer.accept=Accepteer overdracht
+transfer.accept_desc=Overmaken naar "%s"
+transfer.reject=Overdracht afwijzen
+transfer.reject_desc=Annuleer overdracht naar "%s"
+transfer.no_permission_to_accept=Je hebt geen toestemming om te accepteren
+transfer.no_permission_to_reject=Je hebt geen toestemming om te weigeren
desc.private=Privé
desc.public=Openbaar
@@ -738,6 +914,7 @@ desc.archived=Gearchiveerd
template.items=Sjabloon items
template.git_content=Git inhoud (standaard Branch)
template.git_hooks=Git Hooks
+template.git_hooks_tooltip=Je bent momenteel niet in staat om Git Hooks één keer te wijzigen of te verwijderen. Selecteer deze optie alleen als je de sjabloonrepository vertrouwt.
template.webhooks=Webhooks
template.topics=Onderwerpen
template.avatar=Profielfoto
@@ -749,11 +926,20 @@ archive.title=Deze repo is gearchiveerd. U kunt bestanden bekijken en het klonen
archive.issue.nocomment=Deze repo is gearchiveerd. U kunt niet reageren op problemen.
archive.pull.nocomment=Deze repo is gearchiveerd. U kunt niet reageren op pull requests.
+form.reach_limit_of_creation_1=U heeft al uw limiet van %d repository bereikt.
+form.reach_limit_of_creation_n=U heeft al uw limiet van %d repositories bereikt.
form.name_reserved=Repositorienaam '%s' is gereserveerd.
form.name_pattern_not_allowed=Het patroon '%s' is niet toegestaan in de naam van een repository.
+need_auth=Autorisatie
migrate_options=Migratie opties
migrate_service=Migratie Service
+migrate_options_mirror_helper=Deze repositorie zal een spiegel zijn
+migrate_options_lfs=Migreer LFS bestanden
+migrate_options_lfs_endpoint.label=LFS Eindpunt
+migrate_options_lfs_endpoint.description=Migratie zal proberen om je Git remote te gebruiken om de LFS-server te bepalen. Je kan ook een aangepast eindpunt opgeven als de LFS-gegevens ergens anders zijn opgeslagen.
+migrate_options_lfs_endpoint.description.local=Een lokaal serverpad wordt ook ondersteund.
+migrate_options_lfs_endpoint.placeholder=Laat dit leeg om af te leiden uit de kloon-url
migrate_items=Migratie Items
migrate_items_wiki=Wiki
migrate_items_milestones=Mijlpalen
@@ -765,9 +951,12 @@ migrate_items_releases=Releases
migrate_repo=Migreer repository
migrate.clone_address=Migreer / kloon van URL
migrate.clone_address_desc=De HTTP(s)- of 'git clone'-URL van een bestaande repository
+migrate.github_token_desc=Je kunt hier een of meerdere tokens met komma gescheiden plaatsen om sneller te migreren door de GitHub API limiet te beperken. WAARSCHUWING: Het misbruik van deze functie kan in strijd zijn met het beleid van de serviceprovider en leiden tot het blokkeren van rekeningen.
migrate.clone_local_path=of een lokaal pad
migrate.permission_denied=U bent niet gemachtigd om deze lokale repositories te importeren.
+migrate.permission_denied_blocked=Je kunt niet importeren uit niet-toegestane hosts, vraag de beheerder om de instellingen ALLOWED_DOMAINS/ALLOW_LOCALNETWORKS/BLOCKED_DOMAINS te controleren.
migrate.invalid_local_path=Het lokale pad is ongeldig, bestaat niet of is geen map.
+migrate.invalid_lfs_endpoint=Het LFS-eindpunt is niet geldig.
migrate.failed=Migratie is mislukt: %v
migrate.migrate_items_options=Toegangstoken is vereist om extra items te migreren
migrated_from=Gemigreerd van %[2]s
@@ -776,6 +965,22 @@ migrate.migrate=Migreer van %s
migrate.migrating=Migreren van %s...
migrate.migrating_failed=Migreren van %s is mislukt.
migrate.migrating_failed.error=Foutmelding: %s
+migrate.migrating_failed_no_addr=Migratie is mislukt.
+migrate.github.description=Gegevens overzetten van github.com of andere GitHub instanties.
+migrate.git.description=Migreer een repositorie van elke Git service.
+migrate.gitlab.description=Gegevens migreren van gitlab.com of andere GitLab-instanties.
+migrate.gitea.description=Gegevens overzetten van gitea.com of andere Gitea instanties.
+migrate.gogs.description=Gegevens overzetten van notabug.org of andere Gogs instanties.
+migrate.onedev.description=Gegevens overzetten van code.onedev.io of andere OneDev instanties.
+migrate.codebase.description=Gegevens migreren van codebasehq.com.
+migrate.gitbucket.description=Gegevens migreren van GitBucket instanties.
+migrate.migrating_git=Git gegevens migreren
+migrate.migrating_topics=Onderwerpen migreren
+migrate.migrating_milestones=Mijlpalen migreren
+migrate.migrating_labels=Labels migreren
+migrate.migrating_releases=Releases migreren
+migrate.migrating_issues=Issues migreren
+migrate.migrating_pulls=Pull-verzoeken migreren
mirror_from=kopie van
forked_from=geforked van
@@ -797,6 +1002,7 @@ clone_this_repo=Kloon deze repository
create_new_repo_command=Maak een nieuwe repository aan vanaf de console
push_exist_repo=Push een bestaande repositorie vanaf de console
empty_message=Deze repository bevat geen inhoud.
+broken_message=De Git gegevens die ten grondslag liggen aan deze repository kunnen niet worden gelezen. Neem contact op met de beheerder van deze instantie of verwijder deze repository.
code=Code
code.desc=Toegang tot broncode, bestanden, commits en branches.
@@ -804,11 +1010,13 @@ branch=Branch
tree=Tree
clear_ref=`Huidige referentie wissen`
filter_branch_and_tag=Filter op branch of tag
+find_tag=Label zoeken
branches=Branches
tags=Labels
issues=Kwesties
pulls=Pull-aanvragen
project_board=Projecten
+packages=Paketten
labels=Labels
org_labels_desc=Organisatielabel dat gebruikt kan worden met alle repositories onder deze organisatie
org_labels_desc_manage=beheren
@@ -816,27 +1024,46 @@ org_labels_desc_manage=beheren
milestones=Mijlpalen
commits=Commits
commit=Commit
+release=Release
releases=Publicaties
tag=Label
+released_this=heeft dit gepubliceerd
file.title=%s op %s
file_raw=Ruw
file_history=Geschiedenis
+file_view_source=Bron weergeven
+file_view_rendered=Weergave weergeven
file_view_raw=Weergave ruw bestand
file_permalink=Permalink
file_too_large=Dit bestand is te groot om te tonen.
+invisible_runes_header=`Dit bestand bevat onzichtbare Unicode-karakters!`
+invisible_runes_description=`Dit bestand bevat onzichtbare Unicode karakters die mogelijk anders verwerkt worden dan wat hieronder staat. Als uw gebruik opzettelijk en legitiem is, kunt u deze waarschuwing veilig negeren. Gebruik de Escape knop om verborgen karakters te onthullen.`
+ambiguous_runes_header=`Dit bestand bevat dubbelzinnige Unicode karakters!`
+ambiguous_runes_description=`Dit bestand bevat dubbelzinnige Unicode karakters die verward kunnen worden met andere karakters in uw huidige taal. Als je het opzettelijk en legitiem gebruikt, kun je deze waarschuwing veilig negeren. Gebruik de Escape knop om deze karakters te markeren.`
+invisible_runes_line=`Deze lijn heeft onzichtbare unicode karakters`
+ambiguous_runes_line=`Deze lijn heeft dubbelzinnige unicode karakters`
+ambiguous_character=`%[1]c [U+%04[1]X] is verwarrend met %[2]c [U+%04[2]X]`
+escape_control_characters=Escape
+unescape_control_characters=Onescape
+file_copy_permalink=Permalink kopiëren
+view_git_blame=Bekijk Git Blame
video_not_supported_in_browser=Je browser ondersteunt de HTML5 'video'-tag niet.
audio_not_supported_in_browser=Je browser ondersteunt de HTML5 'audio'-tag niet.
stored_lfs=Opgeslagen met Git LFS
symbolic_link=Symbolic link
commit_graph=Commit grafiek
+commit_graph.select=Selecteer branches
+commit_graph.hide_pr_refs=Verberg pull verzoeken
commit_graph.monochrome=Monochroom
commit_graph.color=Kleur
blame=Blame
+download_file=Download het bestand
normal_view=Normale weergave
line=regel
lines=regels
+editor.add_file=Bestand toevoegen
editor.new_file=Nieuw bestand
editor.upload_file=Upload bestand
editor.edit_file=Bewerk bestand
@@ -860,7 +1087,12 @@ editor.add_tmpl='' toevoegen
editor.add='%s' toevoegen
editor.update='%s' updaten
editor.delete='%s' verwijderen
+editor.patch=Patch toepassen
+editor.patching=Patchen:
+editor.fail_to_apply_patch=Kan patch '%s' niet toepassen
+editor.new_patch=Nieuwe Patch
editor.commit_message_desc=Voeg een optionele uitgebreide omschrijving toe…
+editor.signoff_desc=Voeg een Signed-off-by toe aan het einde van het commit logbericht.
editor.commit_directly_to_this_branch=Commit direct naar de branch '%s'.
editor.create_new_branch=Maak een nieuwe branch voor deze commit en start van een pull-aanvraag.
editor.create_new_branch_np=Maak een nieuwe branch voor deze commit.
@@ -883,6 +1115,8 @@ editor.commit_empty_file_text=Het bestand dat u wilt committen is leeg. Doorgaan
editor.no_changes_to_show=Er zijn geen wijzigingen om weer te geven.
editor.fail_to_update_file=Bijwerken/aanmaken van bestand '%s ' mislukt.
editor.fail_to_update_file_summary=Foutmelding:
+editor.push_rejected_no_message=De wijziging is afgewezen door de server zonder bericht. Controleer de Git Hooks alsjeblieft.
+editor.push_rejected=De wijziging is afgewezen door de server. Controleer Controleer de Git Hooks alsjeblieft.
editor.push_rejected_summary=Volledig afwijzingsbericht:
editor.add_subdir=Een map toevoegen…
editor.unable_to_upload_files=Uploaden van bestand '%s' is mislukt: %v
@@ -892,10 +1126,13 @@ editor.cannot_commit_to_protected_branch=Kan niet committen naar de beveiligde b
editor.no_commit_to_branch=Kan niet rechtstreeks naar branch committen omdat:
editor.user_no_push_to_branch=Gebruiker kan niet pushen naar branch
editor.require_signed_commit=Branch vereist een ondertekende commit
+editor.cherry_pick=Cherry-pick %s op:
+editor.revert=%s ongedaan maken op:
commits.desc=Bekijk de broncode-wijzigingsgeschiedenis.
commits.commits=Commits
commits.no_commits=Geen overeenkomstige commits. '%s' en '%s' hebben totaal verschillende histories.
+commits.nothing_to_compare=Deze branches zijn gelijk.
commits.search=Zoek commits…
commits.search.tooltip=U kunt trefwoorden prefixen met "auteur:", "committer:", "na:" of "voor:", bv. "revert auteur:Alice voor:2019-0401".
commits.find=Zoek
@@ -909,11 +1146,23 @@ commits.signed_by=Getekend door
commits.signed_by_untrusted_user=Ondertekend door niet-vertrouwde gebruiker
commits.signed_by_untrusted_user_unmatched=Ondertekend door niet-vertrouwde gebruiker die niet overeenkomt met de committer
commits.gpg_key_id=GPG sleutel-ID
+commits.ssh_key_fingerprint=SSH sleutel vingerafdruk
+commit.actions=Acties
+commit.revert=Ongedaan maken
+commit.revert-header=Maak %s ongedaan
+commit.revert-content=Selecteer een branch om terug te zetten:
+commit.cherry-pick=Cherry-pick
+commit.cherry-pick-header=Cherry-pick: %s
+commit.cherry-pick-content=Selecteer een branch om te cherry-pick op:
+ext_issues=Toegang tot Externe Issues
ext_issues.desc=Koppelen aan een externe kwestie-tracker.
projects=Projecten
+projects.desc=Beheer issues en pulls in projectborden.
+projects.description=Omschrijving (optioneel)
+projects.description_placeholder=Omschrijving
projects.create=Project aanmaken
projects.title=Titel
projects.new=Nieuw project
@@ -937,11 +1186,14 @@ projects.board.edit_title=Nieuwe boardnaam
projects.board.new_title=Nieuwe boardnaam
projects.board.new_submit=Versturen
projects.board.new=Nieuw bord
+projects.board.set_default=Instellen als standaard
+projects.board.set_default_desc=Stel dit board in als standaard voor niet gecategoriseerde issues en pulls
projects.board.delete=Verwijder bord
projects.board.deletion_desc=Als een projectbord wordt verwijdert, worden alle gerelateerde kwesties naar 'Ongecategoriseerd' verplaatst. Doorgaan?
projects.board.color=Kleur
projects.open=Open
projects.close=Sluiten
+projects.board.assigned_to=Toegewezen aan
issues.desc=Organiseer bugrapporten, taken en mijlpalen.
issues.filter_assignees=Filter verantwoordelijke
@@ -988,6 +1240,11 @@ issues.label_templates.info=Er bestaan nog geen labels. Maak een nieuw label met
issues.label_templates.helper=Selecteer een labelset
issues.label_templates.use=Label Set gebruiken
issues.label_templates.fail_to_load_file=Kan het labelsjabloonbestand '%s' niet openen: %v
+issues.add_label=voegde het %s label %s toe
+issues.add_labels=voegde de %s labels %s toe
+issues.remove_label=verwijderde het %s label %s
+issues.remove_labels=verwijderde de %s labels %s
+issues.add_remove_labels=voegde de %s toe en verwijderde de %s labels %s
issues.add_milestone_at=`heeft dit %[2]s aan de mijlpaal %[1]s toegevoegd`
issues.add_project_at=`heeft dit toegevoegd aan het %s project %s`
issues.change_milestone_at='mijlpaal bewerkt van %s %s %s'
@@ -1001,6 +1258,9 @@ issues.add_assignee_at=`was toegekend door %s %s`
issues.remove_assignee_at=`is niet toegewezen door %s %s`
issues.remove_self_assignment=`heeft %s zijn/haar toewijzing verwijderd`
issues.change_title_at='titel aangepast van %s naar %s %s'
+issues.change_ref_at=`wijzig referentie van %s naar %s %s`
+issues.remove_ref_at=`heeft referentie %s verwijderd %s`
+issues.add_ref_at=`heeft referentie %s toegevoegd %s`
issues.delete_branch_at=`heeft %[2]s de branch %[1]s verwijderd.`
issues.filter_label=Label
issues.filter_label_exclude=`Gebruik alt
+ klik/voer
in om labels uit te sluiten
@@ -1009,11 +1269,14 @@ issues.filter_milestone=Mijlpaal
issues.filter_milestone_no_select=Alle mijlpalen
issues.filter_assignee=Aangewezene
issues.filter_assginee_no_select=Alle toegewezen personen
+issues.filter_poster=Auteur
+issues.filter_poster_no_select=Alle auteurs
issues.filter_type=Type
issues.filter_type.all_issues=Alle kwesties
issues.filter_type.assigned_to_you=Aan jou toegewezen
issues.filter_type.created_by_you=Aangemaakt door jou
issues.filter_type.mentioning_you=Vermelden jou
+issues.filter_type.review_requested=Review aangevraagd
issues.filter_sort=Sorteer
issues.filter_sort.latest=Nieuwste
issues.filter_sort.oldest=Oudste
@@ -1027,6 +1290,7 @@ issues.filter_sort.moststars=Meeste sterren
issues.filter_sort.feweststars=Minste sterren
issues.filter_sort.mostforks=Meeste forks
issues.filter_sort.fewestforks=Minste forks
+issues.keyword_search_unavailable=Zoeken op trefwoord is momenteel niet beschikbaar. Neem contact op met de websitebeheerder.
issues.action_open=Open
issues.action_close=Sluit
issues.action_label=Label
@@ -1035,19 +1299,28 @@ issues.action_milestone_no_select=Geen mijlpaal
issues.action_assignee=Toegewezene
issues.action_assignee_no_select=Geen verantwoordelijke
issues.opened_by=%[1]s geopend door %[3]s
+pulls.merged_by=door %[3]s was samengevoegd %[1]s
+pulls.merged_by_fake=bij %[2]s is %[1]s samengevoegd
+issues.closed_by=door %[3]s was gesloten %[1]s
+issues.opened_by_fake=%[1]s geopend door %[2]s
+issues.closed_by_fake=door %[2]s was gesloten %[1]s
issues.previous=Vorige
issues.next=Volgende
issues.open_title=Open
issues.closed_title=Gesloten
+issues.draft_title=Concept
issues.num_comments=%d opmerkingen
issues.commented_at=`reageerde %s`
issues.delete_comment_confirm=Weet u zeker dat u deze reactie wilt verwijderen?
issues.context.copy_link=Link kopiëren
issues.context.quote_reply=Citeer antwoord
+issues.context.reference_issue=Verwijs in nieuw issue
issues.context.edit=Bewerken
issues.context.delete=Verwijder
issues.no_content=Er is nog geen inhoud.
issues.close_issue=Sluit
+issues.pull_merged_at=`commit samengevoegd %[2]s
in %[3]s %[4]s`
+issues.manually_pull_merged_at=`commit handmatig samengevoegd %[2]s
in %[3]s %[4]s`
issues.close_comment_issue=Reageer en sluit
issues.reopen_issue=Heropen
issues.reopen_comment_issue=Heropen en geef commentaar
@@ -1069,6 +1342,8 @@ issues.re_request_review=Opnieuw aanvragen review
issues.is_stale=Er zijn wijzigingen aangebracht in deze PR sinds deze beoordeling
issues.remove_request_review=Verwijder beoordelingsverzoek
issues.remove_request_review_block=Kan beoordelingsverzoek niet verwijderen
+issues.dismiss_review=Beoordeling afwijzen
+issues.dismiss_review_warning=Bent u zeker dat u deze beoordeling wilt afwijzen?
issues.sign_in_require_desc=Log in om deel te nemen aan deze discussie.
issues.edit=Bewerken
issues.cancel=Annuleren
@@ -1112,13 +1387,21 @@ issues.lock.reason=Reden voor vergrendeling
issues.lock.title=Vergrendel gesprek over dit probleem.
issues.unlock.title=Ontgrendel gesprek over dit probleem.
issues.comment_on_locked=Je kunt geen commentaar geven op een vergrendeld probleem.
+issues.delete=Verwijderen
+issues.delete.title=Deze issue verwijderen?
+issues.delete.text=Wilt u deze issue echt verwijderen? (Dit is permanent en verwijdert alle inhoud. Overweeg om deze issue te sluiten, als u liever deze als archief wilt bijhouden)
issues.tracker=Tijdregistratie
+issues.start_tracking_short=Start timer
issues.start_tracking=Start tijdregistratie
issues.start_tracking_history=`%s is begonnen`
issues.tracker_auto_close=Timer wordt automatisch gestopt wanneer dit probleem wordt gesloten
+issues.tracking_already_started=`Je houd al tijd bij voor een ander issue!`
+issues.stop_tracking=Stop timer
issues.stop_tracking_history=`gestopt met werken aan %s`
+issues.cancel_tracking=Weggooien
issues.cancel_tracking_history=`tijd bijhouden geannuleerd: %s`
issues.add_time=Tijd handmatig toevoegen
+issues.del_time=Verwijder deze tijdlog
issues.add_time_short=Timer toevoegen
issues.add_time_cancel=Annuleren
issues.add_time_history=`heeft besteedde tijd toegevoegd: %s`
@@ -1134,6 +1417,7 @@ issues.error_modifying_due_date=Deadline aanpassen mislukt.
issues.error_removing_due_date=Deadline verwijderen mislukt.
issues.push_commit_1=toegevoegd %d commit %s
issues.push_commits_n=toegevoegd %d commits %s
+issues.force_push_codes=`force-push %[1]s van %[2]s
naar %[4]s
%[6]s`
issues.due_date_form=jjjj-mm-dd
issues.due_date_form_add=Vervaldatum toevoegen
issues.due_date_form_edit=Bewerk
@@ -1141,16 +1425,21 @@ issues.due_date_form_remove=Verwijder
issues.due_date_not_writer=Je hebt schrijftoegang in deze repository nodig om de deadline van een kwestie aan te passen.
issues.due_date_not_set=Geen vervaldatum ingesteld.
issues.due_date_added=heeft %[2]s de deadline %[1]s toegevoegd
+issues.due_date_modified=de vervaldatum van %[2]s is gewijzigd naar %[1]s[3]s
issues.due_date_remove=heeft %[2]s de deadline %[1]s verwijderd
issues.due_date_overdue=Over tijd
issues.due_date_invalid=De deadline is ongeldig of buiten bereik. Gebruik het formaat 'jjjj-mm-dd'.
issues.dependency.title=Afhankelijkheden
+issues.dependency.issue_no_dependencies=Geen afhankelijkheden ingesteld.
+issues.dependency.pr_no_dependencies=Geen afhankelijkheden ingesteld.
issues.dependency.add=Voeg afhankelijkheid toe…
issues.dependency.cancel=Annuleer
issues.dependency.remove=Verwijder
issues.dependency.remove_info=Verwijder afhankelijkheid
issues.dependency.added_dependency=`voegde een nieuwe afhankelijkheid %s toe `
issues.dependency.removed_dependency=`verwijderde een afhankelijkheid %s`
+issues.dependency.pr_closing_blockedby=Het sluiten van deze pull-aanvraag is geblokkeerd door de volgende issues
+issues.dependency.issue_closing_blockedby=Het sluiten van dit issue is geblokkeerd door de volgende problemen
issues.dependency.issue_close_blocks=Deze kwestie blokkeert het sluiten van de volgende kwesties
issues.dependency.pr_close_blocks=Deze pull-aanvraag blokkeert het sluiten van de volgende kwesties
issues.dependency.issue_close_blocked=Je moet alle kwesties die deze kwestie blokkeren sluiten voordat je deze kan sluiten.
@@ -1171,6 +1460,8 @@ issues.review.self.approval=Je kan je eigen pull-aanvraag niet goedkeuren.
issues.review.self.rejection=Je kan geen wijzigingen aanvragen op je eigen pull-aanvraag.
issues.review.approve=heeft deze veranderingen %s goedgekeurd
issues.review.comment=beoordeeld %s
+issues.review.dismissed=%s's beoordeling afgewezen %s
+issues.review.dismissed_label=Afgewezen
issues.review.left_comment=heeft een reactie achtergelaten
issues.review.content.empty=Je moet een reactie achterlaten die de gewenste verandering(en) beschrijft.
issues.review.reject=aangevraagde wijzigingen %s
@@ -1179,6 +1470,7 @@ issues.review.add_review_request=heeft een review aangevraagd van %s %s
issues.review.remove_review_request=beoordelingsaanvraag voor %s %s verwijderd
issues.review.remove_review_request_self=beoordeling geweigerd %s
issues.review.pending=In behandeling
+issues.review.pending.tooltip=Deze reactie is momenteel niet zichtbaar voor andere gebruikers. Selecteer '%s' -> '%s/%s/%s' ' boven aan de pagina.
issues.review.review=Review
issues.review.reviewers=Reviewers
issues.review.outdated=Verouderd
@@ -1190,21 +1482,38 @@ issues.review.resolve_conversation=Gesprek oplossen
issues.review.un_resolve_conversation=Gesprek niet oplossen
issues.review.resolved_by=markeerde dit gesprek als opgelost
issues.assignee.error=Niet alle aangewezen personen zijn toegevoegd vanwege een onverwachte fout.
+issues.reference_issue.body=Inhoud
issues.content_history.deleted=verwijderd
issues.content_history.edited=bewerkt
+issues.content_history.created=gecreëerd
+issues.content_history.delete_from_history=Uit geschiedenis verwijderen
+issues.content_history.delete_from_history_confirm=Uit geschiedenis verwijderen?
+issues.content_history.options=Opties
+issues.reference_link=Referentie: %s
compare.compare_base=basis
compare.compare_head=vergelijk
pulls.desc=Schakel pull-aanvragen en code-beoordelingen in.
pulls.new=Nieuwe Pull aanvraag
+pulls.view=Pull verzoek bekijken
pulls.compare_changes=Nieuwe pull-aanvraag
+pulls.allow_edits_from_maintainers=Bewerkingen toestaan van maintainers
+pulls.allow_edits_from_maintainers_desc=Gebruikers met schrijftoegang tot de basis branch kunnen ook pushen naar deze branch
+pulls.allow_edits_from_maintainers_err=Updaten mislukt
pulls.compare_changes_desc=Selecteer de samen te voegen doel- en bron-branch.
+pulls.has_viewed_file=Gezien
+pulls.has_changed_since_last_review=Veranderd sinds de laatste beoordeling
+pulls.viewed_files_label=%[1]d / %[2]d bestanden bekeken
pulls.compare_base=samenvoegen met
pulls.compare_compare=trekken van
+pulls.switch_comparison_type=Wissel vergelijking type
+pulls.switch_head_and_base=Verwissel hoofd en basis
pulls.filter_branch=Filter branch
pulls.no_results=Geen resultaten gevonden.
pulls.nothing_to_compare=Deze branches zijn gelijk. Er is geen pull-aanvraag nodig.
+pulls.nothing_to_compare_and_allow_empty_pr=Deze branches zijn gelijk. Deze pull verzoek zal leeg zijn.
+pulls.has_pull_request=`Een pull-verzoek tussen deze branches bestaat al: %[2]s#%[3]d`
pulls.create=Pull verzoek aanmaken
pulls.title_desc=wil %[1]d commits van %[2]s
samenvoegen met %[3]s
pulls.merged_title_desc=heeft %[1]d commits samengevoegd van %[2]s
naar %[3]s
%[4]s
@@ -1216,17 +1525,26 @@ pulls.reopen_to_merge=Heropen dit pull request aub om een een merge actie uit te
pulls.cant_reopen_deleted_branch=Deze pull-aanvraag kan niet opnieuw worden geopend omdat de branch is verwijderd.
pulls.merged=Samengevoegd
pulls.merged_as=De pull request is samengevoegd als %[2]s
.
+pulls.manually_merged=Handmatig samengevoegd
+pulls.manually_merged_as=Het pull-verzoek is handmatig samengevoegd als %[2]s
.
pulls.is_closed=Deze pull-aanvraag is gesloten.
pulls.has_merged=Deze pull-aanvraag is al samengevoegd.
pulls.title_wip_desc=`Start de titel met %s om te voorkomen dat deze pull-aanvraag per ongeluk wordt samengevoegd.`
+pulls.cannot_merge_work_in_progress=Dit pull request is gemarkeerd als werk in uitvoering.
+pulls.still_in_progress=Nog steeds bezig?
+pulls.add_prefix=Voeg %s prefix toe
+pulls.remove_prefix=Verwijder %s prefix
pulls.data_broken=Deze pull-aanvraag is ongeldig wegens missende fork-informatie.
pulls.files_conflicted=Dit pull request heeft wijzigingen die strijdig zijn met de doel branch.
pulls.is_checking=Controle op samenvoegingsconflicten is nog bezig. Probeer later nog een keer.
+pulls.is_ancestor=Deze branch is al opgenomen in de toegewezen branch. Er is niets om samen te voegen.
+pulls.is_empty=De wijzigingen in deze branch bevinden zich al in de toegewezen branch. Dit zal een lege commit zijn.
pulls.required_status_check_failed=Sommige vereiste controles waren niet succesvol.
pulls.required_status_check_missing=Er ontbreken enkele vereiste controles.
pulls.required_status_check_administrator=Als een beheerder kunt u deze pull-aanvraag nog samenvoegen.
pulls.blocked_by_approvals=Deze pull-aanvraag heeft nog niet genoeg goedkeuringen. %d van de %d goedkeuringen zijn gegeven.
pulls.blocked_by_rejection=Deze pull-aanvraag heeft wijzigingen aangevraagd door een officiële beoordelaar.
+pulls.blocked_by_official_review_requests=Dit pull-verzoek heeft officiële beoordelingsverzoeken.
pulls.blocked_by_outdated_branch=Deze pull-aanvraag is geblokkeerd omdat het verouderd is.
pulls.blocked_by_changed_protected_files_1=Deze pull-aanvraag is geblokkeerd omdat het een beschermd bestand veranderd:
pulls.blocked_by_changed_protected_files_n=Deze pull-aanvraag is geblokkeerd omdat het beschermde bestanden veranderd:
@@ -1241,12 +1559,19 @@ pulls.reject_count_1=%d wijzigingsverzoek
pulls.reject_count_n=%d wijzigingsverzoeken
pulls.waiting_count_1=%d wachtende beoordeling
pulls.waiting_count_n=%d wachtende beoordelingen
+pulls.wrong_commit_id=commit id moet een commit id zijn op de doelbranch
pulls.no_merge_desc=Deze pull-aanvraag kan niet worden samengevoegd, omdat alle samenvoegingsopties zijn uitgeschakeld.
pulls.no_merge_helper=Schakel samenvoegingsopties in in de repositoryinstellingen of voeg de pull-aanvraag handmatig samen.
pulls.no_merge_wip=Deze pull-aanvraag kan niet worden samengevoegd omdat hij als "work in progress" is gemarkeerd.
pulls.no_merge_not_ready=Deze pull-aanvraag is niet klaar om samen te voegen, controleer de status en status controles.
pulls.no_merge_access=Je bent niet gemachtigd om deze pull-aanvraag samen te voegen.
+pulls.merge_pull_request=Maak samenvoeg-commit
+pulls.rebase_merge_pull_request=Herbaseren dan snel-voorwaarts
+pulls.rebase_merge_commit_pull_request=Herbaseren dan samenvoeg-commit maken
+pulls.squash_merge_pull_request=Maak samenvoeg-commit
+pulls.merge_manually=Handmatig samengevoegd
+pulls.merge_commit_id=De merge commit ID
pulls.require_signed_wont_sign=De branch heeft ondertekende commits nodig, maar deze merge zal niet worden ondertekend
pulls.invalid_merge_option=Je kan de samenvoegingsoptie niet gebruiken voor deze pull-aanvraag.
@@ -1257,7 +1582,10 @@ pulls.rebase_conflict_summary=Foutmelding
; %[2]s
%[3]s
pulls.unrelated_histories=Samenvoegen mislukt: de HEAD en base delen geen gemeenschappelijke geschiedenis. Tip: Probeer een andere strategie
pulls.merge_out_of_date=Samenvoegen mislukt: Tijdens het samenvoegen is de basis bijgewerkt. Tip: Probeer het opnieuw.
+pulls.head_out_of_date=Samenvoegen mislukt: tijdens het genereren van de samenvoeging is de kop bijgewerkt. Tip: Probeer het opnieuw.
+pulls.push_rejected=Samenvoegen mislukt: De push is geweigerd. Controleer de Git Hooks voor deze repository.
pulls.push_rejected_summary=Volledig afwijzingsbericht
+pulls.push_rejected_no_message=Samenvoegen mislukt: De push is afgewezen, maar er was geen extern bericht.
Controleer de Git Hooks voor deze repository
pulls.open_unmerged_pull_exists=`Je kan deze pull-aanvraag niet opnieuw openen omdat er een andere (#%d) met identieke eigenschappen open staat.`
pulls.status_checking=Sommige controles zijn in behandeling
pulls.status_checks_success=Alle checks waren succesvol
@@ -1266,15 +1594,29 @@ pulls.status_checks_failure=Sommige controles zijn mislukt
pulls.status_checks_error=Sommige controles hebben foutmeldingen gerapporteerd
pulls.status_checks_requested=Vereist
pulls.status_checks_details=Details
+pulls.update_branch=Update branch via samenvoegen
+pulls.update_branch_rebase=Update branch via herbaseren
pulls.update_branch_success=Branch update is geslaagd
pulls.update_not_allowed=Je hebt geen toestemming om branch bij te werken
pulls.outdated_with_base_branch=Deze branch is verouderd met de basis branch
pulls.closed_at=`heeft deze pull request gesloten %[2]s`
pulls.reopened_at=`heropende deze pull request %[2]s`
+pulls.merge_instruction_hint=`Je kunt ook command line instructies bekijken.`
+pulls.merge_instruction_step1_desc=Vanuit het project, check een branch uit en test de veranderingen.
+pulls.merge_instruction_step2_desc=Voeg de wijzigingen samen en update ze op Gitea.
+
+pulls.auto_merge_button_when_succeed=(Bij geslaagde controles)
+pulls.auto_merge_when_succeed=Automatisch samenvoegen wanneer alle controles gelukt zijn
+pulls.auto_merge_newly_scheduled=De pull-verzoek was gepland om samen te voegen wanneer alle controles geslaagd zijn.
+pulls.auto_merge_has_pending_schedule=%[1]s heeft deze pull-verzoek automatisch samengevoegd wanneer alle checks succesvol zijn geweest %[2]s.
+
+pulls.auto_merge_cancel_schedule=Automatisch samenvoegen annuleren
+pulls.auto_merge_not_scheduled=Deze pull-aanvraag is niet gepland om automatisch samen te voegen.
+pulls.auto_merge_canceled_schedule=De automatisch samenvoegen is geannuleerd voor deze pull-aanvraag.
-
-
+pulls.delete.title=Deze pull-verzoek verwijderen?
+pulls.delete.text=Weet je zeker dat je deze pull-verzoek wilt verwijderen? (Dit zal alle inhoud permanent verwijderen. Overweeg om het te sluiten als je het gearchiveerd wilt houden)
milestones.new=Nieuwe mijlpaal
milestones.closed=%s werd gesloten
@@ -1320,6 +1662,7 @@ signing.wont_sign.commitssigned=De samenvoeging wordt niet ondertekend omdat all
signing.wont_sign.approved=De samenvoeging wordt niet ondertekend omdat de PR niet is goedgekeurd
signing.wont_sign.not_signed_in=U bent niet ingelogd
+ext_wiki=Toegang tot Externe Wiki
ext_wiki.desc=Koppelen aan een externe wiki.
wiki=Wiki
@@ -1344,6 +1687,7 @@ wiki.page_already_exists=Er bestaat al een wiki-pagina met deze naam.
wiki.reserved_page=De wiki-paginanaam '%s' is gereserveerd.
wiki.pages=Pagina’s
wiki.last_updated=Laatst bijgewerkt: %s
+wiki.page_name_desc=Voer een naam in voor deze Wiki pagina. Sommige speciale namen zijn: 'Home', '_Sidebar' en '_Footer'.
activity=Activiteit
activity.period.filter_label=Periode:
@@ -1375,6 +1719,7 @@ activity.closed_issues_count_1=Gesloten problemen
activity.closed_issues_count_n=Gesloten problemen
activity.title.issues_1=%d Probleem
activity.title.issues_n=%d Problemen
+activity.title.issues_closed_from=%s gesloten van %s
activity.title.issues_created_by=%s gemaakt door %s
activity.closed_issue_label=Gesloten
activity.new_issues_count_1=Nieuw probleem
@@ -1412,7 +1757,11 @@ activity.git_stats_deletion_n=%d verwijderingen
search=Zoek
search.search_repo=Zoek repository
+search.fuzzy=Vergelijkbaar
+search.match=Overeenkomst
search.results=Zoek resultaat voor "%s" in %s
+search.code_no_results=Geen broncode gevonden die aan uw zoekterm voldoet.
+search.code_search_unavailable=Er is momenteel geen code zoekfunctie beschikbaar. Neem contact op met uw sitebeheerder.
settings=Instellingen
settings.desc=In de instellingen kan je de instellingen van de repository aanpassen
@@ -1427,17 +1776,18 @@ settings.hooks=Webhooks
settings.githooks=Git-hooks
settings.basic_settings=Basis instellingen
settings.mirror_settings=Kopie Settings
+settings.mirror_settings.mirrored_repository=Gespiegelde repository
settings.mirror_settings.direction=Richting
settings.mirror_settings.direction.pull=Pull
settings.mirror_settings.direction.push=Push
+settings.mirror_settings.last_update=Laatst bijgewerkt
+settings.mirror_settings.push_mirror.none=Geen spiegels geconfigureerd
+settings.mirror_settings.push_mirror.add=Voeg Push Mirror toe
settings.sync_mirror=Synchroniseer
settings.mirror_sync_in_progress=Mirror-synchronisatie is momenteel bezig - kom later terug.
-settings.email_notifications.enable=E-mailnotificaties inschakelen
-settings.email_notifications.onmention=Alleen e-mail op vermelding
-settings.email_notifications.disable=E-mailnotificaties uitschakelen
-settings.email_notifications.submit=E-mailvoorkeur instellen
settings.site=Website
settings.update_settings=Instellingen bewerken
+settings.branches.update_default_branch=Standaard branch bijwerken
settings.advanced_settings=Geavanceerde opties
settings.wiki_desc=Repository-wiki inschakelen
settings.use_internal_wiki=Ingebouwde wiki gebruiken
@@ -1456,6 +1806,8 @@ settings.tracker_url_format_error=Het URL-formaat van de externe wiki is geen ge
settings.tracker_issue_style=Nummerformaat van de externe kwestie-tracker
settings.tracker_issue_style.numeric=Nummeriek
settings.tracker_issue_style.alphanumeric=Alfanummeriek
+settings.tracker_issue_style.regexp=Reguliere expressie
+settings.tracker_issue_style.regexp_pattern=Reguliere expressie patroon
settings.tracker_url_format_desc=Gebruik de aanduidingen {user}
, {repo}
en {index}
voor de gebruikersnaam, repositorynaam en kwestie-index.
settings.enable_timetracker=Tijdregistratie inschakelen
settings.allow_only_contributors_to_track_time=Sta alleen bijdragers toe tijdregistratie te gebruiken
@@ -1615,6 +1967,22 @@ settings.hook_type=Type hook
settings.slack_token=Slack token
settings.slack_domain=Slack domein
settings.slack_channel=Slack kanaal
+settings.web_hook_name_gitea=Gitea
+settings.web_hook_name_gogs=Gogs
+settings.web_hook_name_slack=Slack
+settings.web_hook_name_discord=Discord
+settings.web_hook_name_dingtalk=DingTalk
+settings.web_hook_name_telegram=Telegram
+settings.web_hook_name_matrix=Matrix
+settings.web_hook_name_msteams=Microsoft Teams
+settings.web_hook_name_feishu_or_larksuite=Feishu / Lark Suite
+settings.web_hook_name_feishu=Feishu
+settings.web_hook_name_larksuite=Lark Suite
+settings.web_hook_name_wechatwork=WeCom (Wechat Work)
+settings.web_hook_name_packagist=Packagist
+settings.packagist_username=Packagist gebruikersnaam
+settings.packagist_api_token=API token
+settings.packagist_package_url=Packagist pakket URL
settings.deploy_keys=Installeer sleutels
settings.add_deploy_key=Toevoegen deploy sleutel
settings.deploy_key_desc=Deploy keys hebben alleen-lezen pull-toegang tot de repository.
@@ -1653,6 +2021,7 @@ settings.protect_merge_whitelist_committers_desc=Sta alleen gebruikers of teams
settings.protect_merge_whitelist_users=Toegestane gebruikers voor samenvoegen:
settings.protect_merge_whitelist_teams=Toegestane teams voor samenvoegen:
settings.protect_check_status_contexts=Status controle inschakelen
+settings.protect_check_status_contexts_desc=Statuscontroles zijn vereist om te kunnen samenvoegen. Kies welke statuscontroles moeten slagen voordat branches kunnen worden samengevoegd tot een branch die aan deze regel voldoet. Wanneer ingeschakeld, moeten commits eerst naar een andere branch worden gepusht, vervolgens samengevoegd of gepusht worden naar een branch die overeenkomt met deze regel nadat de statuscontroles zijn uitgevoerd. Als er geen contexten worden geselecteerd, moet de laatste commit succesvol zijn, ongeacht de context.
settings.protect_check_status_contexts_list=Status controles gevonden in de afgelopen week voor deze repository
settings.protect_required_approvals=Vereiste goedkeuringen:
settings.protect_required_approvals_desc=Sta alleen toe om pull request samen te voegen met voldoende positieve beoordelingen.
@@ -1665,6 +2034,9 @@ settings.dismiss_stale_approvals_desc=Wanneer nieuwe commits die de inhoud van h
settings.require_signed_commits=Ondertekende Commits vereisen
settings.require_signed_commits_desc=Weiger pushes naar deze branch als deze niet ondertekend of niet verifieerbaar is.
settings.protect_protected_file_patterns=Beschermde bestandspatronen (gescheiden door een puntkomma '\;'):
+settings.protect_protected_file_patterns_desc=Beschermde bestanden die niet direct gewijzigd mogen worden, zelfs als de gebruiker het recht heeft om bestanden in deze branch toe te voegen, te bewerken of te verwijderen. Meerdere patronen kunnen worden gescheiden met een puntkomma ('\;'). Zie github.com/gobwas/glob documentatie voor patroon syntax. Voorbeelden: .drone.yml
, /docs/**/*.txt
.
+settings.protect_unprotected_file_patterns=Onbeschermde bestandspatronen (gescheiden met een puntkomma '\;'):
+settings.protect_unprotected_file_patterns_desc=Onbeschermde bestanden die direct mogen worden gewijzigd als gebruiker schrijfrechten heeft, waardoor push-beperking wordt omzeild. Meerdere patronen kunnen worden gescheiden met behulp van een puntkomma ('\;'). Zie github.com/gobwas/glob documentatie voor patroon syntax. Voorbeelden: .drone.yml
, /docs/**/*.txt
.
settings.add_protected_branch=Bescherming aanzetten
settings.delete_protected_branch=Bescherming uitzetten
settings.update_protect_branch_success=Branch bescherming voor branch '%s' is bijgewerkt.
@@ -1673,16 +2045,26 @@ settings.protected_branch_deletion=Branch bescherming uitschakelen
settings.protected_branch_deletion_desc=Branch bescherming uitschakelen zorgt ervoor dat gebruikers met schrijfrechten naar de branch kunnen pushen. Doorgaan?
settings.block_rejected_reviews=Samenvoegen van afgewezen beoordelingen blokkeren
settings.block_rejected_reviews_desc=Samenvoegen zal niet mogelijk zijn wanneer er wijzigingen worden aangevraagd door officiële beoordelaars, zelfs niet als er genoeg goedkeuringen zijn.
+settings.block_on_official_review_requests=Blokkeer de samenvoeging van officiële beoordelingsverzoeken
+settings.block_on_official_review_requests_desc=Samenvoegen is niet mogelijk wanneer het officiële herzieningsverzoeken heeft, ook al zijn er genoeg goedkeuringen.
settings.block_outdated_branch=Samenvoegen blokkeren als pull request verouderd is
settings.block_outdated_branch_desc=Samenvoegen is niet mogelijk als de hoofd branch achter loop op de basis branch.
settings.default_branch_desc=Selecteer een standaard repository branch voor pull requests en code commits:
+settings.default_merge_style_desc=Standaard samenvoegstijl voor pull verzoeken:
settings.choose_branch=Kies een branch…
settings.no_protected_branch=Er zijn geen beschermde branches.
settings.edit_protected_branch=Bewerken
settings.protected_branch_required_approvals_min=Vereiste goedkeuringen kunnen niet negatief zijn.
settings.tags=Labels
+settings.tags.protection=Label Bescherming
+settings.tags.protection.pattern=Label Patroon
settings.tags.protection.allowed=Toegestaan
+settings.tags.protection.allowed.users=Toegestane gebruikers
+settings.tags.protection.allowed.teams=Toegestane teams
settings.tags.protection.allowed.noone=Niemand
+settings.tags.protection.create=Beveilig Label
+settings.tags.protection.none=Er zijn geen beveiligde labels.
+settings.tags.protection.pattern.description=U kunt een enkele naam gebruiken of een glob patroon of reguliere expressie om meerdere labels te matchen. Lees meer in de beschermde labels gids.
settings.bot_token=Bot Token
settings.chat_id=Chat-ID
settings.matrix.homeserver_url=Homeserver URL
@@ -1696,6 +2078,7 @@ settings.archive.success=De repo is succesvol gearchiveerd.
settings.archive.error=Er is een fout opgetreden tijdens het archiveren van de repo. Zie het logboek voor meer informatie.
settings.archive.error_ismirror=U kunt geen gespiegelde repo archiveren.
settings.archive.branchsettings_unavailable=Branch instellingen zijn niet beschikbaar als de repo is gearchiveerd.
+settings.archive.tagsettings_unavailable=Labelinstellingen zijn niet beschikbaar als de repo is gearchiveerd.
settings.unarchive.button=Repo De-Archiveren
settings.unarchive.header=Deze Repo de-archiveren
settings.unarchive.text=De-Archiveren van de repo herstelt zijn vermogen om commits en pushes te ontvangen, evenals nieuwe problemen en pull-requests.
@@ -1727,6 +2110,12 @@ settings.lfs_pointers.inRepo=In Repo
settings.lfs_pointers.exists=Bestaat in opslag
settings.lfs_pointers.accessible=Toegankelijk voor gebruiker
settings.lfs_pointers.associateAccessible=Koppel toegankelijke %d OIDs
+settings.rename_branch_failed_exist=Kan branch niet hernoemen omdat doel branch %s bestaat.
+settings.rename_branch_failed_not_exist=Kan branch %s niet hernoemen omdat deze niet bestaat.
+settings.rename_branch_success=Branch %s is succesvol hernoemd naar %s.
+settings.rename_branch_from=oude branch naam
+settings.rename_branch_to=nieuwe branch naam
+settings.rename_branch=Hernoem branch
diff.browse_source=Bladeren bron
diff.parent=bovenliggende
@@ -1745,7 +2134,9 @@ diff.whitespace_ignore_all_whitespace=Witruimte negeren bij het vergelijken van
diff.whitespace_ignore_amount_changes=Negeer veranderingen in de hoeveelheid witruimte
diff.whitespace_ignore_at_eol=Negeren van wijzigingen in witruimte op EOL
diff.stats_desc=%d gewijzigde bestanden met toevoegingen van %d en %d verwijderingen
+diff.stats_desc_file=%d wijzigingen: %d toevoegingen en %d verwijderingen
diff.bin=BIN
+diff.bin_not_shown=Binair bestand niet weergegeven.
diff.view_file=Bestand weergeven
diff.file_before=Voor
diff.file_after=Na
@@ -1753,6 +2144,12 @@ diff.file_image_width=Breedte
diff.file_image_height=Hoogte
diff.file_byte_size=Grootte
diff.file_suppressed=Diff onderdrukt omdat het te groot bestand
+diff.file_suppressed_line_too_long=Bestand-diff onderdrukt omdat een of meer regels te lang zijn
+diff.too_many_files=Sommige bestanden werden niet getoond omdat er teveel bestanden zijn veranderd in deze diff
+diff.show_more=Meer weergeven
+diff.load=Laad Diff
+diff.generated=gegenereerd
+diff.vendored=vendored
diff.comment.placeholder=Opmerking toevoegen
diff.comment.markdown_info=Styling met markdown wordt ondersteund.
diff.comment.add_single_comment=Één reactie toevoegen
@@ -1767,13 +2164,20 @@ diff.review.approve=Goedkeuren
diff.review.reject=Wijzigingen aanvragen
diff.committed_by=gecommit door
diff.protected=Beveiligd
+diff.image.side_by_side=Zij aan zij
+diff.image.swipe=Vegen
+diff.image.overlay=Overlay
+diff.has_escaped=Deze regel heeft verborgen Unicode-tekens
releases.desc=Volg de projectversies en downloads.
release.releases=Publicaties
+release.detail=Release details
+release.tags=Labels
release.new_release=Nieuwe release
release.draft=Concept
release.prerelease=Voorlopige versie
release.stable=Stabiel
+release.compare=Vergelijk
release.edit=bewerken
release.ahead.commits=%d commits
release.ahead.target=aan %s sinds deze release
@@ -1954,6 +2358,7 @@ total=Totaal: %d
dashboard.statistic=Overzicht
dashboard.operations=Onderhoudswerkzaamheden
dashboard.system_status=Systeemtatus
+dashboard.statistic_info=De Gitea database heeft %d gebruikers, %d organisaties, %d openbare sleutels, %d repositories, %d volgers, %d sterren, %d acties, %d participanten, %d issues, %d reacties, %d sociale accounten, %d volgers, %d spiegels, %d publicaties, %d authenticatiebronnen, %d webhooks, %d mijlpalen, %d labels, %d hook taken, %d teams, %d bijgewerkte taken, %d bijlagen.
dashboard.operation_name=Bewerking naam
dashboard.operation_switch=Omschakelen
dashboard.operation_run=Uitvoeren
@@ -2019,6 +2424,7 @@ dashboard.total_gc_time=Totaal GC verwerkingstijd
dashboard.total_gc_pause=Totaal GC verwerkingstijd
dashboard.last_gc_pause=Laatste GC verwerkingstijd
dashboard.gc_times=GC verwerkingen
+dashboard.delete_old_system_notices=Verwijder alle oude systeemmededelingen uit de database
users.user_manage_panel=Gebruikersaccount beheer
users.new_account=Nieuw account aanmaken
@@ -2068,7 +2474,7 @@ emails.filter_sort.email=E-mail
emails.filter_sort.email_reverse=E-mail (omgekeerd)
emails.filter_sort.name=Gebruikersnaam
emails.filter_sort.name_reverse=Gebruikersnaam (omgekeerd)
-emails.updated=E-mail bijgewerkt
+emails.updated=E-mailadres bijgewerkt
emails.not_updated=Bijwerken van het gevraagde e-mailadres is mislukt: %v
emails.duplicate_active=Dit e-mailadres is al actief voor een andere gebruiker.
emails.change_email_header=Update E-mail Eigenschappen
@@ -2248,17 +2654,16 @@ config.queue_length=Lengte van wachtrij
config.deliver_timeout=Bezorging verlooptijd
config.skip_tls_verify=TLS-verificatie overslaan
-config.mailer_config=SMTP Mailerconfiguatie
config.mailer_enabled=Ingeschakeld
-config.mailer_disable_helo=Schakel HELO uit
config.mailer_name=Naam
-config.mailer_host=Host
+config.mailer_smtp_port=SMTP Poort
config.mailer_user=Gebruiker
config.mailer_use_sendmail=Gebruik Sendmail
config.mailer_sendmail_path=Sendmail pad
config.mailer_sendmail_args=Extra argumenten voor Sendmail
config.mailer_sendmail_timeout=Sendmail time-out
-config.test_email_placeholder=E-mail (bijv. test@example.com)
+config.mailer_use_dummy=Dummy
+config.test_email_placeholder=E-mailadres (bijv. test@example.com)
config.send_test_mail=Test e-mail verzenden
config.test_mail_failed=Verzenden van een testmail naar '%s' is mislukt: %v
config.test_mail_sent=Test-email is verstuurd naar '%s'.
@@ -2309,6 +2714,7 @@ config.access_log_template=Sjabloon
config.xorm_log_mode=XORM Log-modus
config.xorm_log_sql=Log SQL
+
monitor.cron=Cron-taken
monitor.name=Naam
monitor.schedule=Planning
@@ -2319,6 +2725,7 @@ monitor.process=Draaiende processen
monitor.desc=Omschrijving
monitor.start=Starttijd
monitor.execute_time=Uitvoertijd
+monitor.last_execution_result=Resultaat
monitor.process.cancel=Annuleer proces
monitor.process.cancel_desc=Annuleren van een proces kan gegevensverlies veroorzaken
monitor.process.cancel_notices=Annuleer: %s?
@@ -2333,6 +2740,7 @@ monitor.queue.review=Configuratie herzien
monitor.queue.review_add=Beoordeel/Voeg workers toe
monitor.queue.configuration=Initiële configuratie
monitor.queue.nopool.title=Geen Worker-pool
+monitor.queue.flush=Spoel werker
monitor.queue.pool.timeout=Time-out
monitor.queue.pool.addworkers.title=Voeg workers toe
monitor.queue.pool.addworkers.submit=Voeg workers toe
@@ -2447,4 +2855,8 @@ error.no_unit_allowed_repo=U heeft geen toegang tot een enkele sectie van deze r
error.unit_not_allowed=U heeft geen toegang tot deze sectie van de repository.
[packages]
+assets=Assets
+rubygems.required.ruby=Vereist Ruby versie
+rubygems.required.rubygems=Vereist RubyGem versie
+settings.link.button=Repository link bijwerken
diff --git a/options/locale/locale_pl-PL.ini b/options/locale/locale_pl-PL.ini
index 5e6c078b2d..e5e5f31861 100644
--- a/options/locale/locale_pl-PL.ini
+++ b/options/locale/locale_pl-PL.ini
@@ -8,7 +8,6 @@ sign_out=Wyloguj
sign_up=Zarejestruj
link_account=Powiąż konto
register=Zarejestruj się
-website=Strona
version=Wersja
powered_by=Wspierane przez %s
page=Strona
@@ -177,7 +176,6 @@ log_root_path_helper=Pliki logów będą zapisywane w tym katalogu.
optional_title=Ustawienia opcjonalne
email_title=Ustawienia e-mail
-smtp_host=Serwer SMTP
smtp_from=Wyślij e-mail jako
smtp_from_helper=Adres e-mail, z którego Gitea będzie korzystać. Wpisz prosty adres e-mail, lub użyj formatu "Nazwa" .
mailer_user=Nazwa użytkownika SMTP
@@ -275,6 +273,7 @@ code_no_results=Nie znaleziono kodu źródłowego odpowiadającego Twojej frazie
code_search_results=Wyniki wyszukiwania dla '%s'
code_last_indexed_at=Ostatnio indeksowane %s
+
[auth]
create_new_account=Zarejestruj konto
register_helper_msg=Masz już konto? Zaloguj się teraz!
@@ -1610,10 +1609,6 @@ settings.mirror_settings.push_mirror.none=Brak skonfigurowanych kopii zapasowych
settings.mirror_settings.push_mirror.remote_url=Adres URL zdalnego repozytorium Git
settings.sync_mirror=Synchronizuj teraz
settings.mirror_sync_in_progress=Synchronizacja kopii lustrzanych jest w toku. Sprawdź ponownie za minutę.
-settings.email_notifications.enable=Włącz powiadomienia e-mail
-settings.email_notifications.onmention=Wyślij wiadomość e-mail wyłącznie przy wzmiankach
-settings.email_notifications.disable=Wyłącz powiadomienia e-mail
-settings.email_notifications.submit=Ustaw preferencje wiadomości e-mail
settings.site=Strona
settings.update_settings=Aktualizuj ustawienia
settings.branches.update_default_branch=Aktualizuj domyślną gałąź
@@ -2498,11 +2493,8 @@ config.queue_length=Długość kolejki
config.deliver_timeout=Limit czasu doręczenia
config.skip_tls_verify=Pomiń weryfikację TLS
-config.mailer_config=Konfiguracja dostawcy SMTP
config.mailer_enabled=Włączona
-config.mailer_disable_helo=Wyłącz HELO
config.mailer_name=Nazwa
-config.mailer_host=Serwer
config.mailer_user=Użytkownik
config.mailer_use_sendmail=Używaj Sendmail
config.mailer_sendmail_path=Ścieżka Sendmail
@@ -2560,6 +2552,7 @@ config.access_log_template=Szablon
config.xorm_log_mode=Tryb dziennika XORM
config.xorm_log_sql=Dziennik SQL
+
monitor.cron=Zadania cron
monitor.name=Nazwa
monitor.schedule=Harmonogram
diff --git a/options/locale/locale_pt-BR.ini b/options/locale/locale_pt-BR.ini
index 72262c6086..04b9a43519 100644
--- a/options/locale/locale_pt-BR.ini
+++ b/options/locale/locale_pt-BR.ini
@@ -9,7 +9,6 @@ sign_out=Sair
sign_up=Cadastrar
link_account=Vincular conta
register=Cadastrar
-website=Site
version=Versão
powered_by=Desenvolvido por %s
page=Página
@@ -179,7 +178,8 @@ log_root_path_helper=Arquivos de log serão gravados neste diretório.
optional_title=Configurações opcionais
email_title=Configurações de e-mail
-smtp_host=Host SMTP
+smtp_addr=Host SMTP
+smtp_port=Porta SMTP
smtp_from=Enviar e-mail como
smtp_from_helper=Endereço de e-mail que o Gitea irá usar. Digite um endereço de e-mail simples ou use o formato "Nome" .
mailer_user=Nome de usuário do SMTP
@@ -277,6 +277,9 @@ org_no_results=Nenhuma organização correspondente foi encontrada.
code_no_results=Nenhum código-fonte correspondente ao seu termo de pesquisa foi encontrado.
code_search_results=Resultados da pesquisa por: '%s'
code_last_indexed_at=Última indexação %s
+relevant_repositories_tooltip=Repositórios que são forks ou que não possuem tópico, nem ícone e nem descrição estão ocultos.
+relevant_repositories=Apenas repositórios relevantes estão sendo mostrados, mostrar resultados não filtrados.
+
[auth]
create_new_account=Cadastrar conta
@@ -799,6 +802,7 @@ email_notifications.enable=Habilitar notificações de e-mail
email_notifications.onmention=Somente e-mail com menção
email_notifications.disable=Desabilitar notificações de e-mail
email_notifications.submit=Atualizar preferências de e-mail
+email_notifications.andyourown=E Suas Próprias Notificações
visibility=Visibilidade do usuário
visibility.public=Pública
@@ -861,6 +865,7 @@ default_branch=Branch Padrão
default_branch_helper=O branch padrão é o branch base para pull requests e commits de código.
mirror_prune=Varrer
mirror_prune_desc=Remover referências obsoletas de controle remoto
+mirror_interval=Intervalo de espelhamento (unidades válidas são 'h', 'm', ou 's'). O desabilita a sincronização automática. (Intervalo mínimo: %s)
mirror_interval_invalid=O intervalo do espelhamento não é válido.
mirror_sync_on_commit=Sincronizar quando commits forem enviados
mirror_address=Clonar de URL
@@ -1033,13 +1038,13 @@ file_view_rendered=Ver Renderizado
file_view_raw=Ver original
file_permalink=Link permanente
file_too_large=O arquivo é muito grande para ser mostrado.
-bidi_bad_header=`Este arquivo contém caracteres Unicode Bidirecionais inesperados!`
-bidi_bad_description=`Este arquivo contém caracteres Unicode bidirecionais inesperados que podem ser processados de forma diferente do que aparece abaixo. Se seu caso de uso for intencional e legítimo, você pode ignorar com segurança esse aviso. Use o botão Escapar para revelar caracteres ocultos.`
-bidi_bad_description_escaped=`Este arquivo contém caracteres Unicode Bidirecionais inesperados. Caracteres unicode ocultos estão escapados abaixo. Use o botão Desescapar para mostrar como eles são mostrados.`
-unicode_header=`Este arquivo contém caracteres Unicode ocultos!`
-unicode_description=`Este arquivo contém caracteres Unicode ocultos que podem ser processados de forma diferente do que aparece abaixo. Se seu caso de uso for intencional e legítimo, você pode ignorar com segurança esse aviso. Use o botão Escapar para revelar caracteres ocultos.`
-unicode_description_escaped=`Este arquivo contém caracteres Unicode ocultos. Caracteres unicode ocultos estão escapados abaixo. Utilize o botão Desescapar para mostrar como eles são mostrados.`
-line_unicode=`Esta linha possui caracteres unicode ocultos`
+invisible_runes_header=`Este arquivo contém caracteres Unicode invisíveis!`
+invisible_runes_description=`Este arquivo contém caracteres Unicode invisíveis que podem ser processados de forma diferente do que aparece abaixo. Se seu caso de uso for intencional e legítimo, você pode ignorar com segurança esse aviso. Use o botão Escapar para revelar caracteres ocultos.`
+ambiguous_runes_header=`Esse arquivo contém caracteres Unicode ambíguos!`
+ambiguous_runes_description=`Este arquivo contém caracteres ambíguos Unicode que podem ser confundidos com outros no seu idioma atual. Se o seu caso de uso for intencional e legítimo, você pode ignorar com segurança este aviso. Use o botão Escapar para destacar esses caracteres.`
+invisible_runes_line=`Esta linha tem caracteres unicode invisíveis`
+ambiguous_runes_line=`Esta linha tem caracteres unicode ambíguos`
+ambiguous_character=`%[1]c [U+%04[1]X] é confundível com o %[2]c [U+%04[2]X]`
escape_control_characters=Escapar
unescape_control_characters=Desescapar
@@ -1060,6 +1065,7 @@ normal_view=Visão normal
line=linha
lines=linhas
+editor.add_file=Adicionar Arquivo
editor.new_file=Novo arquivo
editor.upload_file=Enviar arquivo
editor.edit_file=Editar arquivo
@@ -1225,6 +1231,8 @@ issues.new.add_reviewer_title=Solicitar revisão
issues.choose.get_started=Primeiros passos
issues.choose.blank=Padrão
issues.choose.blank_about=Criar uma issue a partir do modelo padrão.
+issues.choose.ignore_invalid_templates=Modelos inválidos foram ignorados
+issues.choose.invalid_templates=%v modelo(s) inválido(s) encontrado(s)
issues.no_ref=Nenhum branch/tag especificado
issues.create=Criar issue
issues.new_label=Nova etiqueta
@@ -1265,6 +1273,8 @@ issues.filter_milestone=Marco
issues.filter_milestone_no_select=Todos os marcos
issues.filter_assignee=Atribuído
issues.filter_assginee_no_select=Todos os responsáveis
+issues.filter_poster=Autor
+issues.filter_poster_no_select=Todos os autores
issues.filter_type=Tipo
issues.filter_type.all_issues=Todas as issues
issues.filter_type.assigned_to_you=Atribuídos a você
@@ -1419,6 +1429,7 @@ issues.due_date_form_remove=Remover
issues.due_date_not_writer=Você deve ter permissão de escrita no repositório para atualizar a data limite de uma issue.
issues.due_date_not_set=Data limite não informada.
issues.due_date_added=adicionou a data limite %s %s
+issues.due_date_modified=modificou a data limite de %[2]para %[1]s %[3]s
issues.due_date_remove=removeu a data limite %s %s
issues.due_date_overdue=Em atraso
issues.due_date_invalid=A data limite é inválida ou está fora do intervalo. Por favor, use o formato 'dd/mm/aaaa'.
@@ -1530,6 +1541,8 @@ pulls.remove_prefix=Remover o prefixo %s
pulls.data_broken=Este pull request está quebrado devido a falta de informação do fork.
pulls.files_conflicted=Este pull request tem alterações conflitantes com o branch de destino.
pulls.is_checking=Verificação de conflitos do merge está em andamento. Tente novamente em alguns momentos.
+pulls.is_ancestor=Este branch já está incluído no branch de destino. Não há nada para mesclar.
+pulls.is_empty=As alterações neste branch já estão na branch de destino. Este será um commit vazio.
pulls.required_status_check_failed=Algumas verificações necessárias não foram bem sucedidas.
pulls.required_status_check_missing=Estão faltando algumas verificações necessárias.
pulls.required_status_check_administrator=Como administrador, você ainda pode aplicar o merge deste pull request.
@@ -1597,10 +1610,16 @@ pulls.merge_instruction_step1_desc=No repositório do seu projeto, crie um novo
pulls.merge_instruction_step2_desc=Faça merge das alterações e atualize no Gitea.
pulls.auto_merge_button_when_succeed=(Quando a verificação for bem-sucedida)
+pulls.auto_merge_when_succeed=Mesclar automaticamente quando todas as verificações forem bem sucedidas
pulls.auto_merge_newly_scheduled=O merge do pull request foi agendado para quando todas as verificações forem bem-sucedidas.
+pulls.auto_merge_has_pending_schedule=%[1]s agendou este pull request para merge automático quando todas as verificações tiverem sucesso %[2]s.
pulls.auto_merge_cancel_schedule=Cancelar merge automático
+pulls.auto_merge_not_scheduled=Este pull request não está programado para ser automaticamente mesclado.
+pulls.auto_merge_canceled_schedule=O merge automático foi cancelado para este pull request.
+pulls.auto_merge_newly_scheduled_comment=`agendou este pull request para merge automático quando todas as verificações tiverem sucesso %[1]s`
+pulls.auto_merge_canceled_schedule_comment=`cancelou o merge automático deste pull request quando todos as verificações tiverem sucesso %[1]s`
pulls.delete.title=Excluir este pull request?
pulls.delete.text=Você realmente deseja excluir este pull request? (Isto irá remover permanentemente todo o conteúdo. Considere fechá-la em vez disso, se você pretende mantê-la arquivado)
@@ -1774,10 +1793,6 @@ settings.mirror_settings.push_mirror.remote_url=URL do repositório do Git remot
settings.mirror_settings.push_mirror.add=Adicionar Espelho de Push
settings.sync_mirror=Sincronizar agora
settings.mirror_sync_in_progress=Sincronização do espelhamento está em andamento. Verifique novamente em um minuto.
-settings.email_notifications.enable=Habilitar notificações de e-mail
-settings.email_notifications.onmention=Somente e-mail com menção
-settings.email_notifications.disable=Desabilitar notificações de e-mail
-settings.email_notifications.submit=Atualizar preferências de e-mail
settings.site=Site
settings.update_settings=Atualizar configurações
settings.branches.update_default_branch=Atualizar Branch Padrão
@@ -1800,6 +1815,8 @@ settings.tracker_issue_style=Formato de número do issue tracker externo
settings.tracker_issue_style.numeric=Numérico
settings.tracker_issue_style.alphanumeric=Alfanumérico
settings.tracker_issue_style.regexp=Expressão Regular
+settings.tracker_issue_style.regexp_pattern=Padrão de expressão regular
+settings.tracker_issue_style.regexp_pattern_desc=O primeiro grupo capturado será usado no lugar de {index}
.
settings.tracker_url_format_desc=Use os espaços reservados {user}
, {repo}
e {index}
para o nome de usuário, nome do repositório e o índice de problemas.
settings.enable_timetracker=Habilitar Cronômetro
settings.allow_only_contributors_to_track_time=Permitir que apenas os colaboradores acompanhem o contador de tempo
@@ -1881,6 +1898,7 @@ settings.confirm_delete=Excluir repositório
settings.add_collaborator=Adicionar colaborador
settings.add_collaborator_success=O colaborador foi adicionado.
settings.add_collaborator_inactive_user=Não é possível adicionar um usuário inativo como colaborador.
+settings.add_collaborator_owner=Não é possível adicionar um proprietário como um colaborador.
settings.add_collaborator_duplicate=O colaborador já está adicionado a este repositório.
settings.delete_collaborator=Remover
settings.collaborator_deletion=Remover colaborador
@@ -1939,6 +1957,8 @@ settings.event_delete=Excluir
settings.event_delete_desc=Branch ou tag deletado.
settings.event_fork=Fork
settings.event_fork_desc=Feito fork do repositório.
+settings.event_wiki=Wiki
+settings.event_wiki_desc=Página da wiki criada, renomeada, editada ou excluída.
settings.event_release=Versão
settings.event_release_desc=Versão publicada, atualizada ou excluída em um repositório.
settings.event_push=Push
@@ -2418,6 +2438,7 @@ dashboard.new_version_hint=Gitea %s está disponível, você está executando %s
dashboard.statistic=Resumo
dashboard.operations=Operações de manutenção
dashboard.system_status=Status do sistema
+dashboard.statistic_info=O banco de dados do Gitea contém %d usuários, %d organizações, %d chaves públicas, %d repositórios, %d observadores, %d favoritos, ~%d ações, %d acessos, %d issues, %d comentários, %d contas sociais, %d seguidores, %d espelhos, %d versões, %d fontes de autenticação, %d webhooks, %d marcos, %d etiquetas, %d tarefas hook, %d equipes, %d tarefas de atualização, %d anexos.
dashboard.operation_name=Nome da operação
dashboard.operation_switch=Trocar
dashboard.operation_run=Executar
@@ -2526,6 +2547,8 @@ users.delete_account=Excluir conta de usuário
users.cannot_delete_self=Você não pode excluir você mesmo
users.still_own_repo=Este usuário ainda possui um ou mais repositórios. Exclua ou transfira esses repositórios primeiro.
users.still_has_org=Este usuário é membro de uma organização. Remova o usuário de qualquer organização primeiro.
+users.purge=Eliminar usuário
+users.purge_help=Exclua forçosamente o usuário e quaisquer repositórios, organizações e pacotes pertencentes ao usuário. Todos os comentários também serão excluídos.
users.still_own_packages=Este usuário ainda possui um ou mais pacotes. Exclua esses pacotes primeiro.
users.deletion_success=A conta de usuário foi excluída.
users.reset_2fa=Reinicializar 2FA
@@ -2624,6 +2647,8 @@ auths.use_paged_search=Use a pesquisa paginada
auths.search_page_size=Tamanho da página
auths.filter=Filtro de usuário
auths.admin_filter=Filtro de administrador
+auths.restricted_filter=Filtro de restrição
+auths.restricted_filter_helper=Deixe em branco para não definir nenhum usuário como restrito. Use um asterisco ('*') para definir todos os usuários que não correspondem ao Filtro de administrador como restritos.
auths.group_attribute_list_users=Atributo do Grupo que Contém a Lista de Usuários
auths.enable_ldap_groups=Habilitar grupos do LDAP
auths.ms_ad_sa=Atributos de pesquisa do MS AD
@@ -2768,11 +2793,13 @@ config.queue_length=Tamanho da fila
config.deliver_timeout=Intervalo de entrega
config.skip_tls_verify=Ignorar verificação de TLS
-config.mailer_config=Configuração SMTP para envio de e-mail
+config.mailer_config=Configuração de Envio de E-mail
config.mailer_enabled=Habilitado
-config.mailer_disable_helo=Desabilitar HELO
+config.mailer_enable_helo=Ativar HELO
config.mailer_name=Nome
-config.mailer_host=Servidor
+config.mailer_protocol=Protocolo
+config.mailer_smtp_addr=Addr SMTP
+config.mailer_smtp_port=Porta SMTP
config.mailer_user=Usuário
config.mailer_use_sendmail=Usar o Sendmail
config.mailer_sendmail_path=Caminho do Sendmail
@@ -2830,6 +2857,7 @@ config.access_log_template=Modelo
config.xorm_log_mode=Modo log XORM
config.xorm_log_sql=Log SQL
+
monitor.cron=Tarefas cron
monitor.name=Nome
monitor.schedule=Cronograma
@@ -2862,6 +2890,7 @@ monitor.queue.nopool.title=Nenhum conjunto de executores
monitor.queue.nopool.desc=Essa fila agrupa outras filas e não possui um conjunto de executores.
monitor.queue.wrapped.desc=Uma fila agrupada envolve uma fila inicial lenta, armazenando as solicitações da fila em um canal. Ela não possui um conjunto de executores em si.
monitor.queue.persistable-channel.desc=Um canal persistente envolve duas filas, uma fila de canais que tem seu próprio conjunto de executores e uma fila de nível para solicitações persistentes de encerramentos anteriores. Ela não tem um conjunto de executores em si.
+monitor.queue.flush=Liberar executor
monitor.queue.pool.timeout=Tempo de espera
monitor.queue.pool.addworkers.title=Adicionar executores
monitor.queue.pool.addworkers.submit=Adicionar executores
@@ -2992,6 +3021,9 @@ pin=Fixar notificação
mark_as_read=Marcar como lida
mark_as_unread=Marcar como não lida
mark_all_as_read=Marcar todas como lidas
+subscriptions=Inscrições
+watching=Observando
+no_subscriptions=Nenhuma inscrição
[gpg]
default_key=Assinado com a chave padrão
@@ -3014,6 +3046,7 @@ title=Pacotes
desc=Gerenciar pacotes do repositório.
empty=Não há pacotes ainda.
empty.documentation=Para obter mais informações sobre o registro de pacote, consulte a documentação.
+empty.repo=Você enviou um pacote, mas ele não está aqui? Vá para configurações do pacote e vincule-o a este repositório.
filter.type=Tipo
filter.type.all=Todos
filter.no_result=Seu filtro não produziu resultados.
@@ -3050,6 +3083,7 @@ container.details.platform=Plataforma
container.details.repository_site=Site do Repositório
container.details.documentation_site=Site da Documentação
container.pull=Puxe a imagem pela linha de comando:
+container.digest=Digest:
container.documentation=Para obter mais informações sobre o registro de Container, consulte a documentação.
container.multi_arch=S.O. / Arquitetura
container.layers=Camadas da Imagem
@@ -3079,6 +3113,10 @@ npm.dependencies.development=Dependências de Desenvolvimento
npm.dependencies.peer=Dependências Peer
npm.dependencies.optional=Dependências Opcionais
npm.details.tag=Tag
+pub.install=Para instalar o pacote usando Dart, execute o seguinte comando:
+pub.documentation=Para obter mais informações sobre o registro Pub, consulte a documentação.
+pub.details.repository_site=Site do Repositório
+pub.details.documentation_site=Site da Documentação
pypi.requires=Requer Python
pypi.install=Para instalar o pacote usando pip, execute o seguinte comando:
pypi.documentation=Para obter mais informações sobre o registro PyPI, consulte a documentação.
@@ -3089,6 +3127,8 @@ rubygems.dependencies.development=Dependências de Desenvolvimento
rubygems.required.ruby=Requer o Ruby versão
rubygems.required.rubygems=Requer o RubyGem versão
rubygems.documentation=Para obter mais informações sobre o registro do RubyGems, consulte a documentação.
+vagrant.install=Para adicionar uma Vagrant box, execute o seguinte comando:
+vagrant.documentation=Para obter mais informações sobre o registro do Vagrant, consulte a documentação.
settings.link=Vincular este pacote a um repositório
settings.link.description=Se você vincular um pacote a um repositório, o pacote será listado na lista de pacotes do repositório.
settings.link.select=Selecionar Repositório
diff --git a/options/locale/locale_pt-PT.ini b/options/locale/locale_pt-PT.ini
index b18bb0cbb8..6f99e4d6e8 100644
--- a/options/locale/locale_pt-PT.ini
+++ b/options/locale/locale_pt-PT.ini
@@ -9,7 +9,6 @@ sign_out=Terminar sessão
sign_up=Fazer inscrição
link_account=Vincular conta
register=Inscrição
-website=Sítio web
version=Versão
powered_by=Implementado com %s
page=Página
@@ -147,7 +146,7 @@ sqlite_helper=Localização do ficheiro da base de dados em SQLite3.
Insira u
reinstall_error=Está a tentar instalar numa base de dados do Gitea já existente
reinstall_confirm_message=Reinstalar com uma base de dados do Gitea já existente pode causar múltiplos problemas. Na maioria dos casos deve usar o seu "app.ini" existente para correr o Gitea. Se souber o que está a fazer, confirme o seguinte:
reinstall_confirm_check_1=Os dados encriptados pela chave secreta (SECRET_KEY) no ficheiro app.ini poderão ser perdidos: utilizadores poderão não ser capazes de iniciar a sessão com autenticação em dois passos (2FA) ou com chaves de utilização única (OTP) e as réplicas poderão deixar de funcionar em condições. Ao marcar esta opção estará a confirmar que o ficheiro app.ini vigente contém a SECRET_KEY certa.
-reinstall_confirm_check_2=Os repositórios e as configurações poderão ter de voltar a ser sincronizados. Ao marcar esta opção estará a confirmar que vai voltar a sincronizar os automatismos para os repositórios e o ficheiro authorized_keys manualmente. Estará também a confirmar que vai assegurar que as configurações do repositório e das réplicas estão em condições.
+reinstall_confirm_check_2=Os repositórios e as configurações poderão ter de voltar a ser sincronizados. Ao marcar esta opção estará a confirmar que vai voltar a sincronizar manualmente os automatismos para os repositórios e o ficheiro authorized_keys. Estará também a confirmar que vai assegurar que as configurações do repositório e das réplicas estão em condições.
reinstall_confirm_check_3=Você confirma que tem a certeza absoluta de que este Gitea está a correr com a localização certa do ficheiro app.ini e que tem a certeza de que tem de voltar a instalar. Você confirma que tomou conhecimento dos riscos acima descritos.
err_empty_db_path=A localização da base de dados SQLite3 não pode estar vazia.
no_admin_and_disable_registration=Não pode desabilitar a auto-inscrição de utilizadores sem criar uma conta de administrador.
@@ -179,7 +178,8 @@ log_root_path_helper=Os ficheiros de registo serão escritos nesta pasta.
optional_title=Configurações opcionais
email_title=Configurações de email
-smtp_host=Servidor SMTP
+smtp_addr=Servidor SMTP
+smtp_port=Porto do SMTP
smtp_from=Email do remetente
smtp_from_helper=Endereço de email que o Gitea vai usar. Insira um endereço de email simples ou use o formato "Nome" .
mailer_user=Nome de utilizador do SMTP
@@ -268,8 +268,11 @@ users=Utilizadores
organizations=Organizações
search=Procurar
code=Código
+search.type.tooltip=Tipo de pesquisa
search.fuzzy=Aproximada
+search.fuzzy.tooltip=Incluir também os resultados que estejam próximos do termo de pesquisa
search.match=Fiel
+search.match.tooltip=Incluir somente os resultados que correspondam rigorosamente ao termo de pesquisa
code_search_unavailable=A pesquisa por código-fonte não está disponível, neste momento. Entre em contacto com o administrador.
repo_no_results=Não foram encontrados quaisquer repositórios correspondentes.
user_no_results=Não foram encontrados quaisquer utilizadores correspondentes.
@@ -277,6 +280,9 @@ org_no_results=Não foram encontradas quaisquer organizações correspondentes.
code_no_results=Não foi encontrado qualquer código-fonte correspondente à sua pesquisa.
code_search_results=Resultados da pesquisa para '%s'
code_last_indexed_at=Última indexação %s
+relevant_repositories_tooltip=Repositórios que são derivações ou que não têm tópico, nem ícone, nem descrição, estão escondidos.
+relevant_repositories=Apenas estão a ser mostrados os repositórios relevantes. Mostrar resultados não filtrados.
+
[auth]
create_new_account=Fazer inscrição
@@ -504,6 +510,7 @@ activity=Trabalho público
followers=Seguidores
starred=Repositórios favoritos
watched=Repositórios sob vigilância
+code=Código
projects=Planeamentos
following=Que segue
follow=Seguir
@@ -799,6 +806,7 @@ email_notifications.enable=Habilitar notificações por email
email_notifications.onmention=Enviar email somente quando mencionado(a)
email_notifications.disable=Desabilitar notificações por email
email_notifications.submit=Definir preferência do email
+email_notifications.andyourown=e as suas próprias notificações
visibility=Visibilidade do utilizador
visibility.public=Pública
@@ -1034,13 +1042,13 @@ file_view_rendered=Ver resultado processado
file_view_raw=Ver em bruto
file_permalink=Ligação permanente
file_too_large=O ficheiro é demasiado grande para ser apresentado.
-bidi_bad_header=`Este ficheiro contém caracteres Unicode Bidireccionais inesperados!`
-bidi_bad_description=`Este ficheiro contém caracteres Unicode Bidireccionais inesperados que podem ser processados de forma diferente do que aparece abaixo. Se o uso é intencional e legítimo, pode ignorar este aviso com segurança. Use o botão Revelar para mostrar os caracteres escondidos.`
-bidi_bad_description_escaped=`Este ficheiro contém caracteres Unicode Bidireccionais inesperados. Os caracteres escondidos unicode estão revelados abaixo. Use o botão Esconder para mostrar como é que eles são apresentados.`
-unicode_header=`Este ficheiro contém caracteres Unicode escondidos!`
-unicode_description=`Este ficheiro contém caracteres Unicode escondidos que podem ser processados de forma diferente do que aparece abaixo. Se o uso é intencional e legítimo, pode ignorar este aviso com segurança. Use o botão Revelar para mostrar os caracteres escondidos.`
-unicode_description_escaped=`Este ficheiro contém caracteres Unicode escondidos. Os caracteres unicode escondidos estão revelados abaixo. Use o botão Esconder para mostrar como é que eles são apresentados.`
-line_unicode=`Esta linha tem caracteres unicode escondidos`
+invisible_runes_header=`Este ficheiro contém caracteres Unicode invisíveis!`
+invisible_runes_description=`Este ficheiro contém caracteres Unicode invisíveis que podem ser processados de forma diferente do que aparece abaixo. Se o uso é intencional e legítimo, pode ignorar este aviso com segurança. Use o botão Revelar para mostrar os caracteres invisíveis.`
+ambiguous_runes_header=`Este ficheiro contém caracteres Unicode ambíguos!`
+ambiguous_runes_description=`Este ficheiro contém caracteres Unicode ambíguos que podem ser confundidos com outros da sua configuração regional vigente. Se o uso é intencional e legítimo, pode ignorar este aviso com segurança. Use o botão Revelar para realçar esses caracteres.`
+invisible_runes_line=`Esta linha tem caracteres unicode invisíveis`
+ambiguous_runes_line=`Esta linha tem caracteres unicode ambíguos`
+ambiguous_character=`%[1]c [U+%04[1]X] pode confundir-se com %[2]c [U+%04[2]X]`
escape_control_characters=Revelar
unescape_control_characters=Esconder
@@ -1061,6 +1069,7 @@ normal_view=Vista normal
line=linha
lines=linhas
+editor.add_file=Adicionar ficheiro
editor.new_file=Novo ficheiro
editor.upload_file=Carregar ficheiro
editor.edit_file=Editar ficheiro
@@ -1226,6 +1235,8 @@ issues.new.add_reviewer_title=Solicitar revisão
issues.choose.get_started=Começar
issues.choose.blank=Padrão
issues.choose.blank_about=Cria uma questão a partir do modelo padrão.
+issues.choose.ignore_invalid_templates=Modelos inválidos foram ignorados
+issues.choose.invalid_templates=Foram encontrados %v modelos inválidos
issues.no_ref=Sem ramo ou etiqueta especificados
issues.create=Criar questão
issues.new_label=Novo rótulo
@@ -1266,6 +1277,8 @@ issues.filter_milestone=Etapa
issues.filter_milestone_no_select=Todas as etapas
issues.filter_assignee=Encarregado
issues.filter_assginee_no_select=Todos os encarregados
+issues.filter_poster=Autor(a)
+issues.filter_poster_no_select=Todos os autores
issues.filter_type=Tipo
issues.filter_type.all_issues=Todas as questões
issues.filter_type.assigned_to_you=Atribuídas a si
@@ -1754,8 +1767,11 @@ activity.git_stats_deletion_n=%d eliminações
search=Procurar
search.search_repo=Procurar repositório
+search.type.tooltip=Tipo de pesquisa
search.fuzzy=Aproximada
+search.fuzzy.tooltip=Incluir também os resultados que estejam próximos do termo de pesquisa
search.match=Fiel
+search.match.tooltip=Incluir somente os resultados que correspondam rigorosamente ao termo de pesquisa
search.results=Resultados da procura de "%s" em %s
search.code_no_results=Não foi encontrado qualquer código-fonte correspondente à sua pesquisa.
search.code_search_unavailable=A pesquisa por código-fonte não está disponível, neste momento. Entre em contacto com o administrador.
@@ -1772,22 +1788,18 @@ settings.collaboration.undefined=Não definido
settings.hooks=Automatismos web
settings.githooks=Automatismos do Git
settings.basic_settings=Configurações básicas
-settings.mirror_settings=Configurações da réplica
+settings.mirror_settings=Configuração de réplicas
settings.mirror_settings.docs=Configure o seu repositório para puxar e/ou enviar automaticamente as modificações de/para outro repositório. Ramos, etiquetas e cometimentos serão sincronizados automaticamente. Como é que eu faço uma réplica de outro repositório?
settings.mirror_settings.mirrored_repository=Repositório replicado
settings.mirror_settings.direction=Sentido
settings.mirror_settings.direction.pull=Puxada
settings.mirror_settings.direction.push=Envio
settings.mirror_settings.last_update=Última modificação
-settings.mirror_settings.push_mirror.none=Não foram configuradas quaisquer réplicas de envio
+settings.mirror_settings.push_mirror.none=Não foram configuradas quaiquer réplicas deste repositório
settings.mirror_settings.push_mirror.remote_url=URL do repositório remoto Git
-settings.mirror_settings.push_mirror.add=Adicionar réplica de envio
+settings.mirror_settings.push_mirror.add=Adicionar réplica deste repositório
settings.sync_mirror=Sincronizar agora
settings.mirror_sync_in_progress=A sincronização da réplica está em andamento. Volte a verificar daqui a um minuto.
-settings.email_notifications.enable=Habilitar notificações por email
-settings.email_notifications.onmention=Enviar email somente quando mencionado(a)
-settings.email_notifications.disable=Desabilitar notificações por email
-settings.email_notifications.submit=Definir preferência do email
settings.site=Sítio web
settings.update_settings=Modificar configurações
settings.branches.update_default_branch=Definir o ramo principal
@@ -1893,6 +1905,7 @@ settings.confirm_delete=Eliminar repositório
settings.add_collaborator=Adicionar colaborador
settings.add_collaborator_success=O colaborador foi adicionado.
settings.add_collaborator_inactive_user=Não é possível adicionar um utilizador desabilitado como colaborador.
+settings.add_collaborator_owner=Não é possível adicionar um proprietário como um colaborador.
settings.add_collaborator_duplicate=O colaborador já tinha sido adicionado a este repositório.
settings.delete_collaborator=Remover
settings.collaborator_deletion=Remover colaborador
@@ -1951,6 +1964,8 @@ settings.event_delete=Eliminar
settings.event_delete_desc=Ramo ou etiqueta eliminados.
settings.event_fork=Derivar
settings.event_fork_desc=Feita a derivação do repositório.
+settings.event_wiki=Wiki
+settings.event_wiki_desc=Página do wiki criada, renomeada, editada ou eliminada.
settings.event_release=Lançamento
settings.event_release_desc=Lançamento publicado, modificado ou eliminado num repositório.
settings.event_push=Enviar
@@ -2302,6 +2317,7 @@ create_org=Criar organização
repo_updated=Modificado
people=Pessoas
teams=Equipas
+code=Código
lower_members=membros
lower_repositories=repositórios
create_new_team=Nova equipa
@@ -2798,16 +2814,19 @@ config.queue_length=Tamanho da fila
config.deliver_timeout=Prazo da entrega
config.skip_tls_verify=Ignorar validação TLS
-config.mailer_config=Configuração da aplicação SMTP
+config.mailer_config=Configuração de envio de email
config.mailer_enabled=Habilitado
-config.mailer_disable_helo=Desabilitar HELO
+config.mailer_enable_helo=Habilitar HELO
config.mailer_name=Nome
-config.mailer_host=Servidor
+config.mailer_protocol=Protocolo
+config.mailer_smtp_addr=Endereço SMTP
+config.mailer_smtp_port=Porto do SMTP
config.mailer_user=Utilizador
config.mailer_use_sendmail=Usar o sendmail
config.mailer_sendmail_path=Caminho do sendmail
config.mailer_sendmail_args=Argumentos extras para o sendmail
config.mailer_sendmail_timeout=Tempo limite do Sendmail
+config.mailer_use_dummy=Fictício
config.test_email_placeholder=Email (ex.: teste@exemplo.com)
config.send_test_mail=Enviar email de teste
config.test_mail_failed=Falhou o envio de um email de teste para '%s': %v
@@ -2843,7 +2862,7 @@ config.git_max_diff_line_characters=Número máximos de caracteres diff (por lin
config.git_max_diff_files=Número máximo de ficheiros diff a serem apresentados
config.git_gc_args=Argumentos da recolha de lixo
config.git_migrate_timeout=Prazo da migração
-config.git_mirror_timeout=Tempo limite da réplica
+config.git_mirror_timeout=Prazo para sincronização da réplica
config.git_clone_timeout=Prazo da operação de clonagem
config.git_pull_timeout=Prazo da operação de puxar
config.git_gc_timeout=Prazo da operação de recolha de lixo
@@ -2860,6 +2879,9 @@ config.access_log_template=Modelo
config.xorm_log_mode=Modo de registo XORM
config.xorm_log_sql=Registo do SQL
+config.get_setting_failed=Falha ao obter a configuração %s
+config.set_setting_failed=Falha ao definir a configuração %s
+
monitor.cron=Tarefas Cron
monitor.name=Nome
monitor.schedule=Programação
@@ -2890,20 +2912,21 @@ monitor.queue.review_add=Rever/Adicionar trabalhadores
monitor.queue.configuration=Configuração inicial
monitor.queue.nopool.title=Sem agregado de trabalhadores
monitor.queue.nopool.desc=Esta fila engloba outras filas e ela própria não tem um agregado de trabalhadores.
-monitor.queue.wrapped.desc=Uma fila envolvente envolve uma fila de início lento, armazenando pedidos em fila num canal. Ela própria não tem um conjunto de tarefas.
+monitor.queue.wrapped.desc=Uma fila envolvente envolve uma fila de início lento, armazenando pedidos em fila num canal. Ela própria não tem um agregado de trabalhadores.
monitor.queue.persistable-channel.desc=Um canal persistente engloba duas filas, uma fila de canal que tem o seu próprio agregado de trabalhadores e uma fila de nível para pedidos persistentes de encerramentos anteriores. Ele próprio não tem um agregado de trabalhadores.
+monitor.queue.flush=Trabalhador descartável
monitor.queue.pool.timeout=Prazo
monitor.queue.pool.addworkers.title=Adicionar trabalhadores
monitor.queue.pool.addworkers.submit=Adicionar trabalhadores
-monitor.queue.pool.addworkers.desc=Adicione trabalhadores a este agregado com, ou sem, prazo. Se definir um prazo, estes trabalhadores serão removidos do agregado, após o fim do prazo.
+monitor.queue.pool.addworkers.desc=Adicione trabalhadores a este agregado com, ou sem, um prazo. Se definir um prazo, estes trabalhadores serão removidos do agregado, quando terminar esse prazo.
monitor.queue.pool.addworkers.numberworkers.placeholder=Número de trabalhadores
monitor.queue.pool.addworkers.timeout.placeholder=Insira 0 para indicar que não tem prazo
-monitor.queue.pool.addworkers.mustnumbergreaterzero=O número de trabalhadores a adicionar deve ser maior que zero
+monitor.queue.pool.addworkers.mustnumbergreaterzero=O número de trabalhadores a adicionar deve ser maior do que zero
monitor.queue.pool.addworkers.musttimeoutduration=O prazo tem que ser uma duração no formato golang (ex.: 5m) ou 0
monitor.queue.pool.flush.title=Despejar fila
-monitor.queue.pool.flush.desc=O despejo irá adicionar um trabalhador que termina assim que a fila esteja vazia ou o prazo acabe.
-monitor.queue.pool.flush.submit=Adicionar trabalhador de despejo
-monitor.queue.pool.flush.added=Foi adicionado um trabalhador de despejo para %[1]s
+monitor.queue.pool.flush.desc='Descartável' irá adicionar um trabalhador que termina assim que a fila esteja vazia ou o prazo acabe.
+monitor.queue.pool.flush.submit=Adicionar trabalhador descartável
+monitor.queue.pool.flush.added=Foi adicionado um trabalhador descartável para %[1]s
monitor.queue.pool.pause.title=Pausar fila
monitor.queue.pool.pause.desc=Pausar uma fila impede que ela processe dados
monitor.queue.pool.pause.submit=Pausar fila
@@ -2912,11 +2935,11 @@ monitor.queue.pool.resume.desc=Definir esta fila para continuar o trabalho
monitor.queue.pool.resume.submit=Retomar fila
monitor.queue.settings.title=Configurações do agregado
-monitor.queue.settings.desc=Os agregados crescem dinamicamente com um impulso em resposta à ocorrência de bloqueios na sua fila de trabalhadores. Essas mudanças não irão influenciar os grupos de trabalhadores correntes.
+monitor.queue.settings.desc=Os agregados crescem dinamicamente com um aumento em resposta à ocorrência de bloqueios na sua fila de trabalhadores. Essas mudanças não irão influenciar os grupos de trabalhadores correntes.
monitor.queue.settings.timeout=Prazo do impulso
monitor.queue.settings.timeout.placeholder=De momento %[1]v
monitor.queue.settings.timeout.error=O prazo tem que ser uma duração no formato golang (ex: 5m) ou 0
-monitor.queue.settings.numberworkers=Número de trabalhadores do impulso
+monitor.queue.settings.numberworkers=Aumentar o número de trabalhadores
monitor.queue.settings.numberworkers.placeholder=De momento %[1]d
monitor.queue.settings.numberworkers.error=O número de trabalhadores a adicionar tem que ser maior ou igual a zero
monitor.queue.settings.maxnumberworkers=Número máximo de trabalhadores
@@ -2928,14 +2951,14 @@ monitor.queue.settings.blocktimeout=Prazo do bloco corrente
monitor.queue.settings.blocktimeout.value=%[1]v
monitor.queue.pool.none=Esta fila não tem um agregado
-monitor.queue.pool.added=Foi adicionado um agregado de trabalhadores
+monitor.queue.pool.added=Foi adicionado um grupo de trabalhadores
monitor.queue.pool.max_changed=O número máximo de trabalhadores mudou
monitor.queue.pool.workers.title=Grupos de trabalhadores operantes
-monitor.queue.pool.workers.none=Não há agregados de trabalhadores.
-monitor.queue.pool.cancel=Desligar agregado de trabalhadores
-monitor.queue.pool.cancelling=O agregado de trabalhadores está a encerrar
-monitor.queue.pool.cancel_notices=Desligar este agregado de %s trabalhadores?
-monitor.queue.pool.cancel_desc=Deixar uma fila sem quaisquer agregados de trabalhadores pode fazer com que os pedidos bloqueiem indefinidamente.
+monitor.queue.pool.workers.none=Não há grupos de trabalhadores.
+monitor.queue.pool.cancel=Desligar o grupo de trabalhadores
+monitor.queue.pool.cancelling=O grupo de trabalhadores está a encerrar
+monitor.queue.pool.cancel_notices=Desligar este grupo de %s trabalhadores?
+monitor.queue.pool.cancel_desc=Deixar uma fila sem quaisquer grupos de trabalhadores pode fazer com que os pedidos sejam bloqueados indefinidamente.
notices.system_notice_list=Notificações do sistema
notices.view_detail_header=Ver os detalhes da notificação
@@ -3022,6 +3045,9 @@ pin=Fixar notificação
mark_as_read=Marcar como lida
mark_as_unread=Marcar como não lida
mark_all_as_read=Marcar todas como lidas
+subscriptions=Subscrições
+watching=Vigiando
+no_subscriptions=Sem subscrições
[gpg]
default_key=Assinado com a chave padrão
@@ -3044,6 +3070,7 @@ title=Pacotes
desc=Gerir pacotes do repositório.
empty=Ainda não há pacotes.
empty.documentation=Para obter mais informação sobre o registo de pacotes, veja a documentação.
+empty.repo=Carregou um pacote mas este não é apresentado aqui? Vá às configurações do pacote e ligue-o a este repositório.
filter.type=Tipo
filter.type.all=Todos
filter.no_result=O seu filtro não produziu quaisquer resultados.
@@ -3080,6 +3107,7 @@ container.details.platform=Plataforma
container.details.repository_site=Página web do repositório
container.details.documentation_site=Página web da documentação
container.pull=Puxar a imagem usando a linha de comandos:
+container.digest=Resumo:
container.documentation=Para obter mais informações sobre o registo do Container, consulte a documentação.
container.multi_arch=S.O. / Arquit.
container.layers=Camadas de imagem
@@ -3109,6 +3137,10 @@ npm.dependencies.development=Dependências de desenvolvimento
npm.dependencies.peer=Dependências de pares
npm.dependencies.optional=Dependências opcionais
npm.details.tag=Etiqueta
+pub.install=Para instalar o pacote usando o Dart, execute o seguinte comando:
+pub.documentation=Para obter mais informações sobre o registo Pub, consulte a documentação.
+pub.details.repository_site=Página web do repositório
+pub.details.documentation_site=Página web da documentação
pypi.requires=Requer Python
pypi.install=Para instalar o pacote usando o pip, execute o seguinte comando:
pypi.documentation=Para obter mais informações sobre o registo do PyPI, consulte a documentação.
@@ -3116,7 +3148,11 @@ rubygems.install=Para instalar o pacote usando o gem, execute o seguinte comando
rubygems.install2=ou adicione-o ao ficheiro Gemfile
:
rubygems.dependencies.runtime=Dependências do tempo de execução (runtime)
rubygems.dependencies.development=Dependências de desenvolvimento
+rubygems.required.ruby=Requer a versão do Ruby
+rubygems.required.rubygems=Requer a versão do RubyGem
rubygems.documentation=Para obter mais informações sobre o registo do RubyGems, consulte a documentação.
+vagrant.install=Para adicionar uma máquina virtual Vagrant, execute o seguinte comando:
+vagrant.documentation=Para obter mais informações sobre o registo do Vagrant, consulte a documentação.
settings.link=Vincular este pacote a um repositório
settings.link.description=Se você vincular um pacote a um repositório, o pacote será listado na lista de pacotes do repositório.
settings.link.select=Escolha o repositório
diff --git a/options/locale/locale_ru-RU.ini b/options/locale/locale_ru-RU.ini
index e95ceb2453..fab3dfc6d9 100644
--- a/options/locale/locale_ru-RU.ini
+++ b/options/locale/locale_ru-RU.ini
@@ -2,13 +2,13 @@ home=Главная
dashboard=Панель управления
explore=Обзор
help=Помощь
+logo=Логотип
sign_in=Вход
sign_in_with=Войдите с помощью
sign_out=Выход
sign_up=Регистрация
link_account=Привязать аккаунт
register=Регистрация
-website=Веб-сайт
version=Версия
powered_by=Работает на %s
page=Страница
@@ -176,7 +176,6 @@ log_root_path_helper=Файлы журнала будут записыватьс
optional_title=Расширенные настройки
email_title=Настройки электронной почты
-smtp_host=Узел SMTP
smtp_from=Отправить эл. почту как
smtp_from_helper=Адрес электронной почты, который будет использоваться Gitea. Введите обычный адрес электронной почты или используйте формат "Имя" .
mailer_user=SMTP логин
@@ -275,6 +274,7 @@ code_no_results=Соответствующий поисковому запрос
code_search_results=Результаты поиска для '%s'
code_last_indexed_at=Последний проиндексированный %s
+
[auth]
create_new_account=Регистрация аккаунта
register_helper_msg=Уже есть аккаунт? Авторизуйтесь!
@@ -1260,6 +1260,7 @@ issues.previous=Предыдущая
issues.next=Следующая
issues.open_title=Открыто
issues.closed_title=Закрыто
+issues.draft_title=Черновик
issues.num_comments=комментариев: %d
issues.commented_at=`прокомментировал(а) %s`
issues.delete_comment_confirm=Вы уверены, что хотите удалить этот комментарий?
@@ -1281,7 +1282,7 @@ issues.reopened_at=`переоткрыл(а) эту проблему %[2]s`
issues.ref_issue_from=`ссылка на эту проблему %[4]s %[2]s`
issues.ref_pull_from=`ссылается на этот запрос на слияние %[4]s %[2]s`
-issues.ref_closing_from=`ссылается на запрос на слияние %[4], который закроет эту задачу %[2]s`
+issues.ref_closing_from=`ссылается на запрос на слияние %[4]s, который закроет эту задачу %[2]s`
issues.ref_reopening_from=`ссылается на запрос на слияние %[4]s, который вновь откроет эту задачу %[2]s`
issues.ref_closed_from=`закрыл этот запрос %[4]s %[2]s`
issues.ref_reopened_from=`переоткрыл эту задачу %[4]s %[2]s`
@@ -1716,10 +1717,6 @@ settings.mirror_settings.push_mirror.remote_url=URL удалённого хра
settings.mirror_settings.push_mirror.add=Добавить Push-зеркало
settings.sync_mirror=Синхронизировать
settings.mirror_sync_in_progress=Синхронизируются репозитории-зеркала. Подождите минуту и обновите страницу.
-settings.email_notifications.enable=Включить почтовые уведомления
-settings.email_notifications.onmention=Посылать письмо на эл. почту только при упоминании
-settings.email_notifications.disable=Отключить почтовые уведомления
-settings.email_notifications.submit=Установить настройки электронной почты
settings.site=Сайт
settings.update_settings=Обновить настройки
settings.branches.update_default_branch=Обновить ветку по умолчанию
@@ -2683,11 +2680,8 @@ config.queue_length=Длина очереди
config.deliver_timeout=Задержка доставки
config.skip_tls_verify=Пропустить проверку TLS
-config.mailer_config=Настройки почты
config.mailer_enabled=Почта включена
-config.mailer_disable_helo=Отключить HELO
config.mailer_name=Имя
-config.mailer_host=Сервер
config.mailer_user=Пользователь
config.mailer_use_sendmail=Использовать Sendmail
config.mailer_sendmail_path=Путь к Sendmail
@@ -2745,6 +2739,7 @@ config.access_log_template=Шаблон
config.xorm_log_mode=Режим журнала XORM
config.xorm_log_sql=Лог SQL
+
monitor.cron=Задачи cron
monitor.name=Название
monitor.schedule=Расписание
@@ -2932,6 +2927,8 @@ installation=Установка
about=Об этом пакете
requirements=Требования
dependencies=Зависимости
+composer.dependencies=Зависимости
+conan.details.repository=Репозиторий
container.multi_arch=ОС / архитектура
container.labels.key=Ключ
container.labels.value=Значение
diff --git a/options/locale/locale_si-LK.ini b/options/locale/locale_si-LK.ini
index a07ff03788..4ddf97356c 100644
--- a/options/locale/locale_si-LK.ini
+++ b/options/locale/locale_si-LK.ini
@@ -8,7 +8,6 @@ sign_out=නික්මෙන්න
sign_up=ලියාපදිංචිය
link_account=ගිණුම සබැඳින්න
register=ලියාපදිංචිය
-website=වියමන අඩවිය
version=අනුවාදය
powered_by=%s මගින් බලගන්වා ඇත
page=පිටුව
@@ -151,7 +150,6 @@ log_root_path_helper=ලොග් ගොනු මෙම ඩිරෙක්ට
optional_title=වෛකල්පිත සැකසුම්
email_title=වි-තැපෑලේ සැකසුම්
-smtp_host=SMTP සත්කාරක
smtp_from=ලෙස වි-තැපෑල යවන්න
smtp_from_helper=විද්යුත් තැපැල් ලිපිනය Gitea භාවිතා කරනු ඇත. සරල විද්යුත් තැපැල් ලිපිනයක් ඇතුළත් කරන්න හෝ “නම” ආකෘතිය භාවිතා කරන්න.
mailer_user=SMTP පරිශීලක නාමය
@@ -243,6 +241,7 @@ code_no_results=ඔබගේ සෙවුම් පදය ගැලපෙන ප
code_search_results='%s' සඳහා සෙවුම් ප්රතිඵල
code_last_indexed_at=අවසන් සුචිගත %s
+
[auth]
create_new_account=ගිණුමක් ලියාපදිංචි කරන්න
register_helper_msg=දැනටමත් ගිණුමක් තිබේද? දැන්ම පුරනය වන්න!
@@ -1585,10 +1584,6 @@ settings.mirror_settings.push_mirror.remote_url=GIT දුරස්ථ ගබඩ
settings.mirror_settings.push_mirror.add=Push මිරර් එකතු කරන්න
settings.sync_mirror=සමමුහූර්ත කරන්න
settings.mirror_sync_in_progress=මිරර් සමමුහුර්තකරණය ක්රියාත්මක වෙමින් පවතී. විනාඩියකින් නැවත පරීක්ෂා කරන්න.
-settings.email_notifications.enable=වි-තැපැල් දැනුම්දීම් සබල කරන්න
-settings.email_notifications.onmention=සැඳහුම් සඳහා තැපැල් කරන්න
-settings.email_notifications.disable=වි-තැපැල් දැනුම්දීම් අබල කරන්න
-settings.email_notifications.submit=ඊ-තැපැල් මනාප සකසන්න
settings.site=වියමන අඩවිය
settings.update_settings=යාවත්කාල සැකසුම්
settings.branches.update_default_branch=පෙරනිමි ශාඛාව යාවත්කාල කරන්න
@@ -2512,11 +2507,8 @@ config.queue_length=පෝලිම් දිග
config.deliver_timeout=කාලය ගලවාගන්න
config.skip_tls_verify=TLS සත්යාපනය මඟ හරින්න
-config.mailer_config=SMTP තැපැල්කරු වින්යාසය
config.mailer_enabled=සබල කර ඇත
-config.mailer_disable_helo=හෙලෝ අක්රීය කරන්න
config.mailer_name=නම
-config.mailer_host=සත්කාරක
config.mailer_user=පරිශීලක
config.mailer_use_sendmail=සෙන්ඩ්මේල් භාවිතා කරන්න
config.mailer_sendmail_path=සෙන්ඩ්මේල් මාර්ගය
@@ -2574,6 +2566,7 @@ config.access_log_template=සැකිල්ල
config.xorm_log_mode=XORM ලොග් ප්රකාරය
config.xorm_log_sql=ලොග් SQL
+
monitor.cron=Con කාර්යයන්
monitor.name=නම
monitor.schedule=කාලසටහන
diff --git a/options/locale/locale_sk-SK.ini b/options/locale/locale_sk-SK.ini
new file mode 100644
index 0000000000..597b91cb1f
--- /dev/null
+++ b/options/locale/locale_sk-SK.ini
@@ -0,0 +1,1320 @@
+home=Domov
+dashboard=Nástenka
+explore=Preskúmať
+help=Pomoc
+logo=Logo
+sign_in=Prihlásiť sa
+sign_in_with=Prihlásiť sa pomocou
+sign_out=Odhlásiť sa
+sign_up=Registrácia
+link_account=Prepojiť účet
+register=Registrácia
+version=Verzia
+powered_by=Poháňané %s
+page=Stránka
+template=Šablóna
+language=Jazyk
+notifications=Upozornenia
+active_stopwatch=Sledovanie času aktivity
+create_new=Vytvoriť…
+user_profile_and_more=Profil a nastavenia…
+signed_in_as=Prihlásený ako
+enable_javascript=Táto stránka pracuje lepšie s JavaScript-om.
+toc=Obsah
+licenses=Licencie
+return_to_gitea=Naspäť do Gitea
+
+username=Používateľské meno
+email=E-mailová adresa
+password=Heslo
+access_token=Prístupový token
+re_type=Znova zadajte heslo
+captcha=CAPTCHA
+twofa=Dvojfaktorová autentifikácia
+twofa_scratch=Dvojfaktorový dočasný kód
+passcode=Prístupový kód
+
+webauthn_insert_key=Zadajte bezpečnostný kľúč
+webauthn_sign_in=Stlačte tlačidlo na vašom bezpečnostnom kľúči. Ak váš kľúč nemá tlačidlo, vyberte a zasunte ho znova.
+webauthn_press_button=Stlačte, prosím, tlačidlo na vašom bezpečnostnom kľúči…
+webauthn_use_twofa=Použite kód dvojfaktorového overenia z vášho telefónu
+webauthn_error=Nie je možné prečítať váš bezpečnostný kód.
+webauthn_unsupported_browser=Váš prehliadač aktuálne nepodporuje WebAuthn.
+webauthn_error_unknown=Vyskytla sa neznáma chyba. Skúste to znova.
+webauthn_error_insecure=WebAuthn podporuje iba bezpečné spojenia. Na testovanie cez HTTP môžete použiť "localhost" alebo "127.0.0.1"
+webauthn_error_unable_to_process=Server nemohol spracovať vašu požiadavku.
+webauthn_error_duplicated=Bezpečnostný kľúč nie je pre túto požiadavku povolený. Uistite sa, že kľúč ešte nie je zaregistrovaný.
+webauthn_error_empty=Musíte nastaviť meno pre tento kľúč.
+webauthn_error_timeout=Vypršal čas na čítanie vašeho kľúča. Znova načítajte túto stránku a skúste to opäť.
+webauthn_reload=Znovu načítať
+
+repository=Repozitár
+organization=Organizácia
+mirror=Zrkadlo
+new_repo=Nový repozitár
+new_migrate=Nová migrácia
+new_mirror=Nové zrkadlo
+new_fork=Nový fork repozitára
+new_org=Nová organizácia
+new_project=Nový projekt
+new_project_board=Nová nástenka projektu
+manage_org=Spravovať organizácie
+admin_panel=Administrácia
+account_settings=Nastavenia účtu
+settings=Nastavenia
+your_profile=Profil
+your_starred=Obľúbené
+your_settings=Nastavenia
+
+all=Všetko
+sources=Zdrojové kódy
+mirrors=Zrkadlá
+collaborative=Spolupráca
+forks=Forky
+
+activities=Aktivity
+pull_requests=Pull requesty
+issues=Úkoly
+milestones=Míľniky
+
+ok=OK
+cancel=Zrušiť
+save=Uložiť
+add=Pridať
+add_all=Pridať všetko
+remove=Odstrániť
+remove_all=Odstrániť všetko
+edit=Upraviť
+
+copy=Kopírovať
+copy_url=Kopírovať URL
+copy_branch=Kopírovať meno vetvy
+copy_success=Skopírované!
+copy_error=Kopírovanie zlyhalo
+
+write=Zapísať
+preview=Náhľad
+loading=Načítava sa…
+
+step1=Krok 1:
+step2=Krok 2:
+
+error=Chyba
+error404=Stránka, na ktorú sa pokúšate dostať, buď neexistuje, alebo nemáte oprávnenie na jej zobrazenie.
+
+never=Nikdy
+
+rss_feed=RSS kanál
+
+[error]
+occurred=Vyskytla sa chyba
+report_message=Ak ste si istí, že toto je chyba v Gitea, prehľadajte problémy na GitHub alebo otvorte nový issue podľa potreby.
+missing_csrf=Nesprávna žiadosť: neprítomný CSFR token
+invalid_csrf=Nesprávna žiadosť: nesprávny CSFR token
+not_found=Nebolo možné nájsť cieľ.
+network_error=Chyba siete
+
+[startpage]
+app_desc=Jednoducho prístupný vlastný Git
+install=Jednoduchá inštalácia
+install_desc=Jednoducho spusťte binárku pre vašu platformu, dodávanú ako Docker, alebo ju získajte ako balík.
+platform=Multiplatformový
+platform_desc=Gitea beží všade kde je možné preložiť Go: Windows, macOS, Linux, ARM, a podobne. Vyberte si!
+lightweight=Ľahká
+lightweight_desc=Gitea má minimálne požiadavky a môže bežať na Raspberry Pi. Šetrite energiou vášho stroja!
+license=Otvorený zdrojový kód
+license_desc=Získajte code.gitea.io/gitea! Pridajte sa k nám a prispejte, aby bol tento projekt ešte lepší. Nehanbite sa byť prispievateľom!
+
+[install]
+install=Inštalácia
+title=Východzia konfigurácia
+docker_helper=Ak spúšťate Gitea v Docker kontajneri, prečítajte si dokumentáciu pred zmenou akýchkoľvek nastavení.
+require_db_desc=Gitea vyžaduje MySQL, PostgreSQL, MSSQL, SQLite3 alebo TiDB (MySQL protokol).
+db_title=Nastavenie databázy
+db_type=Typ databázy
+host=Host
+user=Používateľské meno
+password=Heslo
+db_name=Názov databázy
+db_helper=Poznámka pre používateľov MySQL: používajte, prosím, formát uloženia dát InnoDB a pokiaľ používate „utf8mb4“, verzia InnoDB musí byť väčšia než 5.6.
+db_schema=Schéma
+db_schema_helper=Nechajte prázdne pre predvolené nastavenie ("public").
+ssl_mode=SSL
+charset=Znaková sada
+path=Cesta
+sqlite_helper=Cesta k súboru databázy SQLite3.
Ak spúšťate Gitea ako službu, zadajte absolútnu cestu.
+reinstall_error=Pokúšate sa inštalovať do existujúcej databázy Gitea
+reinstall_confirm_message=Opätovná inštalácia s existujúcou databázou Gitea môže spôsobiť viacero problémov. Vo väčšine prípadov by ste na spustenie Gitea mali použiť svoj existujúci súbor „app.ini“. Ak viete, čo robíte, potvrďte nasledujúce:
+reinstall_confirm_check_1=Údaje zašifrované pomocou SECRET_KEY v app.ini sa môžu stratiť: používatelia sa možno nebudú môcť prihlásiť s 2FA/OTP a zrkadlá možno nebudú fungovať správne. Začiarknutím tohto políčka potvrdzujete, že aktuálny súbor app.ini obsahuje správny kľúč SECRET_KEY.
+reinstall_confirm_check_2=Repozitáre a nastavenia možno bude potrebné znova synchronizovať. Začiarknutím tohto políčka potvrdzujete, že hooky pre repozitáre a súbor autorizovaných kľúčov znova zosynchronizujete manuálne. Potvrdzujete, že zabezpečíte, aby boli nastavenia úložiska a zrkadla správne.
+reinstall_confirm_check_3=Potvrdzujete, že ste si úplne istí, že táto Gitea beží so správnym umiestnením app.ini a že ste si istí, že ju musíte znova nainštalovať. Potvrdzujete, že beriete na vedomie vyššie uvedené riziká.
+err_empty_db_path=Cesta k databáze SQLite3 nesmie byť prázdna.
+no_admin_and_disable_registration=Nemôžete zakázať registráciu bez vytvorenia administrátorského účtu.
+err_empty_admin_password=Heslo administrátora nemôže byť prázdne.
+err_empty_admin_email=E-mail administrátora nemôže byť prázdny.
+err_admin_name_is_reserved=Používateľské meno administrátora je neplatné, používateľské meno je rezervované
+err_admin_name_pattern_not_allowed=Používateľské meno administrátora je neplatné, používateľské meno zodpovedá vyhradenému vzoru
+err_admin_name_is_invalid=Používateľské meno administrátora je neplatné
+
+general_title=Všeobecné nastavenia
+app_name=Názov webu
+app_name_helper=Sem môžete zadať meno vašej spoločnosti.
+repo_path=Koreňový adresár repozitárov
+repo_path_helper=Vzdialené úložiská Git sa uložia do tohto adresára.
+lfs_path=Koreňový adresár Git LFS
+lfs_path_helper=Súbory sledované systémom Git LFS budú uložené v tomto adresári. Ak chcete deaktivovať, ponechajte prázdne.
+run_user=Spustiť ako používateľ
+run_user_helper=Zadajte používateľské meno pod ktorým beží Gitea v operačnom systéme. Pozor: tento používateľ musí mať prístup ku koreňovému adresáru repozitára.
+domain=Doména serveru
+domain_helper=Adresa domény alebo hostiteľa serveru.
+ssh_port=Port SSH servera
+ssh_port_helper=Číslo portu na ktorom načúva SSH server. Keď ponecháte prázdne, SSH server zakážete.
+http_port=HTTP port pre Gitea
+http_port_helper=Číslo portu na ktorom počúva webový server Gitea.
+app_url=Základná URL Gitea
+app_url_helper=Základná adresa pre klonované HTTP(S) URL adresy a e-mailové upozornenia.
+log_root_path=Adresár logov
+log_root_path_helper=Do tohoto adresára budú uložené súbory protokolu.
+
+optional_title=Voliteľné nastavenia
+email_title=Nastavenia e-mailu
+smtp_addr=SMTP Host
+smtp_port=SMTP Port
+smtp_from=Odoslať e-mail ako
+smtp_from_helper=E-mailová adresa ktorú použije Gitea. Zadajte bežnú e-mailovú adresu alebo použite formát "Meno" .
+mailer_user=Používateľské meno SMTP
+mailer_password=SMTP heslo
+register_confirm=Registrácia vyžaduje potvrdenie e-mailu
+mail_notify=Povoliť e-mailové upozornenia
+server_service_title=Nastavenia servera a ostatných služieb
+offline_mode=Povoliť miestny režim
+offline_mode_popup=Zakázať siete doručovania obsahu tretích strán a poskytovať celý obsah lokálne.
+disable_gravatar=Zakázať Gravatar
+disable_gravatar_popup=Zakázať Gravatar a cudzie zdroje avatarov. Ak používateľ nenahrá avatara, použije sa predvolený.
+federated_avatar_lookup=Povoliť avatary z verejných zdrojov
+federated_avatar_lookup_popup=Povoliť Libavatar na vyhľadávanie avatarov z verejných zdrojov.
+disable_registration=Zakázať registráciu
+disable_registration_popup=Zakázať registráciu. Nové používateľské účty budú môcť vytvárať iba správci.
+allow_only_external_registration_popup=Povoliť registráciu iba skrze externé služby
+openid_signin=Povoliť prihlásenie pomocou OpenID
+openid_signin_popup=Povoliť používateľovi prihlásenie pomocou OpenID.
+openid_signup=Povoliť registráciu pomocou OpenID
+openid_signup_popup=Povoliť používateľskú registráciu založenú na OpenID.
+enable_captcha=Povoliť CAPTCHA pri registrácii
+enable_captcha_popup=Vyžadovať CAPTCHA validáciu pri registrácii používateľa.
+require_sign_in_view=Vyžadovať prihlásenie na prezeranie stránok
+require_sign_in_view_popup=Povoliť prístup k stránkam iba pre prihlásených používateľov. Návštevníci uvidia iba prihlasovaciu a registračnú stránku.
+admin_setting_desc=Vytvorenie správcovského účtu je nepovinné. Prvý zaregistrovaný používateľ sa stane automaticky správcom.
+admin_title=Nastavenia administrátorského účtu
+admin_name=Používateľské meno administrátora
+admin_password=Heslo
+confirm_password=Potvrdiť heslo
+admin_email=E-mailová adresa
+install_btn_confirm=Nainštalovať Gitea
+test_git_failed=Nie je možné otestovať príkaz 'git': %v
+sqlite3_not_available=Táto verzia Gitea nepodporuje SQLite3. Stiahnite si, prosím, oficiálnu verziu z %s (nie verziu "gobuild").
+invalid_db_setting=Nastavenia databázy sú neplatné: %v
+invalid_db_table=Databázová tabuľka '%s' je neplatná: %v
+invalid_repo_path=Koreňová cesta repozitára je neplatná: %v
+invalid_app_data_path=Cesta k údajom aplikácie je neplatná: %v
+run_user_not_match=Používateľské meno pre 'spustiť ako' nie je aktuálne používateľské meno: %s -> %s
+internal_token_failed=Nepodarilo sa vygenerovať interný token: %v
+secret_key_failed=Nepodarilo sa vygenerovať tajný kľúč: %v
+save_config_failed=Nepodarilo sa uložiť konfiguráciu: %v
+invalid_admin_setting=Nastavenie administrátorského účtu je neplatné: %v
+install_success=Vitajte! Ďakujeme že ste si vybrali Gitea. Veľa zábavy a opatrujte sa!
+invalid_log_root_path=Cesta k logom je neplatná: %v
+default_keep_email_private=Skrývanie e-mail adries ako predvolené
+default_keep_email_private_popup=Predvolene skryť e-mailové adresy nových používateľských účtov.
+default_allow_create_organization=Predvolene povoliť vytváranie organizácií
+default_allow_create_organization_popup=V predvolenom nastavení povoľte novým používateľským účtom vytvárať organizácie.
+default_enable_timetracking=Predvolene povoliť sledovanie času
+default_enable_timetracking_popup=Predvolene povoliť sledovanie času pre nové repozitáre.
+no_reply_address=Skrytá e-mailová doména
+no_reply_address_helper=Doménové meno pre používateľov so skrytou e-mailovou adresou. Napríklad, používateľ s menom 'joe' bude zalogovaný v Git-e ako 'joe@noreply.example.org' ak je skrytá e-mailová doména nastavená na 'noreply.example.org'.
+password_algorithm=Hašovací algoritmus hesla
+password_algorithm_helper=Nastavte algoritmus hašovania hesla. Algoritmy majú rozličné požiadavky a silu. Algoritmus `argon2` má síce dobré charakteristiky ale používa veľa pamäte a nemusí byť vhodný pre malé systémy.
+
+[home]
+uname_holder=Používateľské meno alebo emailová adresa
+password_holder=Heslo
+switch_dashboard_context=Prepnúť kontext nástenky
+my_repos=Repozitáre
+show_more_repos=Zobraziť ďalšie repozitáre…
+collaborative_repos=Kolaboratívne repozitáre
+my_orgs=Moje organizácie
+my_mirrors=Moje zrkadlá
+view_home=Zobraziť %s
+search_repos=Nájsť repozitár…
+filter=Ostatné filtre
+filter_by_team_repositories=Filtrovať podľa tímových repozitárov
+feed_of=Informačný kanál „%s“
+
+show_archived=Archivované
+show_both_archived_unarchived=Zobrazujú sa archivované aj nearchivované
+show_only_archived=Zobrazuje sa iba archivované
+show_only_unarchived=Zobrazuje sa iba nearchivované
+
+show_private=Súkromné
+show_both_private_public=Zobrazuje sa verejné aj súkromné
+show_only_private=Zobrazuje sa iba súkromné
+show_only_public=Zobrazuje sa iba verejné
+
+issues.in_your_repos=Vo vašich repozitároch
+
+[explore]
+repos=Repozitáre
+users=Používatelia
+organizations=Organizácie
+search=Hľadať
+code=Zdrojový kód
+search.fuzzy=Fuzzy
+search.match=Zhoda
+code_search_unavailable=Vyhľadávanie kódu momentálne nie je dostupné. Kontaktujte, prosím, správcu.
+repo_no_results=Nenašli sa zodpovedajúce repozitáre.
+user_no_results=Nenašli sa zodpovedajúci používatelia.
+org_no_results=Nenašli sa zodpovedajúce organizácie.
+code_no_results=Nenašiel sa žiaden zdrojový kód zodpovedajúci hľadanému výrazu.
+code_search_results=Výsledky vyhľadávania pre '%s'
+code_last_indexed_at=Naposledy indexované %s
+relevant_repositories_tooltip=Repozitáre, ktoré sú forkami alebo ktoré nemajú tému, žiadnu ikonu ani popis, sú skryté.
+relevant_repositories=Zobrazujú sa iba relevantné repozitáre, zobraziť nefiltrované výsledky.
+
+
+[auth]
+create_new_account=Zaregistrovať účet
+register_helper_msg=Máte už účet? Prihláste sa teraz!
+social_register_helper_msg=Máte už účet? Prepojte ho teraz!
+disable_register_prompt=Registrácia je zakázaná. Obráťte sa, prosím, na administrátora stránky.
+disable_register_mail=E-mailové potvrdzovanie registrácie je zakázané.
+manual_activation_only=Na dokončenie aktivácie kontaktujte správcu webu.
+remember_me=Zapamätať si toto zariadenie
+forgot_password_title=Zabudnuté heslo
+forgot_password=Zabudli ste heslo?
+sign_up_now=Potrebujete účet? Zaregistrujte sa teraz.
+sign_up_successful=Účet bol úspešne vytvorený.
+confirmation_mail_sent_prompt=Na adresu %s bol odoslaný nový potvrdzovací e-mail. Skontrolujte si, prosím, vašu doručenú poštu počas najbližších %s pre dokončenie procesu registrácie.
+must_change_password=Aktualizácia vášho hesla
+allow_password_change=Vyžiadať od používateľa zmenu hesla (doporučuje sa)
+reset_password_mail_sent_prompt=Na adresu %s bol odoslaný potvrdzovací e-mail. Skontrolujte si, prosím, vašu doručenú poštu počas najbližších %s pre dokončenie procesu obnovenia účtu.
+active_your_account=Aktivovať účet
+account_activated=Účet bol aktivovaný
+prohibit_login=Prihlásenie zakázané
+prohibit_login_desc=Váš účet má zakázané prihlásenie, kontaktuje, prosím, správcu servera.
+resent_limit_prompt=Pred malou chvíľou ste už požiadali o aktivačný email. Počkajte, prosím, 3 minúty a potom skúste znova.
+has_unconfirmed_mail=Ahoj %s, tvoja e-mailová adresa (%s) je neoverená. Ak si ešte nedostal potvrdzovací e-mail, alebo je potrebné odoslať nový, klikni, prosím, na tlačidlo nižšie.
+resend_mail=Kliknite sem pre opätovné odoslanie aktivačného e-mailu
+email_not_associate=Táto e-mailová adresa nie je priradená k žiadnemu účtu.
+send_reset_mail=Odoslať e-mail pre obnovenie účtu
+reset_password=Obnovenie účtu
+invalid_code=Váš potvrdzovací kód je chybný alebo vypršala jeho platnosť.
+reset_password_helper=Obnoviť účet
+reset_password_wrong_user=Ste prihlásený ako %s, ale odkaz pre obnovenie účtu je pre %s
+password_too_short=Heslo nemôže obsahovať menej ako %d znakov.
+non_local_account=Externe overovaní používatelia nemôžu aktualizovať svoje heslo prostredníctvom webového rozhrania Gitea.
+verify=Overiť
+scratch_code=Pomocný kód
+use_scratch_code=Použiť pomocný kód
+twofa_scratch_used=Použili ste pomocný kód. Boli ste presmerovaní na stránku nastavenia dvojfaktorového overovania, takže môžete odstrániť registráciu vašeho zariadenia alebo vygenerovať nový pomocný kód.
+twofa_passcode_incorrect=Váš prístupový kód je nesprávny. Ak ste vaše zariadenie umiestnili nesprávne, použite pomocný kód na prihlásenie.
+twofa_scratch_token_incorrect=Váš pomocný kód je nesprávny.
+login_userpass=Prihlásiť sa
+login_openid=OpenID
+oauth_signup_tab=Zaregistrovať nový účet
+oauth_signup_title=Dokončiť nový účet
+oauth_signup_submit=Dokončiť účet
+oauth_signin_tab=Prepojiť s existujúcim účtom
+oauth_signin_title=Prihláste sa na overenie prepojeného účtu
+oauth_signin_submit=Prepojiť účet
+oauth.signin.error=Vyskytla sa chyba počas spracovania vašej autorizačnej žiadosti. Ak chyba pretrváva, kontaktujte, prosím, správcu.
+oauth.signin.error.access_denied=Žiadosť o autorizáciu bola zamietnutá.
+oauth.signin.error.temporarily_unavailable=Autorizácia zlyhala, pretože overovací server je dočasne nedostupný. Skúste to prosím neskôr.
+openid_connect_submit=Pripojiť
+openid_connect_title=Pripojiť k existujúcemu účtu
+openid_connect_desc=Zvolené OpenID URI je neznáme. Združte s novým účtom tu.
+openid_register_title=Vytvoriť nový účet
+openid_register_desc=Zvolené OpenID URI je neznáme. Združte s novým účtom tu.
+openid_signin_desc=Zadajte vaše OpenID URI. Napríklad: https://anne.me, bob.openid.org.cn alebo gnusocial.net/carry.
+disable_forgot_password_mail=Obnovenie účtu je zakázané pretože nie je nastavený e-mail. Kontaktujte, prosím, správcu webu.
+disable_forgot_password_mail_admin=Obnovenie účtu je možné iba po nastavení e-mailu. Pre povolenie obnovy účtu nastavte, prosím, e-mail.
+email_domain_blacklisted=Nemôžete sa zaregistrovať s vašou e-mailovou adresou.
+authorize_application=Autorizovať aplikáciu
+authorize_redirect_notice=Ak autorizujete túto aplikáciu, budete presmerovaní na %s.
+authorize_application_created_by=Túto aplikáciu vytvoril %s.
+authorize_application_description=Ak udelíte prístup, bude možné pristupovať a zapisovať do všetkých vašich informácií o účte, vrátane súkromných repozitárov a organizácií.
+authorize_title=Autorizovať „%s“ pre prístup k vášmu účtu?
+authorization_failed=Autorizácia zlyhala
+authorization_failed_desc=Autorizácia zlyhala pretože sme zistili neplatnú žiadosť. Kontaktujte, prosím, správcu aplikácie ktorou ste sa pokúšali autorizovať.
+sspi_auth_failed=SSPI overenie zlyhalo
+password_pwned=Zvolené heslo je na zozname ukradnutých hesiel ktoré boli v minulosti odhalené pri narušení verejných dát. Skúste znovu s iným heslom.
+password_pwned_err=Nie je možné dokončiť žiadosť na HaveIBeenPwned
+
+[mail]
+view_it_on=Zobraziť na %s
+link_not_working_do_paste=Nefunguje? Skúste ho skopírovať a vložiť do svojho prehliadača.
+hi_user_x=Ahoj %s,
+
+activate_account=Aktivujte si svoj účet, prosím
+activate_account.title=%s, aktivujte si svoj účet, prosím
+activate_account.text_1=Ahoj %[1]s, ďakujeme za registráciu na %[2]s!
+activate_account.text_2=Pre aktiváciu vašeho účtu kliknite, prosím, na nasledovný odkaz do %s:
+
+activate_email=Overte svoju e-mailovú adresu
+activate_email.title=%s, overte prosím, svoju e-mailovú adresu
+activate_email.text=Pre overenie vašej e-mailovej adresy kliknite, prosím, na nasledovný odkaz do %s:
+
+register_notify=Vitajte v Gitea
+register_notify.title=%[1]s, vitajte v %[2]s
+register_notify.text_1=toto je e-mail potvrdzujúci vašu registráciu pre %s!
+register_notify.text_2=Teraz sa môžete prihlásiť s používateľským menom: %s.
+register_notify.text_3=Ak bol tento účet vytvorený pre vás, nastavte prosím najskôr svoje heslo.
+
+reset_password=Obnoviť váš účet
+reset_password.title=%s, požiadali ste o obnovenie vášho účtu
+reset_password.text=Pre obnovenie vašeho účtu kliknite, prosím, na nasledovný odkaz do %s:
+
+register_success=Registrácia prebehla úspešne
+
+issue_assigned.pull=@%[1]s vám pridelil pull request %[2] v repozitári %[3]s.
+issue_assigned.issue=@%[1]s vám pridelil úkol %[2]s v repozitári %[3]s.
+
+issue.x_mentioned_you=@%s vás zmienil:
+issue.action.force_push=%[1]s vynútil nahranie %[2]s z %[3]s do %[4]s.
+issue.action.push_1=@%[1]s nahral %[3]d commit do %[2]s
+issue.action.push_n=@%[1]s nahral %[3]d commity do %[2]s
+issue.action.close=@%[1]s uzavrel #%[2]d.
+issue.action.reopen=@%[1]s znovu otvoril #%[2]d.
+issue.action.merge=@%[1]s zlúčil #%[2]d do %[3]s.
+issue.action.approve=@%[1]s schválil tento pull request.
+issue.action.reject=@%[1]s požadoval zmeny v tomto pull requeste.
+issue.action.review=@%[1]s okomentoval tento pull request.
+issue.action.review_dismissed=@%[1]s zamietol poslednú recenziu od %[2]s pre tento pull request.
+issue.action.ready_for_review=@%[1]s označil tento pull request ako pripravený na revíziu.
+issue.action.new=@%[1] vytvoril/a #%[2]d.
+issue.in_tree_path=V %s:
+
+release.new.subject=%s v %s vydané
+release.new.text=@%[1]s vydal/a %[2]s v %[3]s
+release.title=Názov: %s
+release.note=Poznámka:
+release.downloads=Sťahovania:
+release.download.zip=Zdrojový kód (ZIP)
+release.download.targz=Zdrojový kód (TAR.GZ)
+
+repo.transfer.subject_to=%s by chcel preniesť "%s" do %s
+repo.transfer.subject_to_you=%s by chcel preniesť "%s" k vám
+repo.transfer.to_you=vy
+repo.transfer.body=Ak to chcete prijať alebo odmietnuť, navštívte %s alebo to jednoducho ignorujte.
+
+repo.collaborator.added.subject=%s vás pridal do %s
+repo.collaborator.added.text=Boli ste pridaný ako spolupracovník repozitára:
+
+[modal]
+yes=Áno
+no=Nie
+modify=Aktualizovať
+
+[form]
+UserName=Používateľské meno
+RepoName=Názov repozitára
+Email=E-mailová adresa
+Password=Heslo
+Retype=Znova zadajte heslo
+SSHTitle=Názov SSH kľúča
+HttpsUrl=HTTPS URL
+PayloadUrl=URL nákladu
+TeamName=Názov tímu
+AuthName=Názov autorizácie
+AdminEmail=E-mail administrátora
+
+NewBranchName=Názov novej vetvy
+CommitSummary=Zhrnutie commitu
+CommitMessage=Správa ku commitu
+CommitChoice=Výber commitu
+TreeName=Cesta k súboru
+Content=Obsah
+
+SSPISeparatorReplacement=Oddeľovač
+SSPIDefaultLanguage=Predvolený jazyk
+
+require_error=` nemôže byť prázdne.`
+alpha_dash_error=` by mal obsahovať iba alfanumerické znaky, pomlčku ('-') a podčiarkovník ('_').`
+alpha_dash_dot_error=` by mal obsahovať iba alfanumerické znaky, pomlčku ('-'), podčiarkovník ('_') a bodku ('.').`
+git_ref_name_error=` musí byť správny názov odkazu Git.`
+size_error=` musí byť dĺžky %s.`
+min_size_error=` musí obsahovať minimálne %s znakov.`
+max_size_error=` musí obsahovať maximálne %s znakov.`
+email_error=` nie je platná e-mailová adresa.`
+url_error=`'%s' nieje platná URL.`
+include_error=` musí obsahovať podreťazec '%s'.`
+glob_pattern_error=` glob vzor je neplatný: %s.`
+regex_pattern_error=` regex vzor je neplatný: %s.`
+unknown_error=Neznáma chyba:
+captcha_incorrect=Overovací kód CAPTCHA je nesprávny.
+password_not_match=Heslá sa nezhodujú.
+lang_select_error=Zvoľte jazyk zo zoznamu.
+
+username_been_taken=Používateľské meno je už obsadené.
+username_change_not_local_user=Používatelia overovaní inak ako lokálne si nemôžu zmeniť svoje používateľské meno.
+repo_name_been_taken=Meno repozitára sa už používa.
+repository_force_private=Je aktivované "Iba súkromne": súkromné repozitáre nesmú byť zverejnené.
+repository_files_already_exist=Súbory pre tento repozitár už existujú. Kontaktujte správcu systému.
+repository_files_already_exist.adopt=Súbory pre tento repozitár už existujú dajú sa iba prijať.
+repository_files_already_exist.delete=Súbory pre tento repozitár už existujú. Musíte ich zmazať.
+repository_files_already_exist.adopt_or_delete=Súbory pre tento repozitár už existujú. Buď ich prijmite, alebo zmažte.
+visit_rate_limit=Dosiahnutý limit rýchlosti dotazov pri vzdialenom prístupe.
+2fa_auth_required=Vzdialený prístup vyžaduje dvojfaktorové overovanie.
+org_name_been_taken=Názov organizácie sa už používa.
+team_name_been_taken=Názov tímu sa už používa.
+team_no_units_error=Povoliť prístup aspoň do jednej sekcie repozitára.
+email_been_used=E-mailová adresa sa už používa.
+email_invalid=Táto e-mailová adresa je neplatná.
+openid_been_used=Adresa OpenID '%s' sa už používa.
+username_password_incorrect=Používateľské meno alebo heslo je nesprávne.
+password_complexity=Heslo nesplňuje požiadavky na zložitosť:
+password_lowercase_one=Aspoň jedno malé písmeno
+password_uppercase_one=Aspoň jedno veľké písmeno
+password_digit_one=Aspoň jedna číslica
+password_special_one=Aspoň jeden špeciálny znak (interpunkcia, zátvorky, úvodzovky, atď.)
+enterred_invalid_repo_name=Zadaný názov repozitára je nesprávny.
+enterred_invalid_org_name=Zadaný názov organizácie je nesprávny.
+enterred_invalid_owner_name=Nové meno vlastníka nie je platné.
+enterred_invalid_password=Zadané heslo je nesprávne.
+user_not_exist=Tento používateľ neexistuje.
+team_not_exist=Tento tím neexistuje.
+last_org_owner=Nemôžete odstrániť posledného používateľa z tímu 'vlastníkov'. Musí existovať aspoň jeden vlastník pre organizáciu.
+cannot_add_org_to_team=Organizácia nemôže byť pridaná ako člen tímu.
+
+invalid_ssh_key=Nie je možné overiť váš SSH kľúč: %s
+invalid_gpg_key=Nie je možné overiť váš GPG kľúč: %s
+invalid_ssh_principal=Neplatná identita: %s
+unable_verify_ssh_key=Nie je možné overiť SSH kľúč; znovu skontrolujte chyby.
+auth_failed=Overenie zlyhalo: %v
+
+still_own_repo=Váš účet je vlastníkom jedného alebo viacerých repozitárov; najskôr ich zmažte alebo preveďte.
+still_has_org=Váš účet je členom jednej alebo viacerých organizácií; najskôr ich opustite.
+still_own_packages=Váš účet je vlastníkom jedného alebo viacerých repozitárov; najskôr ich zmažte alebo preveďte.
+org_still_own_repo=Váš účet je stále vlastníkom jedného alebo viacerých repozitárov; najskôr ich zmažte alebo preveďte.
+org_still_own_packages=Váš organizácia je vlastníkom jedného alebo viacerých repozitárov; najskôr ich zmažte alebo preveďte.
+
+target_branch_not_exist=Cieľová vetva neexistuje.
+
+[user]
+change_avatar=Zmeniť svoj avatar…
+join_on=Pripojil sa dňa
+repositories=Repozitáre
+activity=Verejná aktivita
+followers=Sledujúci
+starred=Obľúbené repozitáre
+watched=Sledované repozitáre
+projects=Projekty
+following=Sledovaní
+follow=Sledovať
+unfollow=Zrušiť sledovanie
+heatmap.loading=Načítanie teplotnej mapy…
+user_bio=Životopis
+disabled_public_activity=Tento používateľ zákázal verejnú viditeľnosť aktivity.
+
+form.name_reserved=Toto používateľské meno '%s' je vyhradené.
+form.name_pattern_not_allowed=Tento vzor '%s' nie je povolený v mene používateľa.
+form.name_chars_not_allowed=Používateľské meno '%s' obsahuje neplatné znaky.
+
+[settings]
+profile=Profil
+account=Účet
+appearance=Vzhľad
+password=Heslo
+security=Zabezpečenie
+avatar=Avatar
+ssh_gpg_keys=SSH / GPG kľúče
+social=Sociálne účty
+applications=Aplikácie
+orgs=Spravovať organizácie
+repos=Repozitáre
+delete=Zmazať účet
+twofa=Dvojfaktorové overenie
+account_link=Prepojené účty
+organization=Organizácie
+uid=Uid
+webauthn=Bezpečnostné kľúče
+
+public_profile=Verejný profil
+biography_placeholder=Povedzte nám niečo o sebe
+profile_desc=Vaša e-mailová adresa bude použitá pre oznámenia a iné operácie.
+password_username_disabled=Externí používatelia nemôžu meniť svoje používateľské meno. Kontaktujte, prosím, svojho administrátora kvôli detailom.
+full_name=Celé meno
+website=Webová stránka
+location=Miesto
+update_theme=Aktualizovať tému
+update_profile=Aktualizovať profil
+update_language=Aktualizovať jazyk
+update_language_not_found=Jazyk '%s' nie je dostupný.
+update_language_success=Jazyk bol aktualizovaný.
+update_profile_success=Váš profil sa aktualizoval.
+change_username=Vaše používateľské meno bolo zmenené.
+change_username_prompt=Poznámka: zmeny používateľského mena zmenia tiež URL účtu.
+change_username_redirect_prompt=Staré používateľské meno bude presmerované až pokiaľ nebude znovu obsadené.
+continue=Pokračovať
+cancel=Zrušiť
+language=Jazyk
+ui=Motív
+hidden_comment_types=Skryté typy komentárov
+comment_type_group_reference=Referencia
+comment_type_group_label=Štítok
+comment_type_group_milestone=Míľnik
+comment_type_group_assignee=Príjemca
+comment_type_group_title=Názov
+comment_type_group_branch=Vetva
+comment_type_group_time_tracking=Sledovanie času
+comment_type_group_deadline=Uzávierka
+comment_type_group_dependency=Závislosť
+comment_type_group_lock=Stav zámku
+comment_type_group_review_request=Žiadosť o revíziu
+comment_type_group_pull_request_push=Pridané commity
+comment_type_group_project=Projekt
+comment_type_group_issue_ref=Referenčné číslo úkolu
+saved_successfully=Nastavenia úspešne uložené.
+privacy=Súkromie
+keep_activity_private=Skryť aktivitu z profilovej stránky
+keep_activity_private_popup=Nastaviť aktivitu viditeľnú iba pre vás a administrátorov
+
+lookup_avatar_by_mail=Vyhľadať avatar pomocou e-mailovej adresy
+federated_avatar_lookup=Vyhľadanie avatarov z verejných zdrojov
+enable_custom_avatar=Použiť užívateľský avatar
+choose_new_avatar=Vybrať nový avatar
+update_avatar=Aktualizovať avatar
+delete_current_avatar=Odstrániť aktuálny avatar
+uploaded_avatar_not_a_image=Nahraný súbor nieje obrázok.
+uploaded_avatar_is_too_big=Nahraný súbor prekročil maximálnu veľkosť.
+update_avatar_success=Váš avatar sa aktualizoval.
+update_user_avatar_success=Užívateľov avatar bol aktualizovaný.
+
+change_password=Aktualizovať heslo
+old_password=Aktuálne heslo
+new_password=Nové heslo
+retype_new_password=Znova zadajte nové heslo
+password_incorrect=Aktuálne heslo nie je správne.
+change_password_success=Vaše heslo bolo aktualizované. Od teraz sa prihlasujte novým heslom.
+password_change_disabled=Externe overovaní používatelia nemôžu aktualizovať svoje heslo prostredníctvom webového rozhrania Gitea.
+
+emails=E-mailové adresy
+manage_emails=Správa e-mailových adries
+manage_themes=Nastavenie predvolenej témy
+manage_openid=Správa OpenID adries
+email_desc=Vaša primárna e-mailová adresa bude použitá pre oznámenia a iné operácie.
+theme_desc=Toto bude vaša predvolená téma vzhľadu naprieč stránkou.
+primary=Primárny
+activated=Aktivovaný
+requires_activation=Vyžaduje aktiváciu
+primary_email=Nastaviť ako primárny
+activate_email=Poslať aktiváciu
+activations_pending=Čakajúca aktivácia
+delete_email=Odstrániť
+email_deletion=Vymazať e-mailovú adresu
+email_deletion_desc=E-mailová adresa a pridružené informácie budú z vášho účtu odstránené. Commity Gitu s touto e-mailovou adresou zostanú nezmenené. Pokračovať?
+email_deletion_success=E-mailová adresa bola odstránená.
+theme_update_success=Vaša téma bola aktualizovaná.
+theme_update_error=Vybraná téma vzhľadu neexistuje.
+openid_deletion=Odstrániť OpenID adresu
+openid_deletion_desc=Pokiaľ odstránite OpenID adresu, nebudete ju môcť použiť k prihláseniu. Pokračovať?
+openid_deletion_success=OpenID adresa bola odstránená.
+add_new_email=Pridať novú e-mailovú adresu
+add_new_openid=Pridať nové OpenID URI
+add_email=Pridať e-mailovú adresu
+add_openid=Pridať OpenID URI
+add_email_confirmation_sent=Na adresu %s bol odoslaný potvrdzovací e-mail. Skontrolujte si, prosím, vašu doručenú poštu počas najbližších %s pre potvrdenie vašej e-mailovej adresy.
+add_email_success=Bola pridaná nová e-mailová adresa.
+email_preference_set_success=Boli pridané preferencie pre e-mailovú adresu.
+add_openid_success=Bola pridaná nová adresa OpenID.
+keep_email_private=Skryť e-mailovú adresu
+keep_email_private_popup=Vaša e-mailová adresa bola skrytá pred ostatnými používateľmi.
+openid_desc=OpenID dovoľuje delegovať overovanie na externého poskytovateľa.
+
+manage_ssh_keys=Spravovať SSH kľúče
+manage_ssh_principals=Spravovať SSH certifikačné identity
+manage_gpg_keys=Správa GPG kľúčov
+add_key=Pridať kľúč
+ssh_desc=Tieto verejné SSH kľúče sú prepojené s vašim účtom. Zodpovedajúce súkromné kľúče umožnia plný prístup k vašim repozitárom.
+principal_desc=Tieto SSH certifikačné identity sú prepojené s vašim účtom a umožňujú plný prístup k vašim repozitárom.
+gpg_desc=Tieto verejné GPG kľúče sú prepojené s vašim účtom. Uchovajte vaše súkromné kľúče v bezpečí, pretože umožňujú overenie commitov.
+ssh_helper=Potrebujete pomoc? Pozrite sa do príručky GitHub-u ako vytvoriť svoje SSH kľúče alebo ako riešiť bežné problémy s ktorými sa môžete stretnúť pri používaní SSH.
+gpg_helper=Potrebujete pomoc? Pozrite sa do príručky GitHub-u o GPG.
+add_new_key=Pridať SSH kľúč
+add_new_gpg_key=Pridať GPG kľúč
+key_content_ssh_placeholder=Začína sa s 'ssh-ed25519', 'ssh-rsa', 'ecdsa-sha2-nistp256', 'ecdsa-sha2-nistp384', 'ecdsa-sha2-nistp521', 'sk-ecdsa-sha2-nistp256@openssh.com' alebo 'sk-ssh-ed25519@openssh.com'
+key_content_gpg_placeholder=Začína sa s '-----BEGIN PGP PUBLIC KEY BLOCK-----'
+add_new_principal=Pridať identitu
+ssh_key_been_used=Tento SSH kľúč bol na server už pridaný.
+ssh_key_name_used=SSH kľúč s rovnakým menom už vo vašom účte existuje.
+ssh_principal_been_used=Táto identita bola na server už pridaná.
+gpg_key_id_used=Verejný GPG kľúč s rovnakým ID už existuje.
+gpg_no_key_email_found=Tento GPG kľúč sa nezhoduje zo žiadnou aktivovanou e-mailovou adresou prepojenou s vašim účtom. Stále ho môžete pridať, ak podpíšete poskytnutý token.
+gpg_key_matched_identities=Zhodujúce sa identity:
+gpg_key_matched_identities_long=Vložené identity v tomto kľúči zodpovedajú nasledujúcim aktivovaným e-mailovým adresám tohoto používateľa. Commity zodpovedajúce týmto e-mailovým adresám je možné overiť pomocou tohoto kľúča.
+gpg_key_verified=Overený kľúč
+gpg_key_verified_long=Kľúč bol overený pomocou tokenu a môže byť použitý k overeniu commitov zhodujúcich sa s ľubovoľnou vašou aktivovalo e-mailovou adresou pre tohoto užívateľa naviac k akejkoľvek odpovedajúcej identite tohoto kľúča.
+gpg_key_verify=Overiť
+gpg_invalid_token_signature=Zadaný GPG kľúč, podpis a token sa nezhodujú alebo je token zastaralý.
+gpg_token_required=Musíte zadať podpis pre nižšie uvedený token
+gpg_token=Token
+gpg_token_help=Podpis môžete vygenerovať pomocou:
+gpg_token_code=echo "%s" | gpg -a --default-key %s --detach-sig
+gpg_token_signature=Zakódovaný (ASCII) podpis GPG
+key_signature_gpg_placeholder=Začína s '-----BEGIN PGP SIGNATURE-----'
+verify_gpg_key_success=GPG kľúč '%s' bol overený.
+ssh_key_verified=Overený kľúč
+ssh_key_verified_long=Kľúč bol overený tokenom a možno ho použiť na overenie commitov zhodujúcich sa so všetkými aktivovanými e-mailovými adresami tohto používateľa.
+ssh_key_verify=Overiť
+ssh_invalid_token_signature=Zadaný SSH kľúč, podpis alebo token sa nezhodujú alebo je token zastaralý.
+ssh_token_required=Musíte zadať podpis pre nižšie uvedený token
+ssh_token=Token
+ssh_token_help=Podpis môžete vygenerovať pomocou:
+ssh_token_signature=Zakódovaný (ASCII) podpis SSH
+key_signature_ssh_placeholder=Začína s '-----BEGIN SSH SIGNATURE-----'
+verify_ssh_key_success=SSH kľúč '%s' bol overený.
+subkeys=Podkľúče
+key_id=ID kľúča
+key_name=Názov kľúča
+key_content=Obsah
+principal_content=Obsah
+add_key_success=SSH kľúč „%s“ bol pridaný.
+add_gpg_key_success=GPG kľúč „%s“ bol pridaný.
+add_principal_success=Bol pridaný SSH certifikát identity '%s'.
+delete_key=Odstrániť
+ssh_key_deletion=Odstrániť SSH kľúč
+gpg_key_deletion=Odstrániť GPG kľúč
+ssh_principal_deletion=Vymazať SSH certifikačnú identitu
+ssh_key_deletion_desc=Odstránenie SSH kľúča zruší jeho prístup k vašemu účtu. Pokračovať?
+gpg_key_deletion_desc=Odstránením GPG kľúča zneplatníte overenie commitov, ktoré sú ním podpísané. Pokračovať?
+ssh_principal_deletion_desc=Odstránenie SSH certifikátu identity zruší jeho prístup k vašemu účtu. Pokračovať?
+ssh_key_deletion_success=SSH kľúč bol odstránený.
+gpg_key_deletion_success=GPG kľúč bol odstránený.
+ssh_principal_deletion_success=Identita bola odstránená.
+add_on=Pridané
+valid_until=Platný do
+valid_forever=Platný navždy
+last_used=Naposledy použité
+no_activity=Žiadna nedávna aktivita
+can_read_info=Čítanie
+can_write_info=Zápis
+key_state_desc=Tento kľúč bol použitý behom posledných 7 dní
+token_state_desc=Tento token bol použitý behom posledných 7 dní
+principal_state_desc=Táto identita bola použitá behom posledných 7 dní
+show_openid=Zobraziť v profile
+hide_openid=Odstrániť z profilu
+ssh_disabled=SSH zakázané
+ssh_externally_managed=Tento kľúč SSH je pre tohto používateľa spravovaný externe
+manage_social=Spravovať súvisiace sociálne účty
+social_desc=Tieto sociálne účty sú prepojené s vaším účtom Gitea. Uistite sa, že ich všetky poznáte, pretože sa dajú použiť na prihlásenie do vášho účtu Gitea.
+unbind=Odpojiť
+unbind_success=Sociálny účet bol odpojený od vášho účtu Gitea.
+
+manage_access_token=Správa prístupových tokenov
+generate_new_token=Vygenerovať nový token
+tokens_desc=Tieto tokeny poskytujú prístup k vášmu účtu pomocou rozhrania Gitea API.
+new_token_desc=Aplikácie používajúce token majú úplný prístup k vášmu účtu.
+token_name=Názov tokenu
+generate_token=Vygenerovať token
+generate_token_success=Váš nový token bol vygenerovaný. Skopírujte ho teraz, pretože sa už nebude zobrazovať.
+generate_token_name_duplicate=%s už bol použitý ako názov aplikácie. Použite nový.
+delete_token=Vymazať
+access_token_deletion=Odstrániť prístupový token
+access_token_deletion_cancel_action=Zrušiť
+access_token_deletion_confirm_action=Vymazať
+access_token_deletion_desc=Odstránením tokenu zrušíte prístup k vášmu účtu pre aplikácie, ktoré ho používajú. To nie je možné vrátiť späť. Ďalej?
+delete_token_success=Token bol odstránený. Aplikácie, ktoré ho používajú, už nemajú prístup k vášmu účtu.
+
+manage_oauth2_applications=Správa OAuth2 aplikácií
+edit_oauth2_application=Upraviť aplikáciu OAuth2
+oauth2_applications_desc=OAuth2 aplikácie umožňujú aplikáciám tretej strany bezpečne autentifikovať používateľov v tejto inštancii Gitea.
+remove_oauth2_application=Odstrániť aplikáciu OAuth2
+remove_oauth2_application_desc=Odstránenie aplikácie OAuth2 zruší prístup všetkým podpísaným prístupovým tokenom. Ďalej?
+remove_oauth2_application_success=Aplikácia bola odstránená.
+create_oauth2_application=Vytvoriť novú aplikáciu OAuth2
+create_oauth2_application_button=Vytvoriť aplikáciu
+create_oauth2_application_success=Úspešne ste vytvorili novú aplikáciu OAuth2.
+update_oauth2_application_success=Úspešne ste aktualizovali aplikáciu OAuth2.
+oauth2_application_name=Názov aplikácie
+oauth2_select_type=Ktorý typ aplikácie sa hodí?
+oauth2_redirect_uri=Presmerovanie URI
+save_application=Uložiť
+oauth2_client_id=ID klienta
+oauth2_client_secret=Tajný klientsky kľúč
+oauth2_regenerate_secret=Obnoviť tajný kľúč
+oauth2_regenerate_secret_hint=Stratili ste svoj tajný kľúč?
+oauth2_client_secret_hint=Ak znova navštívite túto stránku, tajný kľúč nebude viditeľný. Prosím, uložte si svoj kľúč.
+oauth2_application_edit=Upraviť
+oauth2_application_create_description=Aplikácie OAuth2 poskytujú aplikáciám tretích strán prístup k používateľským účtom v tejto inštancii.
+oauth2_application_remove_description=Odstránenie aplikácie OAuth2 zabráni v prístupe autorizovaným používateľom v tejto inštancii. Ďalej?
+
+authorized_oauth2_applications=Autorizované aplikácie OAuth2
+authorized_oauth2_applications_description=Týmto aplikáciám tretích strán ste udelili prístup k vášmu osobnému účtu Gitea. Zrušte prístup pre aplikácie, ktoré už nie sú potrebné.
+revoke_key=Odvolať
+revoke_oauth2_grant=Odstrániť prístup
+revoke_oauth2_grant_description=Zrušenie prístupu tejto aplikáciu tretej strany zabráni tejto aplikácii v prístupe k vašim údajom. Ste si istý?
+revoke_oauth2_grant_success=Úspešne ste zrušili prístup.
+
+twofa_desc=Dvojstupňové overenie pridá ďalšiu vrstvu bezpečnosti k vášmu účtu.
+twofa_is_enrolled=Váš účet je momentálne používa dvojfaktorovú autentifikáciu.
+twofa_not_enrolled=Váš účet momentálne nepoužíva dvojfaktorovú autentifikáciu.
+twofa_disable=Vypnúť dvojfaktorovú autentifikáciu
+twofa_scratch_token_regenerate=Obnoviť token
+twofa_scratch_token_regenerated=Váš token je teraz %s. Uložte ho na bezpečnom mieste.
+twofa_enroll=Povoliť dvojfaktorové overovanie
+twofa_disable_note=V prípade potreby môžete zakázať dvojfaktorové overenie.
+twofa_disable_desc=Vypnutím dvojfaktorovej autentifikácie bude váš účet menej bezpečný. Ďalej?
+regenerate_scratch_token_desc=Ak ste svoj token nesprávne umiestnili alebo ste ho už použili na prihlásenie, môžete ho resetovať tu.
+twofa_disabled=Dvojfaktorové overovanie bolo vypnuté.
+scan_this_image=Naskenujte tento obrázok pomocou overovacej aplikácie:
+or_enter_secret=Alebo zadajte tajný kód: %s
+then_enter_passcode=A zadajte prístupový kód zobrazený v aplikácii:
+passcode_invalid=Prístupový kód je nesprávny. Skúste to znova.
+twofa_enrolled=Váš účet bol zaregistrovaný do dvojfaktorovej autentifikácie. Uložte si token (%s) na bezpečnom mieste, pretože sa zobrazuje iba raz!
+twofa_failed_get_secret=Nepodarilo sa získať tajomstvo.
+
+webauthn_register_key=Pridať bezpečnostný kľúč
+webauthn_nickname=Prezývka
+webauthn_delete_key=Odstrániť bezpečnostný kľúč
+webauthn_delete_key_desc=Ak odstránite bezpečnostný kľúč, už sa s ním nebudete môcť prihlásiť. Ďalej?
+
+manage_account_links=Spravovať prepojené kontá
+manage_account_links_desc=Tieto externé účty sú prepojené s vaším účtom Gitea.
+link_account=Pripojiť účet
+
+orgs_none=Nieste členom žiadnej organizácie.
+repos_none=Nevlastníte žiadne repozitáre
+
+delete_account=Odstrániť môj účet
+confirm_delete_account=Potvrdiť odstránenie
+delete_account_title=Odstrániť používateľský účet
+delete_account_desc=Naozaj chcete natrvalo odstrániť tento účet?
+
+email_notifications.enable=Povoliť e-mailové upozornenia
+email_notifications.onmention=E-mail iba pri zmienke
+email_notifications.disable=Vypnúť e-mailové upozornenia
+email_notifications.submit=Nastaviť predvoľby e-mailu
+
+visibility=Viditeľnosť používateľa
+visibility.public=Verejný
+visibility.public_tooltip=Viditeľné pre všetkých užívateľov
+visibility.limited=Obmedzený
+visibility.private=Súkromný
+
+[repo]
+new_repo_helper=Repozitár obsahuje všetky súbory projektu vrátane histórie revízií. Máte ho už inde? Migrovať repozitár.
+owner=Vlastník
+owner_helper=Niektoré organizácie sa nemusia zobraziť v rozbaľovacej ponuke z dôvodu maximálneho limitu počtu repozitárov.
+repo_name=Názov repozitára
+repo_name_helper=Dobrý názov repozitára sa zvyčajne skladá z krátkych, jedinečných a ľahko zapamätateľných kľúčových slov.
+repo_size=Veľkosť repozitára
+template=Šablóna
+template_select=Vyberte šablónu.
+template_helper=Z repozitára vytvoriť šablónu
+template_description=Šablóny repozitárov umožňujú používateľom vytvárať nové repozitáre s rovnakou štruktúrou, súbormi a voliteľnými nastaveniami.
+visibility=Viditeľnosť
+visibility_description=Vidieť ho bude iba vlastník alebo členovia organizácie, ak majú práva.
+visibility_helper=Nastaviť repozitár ako súkromný
+visibility_helper_forced=Váš správca vynucuje že nové repozitáre musia byť súkromné.
+visibility_fork_helper=(Zmena ovplyvní všetky forky.)
+fork_repo=Forknúť repozitár
+fork_from=Forknúť z
+already_forked=Už ste forkli %s
+fork_to_different_account=Fork na iný účet
+fork_visibility_helper=Viditeľnosť forknutého repozitára nemožno zmeniť.
+download_zip=Stiahnuť ZIP
+download_tar=Stiahnuť TAR.GZ
+download_bundle=Stiahnuť BUNDLE
+generate_repo=Generovať repozitár
+generate_from=Generovať z
+repo_desc=Popis
+repo_lang=Jazyk
+repo_gitignore_helper=Vyberte .gitignore šablóny.
+repo_gitignore_helper_desc=Zo zoznamu šablón pre bežné jazyky vyberte, ktoré súbory sa nemajú sledovať. Typické artefakty generované nástrojmi na vytváranie jednotlivých jazykov sú štandardne zahrnuté v .gitignore.
+issue_labels=Štítky úkolov
+issue_labels_helper=Vyberte skupinu štítkov úkolov.
+license=Licencia
+license_helper=Vybrať súbor s licenciou.
+license_helper_desc=Licencia určuje, čo ostatní môžu a nemôžu robiť s vaším kódom. Nie ste si istí, ktorý z nich je vhodný pre váš projekt? Pozrite si výber licencie.
+readme=README
+readme_helper=Vyberte šablónu súboru README.
+readme_helper_desc=Toto je miesto, kde môžete napísať úplný popis vášho projektu.
+auto_init=Inicializovať repozitár (pridá súbory .gitignore, License a README)
+trust_model_helper=Vyberte model dôveryhodnosti na overenie podpisu. Možnosti sú:
+trust_model_helper_collaborator=Spolupracovník: Dôverovať podpisom spolupracovníkov
+trust_model_helper_committer=Prispievateľ: Dôverovať podpisom, ktoré zodpovedajú prispievateľovi
+trust_model_helper_collaborator_committer=Spolupracovník+Prispievateľ: Dôverovať podpisom spolupracovníkov, ktorí zodpovedajú prispievateľovi
+trust_model_helper_default=Predvolené: Pre túto inštaláciu použiť predvolený model dôvery
+create_repo=Vytvoriť repozitár
+default_branch=Východzia vetva
+default_branch_helper=Predvolená vetva je základná vetva pre pull requesty a commity.
+mirror_prune=Vyčistiť
+mirror_interval=Interval zrkadlenia (platné sú 'h', 'm', 's'). 0 na vypnutie periodickej synchronizácie. (Minimálny interval: %s)
+mirror_interval_invalid=Interval zrkadlenia nie je platný.
+mirror_sync_on_commit=Synchronizovať, keď sú odovzdané commity
+mirror_address=Klonovať z URL
+mirror_lfs_endpoint=Koncový bod LFS
+mirror_lfs_endpoint_desc=Synchronizácia sa pokúsi použiť klonovaciu adresu URL na určenie servera LFS. Môžete tiež zadať vlastný koncový bod, ak sú dáta repozitára LFS uložené niekde inde.
+mirror_last_synced=Posledná synchronizácia
+mirror_password_placeholder=(Nezmenené)
+mirror_password_blank_placeholder=(Nenastavené)
+mirror_password_help=Zmenou používateľského mena vymažete uložené heslo.
+watchers=Sledujúci
+stargazers=Hviezdičky
+forks=Forky
+pick_reaction=Vyberte si reakciu
+reactions_more=a %d ďalších
+unit_disabled=Správca stránky zakázal túto sekciu repozitára.
+language_other=Iný
+adopt_search=Ak chcete vyhľadať neprijaté úložiská, zadajte používateľské meno... (pre vyhľadanie všetkých nechajte prázdne)
+adopt_preexisting_label=Prijať súbory
+adopt_preexisting=Prijať už existujúce súbory
+adopt_preexisting_content=Vytvoriť repozitár z %s
+adopt_preexisting_success=Prijaté súbory a vytvorený repozitár z %s
+delete_preexisting_label=Vymazať
+delete_preexisting=Vymazať už existujúce súbory
+delete_preexisting_content=Vymazať súbory v %s
+delete_preexisting_success=Vymazané neprijaté súbory v %s
+
+transfer.accept=Prijať prevod
+transfer.accept_desc=Previesť do "%s"
+transfer.reject=Odmietnuť prevod
+transfer.reject_desc=Zrušiť prevod do "%s"
+transfer.no_permission_to_accept=Nemáte povolenie na prijatie
+
+desc.private=Súkromný
+desc.public=Verejný
+desc.private_template=Súkromná šablóna
+desc.public_template=Šablóna
+desc.internal=Interný
+desc.internal_template=Interná šablóna
+desc.archived=Archivovaný
+
+template.items=Položky šablóny
+template.git_hooks=Git hooky
+template.webhooks=Webhooky
+template.issue_labels=Štítky úkolov
+template.invalid=Vyberte šablónu repozitára
+
+archive.issue.nocomment=Tento repozitár je archivovaný. Nemôžete komentovať úkoly.
+
+form.reach_limit_of_creation_1=Už ste dosiahli svoj limit pre %d repozitár.
+form.reach_limit_of_creation_n=Už ste dosiahli svoj limit pre %d repozitáre.
+form.name_reserved=Repozitár s názvom '%s' je rezervovaný.
+form.name_pattern_not_allowed=Vzor '%s' nie je povolený v mene repozitára.
+
+migrate_options_mirror_helper=Tento repozitár bude zrkadlom
+migrate_options_lfs_endpoint.description=Migrácia sa pokúsi použiť váš vzdialený Git na určenie servera LFS. Môžete tiež zadať vlastný koncový bod, ak sú dáta repozitára LFS uložené niekde inde.
+migrate_items_issues=Úkoly
+migrate_items_pullrequests=Pull requesty
+migrate_repo=Migrovať repozitár
+migrate.clone_address_desc=HTTP(S) alebo Git 'clone' URL pre klonovanie existujúceho repozitára
+migrate.github_token_desc=Sem môžete vložiť jeden alebo viac tokenov oddelených čiarkami, aby sa migrácia zrýchlila z dôvodu limitu rýchlosti rozhrania GitHub API. UPOZORNENIE: Zneužitie tejto funkcie môže porušiť zásady poskytovateľa služieb a viesť k zablokovaniu účtu.
+migrate.permission_denied=Nemáte povolené importovať miestne repozitáre.
+migrate.migrate_items_options=Na migráciu ďalších položiek je potrebný prístupový token
+migrate.migrating_failed.error=Chyba: %s
+migrate.migrating_failed_no_addr=Migrácia zlyhala.
+migrate.git.description=Migrujte repozitár iba z akejkoľvek služby Git.
+migrate.migrating_issues=Migrovanie úkolov
+
+forked_from=forknuté z
+fork_from_self=Repozitár, ktorý vlastníte, nemôžete forknúť.
+fork_guest_user=Prihláste sa, aby ste forkli repozitár.
+watch_guest_user=Pre sledovanie tohoto repozitára sa prihláste.
+unwatch=Už nesledovať
+watch=Sledovať
+unstar=Zrušiť hviezdičku
+star=Hviezdička
+fork=Forknúť
+download_archive=Stiahnuť repozitár
+
+no_desc=Bez popisu
+quick_guide=Rýchly sprievodca
+clone_this_repo=Klonovať tento repozitár
+create_new_repo_command=Vytvoriť nový repozitár v príkazovom riadku
+push_exist_repo=Odoslanie existujúceho repozitára z príkazového riadku
+empty_message=Tento repozitár ešte nemá obsah.
+broken_message=Údaje Git, ktoré sú základom tohto úložiska, sa nedajú prečítať. Kontaktujte správcu tejto inštancie alebo odstráňte toto úložisko.
+
+code=Zdrojový kód
+code.desc=Prístup k zdrojovému kódu, súborom, commitom a vetvám.
+branch=Vetva
+tree=Strom
+clear_ref=`Vymazať aktuálnu referenciu`.
+filter_branch_and_tag=Filter vetvy alebo tagu
+find_tag=Hľadať tag
+branches=Vetvy
+tags=Tagy
+issues=Úkoly
+pulls=Pull requesty
+project_board=Projekty
+
+commits=Commitov
+commit=Commit
+
+escape_control_characters=Ošetriť
+file_copy_permalink=Kopírovať trvalý odkaz
+view_git_blame=Zobraziť Git Blame
+video_not_supported_in_browser=Váš prehliadač nepodporuje HTML5 tag 'video'.
+audio_not_supported_in_browser=Váš prehliadač nepodporuje HTML5 tag 'audio'.
+stored_lfs=Uložené pomocou Git LFS
+symbolic_link=Symbolický odkaz
+commit_graph=Graf commitov
+
+editor.preview_changes=Náhľad zmien
+editor.edit_this_file=Upraviť súbor
+editor.fork_before_edit=Musíte forknúť tento repozitár pre vytvorenie alebo navrhnutie zmeny tohoto súboru.
+editor.delete_this_file=Vymazať súbor
+editor.or=alebo
+editor.cancel_lower=Zrušiť
+editor.commit_signed_changes=Odoslať podpísané zmeny
+editor.commit_changes=Odoslať zmeny
+editor.add=Pridať '%s'
+editor.update=Aktualizovať '%s'
+editor.delete=Vymazať '%s'
+editor.patch=Použiť patch
+editor.commit_directly_to_this_branch=Odoslať zmeny revízie priamo do vetvy %s.
+editor.cancel=Zrušiť
+editor.commit_empty_file_header=Odoslať prázdny súbor
+editor.commit_empty_file_text=Súbor, ktorý sa chystáte odoslať, je prázdny. Pokračovať?
+editor.cannot_commit_to_protected_branch=Nedá sa vytvoriť commit v chránenej vetve '%s'.
+editor.no_commit_to_branch=Nedá sa odoslať priamo do vetvy, pretože:
+editor.require_signed_commit=Vetva vyžaduje podpísaný commit
+
+commits.commits=Commity
+commits.no_commits=Žiadne spoločné commity. '%s' a '%s' majú úplne odlišnú históriu.
+commits.search=Hľadanie commitov…
+commits.search.tooltip=Môžete predradiť kľúčové slová ako „author:“, „committer:“, „after:“ nebo „before:“, napr. „pripojil author:Alice before:2019-04-01“.
+commits.find=Hľadať
+commits.search_all=Všetky vetvy
+commits.author=Autor
+commits.message=Správa
+commits.date=Dátum
+commits.older=Staršie
+commits.newer=Novšie
+commits.signed_by=Podpísané
+commits.signed_by_untrusted_user=Podpísané nedôveryhodným užívateľom
+commits.signed_by_untrusted_user_unmatched=Podpísané nedôveryhodným užívateľom, ktorý sa nezhoduje s prispievateľom
+commits.gpg_key_id=ID GPG kľúča
+commits.ssh_key_fingerprint=odtlačok SSH kľúča
+
+commit.actions=Akcie
+commit.revert=Vrátiť
+commit.revert-header=Vrátiť: %s
+commit.revert-content=Vyberte vetvu pre návrat na:
+commit.cherry-pick=Cherry-pick
+commit.cherry-pick-header=Cherry-pick: %s
+commit.cherry-pick-content=Vyberte vetvu pre cherry-pick na:
+
+ext_issues=Prístup k externým úkolom
+ext_issues.desc=Odkaz na externé sledovanie úkolov.
+
+projects=Projekty
+projects.title=Názov
+projects.new=Nový projekt
+projects.deletion=Vymazať projekt
+projects.edit=Upraviť projekty
+projects.modify=Aktualizovať projekt
+projects.type.none=Žiadny
+projects.board.new_submit=Poslať
+projects.open=Otvoriť
+projects.close=Zavrieť
+projects.board.assigned_to=Priradené k
+
+issues.filter_reviewers=Filtrovať revidentov
+issues.new=Nový úkol
+issues.new.no_reviewers=Žiadni revidenti
+issues.new.add_reviewer_title=Požiadať o revíziu
+issues.choose.blank_about=Vytvoriť úkol z predvolenej šablóny.
+issues.create=Vytvoriť úkol
+issues.filter_type.all_issues=Všetky úkoly
+issues.filter_type.review_requested=Požiadané o revíziu
+issues.filter_sort.mostforks=Najviac forkov
+issues.filter_sort.fewestforks=Najmenej forkov
+issues.context.reference_issue=Odkázať v novom úkole
+issues.closed_at=`uzavrel/a tento úkol %[2]s`
+issues.reopened_at=`znovuotvoril/a tento úkol %[2]s`
+issues.commit_ref_at=`odkázal na tento úkol z commitu %[2]s`
+issues.ref_issue_from=`odkazoval/a na tento úkol %[4]s %[2]s`
+issues.ref_closing_from=`odkazoval/a na pull request %[4]s, ktorý uzavrie tento úkol %[2]s`
+issues.ref_reopening_from=`odkazoval/a na pull request %[4]s, ktorý znovu otvorí tento úkol %[2]s`
+issues.ref_closed_from=`uzavrel/a tento úkol %[4]s %[2]s`
+issues.ref_reopened_from=`znovu otvoril/a tento úkol %[4]s %[2]s`
+issues.owner=Vlastník
+issues.re_request_review=Znovu požiadať o revíziu
+issues.is_stale=Od tejto kontroly došlo k zmenám v tomto pull requeste
+issues.remove_request_review=Odstrániť žiadosť o revíziu
+issues.remove_request_review_block=Nie je možné odstrániť žiadosť o revíziu
+issues.dismiss_review=Zamietnuť revíziu
+issues.dismiss_review_warning=Naozaj chcete zrušiť túto revíziu?
+issues.cancel=Zrušiť
+issues.label_open_issues=%d otvorených úkolov
+issues.label_deletion_desc=Odstránenie štítka ho odstráni zo všetkých úkolov. Ďalej?
+issues.lock.unknown_reason=Nie je možné uzamknúť úkol bez dôvodu.
+issues.lock_duplicate=Úkol nie je možné uzamknúť dvakrát.
+issues.unlock_error=Nie je možné odomknúť úkol, ktorý nie je uzamknutý.
+issues.lock.notice_1=- Ostatní používatelia nemôžu komentovať tento úkol.
+issues.lock.notice_3=- Tento úkol môžete v budúcnosti kedykoľvek znova odomknúť.
+issues.unlock.notice_1=- Všetci budú môcť znovu komentovať tento úkol.
+issues.unlock.notice_2=- Tento úkol môžete v budúcnosti kedykoľvek znova zamknúť.
+issues.lock.title=Uzamknúť konverzáciu o tomto úkole.
+issues.unlock.title=Odomknúť konverzáciu o tomto úkole.
+issues.comment_on_locked=Nemôžete komentovať zamknutý úkol.
+issues.delete.title=Vymazať tento úkol?
+issues.delete.text=Naozaj chcete odstrániť tento úkol? (Týmto natrvalo odstránite všetok obsah. Ak ho chcete ponechať archivovaný, zvážte radšej jeho zatvorenie.)
+issues.tracker_auto_close=Po vyriešení tohto úkolu sa časovač automaticky zastaví
+issues.tracking_already_started=`Už ste spustili sledovanie času v inom úkole!`
+issues.cancel_tracking=Zahodiť
+issues.add_time_cancel=Zrušiť
+issues.push_commit_1=pridal/a %d commit %s
+issues.push_commits_n=pridal/a %d commity %s
+issues.due_date_not_writer=Potrebujete práva na zápis do repozitára pre úpravy termínu dokončenia úkolu.
+issues.dependency.cancel=Zrušiť
+issues.dependency.pr_closing_blockedby=Zatvorenie tohto pull requestu je blokované nasledujúcimi úkolmi
+issues.dependency.issue_closing_blockedby=Zatvorenie tohto úkolu je blokované nasledujúcimi úkolmi
+issues.dependency.issue_close_blocks=Tento úkol blokuje uzavretie nasledujúcich úkolov
+issues.dependency.pr_close_blocks=Tento pull request blokuje uzavretie nasledujúcich úkolov
+issues.dependency.issue_close_blocked=Pred zatvorením tohto úkolu musíte zatvoriť všetky úkoly, ktoré blokujú tento úkol.
+issues.review.comment=revidoval %s
+issues.review.dismissed=zamietol revíziu od %s %s
+issues.review.wait=bol požiadaný o revidovanie %s
+issues.review.add_review_request=požiadal o revidovanie od %s %s
+issues.review.remove_review_request=odstránil žiadosť o revidovanie na %s %s
+issues.review.remove_review_request_self=odmietol revidovať %s
+issues.review.review=Revízia
+issues.review.reviewers=Revidenti
+
+
+pulls.desc=Povoliť pull requesty a revízie kódu.
+pulls.tab_commits=Commity
+pulls.data_broken=Tento pull request je nefunkčný z dôvodu chýbajúcich informácií o forku.
+pulls.blocked_by_rejection=Tento pull request obsahuje zmeny požadované oficiálnym recenzentom.
+pulls.blocked_by_official_review_requests=Tento pull request obsahuje oficiálne žiadosti o revíziu.
+pulls.waiting_count_1=%d čakajúca revízia
+pulls.waiting_count_n=%d čakajúcich revízií
+pulls.wrong_commit_id=ID commitu musí byť ID commitu v cieľovej vetve
+
+pulls.no_merge_not_ready=Tento pull request nie je pripravený na merge, skontrolujte stav revízie a kontroly stavu.
+pulls.rebase_merge_commit_pull_request=Rebase a potom vytvoriť zlučovací commit
+pulls.merge_commit_id=ID zlučovacieho commitu
+
+; %[2]s
%[3]s
+
+
+
+
+
+milestones.cancel=Zrušiť
+
+
+
+
+activity.git_stats_commit_1=%d commit
+activity.git_stats_commit_n=%d commity
+
+
+settings.collaboration.owner=Vlastník
+settings.hooks=Webhooky
+settings.githooks=Git hooky
+settings.mirror_settings.mirrored_repository=Zrkadlený repozitár
+settings.pulls.allow_merge_commits=Povoliť Commit Merging
+settings.pulls.allow_rebase_merge_commit=Povoliť Rebase s explicitnými potvrdeniami commitov (--no-ff)
+
+
+settings.pulls.allow_squash_commits=Povoliť Squash pre merge commity
+settings.admin_enable_close_issues_via_commit_in_any_branch=Zavrieť úkol pomocou commitu v inej ako východzej vetve
+settings.new_owner_has_same_repo=Nový vlastník už repozitár s rovnakým názvom má. Vyberte, prosím, iné meno.
+settings.convert_fork=Konvertovať na bežný repozitár
+settings.convert_fork_desc=Tento fork môžete previesť na bežný repozitár. To nie je možné vrátiť späť.
+settings.convert_fork_notices_1=Táto operácia skonvertuje fork na bežný repozitár a nedá sa vrátiť späť.
+settings.convert_fork_confirm=Konvertovať repozitár
+settings.convert_fork_succeed=Fork bol prevedený na bežný repozitár.
+settings.transfer=Previesť vlastníctvo
+settings.transfer_notices_1=- Ak prenesiete repozitár na užívateľa, stratíte k nemu prístup.
+settings.transfer_notices_2=- Prístup k repozitáru si ponecháte, ak ho prevediete na organizáciu, ktorú (spolu)vlastníte.
+settings.transfer_owner=Nový vlastník
+settings.transfer_started=Tento repozitár bol označený na prenos a čaká na potvrdenie od "%s"
+settings.transfer_succeed=Repozitár bol prenesený.
+settings.trust_model.collaborator.desc=Platné podpisy spolupracovníkov tohto úložiska budú označené ako "dôveryhodné" - (bez ohľadu na to, či sa zhodujú s prispievateľom alebo nie). V opačnom prípade budú platné podpisy označené ako „nedôveryhodné“, ak sa podpis zhoduje s prispievateľom, a „nezhodujúce sa“, ak nie.
+settings.trust_model.committer=Prispievateľ
+settings.trust_model.committer.long=Prispievateľ: Dôverovať podpisom, ktoré sa zhodujú s prispievateľmi (toto sa zhoduje s GitHubom a prinúti Gitea podpísané príkazy, aby mali Gitea ako prispievateľa)
+settings.trust_model.collaboratorcommitter=Spolupracovník+Prispievateľ
+settings.trust_model.collaboratorcommitter.long=Spolupracovník+Prispievateľ: Dôverujte podpisom spolupracovníkov, ktorí zodpovedajú prispievateľovi
+settings.trust_model.collaboratorcommitter.desc=Platné podpisy spolupracovníkov tohto repozitára budú označené ako „dôveryhodné“, ak sa zhodujú s prispievateľom. V opačnom prípade budú platné podpisy označené ako „nedôveryhodné“, ak sa podpis zhoduje s prispievateľom, a v opačnom prípade budú „nezhodujúce sa“. To prinúti Giteu, aby bola označená ako autor na podpísaných odovzdaniach so skutočným autorom označeným ako Co-Authored-By: and Co-Committed-By: na konci commitu. Predvolený kľúč Gitea sa musí zhodovať s používateľom v databáze.
+settings.wiki_delete_desc=Odstránenie údajov wiki je trvalé a nemožno ho vrátiť späť.
+settings.wiki_delete_notices_1=- Natrvalo odstráni a zakáže wiki pre %s.
+settings.wiki_deletion_success=Údaje wiki boli vymazané.
+settings.delete=Odstrániť tento repozitár
+settings.delete_desc=Odstránenie repozitára je trvalé a nemožno ho vrátiť späť.
+settings.delete_notices_2=- Táto operácia natrvalo odstráni repozitár %s vrátane kódu, úkolov, komentárov, wiki a nastavení spolupracovníkov.
+settings.delete_notices_fork_1=- Forky tohto repozitára sa po vymazaní stanú nezávislými.
+settings.deletion_success=Repozitár bol vymazaný.
+settings.update_settings_success=Nastavenia repozitára boli aktualizované.
+settings.confirm_delete=Vymazať repozitár
+settings.add_collaborator_duplicate=Spolupracovník je už pridaný k tomuto repozitáru.
+settings.collaborator_deletion_desc=Odstránenie spolupracovníka zruší jeho prístup k tomuto repozitáru. Ďalej?
+settings.change_team_access_not_allowed=Zmena prístupu tímu k repozitáru bola obmedzená na vlastníka organizácie
+settings.team_not_in_organization=Tím nie je v rovnakej organizácii ako repozitár
+settings.add_team_duplicate=Tím už má repozitár
+settings.add_team_success=Tím má teraz prístup k repozitáru.
+settings.change_team_permission_tip=Oprávnenia tímu sa nastavujú na stránke s nastaveniami tímu a nedajú sa zmeniť pre jednotlivé repozitáre
+settings.delete_team_tip=Tento tím má prístup ku všetkým repozitárom a nemožno ho odstrániť
+settings.add_webhook=Pridať webhook
+settings.add_webhook.invalid_channel_name=Názov kanála webhooku nemôže byť prázdny a nemôže obsahovať iba znak #.
+settings.hooks_desc=Webhooky automaticky odosielajú požiadavky HTTP POST na server, keď sa spustia určité udalosti Gitea. Prečítajte si viac v sprievodcovi webhookmi.
+settings.webhook_deletion=Odstrániť webhook
+settings.webhook_deletion_desc=Odstránením webhooku sa vymažú jeho nastavenia a história doručovania. Ďalej?
+settings.webhook_deletion_success=Webhook bol odstránený.
+settings.webhook.test_delivery_desc=Otestujte tento webhook pomocou testovacej udalosti.
+settings.webhook.replay.description=Zopakujte tento webhook.
+settings.add_webhook_desc=Gitea odošle požiadavky POST
so špecifikovaným typom obsahu na cieľovú adresu URL. Prečítajte si viac v sprievodcovi webhookmi.
+settings.event_header_repository=Udalosti repozitára
+settings.event_fork=Fork
+settings.event_fork_desc=Repozitár forknutý
+settings.event_repository=Repozitár
+settings.event_repository_desc=Repozitár vytvorený alebo odstránený.
+settings.event_pull_request_review=Pull request bol revidovaný
+settings.event_pull_request_review_desc=Pull request schválený, odmietnutý alebo má revízne poznámky.
+settings.active_helper=Informácie o spustených udalostiach budú odoslané na túto URL webhooku.
+settings.add_hook_success=Webhook bol pridaný.
+settings.update_webhook=Aktualizácia Webhooku
+settings.update_hook_success=Webhook bol aktualizovaný.
+settings.delete_webhook=Odstrániť webhook
+settings.slack_token=Token
+settings.web_hook_name_gitea=Gitea
+settings.packagist_api_token=API token
+settings.protect_required_approvals_desc=Umožniť merge iba žiadostiam o natiahnutie s dostatočným počtom pozitívnych revízií.
+settings.require_signed_commits=Vyžadovať podpísané commity
+settings.block_rejected_reviews=Zablokovať zlúčenie pri zamietavých revíziách
+settings.block_rejected_reviews_desc=Zlúčenie nebude možné v prípade že oficiálni revidenti požadujú zmeny, aj keď je k dispozícii dostatok schválení.
+settings.block_on_official_review_requests=Blokovať zlúčenie pri oficiálnych žiadostiach o revíziu
+settings.block_on_official_review_requests_desc=Zlúčenie nebude možné v prípade že existujú oficiálne žiadosti o revíziu, aj keď je k dispozícii dostatok schválení.
+settings.bot_token=Token robota
+settings.matrix.access_token=Prístupový token
+settings.lfs_findcommits=Hľadať commity
+settings.lfs_lfs_file_no_commits=Pre tento súbor LFS sa nenašli žiadne commity
+
+diff.commit=commit
+diff.comment.start_review=Začať revíziu
+diff.review=Revízia
+diff.review.header=Odoslať revíziu
+diff.review.placeholder=Revízna poznámka
+diff.committed_by=odovzdal
+
+release.ahead.commits=%d commitov
+release.cancel=Zrušiť
+
+branch.delete_branch_has_new_commits=Vetva '%s' nemôže byť vymazaná, pretože po zlúčení boli pridané nové commity.
+
+
+
+
+
+
+[org]
+lower_repositories=repozitáre
+
+
+settings.visibility.private=Súkromná (viditeľné iba pre členov organizácie)
+settings.visibility.private_shortname=Súkromný
+
+settings.hooks_desc=Pridajte webhooky, ktoré sa spustia nad všetkými repozitármi v rámci tejto organizácie.
+
+
+members.private=Skrytý
+members.private_helper=zviditeľniť
+members.owner=Vlastník
+
+teams.owners_permission_desc=Vlastníci majú úplný prístup ku všetkým repozitárom a majú prístup správcu tejto organizácie.
+teams.repositories=Repozitáre týmu
+teams.specific_repositories=Konkrétne repozitáre
+teams.specific_repositories_helper=Členovia budú mať prístup iba k úložiskám, ktoré sú vyslovene pridané do tímu. Výber tejto možnosti neodstráni automaticky repozitáre, ktoré už boli pridané pomocou položky Všetky repozitáre.
+teams.all_repositories=Všetky repozitáre
+teams.all_repositories_helper=Tím má prístup ku všetkým repozitárom. Výberom tejto možnosti pridáte do tímu všetky existujúce repozitáre.
+teams.all_repositories_read_permission_desc=Tomuto tímu je pridelený prístup na čítanie ku všetkým repozitárom: členovia môžu prezerať a klonovať repozitáre.
+teams.all_repositories_write_permission_desc=Tomuto tímu je pridelený prístup na Zápis do všetkých repozitárov: členovia môžu prezerať a nahrávať do repozitárov.
+teams.all_repositories_admin_permission_desc=Tomuto tímu je pridelený Admin prístup ku všetkým repozitárom: členovia môžu prezerať, nahrávať do repozitárov a pridávať do nich spolupracovníkov.
+
+[admin]
+repositories=Repozitáre
+hooks=Webhooky
+
+dashboard.statistic_info=Databáza Gitea obsahuje %d používateľov, %d organizácií, %d verejných kľúčov, %d repozitárov, %d sledovaní, %d hviezd, %d akcií, %d prístupov, %d issues, %d komentárov, %d sociálnych účtov, %d nasledovaní, %d mirrorov, %d vydaní, %d zdrojov prihlásení, %d webhookov, %d míľnikov, %d štítkov, %d hook úloh, %d tímov, %d aktualizačných úloh, %d príloh.
+dashboard.delete_generated_repository_avatars=Odstrániť vygenerované avatary repozitárov
+
+
+
+
+repos.owner=Vlastník
+repos.private=Súkromný
+repos.forks=Forky
+
+packages.owner=Vlastník
+packages.repository=Repozitár
+
+defaulthooks=Defaultné webhooky
+defaulthooks.desc=Webhooky automaticky odosielajú požiadavky HTTP POST na server, keď sa spustia určité udalosti Gitea. Tu definované webhooky sú predvolené a skopírujú sa do všetkých nových repozitárov. Prečítajte si viac v sprievodcovi webhookmi.
+defaulthooks.add_webhook=Pridať defaultný webhook
+defaulthooks.update_webhook=Aktualizovať defaultný webhook
+
+systemhooks=Systémové webhooky
+systemhooks.desc=Webhooky automaticky odosielajú požiadavky HTTP POST na server, keď sa spustia určité udalosti Gitea. Tu definované webhooky budú pôsobiť na všetky repozitáre v systéme, takže zvážte akékoľvek dôsledky na výkon, ktoré to môže mať. Prečítajte si viac v sprievodcovi webhookmi.
+systemhooks.add_webhook=Pridať systémový webhook
+systemhooks.update_webhook=Aktualizovať defaultný webhook
+
+auths.enabled=Povolené
+auths.oauth2_tokenURL=Token URL
+auths.sspi_default_language=Predvolený jazyk používateľa
+auths.sspi_default_language_helper=Predvolený jazyk pre používateľov automaticky vytvorený metódou SSPI auth. Ak uprednostňujete automatické zisťovanie jazyka, nechajte pole prázdne.
+
+config.app_ver=Verzia Gitea
+config.app_url=Základná URL Gitea
+
+config.ssh_enabled=Povolené
+
+config.lfs_enabled=Povolené
+
+
+config.default_keep_email_private=Skrývanie e-mail adries ako predvolené
+
+config.webhook_config=Nastavenie webhooku
+
+config.mailer_enabled=Povolené
+config.mailer_enable_helo=Povolené HELO
+
+config.oauth_enabled=Povolené
+
+
+
+
+
+
+
+monitor.process.cancel=Zrušiť proces
+monitor.queue.review=Konfigurácia revidovania
+monitor.queue.review_add=Revidovať/Pridať revidentov
+
+
+
+
+[action]
+compare_commits=Porovnať %d commitov
+compare_commits_general=Porovnať commity
+review_dismissed=`odmietnutá revízia od %[4]s pre %[3]s#%[2]s`
+
+[tool]
+
+[dropzone]
+
+[notification]
+
+[gpg]
+error.no_committer_account=Žiadny účet nie je prepojený s e-mailovou adresou prispievateľa
+error.not_signed_commit=Nie je podpísaný commit
+
+[units]
+
+[packages]
+conan.details.repository=Repozitár
+container.details.repository_site=Stránka repozitára
+pub.details.repository_site=Stránka repozitára
+
diff --git a/options/locale/locale_sv-SE.ini b/options/locale/locale_sv-SE.ini
index c2ea1b327b..08733692b5 100644
--- a/options/locale/locale_sv-SE.ini
+++ b/options/locale/locale_sv-SE.ini
@@ -8,7 +8,6 @@ sign_out=Logga ut
sign_up=Registrera
link_account=Länka konto
register=Registrera dig
-website=Webbplats
version=Version
powered_by=Drivs av %s
page=Sida
@@ -60,7 +59,7 @@ forks=Forks
activities=Aktiviteter
pull_requests=Pull förfrågningar
-issues=Problem
+issues=Ärenden
milestones=Milstolpar
cancel=Avbryt
@@ -138,7 +137,6 @@ log_root_path_helper=Loggfiler kommer skrivas till denna katalog.
optional_title=Övriga inställningar
email_title=Mejlinställningar
-smtp_host=SMTP-server
smtp_from=Skicka Mejl Som
smtp_from_helper=Mejladress som Gitea kommer att använda. Anges i simpelt ('email@example.com') eller fullständigt ('Name ') format.
mailer_user=SMTP-Användarnamn
@@ -226,6 +224,7 @@ code_no_results=Ingen källkod hittades som matchar din sökterm.
code_search_results=Söktresultat för '%s'
code_last_indexed_at=Indexerades senast %s
+
[auth]
create_new_account=Registrera Konto
register_helper_msg=Har du redan ett konto? Logga in nu!
@@ -644,15 +643,19 @@ generate_from=Generera från
repo_desc=Beskrivning
repo_lang=Språk
repo_gitignore_helper=Välj .gitignore-mallar.
+repo_gitignore_helper_desc=Välj vilka filer som inte ska spåras från en lista med mallar för vanliga språk. Typiska artefakter som genereras av varje språk byggverktyg ingår i .gitignore som standard.
issue_labels=Ärendeetiketter
issue_labels_helper=Välj en grupp av ärendeetiketter.
license=Licens
license_helper=Välj licensfil.
+license_helper_desc=En licens styr vad andra kan och inte kan göra med din kod. Inte säker på vilken som är rätt för ditt projekt? Se Välj en licens.
readme=README
readme_helper=Välj en mall för README-filen.
+readme_helper_desc=Här kan du skriva en fullständig beskrivning för ditt projekt.
auto_init=Initiera utvecklingskatalog (Lägger till .gitignore, License and README)
create_repo=Skapa utvecklingskatalog
default_branch=Standardgren
+default_branch_helper=Den förvalda grenen är bas-gren för pull requests och kod-commits.
mirror_prune=Rensa
mirror_prune_desc=Ta bort förlegade fjärrföljande referenser
mirror_interval_invalid=Speglingsintervallen är inte giltig.
@@ -721,6 +724,7 @@ migrated_from_fake=Migrerad från %[1]s
migrate.migrate=Migrera från %s
migrate.migrating=Migrerar från %s ...
migrate.migrating_failed=Migrering från %s misslyckades.
+migrate.migrating_issues=Migrerar Ärenden
mirror_from=spegling av
forked_from=forkad från
@@ -907,7 +911,7 @@ issues.new.add_reviewer_title=Begär granskning
issues.choose.get_started=Kom igång
issues.choose.blank=Standard
issues.choose.blank_about=Skapa ett ärende från standardmall.
-issues.no_ref=Ingen branch/Tag specificerad
+issues.no_ref=Ingen Branch/Tag specificerad
issues.create=Skapa Ärende
issues.new_label=Ny etikett
issues.new_label_placeholder=Etikettsnamn
@@ -973,6 +977,7 @@ issues.commented_at=`kommenterad %s`
issues.delete_comment_confirm=Är du säker på att du vill ta bort den här kommentaren?
issues.context.copy_link=Kopiera länk
issues.context.quote_reply=Citerat svar
+issues.context.reference_issue=Referens i nytt ärende
issues.context.edit=Redigera
issues.context.delete=Ta bort
issues.no_content=Det finns inget innehåll än.
@@ -982,6 +987,7 @@ issues.reopen_issue=Återöppna
issues.reopen_comment_issue=Kommentera och återöppna
issues.create_comment=Kommentera
issues.closed_at=`stängde ärendet %[2]s`
+issues.reopened_at=`återöppnade detta ärende %[2]s`
issues.commit_ref_at=`refererade till detta ärende från en incheckning %[2]s`
issues.ref_issue_from=`refererade till detta ärende %[4]s %[2]s`
issues.ref_pull_from=`refererade till denna pull-förfrågan %[4]s %[2]s`
@@ -1039,10 +1045,13 @@ issues.lock.reason=Anledningen till att låsa
issues.lock.title=Lås konversationen för detta ärende.
issues.unlock.title=Lås upp konversation för ärendet.
issues.comment_on_locked=Du kan inte kommentera ett låst ärende.
+issues.delete.title=Radera detta ärende?
+issues.delete.text=Vill du verkligen ta bort detta ärende? (Detta kommer att permanent ta bort allt innehåll. Överväg att stänga det istället om du avser att hålla det arkiverat)
issues.tracker=Tidsredovisning
issues.start_tracking=Starta tidsredovisning
issues.start_tracking_history=`började arbeta %s`
issues.tracker_auto_close=Timern stoppas automatiskt när ärendet stängs
+issues.tracking_already_started=`Du har redan påbörjat tidredovisning på ett annat ärende!`
issues.stop_tracking_history=`slutade arbeta %s`
issues.cancel_tracking_history=”avbröt tidredovisning %s'
issues.add_time=Lägg till tid manuellt
@@ -1115,6 +1124,7 @@ issues.review.hide_resolved=Dölj löst
issues.review.resolve_conversation=Lös konversation
issues.review.resolved_by=markerade denna konversation som löst
issues.assignee.error=Inte alla tilldelade har lagts till på grund av ett oväntat fel.
+issues.content_history.options=Alternativ
pulls.desc=Aktivera pull-förfrågningar och kodgranskning.
@@ -1158,6 +1168,7 @@ pulls.invalid_merge_option=Du kan inte använda detta mergealternativet för den
; %[2]s
%[3]s
pulls.open_unmerged_pull_exists=`Du kan inte återuppliva denna pull-request då det redan finns en identisk pull-request öppen (#%d).`
pulls.update_branch_success=Uppdatering av branchen lyckades
+pulls.update_not_allowed=Du är inte behörig att uppdatera grenen
pulls.outdated_with_base_branch=Denna branch är föråldrad gentemot bas-branchen
@@ -1270,6 +1281,7 @@ activity.git_stats_exclude_merges=Exkludera merger,
activity.git_stats_author_1=%d författare
activity.git_stats_author_n=%d författare
activity.git_stats_push_to_all_branches=till alla brancher.
+activity.git_stats_on_default_branch=På %s,
activity.git_stats_file_1=%d fil
activity.git_stats_file_n=%d filer
activity.git_stats_files_changed_1=har ändrats
@@ -1299,10 +1311,6 @@ settings.basic_settings=Basinställningar
settings.mirror_settings=Inställningar för spegling
settings.sync_mirror=Synkronisera nu
settings.mirror_sync_in_progress=Synkronisering utav speglingar pågår. Kontrollera igen om en minut.
-settings.email_notifications.enable=Aktivera notiser via mejl
-settings.email_notifications.onmention=Endast e-post vid omnämnande
-settings.email_notifications.disable=Inaktivera notiser via mejl
-settings.email_notifications.submit=Ställ in e-postinställningar
settings.site=Webbplats
settings.update_settings=Uppdatera inställningar
settings.advanced_settings=Advancerade Inställningar
@@ -1418,6 +1426,7 @@ settings.event_push=Pusha
settings.event_push_desc=Git push till en utvecklingskatalog.
settings.event_repository=Utvecklingskatalog
settings.event_repository_desc=Utvecklingskatalogen skapad eller borttagen.
+settings.event_header_issue=Ärendehändelser
settings.event_issues=Ärenden
settings.event_issue_comment=Kommentar
settings.event_issue_comment_desc=Kommentar skapad, ändrad eller borttagen.
@@ -1520,6 +1529,7 @@ settings.lfs_force_unlock=Tvinga upplåsning
settings.lfs_pointers.sha=Blob SHA
settings.lfs_pointers.oid=OID
settings.lfs_pointers.inRepo=I utvecklingskatalogen
+settings.rename_branch_failed_not_exist=Kan inte byta namn på branchen %s eftersom den inte finns.
diff.browse_source=Bläddra i källkod
diff.parent=förälder
@@ -1973,11 +1983,8 @@ config.queue_length=Kölängd
config.deliver_timeout=Tidsfrist för leverans
config.skip_tls_verify=Skippa TLS verifiering
-config.mailer_config=SMTP-Mailer konfiguration
config.mailer_enabled=Aktiverad
-config.mailer_disable_helo=Avaktivera HELO
config.mailer_name=Namn
-config.mailer_host=Server
config.mailer_user=Användare
config.mailer_use_sendmail=Använd Sendmail
config.mailer_sendmail_path=Sendmail sökväg
@@ -2026,6 +2033,7 @@ config.disabled_logger=Inaktiverad
config.access_log_template=Mall
config.xorm_log_sql=Logga SQL
+
monitor.cron=Cron-jobb
monitor.name=Namn
monitor.schedule=Schemaläggning
diff --git a/options/locale/locale_tr-TR.ini b/options/locale/locale_tr-TR.ini
index d2a19bc8f5..ea48962856 100644
--- a/options/locale/locale_tr-TR.ini
+++ b/options/locale/locale_tr-TR.ini
@@ -2,13 +2,13 @@ home=Ana Sayfa
dashboard=Pano
explore=Keşfet
help=Yardım
+logo=Logo
sign_in=Giriş Yap
sign_in_with=Şununla giriş yap
sign_out=Çıkış Yap
sign_up=Kaydol
link_account=Bağlantı hesabı
register=Üye Ol
-website=Web sitesi
version=Sürüm
powered_by=%s tarafından desteklenen
page=Sayfa
@@ -34,6 +34,19 @@ twofa=İki Aşamalı Doğrulama
twofa_scratch=İki aşamalı kazınmış kod
passcode=Şifre
+webauthn_insert_key=Güvenlik anahtarınızı ekleyin
+webauthn_sign_in=Güvenlik anahtarınızdaki düğmeye basın. Eğer düğme yoksa güvenlik anahtarınızı tekrar ekleyin.
+webauthn_press_button=Lütfen güvenlik anahtarınızdaki düğmeye basın…
+webauthn_use_twofa=Telefonunuzdan iki aşamalı doğrulama kodu kullanın
+webauthn_error=Güvenlik anahtarınız okunamıyor.
+webauthn_unsupported_browser=Tarayıcınız henüz WebAuthn desteklemiyor.
+webauthn_error_unknown=Bilinmeyen bir hata oluştu. Lütfen tekrar deneyin.
+webauthn_error_insecure=WebAuthn sadece güvenli bağlantıyı destekler. HTTP üzerinden test etmek için "localhost" veya "127.0.0.1" adreslerini kullanabilirsiniz.
+webauthn_error_unable_to_process=Sunucu isteğinizi işleyemedi.
+webauthn_error_duplicated=Güvenlik anahtarının bu istek için izni yok. Anahtarın halihazırda kayıtlı olmadığından emin olun.
+webauthn_error_empty=Bu anahtar için bir isim belirlemelisiniz.
+webauthn_error_timeout=Anahtarınız okunamadan zaman aşımı oldu. Lütfen sayfayı yenileyin ve tekrar deneyin.
+webauthn_reload=Yeniden yükle
repository=Depo
organization=Organizasyon
@@ -91,9 +104,15 @@ error404=Ulaşmaya çalıştığınız sayfa mevcut değil veya
never=Asla
+rss_feed=RSS Beslemesi
[error]
+occurred=Bir hata oluştu
+report_message=Bunun bir Gitea hatası olduğundan eminseniz, lütfen GitHub sayfasında sorunu arayın veya gerekiyorsa yeni bir sorun açın.
missing_csrf=Hatalı İstek: CSRF anahtarı yok
+invalid_csrf=Hatalı İstek: geçersiz CSRF erişim anahtarı
+not_found=Hedef bulunamadı.
+network_error=Ağ hatası
[startpage]
app_desc=Zahmetsiz, kendi sunucunuzda barındırabileceğiniz Git servisi
@@ -159,7 +178,8 @@ log_root_path_helper=Günlük dosyaları bu dizine kaydedilecektir.
optional_title=İsteğe Bağlı Ayarlar
email_title=E-posta Ayarları
-smtp_host=SMTP Sunucusu
+smtp_addr=SMTP Sunucusu
+smtp_port=SMTP Portu
smtp_from=E-posta Gönderen
smtp_from_helper=Gitea'nın kullanacağı e-posta adresi. Yalın bir e-posta adresi girin veya "İsim" biçimini kullanın.
mailer_user=SMTP Kullanıcı Adı
@@ -196,7 +216,10 @@ sqlite3_not_available=Bu Gieta sürümü SQLite3 desteklemiyor. Lütfen %s adres
invalid_db_setting=Veritabanı ayarları geçersiz: %v
invalid_db_table='%s' veritabanı tablosu geçersiz: %v
invalid_repo_path=Depo kök dizini geçersiz: %v
+invalid_app_data_path=Uygulama veri yolu geçersiz: %v
run_user_not_match='Birlikte çalıştır' kullanıcı adı şimdiki kullanıcı adından farklıdır: %s -> %s
+internal_token_failed=Dahili belirteç oluşturulamadı: %v
+secret_key_failed=Gizli anahtar oluşturulamadı: %v
save_config_failed=%v Yapılandırması kaydedilirken hata oluştu
invalid_admin_setting=Yönetici hesap ayarları geçersiz: %v
install_success=Hoşgeldiniz! Gitea'yı seçtiğiniz için teşekkür ederiz. Eğlenin ve kendinize iyi bakın!
@@ -225,6 +248,7 @@ view_home=%s Görüntüle
search_repos=Depo bul…
filter=Diğer Süzgeçler
filter_by_team_repositories=Takım depolarına göre süz
+feed_of="%s" beslemesi
show_archived=Arşivlenmiş
show_both_archived_unarchived=Arşivlenenler ve arşivlenmeyenlerin hepsi gösteriliyor
@@ -244,14 +268,21 @@ users=Kullanıcılar
organizations=Organizasyonlar
search=Ara
code=Kod
+search.type.tooltip=Arama türü
search.fuzzy=Belirsiz
+search.fuzzy.tooltip=Arama terimine benzeyen sonuçları da içer
search.match=Eşleştir
+search.match.tooltip=Sadece arama terimiyle tamamen eşleşen sonuçları içer
+code_search_unavailable=Kod arama şu an mevcut değil. Lütfen site yöneticinizle bağlantıya geçin.
repo_no_results=Eşleşen bir depo bulunamadı.
user_no_results=Eşleşen kullanıcı bulunamadı.
org_no_results=Eşleşen organizasyon bulunamadı.
code_no_results=Aranan terimlerle eşleşen bir kaynak kod bulunamadı.
code_search_results='%s' için arama sonuçları
code_last_indexed_at=Son endekslenen %s
+relevant_repositories_tooltip=Çatal olan veya konusu, simgesi veya açıklaması olmayan depolar gizlenmiştir.
+relevant_repositories=Sadece ilişkili depolar gösteriliyor, filtrelenmemiş sonuçları göster.
+
[auth]
create_new_account=Hesap Oluştur
@@ -259,6 +290,7 @@ register_helper_msg=Bir hesabınız var mı? Şimdi giriş yapın!
social_register_helper_msg=Hesabınız var mı? Hemen bağlayın!
disable_register_prompt=Kayıt işlemi devre dışıdır. Lütfen site yöneticinizle iletişim kurun.
disable_register_mail=Kayıt için e-posta doğrulama devre dışıdır.
+manual_activation_only=Etkinleştirmeyi tamamlamak için site yöneticinizle bağlantıya geçin.
remember_me=Bu Aygıtı hatırla
forgot_password_title=Şifremi unuttum
forgot_password=Şifrenizi mi unuttunuz?
@@ -297,6 +329,9 @@ oauth_signup_submit=Hesabı Tamamla
oauth_signin_tab=Mevcut Hesaba Bağla
oauth_signin_title=Bağlantılı Hesabı Yetkilendirmek için Giriş Yapın
oauth_signin_submit=Hesabı Bağla
+oauth.signin.error=Yetkilendirme isteğini işlerken bir hata oluştu. Eğer hata devam ederse lütfen site yöneticisiyle bağlantıya geçin.
+oauth.signin.error.access_denied=Yetkilendirme isteği reddedildi.
+oauth.signin.error.temporarily_unavailable=Yetkilendirme sunucusu geçici olarak erişilemez olduğu için yetkilendirme başarısız oldu. Lütfen daha sonra tekrar deneyin.
openid_connect_submit=Bağlan
openid_connect_title=Mevcut olan bir hesaba bağlan
openid_connect_desc=Seçilen OpenID URI'si bilinmiyor. Burada yeni bir hesapla ilişkilendir.
@@ -413,6 +448,7 @@ size_error=` uzunluk en fazla %s olmalıdır.`
min_size_error=` en az %s karakter içermelidir.`
max_size_error=` en fazla %s karakter içermelidir.`
email_error=' geçerli bir e-posta adresi değil.'
+url_error=`'%s' geçerli bir bağlantı değil.`
include_error=` '%s' içermelidir.`
glob_pattern_error=` glob deseni geçersiz: %s.`
regex_pattern_error=` regex dizisi geçersiz: %s.`
@@ -424,6 +460,7 @@ lang_select_error=Listeden bir dil seçin.
username_been_taken=Bu kullanıcı adı daha önce alınmış.
username_change_not_local_user=Yerel olmayan kullanıcılar kendi kullanıcı adlarını değiştiremezler.
repo_name_been_taken=Depo adı zaten kullanılıyor.
+repository_force_private=Gizliyi Zorla devrede: gizli depolar herkese açık yapılamaz.
repository_files_already_exist=Bu depo için dosyalar zaten var. Sistem yöneticisine başvurun.
repository_files_already_exist.adopt=Bu depo için dosyalar zaten var ve yalnızca Kabul Edilebilir.
repository_files_already_exist.delete=Bu depo için dosyalar zaten var. Onları silmelisiniz.
@@ -459,7 +496,9 @@ auth_failed=Kimlik doğrulaması başarısız oldu: %v
still_own_repo=Hesabınız bir veya daha fazla depoya sahip; önce onları silin veya transfer edin.
still_has_org=Hesabınız bir veya daha fazla organizasyonun üyesi; öncelikle onlardan ayrılın.
+still_own_packages=Hesabınız bir veya daha fazla pakete sahip; önce onları silin.
org_still_own_repo=Bu organizasyon hala bir veya daha fazla depoya sahip; önce onları silin veya transfer edin.
+org_still_own_packages=Bu organizasyon hala bir veya daha fazla pakete sahip; önce onları silin.
target_branch_not_exist=Hedef dal mevcut değil.
@@ -471,6 +510,7 @@ activity=Genel Aktivite
followers=Takipçiler
starred=Yıldızlanmış depolar
watched=İzlenen Depolar
+code=Kod
projects=Projeler
following=Takip Edilenler
follow=Takip Et
@@ -486,6 +526,7 @@ form.name_chars_not_allowed='%s' kullanıcı adı geçersiz karakterler içeriyo
[settings]
profile=Profil
account=Hesap
+appearance=Görünüm
password=Parola
security=Güvenlik
avatar=Avatar
@@ -499,6 +540,7 @@ twofa=İki Aşamalı Doğrulama
account_link=Bağlı Hesaplar
organization=Organizasyonlar
uid=Tekil ID
+webauthn=Güvenlik Anahtarları
public_profile=Herkese Açık Profil
biography_placeholder=Bize biraz kendinizden bahsedin
@@ -509,7 +551,9 @@ website=Web Sitesi
location=Konum
update_theme=Temayı Güncelle
update_profile=Profili Güncelle
+update_language=Dili Güncelle
update_language_not_found=‘%s‘ dili mevcut değil.
+update_language_success=Dil güncellendi.
update_profile_success=Profil resminiz güncellendi.
change_username=Kullanıcı adınız değiştirildi.
change_username_prompt=Not: Kullanıcı adı değişiklikleri hesap URL'nizi de değiştirir.
@@ -518,6 +562,22 @@ continue=Devam Et
cancel=İptal
language=Dil
ui=Tema
+hidden_comment_types=Gizli yorum türleri
+comment_type_group_reference=Referans
+comment_type_group_label=Etiket
+comment_type_group_milestone=Dönüm noktası
+comment_type_group_assignee=Atanan
+comment_type_group_title=Başlık
+comment_type_group_branch=Dal
+comment_type_group_time_tracking=Zaman İzleme
+comment_type_group_deadline=Son Tarih
+comment_type_group_dependency=Bağımlılık
+comment_type_group_lock=Kilit Durumu
+comment_type_group_review_request=İnceleme isteği
+comment_type_group_pull_request_push=Eklenen işlemeler
+comment_type_group_project=Proje
+comment_type_group_issue_ref=Konu referansı
+saved_successfully=Ayarlarınız başarılı bir şekilde kaydedildi.
privacy=Gizlilik
keep_activity_private=Etkinliği profil sayfasından gizle
keep_activity_private_popup=Etkinliği yalnızca siz ve yöneticiler için görünür hale getirir
@@ -531,6 +591,7 @@ delete_current_avatar=Güncel Avatarı Sil
uploaded_avatar_not_a_image=Yüklenen dosya bir resim dosyası değil.
uploaded_avatar_is_too_big=Yüklenen dosya maksimum boyutu aştı.
update_avatar_success=Profil resminiz değiştirildi.
+update_user_avatar_success=Kullanıcının avatarı güncellendi.
change_password=Parolayı Güncelle
old_password=Mevcut Parola
@@ -600,7 +661,21 @@ gpg_key_verify=Doğrula
gpg_invalid_token_signature=Verilen GPG anahtarı, imza ve anahtar uyuşmuyor veya anahtar çok eski.
gpg_token_required=Aşağıdaki anahtar için bir imza sağlamalısınız
gpg_token=Anahtar
+gpg_token_help=Şunu kullanarak bir imza oluşturabilirsiniz:
+gpg_token_code=echo "%s" | gpg -a --default-key %s --detach-sig
+gpg_token_signature=Korumalı GPG imzası
key_signature_gpg_placeholder='-----PGP İMZA BAŞLAT -----' ile başlar
+verify_gpg_key_success=GPG anahtarı '%s' doğrulandı.
+ssh_key_verified=Doğrulanmış Anahtar
+ssh_key_verified_long=Bu anahtar bir belirteç ile doğrulandı ve bu kullanıcı için etkinleştirilmiş herhangi bir e-posta adresi ile uyuşan işlemeleri doğrulamak için kullanılabilir.
+ssh_key_verify=Doğrula
+ssh_invalid_token_signature=Verilen SSH anahtarı, imza veya erişim anahtarı uyuşmuyor veya erişim anahtarı çok eski.
+ssh_token_required=Aşağıdaki erişim anahtarı için bir imza sağlamalısınız
+ssh_token=Erişim Anahtarı
+ssh_token_help=Şunu kullanarak bir imza oluşturabilirsiniz:
+ssh_token_signature=Korumalı SSH imzası
+key_signature_ssh_placeholder='-----BEGIN SSH SIGNATURE-----' ile başlar
+verify_ssh_key_success=SSH anahtarı '%s' doğrulandı.
subkeys=Alt anahtarlar
key_id=Anahtar Kimliği
key_name=Anahtar İsmi
@@ -648,6 +723,9 @@ generate_token_success=Yeni bir jeton oluşturuldu. Tekrar gösterilmeyeceği i
generate_token_name_duplicate=%s zaten bir uygulama adı olarak kullanılmış. Lütfen yeni bir tane kullanın.
delete_token=Sil
access_token_deletion=Erişim Jetonunu Sil
+access_token_deletion_cancel_action=İptal
+access_token_deletion_confirm_action=Sil
+access_token_deletion_desc=Bir erişim anahtarını silmek, onu kullanan uygulamaların hesabınıza erişimini kaldırır. Bu geri alınamaz. Devam edilsin mi?
delete_token_success=Jeton silindi. Onu kullanan uygulamalar artık hesabınıza erişemez.
manage_oauth2_applications=OAuth2 Uygulamalarını Yönet
@@ -700,10 +778,16 @@ passcode_invalid=Şifre geçersiz. Tekrar deneyin.
twofa_enrolled=Hesabınız iki faktörlü kimlik doğrulamasına kaydedildi. Kazıma belirtecini (%s) yalnızca bir kez gösterdiği gibi güvenli bir yerde saklayın!
twofa_failed_get_secret=Gizlilik elde edilemedi.
+webauthn_desc=Güvenlik anahtarları, şifreleme anahtarlarını içeren donanım aygıtlarıdır. İki aşamalı kimlik doğrulama için kullanılabilirler. Güvenlik anahtarları WebAuthn Authenticator standardını desteklemelidir.
+webauthn_register_key=Güvenlik Anahtarı Ekle
+webauthn_nickname=Takma Ad
+webauthn_delete_key=Güvenlik Anahtarını Kaldır
+webauthn_delete_key_desc=Bir güvenlik anahtarını kaldırırsanız, onunla artık giriş yapamazsınız. Devam edilsin mi?
manage_account_links=Bağlı Hesapları Yönet
manage_account_links_desc=Bu harici hesaplar Gitea hesabınızla bağlantılı.
account_links_not_available=Şu anda Gitea hesabınıza bağlı harici bir hesap yok.
+link_account=Hesap Bağla
remove_account_link=Bağlantılı Hesabı Kaldır
remove_account_link_desc=Bağlantılı bir hesabı kaldırmak, onunla Gitea hesabınıza erişimi iptal edecektir. Devam edilsin mi?
remove_account_link_success=Bağlantılı hesap kaldırıldı.
@@ -722,6 +806,7 @@ email_notifications.enable=E-posta Bildirimlerini Etkinleştir
email_notifications.onmention=Sadece Bahsedilen E-posta
email_notifications.disable=E-posta Bildirimlerini Devre Dışı Bırak
email_notifications.submit=E-posta Tercihlerini Ayarla
+email_notifications.andyourown=Ve Sizin Bildirimleriniz
visibility=Kullanıcı görünürlüğü
visibility.public=Herkese Açık
@@ -750,8 +835,14 @@ visibility_fork_helper=(Bunu değiştirmek tüm çatallamaları etkileyecektir.)
clone_helper=Klonlama konusunda yardıma mı ihtiyacınız var? Yardım adresini ziyaret edin.
fork_repo=Depoyu Çatalla
fork_from=Buradan Çatalla
+already_forked=%s deposunu zaten çatalladınız
+fork_to_different_account=Başka bir hesaba çatalla
fork_visibility_helper=Çatallanmış bir deponun görünürlüğü değiştirilemez.
use_template=Bu şablonu kullan
+clone_in_vsc=VS Code'ta klonla
+download_zip=ZIP indir
+download_tar=TAR.GZ indir
+download_bundle=BUNDLE indir
generate_repo=Depo Oluştur
generate_from=Şuradan Oluştur
repo_desc=Açıklama
@@ -778,7 +869,9 @@ default_branch=Varsayılan Dal
default_branch_helper=Varsayılan dal, değişiklik istekleri ve kod işlemeleri için temel daldır.
mirror_prune=Buda
mirror_prune_desc=Kullanılmayan uzak depoları izleyen referansları kaldır
+mirror_interval=Yansı Aralığı (geçerli zaman birimleri 'h', 'm', 's'). Periyodik senkronizasyonu devre dışı bırakmak için 0 kullanın. (Asgari aralık: %s)
mirror_interval_invalid=Yansı süre aralığı geçerli değil.
+mirror_sync_on_commit=İşlemeler gönderildiğinde senkronize et
mirror_address=URL'den Klonla
mirror_address_desc=Yetkilendirme bölümüne gerekli tüm kimlik bilgilerini girin.
mirror_address_url_invalid=Sağlanan Url geçersiz. Url'nin tüm bileşenlerinden doğru olarak kaçmalısınız.
@@ -826,7 +919,8 @@ desc.archived=Arşivlenmiş
template.items=Şablon Öğeleri
template.git_content=Git İçeriği (Varsayılan Dal)
-template.git_hooks=Git İstekleri
+template.git_hooks=Git İstemcileri
+template.git_hooks_tooltip=Eklendikten sonra Git İstemcilerini değiştirmek veya kaldırmak mümkün değildir. Bunu yalnızca şablon deposuna güveniyorsanız seçin.
template.webhooks=Web İstemcileri
template.topics=Konular
template.avatar=Profil Resmi
@@ -846,6 +940,7 @@ form.name_pattern_not_allowed='%s' deseni, depo adı için geçerli değildir.
need_auth=Yetkilendirme
migrate_options=Göç Seçenekleri
migrate_service=Göç Hizmeti
+migrate_options_mirror_helper=Bu depo bir yansı olacaktır
migrate_options_lfs=LFS dosyalarını taşı
migrate_options_lfs_endpoint.label=LFS Uç Noktası
migrate_options_lfs_endpoint.description=Taşıma, LFS sunucusunu belirlemek için Git uzak sunucusunu kullanmaya çalışacak. Eğer LFS veri deposu başka yerdeyse özel bir uç nokta da belirtebilirsiniz.
@@ -862,8 +957,10 @@ migrate_items_releases=Sürümler
migrate_repo=Depoyu Göç Ettir
migrate.clone_address=URL'den Taşı / Klonla
migrate.clone_address_desc=Varolan bir deponun HTTP(S) veya Git 'klonlama' URL'si
+migrate.github_token_desc=GitHub API hız sınırı nedeniyle göçü hızlandırmak için buraya virgülle ayrılmış bir veya daha fazla erişm anahtarı koyabilirsiniz. UYARI: Bu özelliğin kötüye kullanılması, hizmet sağlayıcının politikasını ihlal edebilir ve hesabın engellenmesine yol açabilir.
migrate.clone_local_path=veya bir yerel sunucu yolu
migrate.permission_denied=Yerel depoları içeri aktarma izniniz yok.
+migrate.permission_denied_blocked=İzin verilmeyen sunuculardan içe aktaramazsınız, lütfen yöneticiden ALLOWED_DOMAINS/ALLOW_LOCALNETWORKS/BLOCKED_DOMAINS ayarlarını kontrol etmesini isteyin.
migrate.invalid_local_path=Yerel yol geçersiz. Mevcut değil veya bir dizin değil.
migrate.invalid_lfs_endpoint=LFS Uç noktası geçerli değil.
migrate.failed=Göç başarısız: %v
@@ -874,6 +971,15 @@ migrate.migrate=%s Konumundan Göç Et
migrate.migrating=%s konumundan taşınıyor ...
migrate.migrating_failed=%s konumundan taşıma başarısız oldu.
migrate.migrating_failed.error=Hata: %s
+migrate.migrating_failed_no_addr=Göç başarısız oldu.
+migrate.github.description=Github.com veya diğer Github sunucularından veri aktar.
+migrate.git.description=Herhangi bir Git hizmetinden sadece bir depoyu aktar.
+migrate.gitlab.description=Gitlab.com veya diğer Gitlab sunucularından veri aktar.
+migrate.gitea.description=Gitea.com veya diğer Gitea sunucularından veri aktar.
+migrate.gogs.description=Notabug.org veya diğer Gogs sunucularından veri aktar.
+migrate.onedev.description=Code.onedev.io ve diğer OneDev sunucularından veri aktar.
+migrate.codebase.description=Codebasehq.com sitesinden veri aktar.
+migrate.gitbucket.description=GitBucket sunucularından veri aktar.
migrate.migrating_git=Git Verilerini Taşıma
migrate.migrating_topics=Konuları Taşıma
migrate.migrating_milestones=Kilometre Taşlarını Taşıma
@@ -902,6 +1008,7 @@ clone_this_repo=Bu depoyu klonla
create_new_repo_command=Komut satırında yeni bir depo oluşturuluyor
push_exist_repo=Komut satırından mevcut bir depo itiliyor
empty_message=Bu depoda herhangi bir içerik yok.
+broken_message=Bu deponun altındaki Git verisi okunamıyor. Bu sunucunun yöneticisiyle bağlantıya geçin veya bu depoyu silin.
code=Kod
code.desc=Kaynak koda, dosyalara, işlemelere ve dallara eriş.
@@ -915,6 +1022,7 @@ tags=Etiket
issues=Konular
pulls=Değişiklik İstekleri
project_board=Projeler
+packages=Paketler
labels=Etiketler
org_labels_desc=Bu organizasyon altında tüm depolarla kullanılabilen organizasyon düzeyinde etiketler
org_labels_desc_manage=yönet
@@ -926,6 +1034,7 @@ release=Sürüm
releases=Sürüm
tag=Etiket
released_this=bu sürümü yayınladı
+file.title=%s dalındaki/etiketindeki %s
file_raw=Ham
file_history=Geçmiş
file_view_source=Kaynağı Görüntüle
@@ -933,7 +1042,18 @@ file_view_rendered=Oluşturulanları Görüntüle
file_view_raw=Ham Görünüm
file_permalink=Kalıcı Bağlantı
file_too_large=Bu dosya görüntülemek için çok büyük.
+invisible_runes_header=`Bu dosya görünmez Evrensel Kodlu karakter içeriyor!`
+invisible_runes_description=`Bu dosya, aşağıda görünenden farklı bir şekilde işlenebilecek görünmez Evrensel Kodlu karakter içeriyor. Eğer bunu kasıtlı ve meşru olarak yaptıysanız bu uyarıyı yok sayabilirsiniz. Gizli karakterleri göstermek için Kaçış düğmesine tıklayın.`
+ambiguous_runes_header=`Bu dosya muğlak Evrensel Kodlu karakter içeriyor!`
+ambiguous_runes_description=`Bu dosya, aşağıda görünenden farklı bir şekilde işlenebilecek muğlak Evrensel Kodlu karakter içeriyor. Eğer bunu kasıtlı ve meşru olarak yaptıysanız bu uyarıyı yok sayabilirsiniz. Bu karakterleri göstermek için Kaçış düğmesine tıklayın.`
+invisible_runes_line=`Bu satırda görünmez evrensel kodlu karakter var`
+ambiguous_runes_line=`Bu satırda muğlak evrensel kodlu karakter var`
+ambiguous_character=`%[1]c [U+%04[1]X], %[2]c [U+%04[2]X] ile karıştırılabilir`
+escape_control_characters=Kaçış Karakterli
+unescape_control_characters=Kaçış Karaktersiz
+file_copy_permalink=Kalıcı Bağlantıyı Kopyala
+view_git_blame=Git Suç Görüntüle
video_not_supported_in_browser=Tarayıcınız HTML5 'video' etiketini desteklemiyor.
audio_not_supported_in_browser=Tarayıcınız HTML5 'audio' etiketini desteklemiyor.
stored_lfs=Git LFS ile depolandı
@@ -942,11 +1062,14 @@ commit_graph=İşleme Grafiği
commit_graph.select=Dalları seç
commit_graph.hide_pr_refs=Değişiklik İsteklerini Gizle
commit_graph.monochrome=Siyah Beyaz
+commit_graph.color=Renk
blame=Suçlama
+download_file=Dosya indir
normal_view=Normal Görünüm
line=satır
lines=satır
+editor.add_file=Dosya Ekle
editor.new_file=Yeni dosya
editor.upload_file=Dosya Yükle
editor.edit_file=Dosyayı Düzenle
@@ -970,6 +1093,10 @@ editor.add_tmpl='' eklendi
editor.add='%s' ekle
editor.update='%s' güncelle
editor.delete='%s' sil
+editor.patch=Yama Uygula
+editor.patching=Yamalanıyor:
+editor.fail_to_apply_patch='%s' yaması uygulanamıyor
+editor.new_patch=Yeni Yama
editor.commit_message_desc=İsteğe bağlı uzun bir açıklama ekleyin…
editor.signoff_desc=İşleme günlüğü mesajının sonuna işleyen tarafından imzalanan bir fragman ekleyin.
editor.commit_directly_to_this_branch=Doğrudan %s bölümüne uygula.
@@ -994,6 +1121,8 @@ editor.commit_empty_file_text=İşlemek üzere olduğunuz dosya boş. Devam edil
editor.no_changes_to_show=Gösterilecek değişiklik yok.
editor.fail_to_update_file='%s' dosyası güncellenemedi/oluşturulamadı.
editor.fail_to_update_file_summary=Hata Mesajı:
+editor.push_rejected_no_message=Değişiklik, bir ileti olmadan sunucu tarafından reddedildi. Git Hooks'u kontrol edin.
+editor.push_rejected=Değişiklik sunucu tarafından reddedildi. Lütfen Git Hooks'u kontrol edin.
editor.push_rejected_summary=Tam Red Mesajı:
editor.add_subdir=Bir dizin ekle…
editor.unable_to_upload_files=Şu hata ile dosyalar '%s' 'a yüklenemedi: %v
@@ -1003,6 +1132,8 @@ editor.cannot_commit_to_protected_branch=Korunan '%s' dalına işleme yapılamı
editor.no_commit_to_branch=Doğrudan dala işleme yapılamıyor çünkü:
editor.user_no_push_to_branch=Kullanıcı dala gönderemez
editor.require_signed_commit=Dal imzalı bir işleme gerektirir
+editor.cherry_pick=%s şunun üzerine cımbızla:
+editor.revert=%s şuna geri döndür:
commits.desc=Kaynak kodu değişiklik geçmişine göz atın.
commits.commits=İşleme
@@ -1021,8 +1152,17 @@ commits.signed_by=İmzalayan
commits.signed_by_untrusted_user=Güvenilmeyen kullanıcı tarafından imzalandı
commits.signed_by_untrusted_user_unmatched=İşleyici ile eşleşmeyen güvenilmeyen kullanıcı tarafından imzalanmış
commits.gpg_key_id=GPG Anahtar Kimliği
+commits.ssh_key_fingerprint=SSH Anahtar Parmak İzi
+commit.actions=Eylemler
+commit.revert=Geri Al
+commit.revert-header=Geri al: %s
+commit.revert-content=Geri almak için dal seçin:
+commit.cherry-pick=Cımbızla
+commit.cherry-pick-header=Cımbızla: %s
+commit.cherry-pick-content=Cımbızlamak için dal seçin:
+ext_issues=Harici Konulara Erişim
ext_issues.desc=Dışsal konu takip sistemine bağla.
projects=Projeler
@@ -1056,8 +1196,10 @@ projects.board.set_default=Varsayılana Ayarla
projects.board.set_default_desc=Kategorize edilmemiş konular ve çekme istekleri için bu panoyu varsayılan olarak ayarlayın
projects.board.delete=Panoyu Sil
projects.board.deletion_desc=Bir proje panosunun silinmesi, ilgili tüm konuları 'Kategorize edilmemiş'e taşır. Devam edilsin mi?
+projects.board.color=Renk
projects.open=Aç
projects.close=Kapat
+projects.board.assigned_to=Atanan
issues.desc=Hata raporlarını, görevleri ve kilometre taşlarını yönetmenizi sağlar.
issues.filter_assignees=Atama Süzgeci
@@ -1093,6 +1235,8 @@ issues.new.add_reviewer_title=İnceleme iste
issues.choose.get_started=Başla
issues.choose.blank=Varsayılan
issues.choose.blank_about=Varsayılan şablondan bir konu oluşturun.
+issues.choose.ignore_invalid_templates=Geçersiz şablonlar göz ardı edildi
+issues.choose.invalid_templates=%v geçersiz şablon bulundu
issues.no_ref=Bölüm/Etiket Belirtilmedi
issues.create=Konu Oluştur
issues.new_label=Yeni Etiket
@@ -1122,6 +1266,9 @@ issues.add_assignee_at=`%[2]s %[1]s tarafından atandı`
issues.remove_assignee_at=`ataması %[2]s %[1]s tarafından kaldırıldı`
issues.remove_self_assignment=`atamalarını kaldırdı %s`
issues.change_title_at=`başlığı %s iken %s olarak %s değiştirdi`
+issues.change_ref_at=`%s referans %s %s olarak değiştirildi`
+issues.remove_ref_at=`%s referansı %s tarihinde kaldırıldı`
+issues.add_ref_at=`%s referansı %s tarihinde eklendi`
issues.delete_branch_at=`%s dalı silindi %s`
issues.filter_label=Etiket
issues.filter_label_exclude=`Etiketleri hariç tutmak için alt
+ tıkla/enter
kullanın`
@@ -1130,6 +1277,8 @@ issues.filter_milestone=Kilometre Taşı
issues.filter_milestone_no_select=Tüm kilometre taşları
issues.filter_assignee=Atanan
issues.filter_assginee_no_select=Tüm atananlar
+issues.filter_poster=Yazar
+issues.filter_poster_no_select=Tüm yazarlar
issues.filter_type=Tür
issues.filter_type.all_issues=Tüm konular
issues.filter_type.assigned_to_you=Size atanan
@@ -1149,6 +1298,7 @@ issues.filter_sort.moststars=En çok yıldızlılar
issues.filter_sort.feweststars=En az yıldızlılar
issues.filter_sort.mostforks=En çok çatallananlar
issues.filter_sort.fewestforks=En az çatallananlar
+issues.keyword_search_unavailable=Anahtar kelime ile arama şu an mevcut değil. Lütfen site yöneticisiyle iletişime geçin.
issues.action_open=Açık
issues.action_close=Kapat
issues.action_label=Etiket
@@ -1157,11 +1307,16 @@ issues.action_milestone_no_select=Kilometre Taşı Yok
issues.action_assignee=Atanan
issues.action_assignee_no_select=Atanan yok
issues.opened_by=%[3]s tarafından %[1]s açıldı
+pulls.merged_by=%[1]s %[3]s tarafından açılan istek birleştirildi
+pulls.merged_by_fake=%[2]s tarafından açılan istek %[1]s birleştirildi
+issues.closed_by=%[3]s tarafından %[1]s kapatıldı
issues.opened_by_fake=%[2]s tarafından %[1]s açıldı
+issues.closed_by_fake=%[2]s tarafından %[1]s kapatıldı
issues.previous=Önceki
issues.next=Sonraki
issues.open_title=Açık
issues.closed_title=Kapalı
+issues.draft_title=Taslak
issues.num_comments=%d yorum
issues.commented_at=`%s yorum yaptı`
issues.delete_comment_confirm=Bu yorumu silmek istediğinizden emin misiniz?
@@ -1183,7 +1338,7 @@ issues.reopened_at=`%[2]s konusunu yeniden açt
issues.commit_ref_at=`%[2]s işlemesinde bu konuyu işaret etti`
issues.ref_issue_from=`bu konuya referansta bulundu %[4]s %[2]s`
issues.ref_pull_from=`bu değişiklik isteğine referansta bulundu %[4]s %[2]s`
-issues.ref_closing_from=`bir değişiklik isteğine referansta bulundu %[4] bu konu kapatılacak %[2]s`
+issues.ref_closing_from=`bir değişiklik isteğine referansta bulundu %[4]s bu konu kapatılacak %[2]s`
issues.ref_reopening_from=`bir değişiklik isteğine referansta bulundu %[4]s bu konu yeniden açılacak %[2]s`
issues.ref_closed_from=`bu konuyu kapat%[4]s %[2]s`
issues.ref_reopened_from=`konuyu yeniden aç%[4]s %[2]s`
@@ -1240,6 +1395,9 @@ issues.lock.reason=Kilitleme nedeni
issues.lock.title=Konuşmayı kilitle.
issues.unlock.title=Konuşmanın kilidini aç.
issues.comment_on_locked=Kilitli bir konuya yorum yapamazsınız.
+issues.delete=Sil
+issues.delete.title=Bu konu silinsin mi?
+issues.delete.text=Bu konuyu gerçekten silmek istiyor musunuz? (Bu işlem tüm içeriği kalıcı olarak silecektir. Arşivde tutma niyetiniz varsa silmek yerine kapatmayı düşünün)
issues.tracker=Zaman Takibi
issues.start_tracking_short=Zamanlayıcıyı Başlat
issues.start_tracking=Zaman İzlemeyi Başlat
@@ -1274,11 +1432,14 @@ issues.due_date_form_edit=Düzenle
issues.due_date_form_remove=Kaldır
issues.due_date_not_writer=Bir konunun bitiş tarihini değiştirmek için depoda yazma hakkınız olmalıdır.
issues.due_date_not_set=Bitiş tarihi atanmadı.
-issues.due_date_added=%[2]s %[1]s bitiş tarihini ekledi
+issues.due_date_added=bitiş tarihini %s olarak %s ekledi
+issues.due_date_modified=bitiş tarihini %[2]s iken %[1]s olarak %[3]s değiştirdi
issues.due_date_remove=%[2]s %[1]s bitiş tarihini kaldırdı
issues.due_date_overdue=Süresi Geçmiş
issues.due_date_invalid=Bitiş tarihi geçersiz veya aralık dışında. Lütfen 'yyyy-aa-gg' biçimini kullanın.
issues.dependency.title=Bağımlılıklar
+issues.dependency.issue_no_dependencies=Bağımlılık yok.
+issues.dependency.pr_no_dependencies=Bağımlılık yok.
issues.dependency.add=Bağımlılık ekle…
issues.dependency.cancel=İptal
issues.dependency.remove=Kaldır
@@ -1317,6 +1478,7 @@ issues.review.add_review_request=%s tarafından %s inceleme istedi
issues.review.remove_review_request=%s %s için inceleme isteği kaldırıldı
issues.review.remove_review_request_self=%s incelemeyi reddetti
issues.review.pending=Beklemede
+issues.review.pending.tooltip=Bu yorum başkaları tarafından görünmüyor. Bekleyen yorumlarınızı göndermek için, sayfanın üstünde '%s' -> '%s/%s/%s' seçin.
issues.review.review=Gözden Geçir
issues.review.reviewers=Gözden Geçirenler
issues.review.outdated=Eskimiş
@@ -1329,14 +1491,28 @@ issues.review.un_resolve_conversation=Konuşmayı çözme
issues.review.resolved_by=bu konuşmayı çözümlenmiş olarak işaretledi
issues.assignee.error=Beklenmeyen bir hata nedeniyle tüm atananlar eklenmedi.
issues.reference_issue.body=Gövde
+issues.content_history.deleted=silindi
+issues.content_history.edited=düzenlendi
+issues.content_history.created=oluşturuldu
+issues.content_history.delete_from_history=Geçmişten kaldır
+issues.content_history.delete_from_history_confirm=Geçmişten kaldırılsın mı?
+issues.content_history.options=Seçenekler
+issues.reference_link=Referans: %s
compare.compare_base=temel
compare.compare_head=karşılaştır
pulls.desc=Değişiklik isteklerini ve kod incelemelerini etkinleştir.
pulls.new=Yeni Değişiklik İsteği
+pulls.view=Değişiklik İsteği Görüntüle
pulls.compare_changes=Yeni Değişiklik İsteği
+pulls.allow_edits_from_maintainers=Bakımcıların düzenlemelerine izin ver
+pulls.allow_edits_from_maintainers_desc=Ana dala yazma hakkı olan kullanıcılar bu dala da gönderebilirler
+pulls.allow_edits_from_maintainers_err=Güncelleme başarısız oldu
pulls.compare_changes_desc=Birleştirmek için hedef ve kaynak dalı seçin.
+pulls.has_viewed_file=Görüldü
+pulls.has_changed_since_last_review=Son incelemenizden sonra değişti
+pulls.viewed_files_label=%[1]d / %[2]d dosya görüldü
pulls.compare_base=birleştir
pulls.compare_compare=şuradan çek
pulls.switch_comparison_type=Karşılaştırma türünü değiştir
@@ -1345,6 +1521,7 @@ pulls.filter_branch=Dal filtrele
pulls.no_results=Sonuç bulunamadı.
pulls.nothing_to_compare=Bu dallar eşit. Değişiklik isteği oluşturmaya gerek yok.
pulls.nothing_to_compare_and_allow_empty_pr=Bu dallar eşittir. Bu Dİ boş olacak.
+pulls.has_pull_request=`Bu dallar arasında zaten bir değişiklik isteği var: %[2]s#%[3]d`
pulls.create=Değişiklik İsteği Oluştur
pulls.title_desc=%[2]s
içindeki %[1]d işlemeyi %[3]s
ile birleştirmek istiyor
pulls.merged_title_desc=%[4]s %[2]s
içindeki %[1]d işlemeyi %[3]s
ile birleştirdi
@@ -1368,6 +1545,8 @@ pulls.remove_prefix=%s ön ekini kaldır
pulls.data_broken=Bu değişiklik isteği, çatallama bilgilerinin eksik olması nedeniyle bozuldu.
pulls.files_conflicted=Bu değişiklik isteğinde, hedef dalla çakışan değişiklikler var.
pulls.is_checking=Birleştirme çakışması denetimi devam ediyor. Birkaç dakika sonra tekrar deneyin.
+pulls.is_ancestor=Bu dal zaten hedef dalda mevcut. Birleştirilecek bir şey yok.
+pulls.is_empty=Bu daldaki değişiklikler zaten hedef dalda mevcut. Bu boş bir işleme olacaktır.
pulls.required_status_check_failed=Bazı gerekli denetimler başarılı olmadı.
pulls.required_status_check_missing=Gerekli bazı kontroller eksik.
pulls.required_status_check_administrator=Yönetici olarak, bu değişiklik isteğini yine de birleştirebilirsiniz.
@@ -1411,7 +1590,10 @@ pulls.rebase_conflict_summary=Hata Mesajı
; %[2]s
%[3]s
pulls.unrelated_histories=Birleştirme Başarısız: Birleştirme başlığı ve tabanı ortak bir geçmişi paylaşmıyor. İpucu: Farklı bir strateji deneyin
pulls.merge_out_of_date=Birleştirme Başarısız: Birleştirme oluşturulurken, taban güncellendi. İpucu: Tekrar deneyin.
+pulls.head_out_of_date=Birleştirme Başarısız: Birleştirme oluşturulurken, ana güncellendi. İpucu: Tekrar deneyin.
+pulls.push_rejected=Birleştirme Başarısız Oldu: Gönderme reddedildi. Bu depo için Git İstemcilerini inceleyin.
pulls.push_rejected_summary=Tam Red Mesajı
+pulls.push_rejected_no_message=Birleştirme başarısız oldu: Gönderme reddedildi, ancak uzak bir mesaj yoktu.
Bu depo için Git İstemcilerini inceleyin
pulls.open_unmerged_pull_exists=`Aynı özelliklere sahip bekleyen bir değişiklik isteği (#%d) olduğundan yeniden açma işlemini gerçekleştiremezsiniz.`
pulls.status_checking=Bazı denetlemeler beklemede
pulls.status_checks_success=Tüm denetlemeler başarılı oldu
@@ -1431,9 +1613,20 @@ pulls.merge_instruction_hint=`komut satırı talimat
pulls.merge_instruction_step1_desc=Proje deponuzdan yeni bir dala göz atın ve değişiklikleri test edin.
pulls.merge_instruction_step2_desc=Gitea'daki değişiklikleri ve güncellemeleri birleştirin.
+pulls.auto_merge_button_when_succeed=(Denetlemeler başarılı olduğunda)
+pulls.auto_merge_when_succeed=Tüm denetlemeler başarılı olduğundan otomatik olarak birleştir
+pulls.auto_merge_newly_scheduled=Değişiklik İsteği tüm denetlemeler başarılı olduğunda birleştirilecek şekilde ayarlanmış.
+pulls.auto_merge_has_pending_schedule=%[1]s, bu değişiklik isteğini tüm denetlemeler başarılı olduğunda %[2]s, otomatik olarak birleşecek şekilde ayarlamış.
+pulls.auto_merge_cancel_schedule=Otomatik birleştirmeyi iptal et
+pulls.auto_merge_not_scheduled=Bu değişiklik isteği için otomatik birleştirme zamanlanmamış.
+pulls.auto_merge_canceled_schedule=Bu değişiklik isteği için otomatik birleştirme iptal edildi.
+pulls.auto_merge_newly_scheduled_comment=`bu değişiklik isteği, tüm denetlemeler başarılı olduğunda %[1]s, otomatik olarak birleşecek şekilde ayarlandı`
+pulls.auto_merge_canceled_schedule_comment=`bu değişiklik isteğinin, tüm denetlemeler başarılı olduğunda %[1]s, otomatik birleştirmesi iptal edildi`
+pulls.delete.title=Bu değişiklik isteği silinsin mi?
+pulls.delete.text=Bu değişiklik isteğini gerçekten silmek istiyor musunuz? (Bu işlem tüm içeriği kalıcı olarak silecektir. Arşivde tutma niyetiniz varsa silmek yerine kapatmayı düşünün)
milestones.new=Yeni Kilometre Taşı
milestones.closed=Kapalı %s
@@ -1479,6 +1672,7 @@ signing.wont_sign.commitssigned=İlişkili tüm işlemeler imzalanmadığı içi
signing.wont_sign.approved=Değişiklik İsteği onaylanmadığı için birleştirme imzalanmayacak
signing.wont_sign.not_signed_in=Oturum açmadınız
+ext_wiki=Harici Vikiye Erişim
ext_wiki.desc=Harici bir wiki'ye bağlantı.
wiki=Wiki
@@ -1503,6 +1697,7 @@ wiki.page_already_exists=Aynı isimde bir Wiki sayfası zaten var.
wiki.reserved_page='%s' wiki sayfa adı rezerve edilmiştir.
wiki.pages=Sayfalar
wiki.last_updated=Son güncelleme %s
+wiki.page_name_desc=Bu Viki sayfası için bir ad girin. Bazı özel isimler 'Home', '_Sidebar' ve '_Footer' şeklindedir.
activity=Aktivite
activity.period.filter_label=Dönem:
@@ -1572,9 +1767,14 @@ activity.git_stats_deletion_n=%d silme oldu
search=Ara
search.search_repo=Depo ara
+search.type.tooltip=Arama türü
search.fuzzy=Belirsiz
+search.fuzzy.tooltip=Arama terimine benzeyen sonuçları da içer
search.match=Eşleştir
+search.match.tooltip=Sadece arama terimiyle tamamen eşleşen sonuçları içer
search.results="%s" için %s içinde sonuçları ara
+search.code_no_results=Arama teriminizle eşleşen bir kaynak kod bulunamadı.
+search.code_search_unavailable=Kod arama şu an mevcut değil. Lütfen site yöneticisiyle iletişime geçin.
settings=Ayarlar
settings.desc=Ayarlar, depo için ayarları yönetebileceğiniz yerdir
@@ -1586,7 +1786,7 @@ settings.collaboration.read=Oku
settings.collaboration.owner=Sahibi
settings.collaboration.undefined=Belirsiz
settings.hooks=Web İstemcileri
-settings.githooks=Git İstekleri
+settings.githooks=Git İstemcileri
settings.basic_settings=Temel Ayarlar
settings.mirror_settings=Yansıma Ayarları
settings.mirror_settings.docs=Projenizi, değişiklikleri başka bir depoya/depodan otomatik olarak gönderecek ve/veya çekecek şekilde ayarlayın. Dallar, etiketler ve işlemeler otomatik olarak senkronize edilecektir. Depoları nasıl yansıtrım?
@@ -1600,10 +1800,6 @@ settings.mirror_settings.push_mirror.remote_url=Git Uzak Depo URL'si
settings.mirror_settings.push_mirror.add=Yansı Gönderimi Ekle
settings.sync_mirror=Şimdi Eşitle
settings.mirror_sync_in_progress=Yansı senkronizasyonu devam ediyor. Bir dakika sonra tekrar kontrol edin.
-settings.email_notifications.enable=E-posta Bildirimlerini Etkinleştir
-settings.email_notifications.onmention=Sadece Bahsedilen E-posta
-settings.email_notifications.disable=E-posta Bildirimlerini Devre Dışı Bırak
-settings.email_notifications.submit=E-posta Tercihlerini Ayarla
settings.site=Web Sitesi
settings.update_settings=Ayarları Güncelle
settings.branches.update_default_branch=Varsayılan Dalı Değiştir
@@ -1625,6 +1821,9 @@ settings.tracker_url_format_error=Harici konu izleyici URL biçimi geçerli bir
settings.tracker_issue_style=Harici Konu İzleyici Numara Biçimi
settings.tracker_issue_style.numeric=Sayısal
settings.tracker_issue_style.alphanumeric=Alfanumerik
+settings.tracker_issue_style.regexp=Düzenli ifade
+settings.tracker_issue_style.regexp_pattern=Düzenli İfade Kalıbı
+settings.tracker_issue_style.regexp_pattern_desc={index}
yerine ilk eşleşen grup kullanılacaktır.
settings.tracker_url_format_desc=Kullanıcı adı, depo adı ve yayın dizini için {user}
, {repo}
ve {index}
yer tutucularını kullanın.
settings.enable_timetracker=Zaman Takibini Etkinleştir
settings.allow_only_contributors_to_track_time=Sadece Katkıcılar İçin Zaman Takibine İzin Ver
@@ -1636,10 +1835,18 @@ settings.pulls.allow_rebase_merge_commit=Açık birleştirme işlemeleri ile Yen
settings.pulls.allow_squash_commits=İşlemeleri Birleştirmek için Ezmeyi Etkinleştir
settings.pulls.allow_manual_merge=Dİ'yi elle birleştirilmiş olarak işaretlemeyi etkinleştir
settings.pulls.enable_autodetect_manual_merge=Kendiliğinden algılamalı elle birleştirmeyi etkinleştir (Not: Bazı özel durumlarda yanlış kararlar olabilir)
+settings.pulls.allow_rebase_update=Değişiklik isteği dalının yeniden yapılandırmayla güncellenmesine izin ver
settings.pulls.default_delete_branch_after_merge=Varsayılan olarak birleştirmeden sonra değişiklik isteği dalını sil
+settings.packages_desc=Depo Paket Kütüğünü Etkinleştir
settings.projects_desc=Depo Projelerini Etkinleştir
settings.admin_settings=Yönetici Ayarları
settings.admin_enable_health_check=Depo Sağlık Kontrollerini Etkinleştir (git fsck)
+settings.admin_code_indexer=Kod Dizinleyici
+settings.admin_stats_indexer=Kod İstatistiği Dizinleyici
+settings.admin_indexer_commit_sha=Son Dizinlenen SHA
+settings.admin_indexer_unindexed=Dizinlenmemiş
+settings.reindex_button=Yeniden Dizinleme Kuyruğuna Ekle
+settings.reindex_requested=Yeniden Dizinleme İstendi
settings.admin_enable_close_issues_via_commit_in_any_branch=Varsayılan olmayan bir dalda yapılan bir işlemeyle konuyu kapat
settings.danger_zone=Tehlike Alanı
settings.new_owner_has_same_repo=Yeni sahibin aynı isimde başka bir deposu var. Lütfen farklı bir isim seçin.
@@ -1698,6 +1905,7 @@ settings.confirm_delete=Depoyu Sil
settings.add_collaborator=Katkıcı Ekle
settings.add_collaborator_success=Katkıcı eklendi.
settings.add_collaborator_inactive_user=Etkin olmayan bir kullanıcı katkıcı olarak eklenemez.
+settings.add_collaborator_owner=Bir sahip katkıcı olarak eklenemez.
settings.add_collaborator_duplicate=Katkıcı bu depoya zaten eklenmiş.
settings.delete_collaborator=Sil
settings.collaborator_deletion=Katkıcıyı Sil
@@ -1728,6 +1936,9 @@ settings.webhook.response=Cevaplar
settings.webhook.headers=Başlıklar
settings.webhook.payload=İçerik
settings.webhook.body=Gövde
+settings.webhook.replay.description=Bu web kancasını tekrar çalıştır.
+settings.webhook.delivery.success=Teslim kuyruğuna bir olay eklendi. Teslim geçmişinde görünmesi birkaç saniye alabilir.
+settings.githooks_desc=Git İstemcileri Git'in kendisi tarafından desteklenmektedir. Özel işlemler ayarlamak için aşağıdaki istemci dosyalarını düzenleyebilirsiniz.
settings.githook_edit_desc=İstek aktif değilse örnek içerik sunulacaktır. İçeriği boş bırakmak, isteği devre dışı bırakmayı beraberinde getirecektir.
settings.githook_name=İstek İsmi
settings.githook_content=İstek İçeriği
@@ -1739,6 +1950,7 @@ settings.content_type=POST İçerik Türü
settings.secret=Gizli
settings.slack_username=Kullanıcı Adı
settings.slack_icon_url=Simge Bağlantısı
+settings.slack_color=Renk
settings.discord_username=Kullanıcı adı
settings.discord_icon_url=Simge URL'si
settings.event_desc=Tetikleyici Açık:
@@ -1752,6 +1964,8 @@ settings.event_delete=Sil
settings.event_delete_desc=Dal veya etiket silindi.
settings.event_fork=Çatalla
settings.event_fork_desc=Depo çatallandı.
+settings.event_wiki=Viki
+settings.event_wiki_desc=Viki sayfası oluşturuldu, adı değiştirildi, düzenlendi veya silindi.
settings.event_release=Sürüm
settings.event_release_desc=Sürüm yayınlandığında, güncellendiğinde veya silindiğinde.
settings.event_push=Çek
@@ -1784,6 +1998,8 @@ settings.event_pull_request_review=Değişiklik İsteği İncelendi
settings.event_pull_request_review_desc=Değişiklik isteği onaylandı, reddedildi veya yorumu incelendi.
settings.event_pull_request_sync=Değişiklik İsteği Senkronize Edildi
settings.event_pull_request_sync_desc=Değişiklik isteği senkronize edildi.
+settings.event_package=Paket
+settings.event_package_desc=Bir depoda paket oluşturuldu veya silindi.
settings.branch_filter=Dal filtresi
settings.branch_filter_desc=Gönderme, dal oluşturma ve dal silme olayları için glob deseni olarak belirtilen dal beyaz listesi. Boşsa veya *
ise, tüm dallar için olaylar raporlanır. Sözdizimi için github.com/gobwas/glob belgelerine bakın. Örnekler: master
, {master,release*}
.
settings.active=Etkin
@@ -1797,6 +2013,23 @@ settings.hook_type=İstek Türü
settings.slack_token=Erişim Anahtarı
settings.slack_domain=Alan Adı
settings.slack_channel=Kanal
+settings.add_web_hook_desc=%s web kancasını deponuza ekleyin.
+settings.web_hook_name_gitea=Gitea
+settings.web_hook_name_gogs=Gogs
+settings.web_hook_name_slack=Slack
+settings.web_hook_name_discord=Discord
+settings.web_hook_name_dingtalk=DingTalk
+settings.web_hook_name_telegram=Telegram
+settings.web_hook_name_matrix=Matrix
+settings.web_hook_name_msteams=Microsoft Teams
+settings.web_hook_name_feishu_or_larksuite=Feishu / Lark Suite
+settings.web_hook_name_feishu=Feishu
+settings.web_hook_name_larksuite=Lark Suite
+settings.web_hook_name_wechatwork=WeCom (Wechat Work)
+settings.web_hook_name_packagist=Packagist
+settings.packagist_username=Packagist kullanıcı adı
+settings.packagist_api_token=API erişim anahtarı
+settings.packagist_package_url=Packagist paket URL'si
settings.deploy_keys=Dağıtım Anahtarları
settings.add_deploy_key=Dağıtım Anahtarı Ekle
settings.deploy_key_desc=Dağıtım anahtarları, depoyu salt okunur çekme yetkisine sahip.
@@ -1924,6 +2157,12 @@ settings.lfs_pointers.inRepo=Depoda
settings.lfs_pointers.exists=Mağazada var
settings.lfs_pointers.accessible=Kullanıcı tarafından erişilebilir
settings.lfs_pointers.associateAccessible=Erişilebilir %d OID ilişkilendirme
+settings.rename_branch_failed_exist=%s dalı zaten mevcut olduğu için dalın adı değiştirilemiyor.
+settings.rename_branch_failed_not_exist=%s dalının adı değiştirilemiyor, çünkü böyle bir dal yok.
+settings.rename_branch_success=%s dalının adı başarılı bir şekilde %s oldu.
+settings.rename_branch_from=önceki dal adı
+settings.rename_branch_to=yeni dal adı
+settings.rename_branch=Dalı yeniden adlandır
diff.browse_source=Kaynağa Gözat
diff.parent=ebeveyn
@@ -1953,6 +2192,9 @@ diff.file_image_height=Yükseklik
diff.file_byte_size=Boyut
diff.file_suppressed=Dosya farkı çok büyük olduğundan ihmal edildi
diff.file_suppressed_line_too_long=Dosya farkları bir veya daha fazla satır çok uzun olduğundan bastırıldı
+diff.too_many_files=Bu fark içinde çok fazla dosya değişikliği olduğu için bazı dosyalar gösterilmiyor
+diff.show_more=Daha Fazla Göster
+diff.load=Fark Yükle
diff.generated=üretilen
diff.vendored=sağlanmış
diff.comment.placeholder=Yorum Yap
@@ -1972,6 +2214,7 @@ diff.protected=Korumalı
diff.image.side_by_side=Yan Yana
diff.image.swipe=Kaydır
diff.image.overlay=Arayüz
+diff.has_escaped=Bu satırda gizli evrensel kod karakterler var
releases.desc=Proje sürümlerini ve indirmeleri takip edin.
release.releases=Sürümler
@@ -2042,10 +2285,15 @@ branch.included_desc=Bu dal varsayılan dalın bir parçasıdır
branch.included=Dahil
branch.create_new_branch=Şu daldan dal oluştur:
branch.confirm_create_branch=Dal oluştur
+branch.create_branch_operation=Dal oluştur
branch.new_branch=Yeni dal oluştur
branch.new_branch_from='%s' dalından yeni dal oluştur
+branch.renamed=%s dalının adı %s olarak değiştirildi.
tag.create_tag=%s etiketi oluştur
+tag.create_tag_operation=Etiket oluştur
+tag.confirm_create_tag=Etiket oluştur
+tag.create_tag_from='%s' kullanarak yeni etiket oluştur
tag.create_success='%s' etiketi oluşturuldu.
@@ -2054,6 +2302,8 @@ topic.done=Bitti
topic.count_prompt=25'ten fazla konu seçemezsiniz
topic.format_prompt=Konular bir harf veya rakamla başlamalı, kısa çizgiler ('-') içerebilir ve en fazla 35 karakter uzunluğunda olabilir.
+find_file.go_to_file=Dosyaya git
+find_file.no_matching=Eşleşen dosya bulunamadı
error.csv.too_large=Bu dosya çok büyük olduğu için işlenemiyor.
error.csv.unexpected=%d satırı ve %d sütununda beklenmeyen bir karakter içerdiğinden bu dosya işlenemiyor.
@@ -2067,6 +2317,7 @@ create_org=Organizasyon Oluştur
repo_updated=Güncellendi
people=İnsanlar
teams=Takımlar
+code=Kod
lower_members=üyeler
lower_repositories=depo
create_new_team=Yeni Takım
@@ -2134,7 +2385,13 @@ teams.leave=Ayrıl
teams.leave.detail=%s bırakılsın mı?
teams.can_create_org_repo=Depoları oluştur
teams.can_create_org_repo_helper=Üyeler organizasyonda yeni depolar oluşturabilirler. Oluşturan yeni depoya yönetici erişimi sağlayacak.
+teams.none_access=Erişim Yok
+teams.none_access_helper=Üyeler bu birimi görüntüleyemez veya üzerinde başka bir işlem yapamaz.
+teams.general_access=Genel Erişim
+teams.general_access_helper=Üyelerin izinleri aşağıdaki izin tablosuna göre kararlaştırılacaktır.
+teams.read_access=Okuma
teams.read_access_helper=Üyeler, takım depolarını görüntüleyebilir ve klonlayabilir.
+teams.write_access=Yazma
teams.write_access_helper=Üyeler takım depolarını okuyabilir ve itme yapabilir.
teams.admin_access=Yönetici Erişimi
teams.admin_access_helper=Üyeler takım depolarını çekip itebilir ve katkıcı ekleyebilir.
@@ -2185,9 +2442,11 @@ first_page=İlk
last_page=Son
total=Toplam: %d
+dashboard.new_version_hint=Gitea %s şimdi hazır, %s çalıştırıyorsunuz. Ayrıntılar için blog'a bakabilirsiniz.
dashboard.statistic=Özet
dashboard.operations=Bakım İşlemleri
dashboard.system_status=Sistem Durumu
+dashboard.statistic_info=Gitea veritabanında %d kullanıcılar, %d organizasyonlar, %d açık anahtarlar, %d depolar, %d izlemeler, %d yıldızlar, ~%d eylemler, %d erişimler, %d konular, %d yorumlar, %d sosyal hesaplar, %d takipler, %d yansılar, %d sürümler, %d kimlik doğrulama kaynakları, %d web istemcileri, %d dönüm noktaları, %d etiketler, %d istemci görevler, %d takımlar, %d güncelleme görevleri, %d ekler bulunuyor.
dashboard.operation_name=İşlem Adı
dashboard.operation_switch=Geç
dashboard.operation_run=Çalıştır
@@ -2226,6 +2485,7 @@ dashboard.resync_all_hooks=Tüm depoların alma öncesi, güncelleme ve alma son
dashboard.reinit_missing_repos=Kayıtları bulunanlar için tüm eksik Git depolarını yeniden başlat
dashboard.sync_external_users=Harici kullanıcı verisini senkronize et
dashboard.cleanup_hook_task_table=Hook_task tablosunu temizleme
+dashboard.cleanup_packages=Süresi dolmuş paketleri temizleme
dashboard.server_uptime=Sunucunun Ayakta Kalma Süresi
dashboard.current_goroutine=Güncel Goroutine'ler
dashboard.current_memory_usage=Güncel Bellek Kullanımı
@@ -2257,6 +2517,8 @@ dashboard.last_gc_pause=Son GC Durması
dashboard.gc_times=GC Zamanları
dashboard.delete_old_actions=Veritabanından tüm eski eylemleri sil
dashboard.delete_old_actions.started=Veritabanından başlatılan tüm eski eylemleri silin.
+dashboard.update_checker=Denetleyiciyi güncelle
+dashboard.delete_old_system_notices=Veritabanından tüm eski sistem bildirimlerini sil
users.user_manage_panel=Kullanıcı Hesap Yönetimi
users.new_account=Yeni Kullanıcı Hesabı
@@ -2291,10 +2553,26 @@ users.allow_import_local=Yerel Depoları Alabilir
users.allow_create_organization=Organizasyon Oluşturabilir
users.update_profile=Kullanıcı Hesabını Güncelle
users.delete_account=Kullanıcı Hesabını Sil
+users.cannot_delete_self=Kendinizi silemezsiniz
users.still_own_repo=Bu kullanıcı hala bir veya daha fazla depoya sahip. Önce bu depoları silin veya transfer edin.
users.still_has_org=Bu kullanıcı bir organizasyonun üyesidir. Önce kullanıcıyı tüm organizasyonlardan çıkarın.
+users.purge=Kullanıcıyı Temizle
+users.purge_help=Kullanıcıyı ve sahip olduğu herhangi bir depoyu, organizasyonu ve paketleri zorla sil. Tüm yorumlar da silinecektir.
+users.still_own_packages=Kullanıcının bir veya daha fazla paketi var. Önce bu paketleri silin.
users.deletion_success=Kullanıcı hesabı silindi.
users.reset_2fa=2FD'yi sıfırla
+users.list_status_filter.menu_text=Filtre
+users.list_status_filter.reset=Sıfırla
+users.list_status_filter.is_active=Etkin
+users.list_status_filter.not_active=Etkin değil
+users.list_status_filter.is_admin=Yönetici
+users.list_status_filter.not_admin=Yönetici Değil
+users.list_status_filter.is_restricted=Kısıtlanmış
+users.list_status_filter.not_restricted=Kısıtlanmamış
+users.list_status_filter.is_prohibit_login=Oturum Açmayı Önle
+users.list_status_filter.not_prohibit_login=Oturum Açmaya İzin Ver
+users.list_status_filter.is_2fa_enabled=2FA Etkin
+users.list_status_filter.not_2fa_enabled=2FA Devre Dışı
emails.email_manage_panel=Kullanıcı E-posta Yönetimi
emails.primary=Birincil
@@ -2327,6 +2605,16 @@ repos.forks=Çatallar
repos.issues=Konular
repos.size=Boyut
+packages.package_manage_panel=Paket Yönetimi
+packages.total_size=Toplam Boyut: %s
+packages.owner=Sahibi
+packages.creator=Oluşturan
+packages.name=İsim
+packages.version=Sürüm
+packages.type=Tür
+packages.repository=Depo
+packages.size=Boyut
+packages.published=Yayınlandı
defaulthooks=Varsayılan Web İstemcileri
defaulthooks.desc=Web İstemcileri, belirli Gitea olayları tetiklendiğinde otomatik olarak HTTP POST isteklerini sunucuya yapar. Burada tanımlanan Web İstemcileri varsayılandır ve tüm yeni depolara kopyalanır. web istemcileri kılavuzunda daha fazla bilgi edinin.
@@ -2370,9 +2658,13 @@ auths.filter=Kullanıcı Filtresi
auths.admin_filter=Yönetici Filtresi
auths.restricted_filter=Kısıtlı Süzgeç
auths.restricted_filter_helper=Hiçbir kullanıcıyı kısıtlı olarak ayarlamamak için boş bırakın. Yönetici Süzgeci ile eşleşmeyen tüm kullanıcıları kısıtlanmış olarak ayarlamak için yıldız işareti ('*') kullanın.
+auths.verify_group_membership=LDAP'ta grup üyeliğini doğrula (atlamak için filtreyi boş bırakın)
auths.group_search_base=Grup Arama Tabanı DN
auths.group_attribute_list_users=Kullanıcı Listesi İçeren Grup Özelliği
auths.user_attribute_in_group=Grupta Listelenen Kullanıcı Özelliği
+auths.map_group_to_team=LDAP gruplarını Organizasyon takımlarına eşle (atlamak için bu alanı boş bırakın)
+auths.map_group_to_team_removal=Eğer kullanıcı ilişkili LDAP grubuna ait değilse, kullanıcıları eşleşmiş takımlardan çıkarın
+auths.enable_ldap_groups=LDAP gruplarını etkinleştir
auths.ms_ad_sa=MS AD Arama Nitelikleri
auths.smtp_auth=SMTP Yetkilendirme Türü
auths.smtphost=SMTP Sunucusu
@@ -2400,6 +2692,14 @@ auths.oauth2_emailURL=E-posta URL'si
auths.skip_local_two_fa=Yerel 2FA'yı atla
auths.skip_local_two_fa_helper=Bunu seçmediğinizde, 2FA ayarlamış olan yerel kullanıcıların, giriş yapabilmek için 2FA'yı yine de geçmeleri gerekiyor
auths.oauth2_tenant=Kiracı
+auths.oauth2_scopes=Ek Kapsamlar
+auths.oauth2_required_claim_name=Gerekli Talep İsmi
+auths.oauth2_required_claim_name_helper=Bu ismi, bu kaynağa oturum açmayı bu isimdeki talebe sahip kullanıcıların girişiyle sınırlamak için ayarlayın
+auths.oauth2_required_claim_value=Gerekli Talep Değeri
+auths.oauth2_required_claim_value_helper=Bu değeri, bu kaynağa oturum açmayı bu isimdeki ve değerdeki talebe sahip kullanıcıların girişiyle sınırlamak için ayarlayın
+auths.oauth2_group_claim_name=Talep ismi bu kaynak için grup isimlerini sağlıyor. (İsteğe bağlı)
+auths.oauth2_admin_group=Yönetici kullanıcıları için Grup Talep değeri. (İsteğe bağlı, yukarıda talep ismine gerek duyar)
+auths.oauth2_restricted_group=Kısıtlı kullanıcılar için Grup Talep değeri. (İsteğe bağlı, yukarıda talep ismine gerek duyar)
auths.enable_auto_register=Otomatik Kaydolmayı Etkinleştir
auths.sspi_auto_create_users=Kullanıcıları otomatik olarak oluştur
auths.sspi_auto_create_users_helper=SSPI kimlik doğrulama yönteminin ilk kez oturum açan kullanıcılar için otomatik olarak yeni hesaplar oluşturmasına izin ver
@@ -2447,6 +2747,7 @@ config.app_ver=Gitea Sürümü
config.app_url=Gitea Taban URL'si
config.custom_conf=Yapılandırma Dosyası Yolu
config.custom_file_root_path=Özel Dosya Kök Yolu
+config.domain=Sunucu Alan Adı
config.offline_mode=Yerel Kip
config.disable_router_log=Yönlendirici Log'larını Devre Dışı Bırak
config.run_user=Şu Kullanıcı Olarak Çalıştır
@@ -2462,6 +2763,7 @@ config.reverse_auth_user=Tersine Yetkilendirme Kullanıcısı
config.ssh_config=SSH Yapılandırması
config.ssh_enabled=Aktif
config.ssh_start_builtin_server=Yerleşik Sunucuyu Kullan
+config.ssh_domain=SSH Sunucusu Alan Adı
config.ssh_port=Bağlantı Noktası
config.ssh_listen_port=Port'u Dinle
config.ssh_root_path=Kök Yol
@@ -2512,16 +2814,19 @@ config.queue_length=Kuyruk Uzunluğu
config.deliver_timeout=Dağıtım Zaman Aşımı
config.skip_tls_verify=TLS Doğrulamasını Geç
-config.mailer_config=SMTP Mailer Yapılandırması
+config.mailer_config=Mailer Yapılandırması
config.mailer_enabled=Aktif
-config.mailer_disable_helo=HELO'yu Devre Dışı Bırak
+config.mailer_enable_helo=HELO'yu etkinleştir
config.mailer_name=İsim
-config.mailer_host=Sunucu
+config.mailer_protocol=Protokol
+config.mailer_smtp_addr=SMTP Adresi
+config.mailer_smtp_port=SMTP Portu
config.mailer_user=Kullanıcı
config.mailer_use_sendmail=Sendmail Kullan
config.mailer_sendmail_path=Sendmail Yolu
config.mailer_sendmail_args=Sendmail İçin İlave Değişkenler
config.mailer_sendmail_timeout=Sendmail Zaman Aşımı
+config.mailer_use_dummy=Sahte
config.test_email_placeholder=E-posta (ör. test@example.com)
config.send_test_mail=Test E-postası Gönder
config.test_mail_failed='%s' adresine test e-postası gönderilemedi: %v
@@ -2574,6 +2879,7 @@ config.access_log_template=Şablon
config.xorm_log_mode=XORM Günlük Kipi
config.xorm_log_sql=SQL Günlüğü
+
monitor.cron=Cron Görevleri
monitor.name=İsim
monitor.schedule=Program
@@ -2581,12 +2887,16 @@ monitor.next=Sonraki Zaman
monitor.previous=Önceki Zaman
monitor.execute_times=Çalıştırma
monitor.process=Çalışan Süreçler
+monitor.stacktrace=Yığın izleme
+monitor.goroutines=%d Gorutinleri
monitor.desc=Açıklama
monitor.start=Başlangıç Zamanı
monitor.execute_time=Çalıştırma Zamanı
+monitor.last_execution_result=Sonuç
monitor.process.cancel=İşlemi iptal et
monitor.process.cancel_desc=Bir işlemi iptal etmek veri kaybına neden olabilir
monitor.process.cancel_notices=İptal et: %s?
+monitor.process.children=Çocuklar
monitor.queues=Kuyruklar
monitor.queue=Kuyruk: %s
monitor.queue.name=İsim
@@ -2594,6 +2904,7 @@ monitor.queue.type=Tür
monitor.queue.exemplar=Örnek Türü
monitor.queue.numberworkers=Çalışan Sayısı
monitor.queue.maxnumberworkers=En Fazla Çalışan Sayısı
+monitor.queue.numberinqueue=Kuyruktaki Sayı
monitor.queue.review=Yapılandırmayı İncele
monitor.queue.review_add=Çalışanları İncele/Ekle
monitor.queue.configuration=Başlangıç Yapılandırması
@@ -2601,6 +2912,7 @@ monitor.queue.nopool.title=Çalışan Havuzu Yok
monitor.queue.nopool.desc=Bu kuyruk diğer kuyrukları sarar ve kendisinin bir işçi havuzu yoktur.
monitor.queue.wrapped.desc=Sarılmış bir kuyruk, yavaş bir başlangıç kuyruğunu sararak kanaldaki kuyruk isteklerini arabelleğe alır. Bir işçi havuzu yoktur.
monitor.queue.persistable-channel.desc=Kesintisiz bir kanal, kendi alt havuzuna sahip bir kanal kuyruğu ve önceki kapanmalardan gelen kalıcı istekler için bir seviye kuyruğu olan iki kuyruğu sarar. Bir işçi havuzu yoktur.
+monitor.queue.flush=Çalışanı boşalt
monitor.queue.pool.timeout=Zaman aşımı
monitor.queue.pool.addworkers.title=Çalışan Ekle
monitor.queue.pool.addworkers.submit=Çalışan Ekle
@@ -2613,6 +2925,12 @@ monitor.queue.pool.flush.title=Kuyruk Temizleme
monitor.queue.pool.flush.desc=Temizleme, kuyruk boş olduğunda veya zaman aşımına uğradığında sona erecek bir işçi ekler.
monitor.queue.pool.flush.submit=Temizleme İşçisi Ekle
monitor.queue.pool.flush.added=%[1]s için Temizleme İşçisi eklendi
+monitor.queue.pool.pause.title=Kuyruğu Duraklat
+monitor.queue.pool.pause.desc=Kuyruğun duraklatılması veriyi işlemesini önleyecektir
+monitor.queue.pool.pause.submit=Kuyruğu Duraklat
+monitor.queue.pool.resume.title=Kuyruğu Sürdür
+monitor.queue.pool.resume.desc=Bu kuyruğun çalışmasını sürdür
+monitor.queue.pool.resume.submit=Kuyruğu Sürdür
monitor.queue.settings.title=Havuz Ayarları
monitor.queue.settings.desc=Havuzlar, çalışan kuyruğunun engellenmesine yanıt olarak dinamik bir şekilde büyür. Bu değişiklikler mevcut çalışan gruplarını etkilemeyecektir.
@@ -2658,14 +2976,34 @@ notices.delete_success=Sistem bildirimleri silindi.
[action]
create_repo=depo %s oluşturuldu
rename_repo=%[1]s
olan depo adını %[3]s buna çevirdi
+commit_repo=%[4]s deposuna %[3]s dalını gönderdi
+create_issue=`%[3]s#%[2]s konusunu açtı`
+close_issue=`%[3]s#%[2]s konusunu kapattı`
+reopen_issue=`%[3]s#%[2]s konusunu tekrar açtı`
+create_pull_request=`%[3]s#%[2]s değişiklik isteğini oluşturdu`
+close_pull_request=`%[3]s#%[2]s değişiklik isteğini kapattı`
+reopen_pull_request=`%[3]s#%[2]s değişiklik isteğini yeniden açtı`
+comment_issue=`%[3]s#%[2]s konusuna yorum yaptı`
+comment_pull=`%[3]s#%[2]s değişiklik isteğine yorum yaptı`
+merge_pull_request=`%[3]s#%[2]s değişiklik isteğini birleştirdi`
transfer_repo=depo %s
%s'a aktarıldı
+push_tag=%[3]s etiketini %[4]s dalına gönderdi
delete_tag=%[2]s etiketi %[3]s deposundan silindi
delete_branch=%[3]s deposundan %[2]s dalı silindi
compare_branch=Karşılaştır
compare_commits=%d işlemeyi karşılaştır
compare_commits_general=İşlemeleri karşılaştır
+mirror_sync_push=yansıdan %[4]s deposundaki %[3]s dalına işlemeleri eşitledi
+mirror_sync_create=%[3]s yeni referansını, %[4]s olarak yansıdan eşledi
mirror_sync_delete=%[3]s adresindeki %[2]s
referansını eşitledi ve sildi
+approve_pull_request=`%[3]s#%[2]s değişiklik isteğini onayladı`
+reject_pull_request=`%[3]s#%[2]s için değişiklikler önerdi`
+publish_release=`%[3]s deposu için "%[4]s" sürümü yayınlandı`
+review_dismissed=`%[3]s#%[2]s için %[4]s yorumunu reddetti`
review_dismissed_reason=Sebep:
+create_branch=%[4]s deposunda %[3]s dalını oluşturdu
+starred_repo=%[2]s deposuna yıldız bıraktı
+watched_repo=%[2]s deposunu izlemeye başladı
[tool]
ago=%s önce
@@ -2705,6 +3043,9 @@ pin=Pin bildirimi
mark_as_read=Okundu olarak işaretle
mark_as_unread=Okunmadı olarak işaretle
mark_all_as_read=Tümünü okundu olarak işaretle
+subscriptions=Abonelikler
+watching=İzleniyor
+no_subscriptions=Abonelik yok
[gpg]
default_key=Varsayılan anahtarla imzalanmış
@@ -2718,8 +3059,107 @@ error.probable_bad_signature=UYARI! Veritabanında bu kimliğe sahip bir anahtar
error.probable_bad_default_signature=UYARI! Varsayılan anahtarın bu kimliği olmasına rağmen, bu işlemeyi doğrulamaz! Bu işleme ŞÜPHELİDİR.
[units]
+unit=Birim
error.no_unit_allowed_repo=Bu deponun hiçbir bölümüne erişme izniniz yok.
error.unit_not_allowed=Bu depo bölümüne erişme izniniz yok.
[packages]
+title=Paketler
+desc=Depo paketlerini yönet.
+empty=Henüz hiçbir paket yok.
+empty.documentation=Paket kütüğü hakkında daha fazla bilgi için, belgeye bakabilirsiniz.
+empty.repo=Bir paket yüklediniz ama burada gösterilmiyor mu? Paket ayarlarına gidin ve bu depoya bağlantı verin.
+filter.type=Tür
+filter.type.all=Tümü
+filter.no_result=Filtreniz herhangi bir sonuç döndürmedi.
+filter.container.tagged=Etiketlenmiş
+filter.container.untagged=Etiketlenmemiş
+published_by=%[1]s, %[3]s tarafından yayınlandı
+published_by_in=%[1]s, %[3]s tarafından %[5]s içerisinde yayınlanmış
+installation=Kurulum
+about=Bu paket hakkında
+requirements=Gereksinimler
+dependencies=Bağımlılıklar
+keywords=Anahtar Kelimeler
+details=Ayrıntılar
+details.author=Yazar
+details.project_site=Proje Web Sitesi
+details.license=Lisans
+assets=Varlıklar
+versions=Sürümler
+versions.on=açık
+versions.view_all=Tümünü görüntüle
+dependency.id=Kimlik
+dependency.version=Sürüm
+composer.registry=Bu kütüğü ~/.composer/config.json
dosyasında ayarlayın:
+composer.install=Paketi Composer ile kurmak için, şu komutu çalıştırın:
+composer.documentation=Composer kütüğü hakkında daha fazla bilgi için, belgeye bakabilirsiniz.
+composer.dependencies=Bağımlılıklar
+composer.dependencies.development=Geliştirme Bağımlılıkları
+conan.details.repository=Depo
+conan.registry=Bu kütüğü komut satırını kullanarak kurun:
+conan.install=Conan ile paket kurmak için aşağıdaki komutu çalıştırın:
+conan.documentation=Conan kütüğü hakkında daha fazla bilgi için, belgeye bakabilirsiniz.
+container.details.type=Görüntü Türü
+container.details.platform=Platform
+container.details.repository_site=Depo Sitesi
+container.details.documentation_site=Belge Sitesi
+container.pull=Görüntüyü komut satırını kullanarak çekin:
+container.digest=Özet:
+container.documentation=Taşıyıcı kütüğü hakkında daha fazla bilgi için, belgeye bakabilirsiniz.
+container.multi_arch=İşletim Sistemi / Mimari
+container.layers=Görüntü Katmanları
+container.labels=Etiketler
+container.labels.key=Anahtar
+container.labels.value=Değer
+generic.download=Paketi komut satırında indirin:
+generic.documentation=Genel kütük hakkında daha fazla bilgi için, belgeye bakabilirsiniz.
+helm.registry=Bu kütüğü komut satırını kullanarak kurun:
+helm.install=Paketi kurmak için, aşağıdaki komutu çalıştırın:
+helm.documentation=Helm kütüğü hakkında daha fazla bilgi için, belgeye bakabilirsiniz.
+maven.registry=Bu kütüğü projenizdeki pom.xml
dosyasında ayarlayın:
+maven.install=Paketi kullanmak için aşağıdaki dependencies
parçasını pom.xml
dosyasınıza ekleyin:
+maven.install2=Komut satırında çalıştırın:
+maven.download=Bağımlılığı indirmek için, komut satırında çalıştırın:
+maven.documentation=Maven kütüğü hakkında daha fazla bilgi için, belgeye bakabilirsiniz.
+nuget.registry=Bu kütüğü komut satırını kullanarak kurun:
+nuget.install=Paketi NuGet ile kurmak için, şu komutu çalıştırın:
+nuget.documentation=NuGet kütüğü hakkında daha fazla bilgi için, belgeye bakabilirsiniz.
+nuget.dependency.framework=Hedef Çerçeve
+npm.registry=Bu kütüğü projenizdeki .npmrc
dosyasında ayarlayın:
+npm.install=Paketi npm ile kurmak için, şu komutu çalıştırın:
+npm.install2=veya paketi package.json dosyasına ekleyin:
+npm.documentation=Npm kütüğü hakkında daha fazla bilgi için, belgeye bakabilirsiniz.
+npm.dependencies=Bağımlılıklar
+npm.dependencies.development=Geliştirme Bağımlılıkları
+npm.dependencies.peer=Eş Bağımlılıkları
+npm.dependencies.optional=İsteğe Bağlı Bağımlılıklar
+npm.details.tag=Etiket
+pub.install=Paketi Dart ile kurmak için, şu komutu çalıştırın:
+pub.documentation=Pub kütüğü hakkında daha fazla bilgi için, belgeye bakabilirsiniz.
+pub.details.repository_site=Depo Sitesi
+pub.details.documentation_site=Belge Sitesi
+pypi.requires=Gereken Python
+pypi.install=Paketi pip ile kurmak için, şu komutu çalıştırın:
+pypi.documentation=PyPI kütüğü hakkında daha fazla bilgi için, belgeye bakabilirsiniz.
+rubygems.install=Paketi gem ile kurmak için, şu komutu çalıştırın:
+rubygems.install2=veya paketi Gemfile dosyasına ekleyin:
+rubygems.dependencies.runtime=Çalışma Zamanı Bağımlılıkları
+rubygems.dependencies.development=Geliştirme Bağımlılıkları
+rubygems.required.ruby=Gereken Ruby sürümü
+rubygems.required.rubygems=Gereken RubyGem sürümü
+rubygems.documentation=RubyGems kütüğü hakkında daha fazla bilgi için, belgeye bakabilirsiniz.
+vagrant.install=Vagrant paketi eklemek için aşağıdaki komutu çalıştırın:
+vagrant.documentation=Vagrant kütüğü hakkında daha fazla bilgi için, belgeye bakabilirsiniz.
+settings.link=Bu paketi bir depoya bağlayın
+settings.link.description=Eğer bir paketi bir depoya bağlarsanız, paket deponun paket listesinde listelenecektir.
+settings.link.select=Depo Seç
+settings.link.button=Depo Bağlantısını Güncelle
+settings.link.success=Depo bağlantısı başarıyla güncellendi.
+settings.link.error=Depo bağlantısı güncellenemedi.
+settings.delete=Paket Sil
+settings.delete.description=Bir paketi silmek kalıcıdır ve geri alınamaz.
+settings.delete.notice=%s (%s) paketini silmek üzeresiniz. Bu işlem geri alınamaz, emin misiniz?
+settings.delete.success=Paket silindi.
+settings.delete.error=Paket silinemedi.
diff --git a/options/locale/locale_uk-UA.ini b/options/locale/locale_uk-UA.ini
index 7fbb0b62d1..a98f35dd7f 100644
--- a/options/locale/locale_uk-UA.ini
+++ b/options/locale/locale_uk-UA.ini
@@ -8,7 +8,6 @@ sign_out=Вийти
sign_up=Реєстрація
link_account=Прив'язати обліковий запис
register=Реєстрація
-website=Веб-сайт
version=Версія
powered_by=Працює на %s
page=Сторінка
@@ -161,7 +160,6 @@ log_root_path_helper=Файли журналу будуть записані в
optional_title=Додаткові налаштування
email_title=Налаштування Email
-smtp_host=SMTP хост
smtp_from=Відправляти Email від імені
smtp_from_helper=Електронна пошта для використання в Gіtea. Введіть звичайну електронну адресу або використовуйте формат: "Ім'я" .
mailer_user=SMTP Ім'я кристувача
@@ -259,6 +257,7 @@ code_no_results=Відповідний пошуковому запитанню
code_search_results=Результати пошуку '%s'
code_last_indexed_at=Останні індексовані %s
+
[auth]
create_new_account=Реєстрація облікового запису
register_helper_msg=Вже зареєстровані? Увійдіть зараз!
@@ -357,7 +356,7 @@ issue.action.force_push=%[1]s force-pushed %[2]s з %[3]s в %[4]s
issue.action.push_1=@%[1]s надіслав %[3]d коміти %[2]s
issue.action.push_n=@%[1]s відправив %[3]d коміти до %[2]s
issue.action.close=@%[1]s закрито #%[2]d.
-issue.action.reopen=@%[1] заново відкрив #%[2]d.
+issue.action.reopen=@%[1]s заново відкрив #%[2]d.
issue.action.merge=@%[1]s об'єднав #%[2]d до %[3]s.
issue.action.approve=@%[1]s затвердили цей запит на злиття.
issue.action.reject=@%[1]s запитують зміни на цей запит на злиття.
@@ -1650,10 +1649,6 @@ settings.mirror_settings.push_mirror.remote_url=URL віддаленого ре
settings.mirror_settings.push_mirror.add=Додати Push дзеркало
settings.sync_mirror=Синхронізувати зараз
settings.mirror_sync_in_progress=Синхронізуються репозиторії-дзеркала. Зачекайте хвилину і обновіть сторінку.
-settings.email_notifications.enable=Увімкнути сповіщення email
-settings.email_notifications.onmention=Повідомнення email тільки при згадуванні
-settings.email_notifications.disable=Вимкнути email сповіщення
-settings.email_notifications.submit=Налаштувати параметри email
settings.site=Веб-сайт
settings.update_settings=Оновити налаштування
settings.branches.update_default_branch=Оновити гілку за замовчуванням
@@ -2586,11 +2581,8 @@ config.queue_length=Довжина черги
config.deliver_timeout=Затримка доставки
config.skip_tls_verify=Пропустити перевірку TLS
-config.mailer_config=Конфігурація SMTP-сервера
config.mailer_enabled=Увімкнено
-config.mailer_disable_helo=Вимкнути HELO
config.mailer_name=Ім'я
-config.mailer_host=Хост
config.mailer_user=Користувач
config.mailer_use_sendmail=Використовувати Sendmail
config.mailer_sendmail_path=Шлях до Sendmail
@@ -2648,6 +2640,7 @@ config.access_log_template=Шаблон
config.xorm_log_mode=XORM-режим запису журналу
config.xorm_log_sql=Журнал SQL
+
monitor.cron=Завдання cron
monitor.name=Ім'я
monitor.schedule=Розклад
diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini
index 2823b9a2eb..99202b9e17 100644
--- a/options/locale/locale_zh-CN.ini
+++ b/options/locale/locale_zh-CN.ini
@@ -9,7 +9,6 @@ sign_out=退出
sign_up=注册
link_account=链接账户
register=注册
-website=官方网站
version=当前版本
powered_by=Powered by %s
page=页面
@@ -20,7 +19,7 @@ active_stopwatch=活动时间跟踪器
create_new=创建…
user_profile_and_more=个人信息和配置
signed_in_as=已登录用户
-enable_javascript=使用 JavaScript能使本网站更好的工作。
+enable_javascript=使用 JavaScript 能使本网站更好的工作。
toc=目录
licenses=许可证
return_to_gitea=返回 Gitea
@@ -179,9 +178,10 @@ log_root_path_helper=日志文件将写入此目录。
optional_title=可选设置
email_title=电子邮箱设置
-smtp_host=SMTP 主机
+smtp_addr=SMTP 主机地址
+smtp_port=SMTP 端口
smtp_from=电子邮件发件人
-smtp_from_helper=电子邮件地址 Gitea 将使用。输入一个普通的电子邮件地址或使用 "名称" 格式。
+smtp_from_helper=请输入一个用于 Gitea 的电子邮件地址,或者使用完整格式:"名称"
mailer_user=SMTP 用户名
mailer_password=SMTP 密码
register_confirm=需要发电子邮件确认注册
@@ -268,8 +268,11 @@ users=用户
organizations=组织
search=搜索
code=代码
+search.type.tooltip=搜索类型
search.fuzzy=模糊
+search.fuzzy.tooltip=包含近似匹配搜索词的结果
search.match=匹配
+search.match.tooltip=仅包含精确匹配搜索词的结果
code_search_unavailable=当前代码搜索不可用。请与网站管理员联系。
repo_no_results=未找到匹配的仓库。
user_no_results=未找到匹配的用户。
@@ -277,6 +280,9 @@ org_no_results=未找到匹配的组织。
code_no_results=未找到与搜索字词匹配的源代码。
code_search_results=“%s” 的搜索结果是
code_last_indexed_at=最后索引于 %s
+relevant_repositories_tooltip=派生的仓库,以及缺少主题、图标和描述的仓库将被隐藏。
+relevant_repositories=只显示相关的仓库, 显示未过滤结果。
+
[auth]
create_new_account=注册帐号
@@ -504,6 +510,7 @@ activity=公开活动
followers=关注者
starred=已点赞
watched=已关注仓库
+code=代码
projects=项目
following=关注中
follow=关注
@@ -799,6 +806,7 @@ email_notifications.enable=启用邮件通知
email_notifications.onmention=只在被提到时邮件通知
email_notifications.disable=停用邮件通知
email_notifications.submit=邮件通知设置
+email_notifications.andyourown=和您自己的通知
visibility=用户可见性
visibility.public=公开
@@ -864,7 +872,7 @@ mirror_prune_desc=删除过时的远程跟踪引用
mirror_interval=镜像间隔 (有效的时间单位是 'h', 'm', 's')。0 禁用自动定期同步 (最短间隔: %s)
mirror_interval_invalid=镜像间隔无效。
mirror_sync_on_commit=推送提交时同步
-mirror_address=从URL克隆
+mirror_address=从 URL 克隆
mirror_address_desc=在授权框中输入必要的凭据。
mirror_address_url_invalid=URL无效。请检查您所输入的URL是否正确。
mirror_address_protocol_invalid=提供的 url 无效。只能从 http(s):// 或 git:// 位置进行镜像。
@@ -932,6 +940,7 @@ form.name_pattern_not_allowed=仓库名称中不允许使用模式 "%s"。
need_auth=授权
migrate_options=迁移选项
migrate_service=迁移服务
+migrate_options_mirror_helper=该仓库将是一个镜像
migrate_options_lfs=迁移 LFS 文件
migrate_options_lfs_endpoint.label=LFS 网址
migrate_options_lfs_endpoint.description=迁移将尝试使用你的 Git remote 来 确定 LFS 服务器。如果仓库 LFS 数据存储在其他位置,你还可以指定自定义网址。
@@ -1033,13 +1042,13 @@ file_view_rendered=渲染模式
file_view_raw=查看原始文件
file_permalink=永久链接
file_too_large=文件过大,无法显示。
-bidi_bad_header=`此文件包含意外的双向 Unicode 字符!`
-bidi_bad_description=`此文件包含意外的双向 Unicode 字符,其处理方式可能与下面显示的不同。 如果您是有意且合法地使用它们,可以放心地忽略此警告。 使用 Escape 按钮显示隐藏的字符。`
-bidi_bad_description_escaped=`此文件包含意外的双向 Unicode 字符。隐藏的 Unicode 字符在下面被转义。使用 Unescape 按钮来显示它们是如何渲染的。`
-unicode_header=`此文件包含隐藏的 Unicode 字符!`
-unicode_description=`该文件包含隐藏的 Unicode 字符,这些字符的处理方式可能与下面显示的不同。 如果您是有意且合法地使用它们,可以放心地忽略此警告。 使用 Escape 按钮显示隐藏的字符。`
-unicode_description_escaped=`此文件包含隐藏的 Unicode 字符。隐藏的 unicode 字符在下面被转义。请使用 Unescape 按钮来显示它们是如何渲染的。`
-line_unicode=`这一行有隐藏的 Unicode 字符`
+invisible_runes_header=`此文件包含不可见的 Unicode 字符!`
+invisible_runes_description=`这个文件包含不可见的 Unicode 字符,其处理方式可能不同于下面显示的字符。 如果您是有意且正当地使用它们,您可以安全地忽略这个警告。使用 Escape 按钮来显示隐藏的字符。
+ambiguous_runes_header=`此行包含模棱两可的 Unicode 字符!`
+ambiguous_runes_description=`此文件包含模棱两可的 Unicode 字符,这些字符可能会与您当前语言环境的其他字符混淆。 如果您是有意且正当地使用它们,您可以安全地忽略这个警告。使用 Escape 按钮来高亮这些字符。`
+invisible_runes_line=`此行含有不可见的 unicode 字符`
+ambiguous_runes_line=`此行有模棱两可的 unicode 字符`
+ambiguous_character=`%[1]c [U+%04[1]X] 容易和 %[2]c [U+%04[2]X] 混淆`
escape_control_characters=Escape
unescape_control_characters=Unescape
@@ -1060,6 +1069,7 @@ normal_view=普通视图
line=行
lines=行
+editor.add_file=添加文件
editor.new_file=新建文件
editor.upload_file=上传文件
editor.edit_file=编辑文件
@@ -1225,6 +1235,8 @@ issues.new.add_reviewer_title=请求审核
issues.choose.get_started=开始
issues.choose.blank=默认模板
issues.choose.blank_about=从默认模板创建一个工单。
+issues.choose.ignore_invalid_templates=已忽略无效模板
+issues.choose.invalid_templates=发现了 %v 个无效模板
issues.no_ref=分支/标记未指定
issues.create=创建工单
issues.new_label=创建标签
@@ -1265,6 +1277,8 @@ issues.filter_milestone=里程碑筛选
issues.filter_milestone_no_select=所有里程碑
issues.filter_assignee=指派人筛选
issues.filter_assginee_no_select=所有指派成员
+issues.filter_poster=作者
+issues.filter_poster_no_select=所有作者
issues.filter_type=类型筛选
issues.filter_type.all_issues=所有工单
issues.filter_type.assigned_to_you=指派给您的
@@ -1419,6 +1433,7 @@ issues.due_date_form_remove=删除
issues.due_date_not_writer=你需要仓库写入权限来修改工单到期时间。
issues.due_date_not_set=未设置到期时间。
issues.due_date_added=于 %[2]s 设置到期时间为 %[1]s
+issues.due_date_modified=将到期日从 %[2]s 修改为 %[1]s %[3]s
issues.due_date_remove=于 %[2]s 删除了到期时间 %[1]s
issues.due_date_overdue=过期
issues.due_date_invalid=到期日期无效或超出范围。请使用 'yyyy-mm-dd' 格式。
@@ -1530,6 +1545,8 @@ pulls.remove_prefix=删除 %s 前缀
pulls.data_broken=此合并请求因为派生仓库信息缺失而中断。
pulls.files_conflicted=此合并请求有变更与目标分支冲突。
pulls.is_checking=正在进行合并冲突检测,请稍后再试。
+pulls.is_ancestor=此分支已经包含在目标分支中,没有什么可以合并。
+pulls.is_empty=此分支上的更改已经在目标分支上。这将是一个空提交。
pulls.required_status_check_failed=一些必要的检查没有成功
pulls.required_status_check_missing=缺少一些必要的检查。
pulls.required_status_check_administrator=作为管理员,您仍可合并此合并请求
@@ -1680,7 +1697,7 @@ wiki.page_already_exists=相同名称的 Wiki 页面已经存在。
wiki.reserved_page=维基名称 '%s' 是被保留的。
wiki.pages=所有页面
wiki.last_updated=最后更新于 %s
-wiki.page_name_desc=输入此 Wiki 页面的名称。特殊名称有:'Home', '_Sidebar ' 和 '_Footer'。
+wiki.page_name_desc=输入此 Wiki 页面的名称。特殊名称有:'Home', '_Sidebar' 和 '_Footer'。
activity=动态
activity.period.filter_label=周期:
@@ -1750,8 +1767,11 @@ activity.git_stats_deletion_n=删除 %d 行
search=搜索
search.search_repo=搜索仓库...
+search.type.tooltip=搜索类型
search.fuzzy=模糊
+search.fuzzy.tooltip=包含近似匹配搜索词的结果
search.match=匹配
+search.match.tooltip=仅包含精确匹配搜索词的结果
search.results=在 %[3]s 中搜索 "%[1]s" 的结果
search.code_no_results=未找到与搜索字词匹配的源代码。
search.code_search_unavailable=当前代码搜索不可用。请与网站管理员联系。
@@ -1780,10 +1800,6 @@ settings.mirror_settings.push_mirror.remote_url=Git 远程仓库链接
settings.mirror_settings.push_mirror.add=添加推送镜像
settings.sync_mirror=同步
settings.mirror_sync_in_progress=镜像同步正在进行中,请稍后再试。
-settings.email_notifications.enable=启用邮件通知
-settings.email_notifications.onmention=只在被提到时邮件通知
-settings.email_notifications.disable=停用邮件通知
-settings.email_notifications.submit=邮件通知设置
settings.site=网站
settings.update_settings=更新仓库设置
settings.branches.update_default_branch=更新默认分支
@@ -1889,6 +1905,7 @@ settings.confirm_delete=删除本仓库
settings.add_collaborator=增加协作者
settings.add_collaborator_success=协作者添加成功!
settings.add_collaborator_inactive_user=无法添加未激活的用户作为合作者。
+settings.add_collaborator_owner=不能将所有者添加为协作者。
settings.add_collaborator_duplicate=合作者已经被添加到本仓库。
settings.delete_collaborator=删除
settings.collaborator_deletion=删除协作者
@@ -1947,6 +1964,8 @@ settings.event_delete=刪除
settings.event_delete_desc=分支或标签已删除。
settings.event_fork=派生
settings.event_fork_desc=仓库被派生。
+settings.event_wiki=百科
+settings.event_wiki_desc=创建、重命名、编辑或删除了百科页面。
settings.event_release=版本发布
settings.event_release_desc=发布、更新或删除版本时。
settings.event_push=推送
@@ -2208,7 +2227,7 @@ release.stable=稳定
release.compare=比较
release.edit=编辑
release.ahead.commits=%d 次提交
-release.ahead.target=到 %s 自发布后
+release.ahead.target=在此版本发布后被加入到 %s
release.source_code=源代码
release.new_subheader=版本发布组织项目的版本。
release.edit_subheader=版本发布组织项目的版本。
@@ -2298,6 +2317,7 @@ create_org=创建组织
repo_updated=最后更新于
people=组织成员
teams=组织团队
+code=代码
lower_members=名成员
lower_repositories=个仓库
create_new_team=新建团队
@@ -2319,7 +2339,7 @@ form.create_org_not_allowed=此账号禁止创建组织
settings=组织设置
settings.options=组织
settings.full_name=组织全名
-settings.website=官方网站
+settings.website=网站
settings.location=所在地区
settings.permission=权限
settings.repoadminchangeteam=仓库管理员可以添加或移除团队的访问权限
@@ -2457,9 +2477,9 @@ dashboard.archive_cleanup=删除旧的仓库存档
dashboard.deleted_branches_cleanup=清理已删除的分支
dashboard.update_migration_poster_id=更新迁移的发表者ID
dashboard.git_gc_repos=对仓库进行垃圾回收
-dashboard.resync_all_sshkeys=使用 Gitea SSH 密钥更新'.ssh/authorized_keys' 文件。
+dashboard.resync_all_sshkeys=使用 Gitea 的 SSH 密钥更新 '.ssh/authorized_keys' 文件。
dashboard.resync_all_sshkeys.desc=(内置的 SSH 服务器不需要。)
-dashboard.resync_all_sshprincipals=使用 Gitea SSH 规则更新 '.ssh/authorized_principals' 文件。
+dashboard.resync_all_sshprincipals=使用 Gitea 的 SSH 规则更新 '.ssh/authorized_principals' 文件。
dashboard.resync_all_sshprincipals.desc=(内置的 SSH 服务器不需要。)
dashboard.resync_all_hooks=重新同步所有仓库的 pre-receive、update 和 post-receive 钩子
dashboard.reinit_missing_repos=重新初始化所有丢失的 Git 仓库存在的记录
@@ -2536,6 +2556,8 @@ users.delete_account=删除帐户
users.cannot_delete_self=你不能删除自己
users.still_own_repo=此用户仍然拥有一个或多个仓库。必须首先删除或转让这些仓库。
users.still_has_org=此用户是组织的成员。必须先从组织中删除用户。
+users.purge=清理用户
+users.purge_help=强制删除用户和用户拥有的任何仓库、组织和软件包。所有评论也将被删除。
users.still_own_packages=此用户仍然拥有一个或多个软件包。请先删除这些软件包。
users.deletion_success=用户帐户已被删除。
users.reset_2fa=重置两步验证
@@ -2792,16 +2814,19 @@ config.queue_length=队列长度
config.deliver_timeout=推送超时
config.skip_tls_verify=跳过 TLS 验证
-config.mailer_config=邮件配置
+config.mailer_config=Mailer 配置
config.mailer_enabled=启用服务
-config.mailer_disable_helo=禁用 HELO 操作
+config.mailer_enable_helo=启用HELO
config.mailer_name=任务名称
-config.mailer_host=邮件主机地址
+config.mailer_protocol=协议
+config.mailer_smtp_addr=SMTP 地址
+config.mailer_smtp_port=SMTP 端口
config.mailer_user=发送者帐号
config.mailer_use_sendmail=使用 Sendmail
config.mailer_sendmail_path=Sendmail 路径
config.mailer_sendmail_args=Sendmail 的额外参数
config.mailer_sendmail_timeout=Sendmail 超时
+config.mailer_use_dummy=Dummy
config.test_email_placeholder=电子邮址 (例如,test@example.com)
config.send_test_mail=发送测试邮件
config.test_mail_failed=发送测试邮件至 '%s' 时失败:%v
@@ -2854,6 +2879,9 @@ config.access_log_template=模板
config.xorm_log_mode=XORM 日志模式
config.xorm_log_sql=日志 SQL
+config.get_setting_failed=获取设置 %s 失败
+config.set_setting_failed=设置 %s 失败
+
monitor.cron=Cron 任务
monitor.name=任务名称
monitor.schedule=任务安排
@@ -2886,6 +2914,7 @@ monitor.queue.nopool.title=没有工作者池
monitor.queue.nopool.desc=此队列包装其它队列,本身没有工作者池。
monitor.queue.wrapped.desc=一个包装队列包装一个启动缓慢队列,缓存队列请求到 channel 中。它本身没有一个工作者池。
monitor.queue.persistable-channel.desc=一个 persistable-channel 队列包装2个队列,一个 channel 队列拥有自己的工作者池,一个 level 队列用于永久存储。它没有自己的工作者池。
+monitor.queue.flush=Flush worker
monitor.queue.pool.timeout=超时
monitor.queue.pool.addworkers.title=新增工作者
monitor.queue.pool.addworkers.submit=新增工作者
@@ -3016,6 +3045,9 @@ pin=Pin 通知
mark_as_read=标记为已读
mark_as_unread=标记为未读
mark_all_as_read=全部标记为已读
+subscriptions=订阅
+watching=关注
+no_subscriptions=无订阅
[gpg]
default_key=使用默认密钥签名
@@ -3038,12 +3070,13 @@ title=软件包
desc=管理仓库软件包。
empty=还没有软件包。
empty.documentation=关于软件包注册中心的更多信息,请参阅 文档 。
+empty.repo=您上传了一个包,但没有显示在这里吗?转到 包设置 并将其链接到这个仓库中。
filter.type=类型
filter.type.all=所有
filter.no_result=您的过滤器没有产生任何结果。
filter.container.tagged=已加标签
filter.container.untagged=未加标签
-published_by=于 %[1]s 发布了 %[3]s
+published_by=由 %[3]s 发布于 %[1]s
published_by_in=%[3]s 于 %[1]s 发布了 %[5]s
installation=安装
about=关于这个软件包
@@ -3074,6 +3107,7 @@ container.details.platform=平台
container.details.repository_site=仓库站点
container.details.documentation_site=文档网站
container.pull=从命令行拉取镜像:
+container.digest=摘要:
container.documentation=关于 Container 注册中心的更多信息,请参阅 文档。
container.multi_arch=OS / Arch
container.layers=镜像层
@@ -3103,6 +3137,10 @@ npm.dependencies.development=开发依赖
npm.dependencies.peer=Peer 依赖
npm.dependencies.optional=可选依赖
npm.details.tag=标签
+pub.install=要使用 Dart 安装软件包,请运行以下命令:
+pub.documentation=关于 Pub 注册中心的信息,请参阅 文档。
+pub.details.repository_site=仓库站点
+pub.details.documentation_site=文档站点
pypi.requires=需要 Python
pypi.install=要使用 pip 安装软件包,请运行以下命令:
pypi.documentation=关于 PyPI 注册中心的信息,请参阅 文档。
@@ -3113,6 +3151,8 @@ rubygems.dependencies.development=开发依赖
rubygems.required.ruby=需要 Ruby 版本
rubygems.required.rubygems=需要 RubyGem 版本
rubygems.documentation=关于 RubyGems 注册中心的更多信息,请参阅 文档。
+vagrant.install=若要添加一个 Vagrant box,请运行以下命令:
+vagrant.documentation=关于 Vagrant 注册中心的更多信息,请参阅 文档。
settings.link=将此软件包链接到仓库
settings.link.description=如果您将一个软件包与一个代码库链接起来,软件包将显示在代码库的软件包列表中。
settings.link.select=选择仓库
diff --git a/options/locale/locale_zh-HK.ini b/options/locale/locale_zh-HK.ini
index c6ea7ce673..ba828365d5 100644
--- a/options/locale/locale_zh-HK.ini
+++ b/options/locale/locale_zh-HK.ini
@@ -6,7 +6,6 @@ sign_in=登入
sign_out=登出
link_account=連結帳戶
register=註冊
-website=網站
version=版本
page=頁面
template=樣板
@@ -64,7 +63,6 @@ repo_path=儲存庫的根目錄
log_root_path=日誌路徑
optional_title=可選設定
-smtp_host=SMTP 主機
federated_avatar_lookup_popup=開啟聯合頭像查詢並使用基於開放源碼的 libravatar 服務
enable_captcha_popup=要求在用戶註冊時輸入驗證碼
admin_password=管理員密碼
@@ -91,6 +89,7 @@ users=使用者
organizations=組織
search=搜尋
+
[auth]
register_helper_msg=已經註冊?立即登錄!
forgot_password_title=忘記密碼
@@ -760,9 +759,7 @@ config.deliver_timeout=推送超時
config.skip_tls_verify=略過 TLS 驗證
config.mailer_enabled=啟用服務
-config.mailer_disable_helo=禁用 HELO 操作
config.mailer_name=發送者名稱
-config.mailer_host=郵件主機地址
config.mailer_user=發送者帳號
config.oauth_config=社交帳號設定
@@ -801,6 +798,7 @@ config.git_gc_timeout=GC 操作超時
config.log_config=日誌設定
config.log_mode=日誌模式
+
monitor.cron=Cron 任務
monitor.name=任務名稱
monitor.schedule=任務安排
diff --git a/options/locale/locale_zh-TW.ini b/options/locale/locale_zh-TW.ini
index f795879e11..0d6e6f617d 100644
--- a/options/locale/locale_zh-TW.ini
+++ b/options/locale/locale_zh-TW.ini
@@ -9,7 +9,6 @@ sign_out=登出
sign_up=註冊
link_account=連結帳戶
register=註冊
-website=網站
version=版本
powered_by=技術提供: %s
page=頁面
@@ -109,7 +108,7 @@ rss_feed=RSS 摘要
[error]
occurred=發生錯誤
-report_message=如果您確定這是一個 Gitea 的 bug,請到 GitHub 搜尋相關的問題,如果有需要您也可以建立新問題。
+report_message=如果您確定這是 Gitea 的 bug,請到 GitHub 搜尋相關的問題,如果有需要您也可以建立新問題。
missing_csrf=錯誤的請求:未提供 CSRF token
invalid_csrf=錯誤的請求:無效的 CSRF token
not_found=找不到目標。
@@ -179,7 +178,8 @@ log_root_path_helper=日誌檔將寫入此目錄。
optional_title=可選設定
email_title=電子郵件設定
-smtp_host=SMTP 主機
+smtp_addr=SMTP 主機
+smtp_port=SMTP 連接埠
smtp_from=電子郵件寄件者
smtp_from_helper=Gitea 將會使用的電子信箱,直接輸入電子信箱或使用「"名稱" 」的格式。
mailer_user=SMTP 帳號
@@ -268,8 +268,11 @@ users=使用者
organizations=組織
search=搜尋
code=程式碼
+search.type.tooltip=搜尋類型
search.fuzzy=模糊
+search.fuzzy.tooltip=包含近似關鍵字的結果
search.match=符合
+search.match.tooltip=只包含完全符合關鍵字的結果
code_search_unavailable=現在無法使用程式碼搜尋。請與網站管理員聯絡。
repo_no_results=沒有找到符合的儲存庫。
user_no_results=沒有找到符合的使用者。
@@ -278,6 +281,7 @@ code_no_results=找不到符合您關鍵字的原始碼。
code_search_results=「%s」的搜尋結果
code_last_indexed_at=最後索引 %s
+
[auth]
create_new_account=註冊帳戶
register_helper_msg=已經有帳戶了?立即登入!
@@ -504,6 +508,7 @@ activity=公開動態
followers=追蹤者
starred=已加星號
watched=關注的儲存庫
+code=程式碼
projects=專案
following=追蹤中
follow=追蹤
@@ -758,7 +763,7 @@ twofa_is_enrolled=您的帳戶已經啟用兩步驟驗證。
twofa_not_enrolled=您的帳戶目前尚未啟用兩步驟驗證。
twofa_disable=停用兩步驟驗證
twofa_scratch_token_regenerate=重新產生備用驗證碼
-twofa_scratch_token_regenerated=您的備用驗證碼是 %s。請將它保存到一個安全的地方。
+twofa_scratch_token_regenerated=您的備用驗證碼是 %s。請將它保存到安全的地方。
twofa_enroll=啟用兩步驟驗證
twofa_disable_note=如有需要,您可以停用兩步驟驗證。
twofa_disable_desc=關閉兩步驟驗證會使您的帳戶安全性降低,是否繼續?
@@ -768,7 +773,7 @@ scan_this_image=使用您的授權應用程式來掃瞄圖片:
or_enter_secret=或者輸入密碼: %s
then_enter_passcode=然後輸入應用程式中顯示的驗證碼:
passcode_invalid=無效的驗證碼,請重試。
-twofa_enrolled=您的帳戶已經啟用了兩步驟驗證。請將備用驗證碼 (%s) 保存到一個安全的地方,它只會顯示這麼一次!
+twofa_enrolled=您的帳戶已經啟用了兩步驟驗證。請將備用驗證碼 (%s) 保存到安全的地方,它只會顯示這麼一次!
twofa_failed_get_secret=取得密鑰 (Secret) 失敗。
webauthn_desc=安全金鑰是包含加密密鑰的硬體設備,它們可以用於兩步驟驗證。安全金鑰必須支援 WebAuthn Authenticator 標準。
@@ -799,6 +804,7 @@ email_notifications.enable=啟用郵件通知
email_notifications.onmention=只在被提到時傳送郵件通知
email_notifications.disable=關閉郵件通知
email_notifications.submit=套用郵件偏好設定
+email_notifications.andyourown=和您自己的通知
visibility=使用者瀏覽權限
visibility.public=公開
@@ -843,14 +849,14 @@ repo_lang=儲存庫語言
repo_gitignore_helper=選擇 .gitignore 範本
repo_gitignore_helper_desc=從常見語言範本清單中挑選忽略追蹤的檔案。預設情況下各種語言建置工具產生的特殊檔案都包含在 .gitignore 中。
issue_labels=問題標籤
-issue_labels_helper=選擇一個問題標籤集
+issue_labels_helper=選擇問題標籤集
license=授權條款
license_helper=請選擇授權條款檔案
license_helper_desc=授權條款定義了他人使用您原始碼的允許和禁止事項。不確定哪個適用於您的專案?查看選擇授權條款。
readme=讀我檔案
readme_helper=選擇讀我檔案範本。
readme_helper_desc=這是您能為專案撰寫完整描述的地方。
-auto_init=初始化儲存庫 (加入 .gitignore、授權條款和讀我檔案)
+auto_init=初始化儲存庫 (加入 .gitignore、授權條款、讀我檔案)
trust_model_helper=選擇簽署驗證的信任模型。可用的選項:
trust_model_helper_collaborator=協作者: 信任協作者的簽署
trust_model_helper_committer=提交者: 信任與提交者相符的簽署
@@ -932,6 +938,7 @@ form.name_pattern_not_allowed=儲存庫名稱不可包含字元「%s」。
need_auth=授權
migrate_options=遷移選項
migrate_service=遷移服務
+migrate_options_mirror_helper=將此儲存庫設定為鏡像
migrate_options_lfs=遷移 LFS 檔案
migrate_options_lfs_endpoint.label=LFS 端點
migrate_options_lfs_endpoint.description=遷移將會嘗試使用您的 Git Remote 來確認 LFS 伺服器。如果存儲庫的 LFS 資料放在其他地方,您也可以指定自訂的端點。
@@ -1033,13 +1040,13 @@ file_view_rendered=檢視渲染圖
file_view_raw=查看原始文件
file_permalink=永久連結
file_too_large=檔案太大,無法顯示。
-bidi_bad_header=`此檔案含有未預期的 Bidirectional Unicode 字元!`
-bidi_bad_description=`此檔案含有未預期的 Bidirectional Unicode 字元,這些字元的處理方式可能和下面呈現的不同。若您是有意且合理的使用,您可以放心地忽略此警告。使用 Escape 按鈕顯示隱藏的字元。`
-bidi_bad_description_escaped=`此檔案含有未預期的 Bidirectional Unicode 字元。隱藏的 Unicode 字元已在下面被跳脫 (Escaped)。使用 Unescape 按鈕以顯示它們呈現的樣子。`
-unicode_header=`此檔案含有隱藏的 Unicode 字元!`
-unicode_description=`此檔案含有隱藏的 Unicode 字元,這些字元的處理方式可能和下面呈現的不同。若您是有意且合理的使用,您可以放心地忽略此警告。使用 Escape 按鈕顯示隱藏的字元。`
-unicode_description_escaped=`此檔案含有隱藏的 Unicode 字元。隱藏的 Unicode 字元已在下面被跳脫 (Escaped)。使用 Unescape 按鈕以顯示它們呈現的樣子。`
-line_unicode=`這一行有隱藏的 Unicode 字元`
+invisible_runes_header=`此檔案含有看不見的 Unicode 字元!`
+invisible_runes_description=`此檔案含有看不見的 Unicode 字元,這些字元的處理方式可能和下面呈現的不同。若您是有意且合理的使用,您可以放心地忽略此警告。使用 Escape 按鈕顯示隱藏的字元。`
+ambiguous_runes_header=`此檔案含有易混淆的 Unicode 字元!`
+ambiguous_runes_description=`此檔案含有易混淆的 Unicode 字元,這些字元的處理方式可能和下面呈現的不同。若您是有意且合理的使用,您可以放心地忽略此警告。使用 Escape 按鈕標記這些字元。`
+invisible_runes_line=`這一行有看不見的 Unicode 字元`
+ambiguous_runes_line=`這一行有易混淆的 Unicode 字元`
+ambiguous_character=`%[1]c [U+%04[1]X] 容易與 %[2]c [U+%04[2]X] 混淆`
escape_control_characters=Escape
unescape_control_characters=Unescape
@@ -1060,13 +1067,14 @@ normal_view=標準檢視
line=行
lines=行
-editor.new_file=新增文件
-editor.upload_file=上傳文件
-editor.edit_file=編輯文件
+editor.add_file=加入檔案
+editor.new_file=新增檔案
+editor.upload_file=上傳檔案
+editor.edit_file=編輯檔案
editor.preview_changes=預覽更改
editor.cannot_edit_lfs_files=無法在 web 介面中編輯 LFS 檔。
editor.cannot_edit_non_text_files=網站介面不能編輯二進位檔案
-editor.edit_this_file=編輯文件
+editor.edit_this_file=編輯檔案
editor.this_file_locked=檔案已被鎖定
editor.must_be_on_a_branch=你必須在一個分支或提出對此檔的更改。
editor.fork_before_edit=如果你想要對這個檔案進行或提出修改,請先 fork 這個儲存庫。
@@ -1091,7 +1099,7 @@ editor.commit_message_desc=(選用) 加入詳細說明...
editor.signoff_desc=在提交訊息底部加入提交者的「Signed-off-by」資訊。
editor.commit_directly_to_this_branch=直接提交到 %s 分支。
editor.create_new_branch=為此提交建立新分支並提出合併請求。
-editor.create_new_branch_np=為本次提交建立一個 新分支。
+editor.create_new_branch_np=為本次提交建立新分支。
editor.propose_file_change=提出檔案變更
editor.new_branch_name_desc=新的分支名稱...
editor.cancel=取消
@@ -1130,7 +1138,7 @@ commits.commits=次程式碼提交
commits.no_commits=沒有共同的提交。「%s」和「%s」的歷史完全不同。
commits.nothing_to_compare=這些分支是相同的。
commits.search=搜尋提交歷史...
-commits.search.tooltip=你可以用「author:」、「committer:」、「after:」或「before:」作為關鍵詞的前綴,例如:「revert author:Alice before:2019-04-01」。
+commits.search.tooltip=你可以用「author:」、「committer:」、「after:」、「before:」等作為關鍵字的前綴,例如: 「revert author:Alice before:2019-04-01」。
commits.find=搜尋
commits.search_all=所有分支
commits.author=作者
@@ -1175,7 +1183,7 @@ projects.type.none=無
projects.type.basic_kanban=基本看板
projects.type.bug_triage=Bug 檢傷分類
projects.template.desc=專案範本
-projects.template.desc_helper=選擇一個專案範本以開始
+projects.template.desc_helper=選擇專案範本以開始
projects.type.uncategorized=未分類
projects.board.edit=編輯看板
projects.board.edit_title=新看板名稱
@@ -1225,6 +1233,8 @@ issues.new.add_reviewer_title=請求審核
issues.choose.get_started=開始
issues.choose.blank=預設
issues.choose.blank_about=從預設範本建立問題。
+issues.choose.ignore_invalid_templates=已忽略無效的範本
+issues.choose.invalid_templates=找到了 %v 個無效的範本
issues.no_ref=未指定分支或標籤
issues.create=建立問題
issues.new_label=新增標籤
@@ -1265,6 +1275,8 @@ issues.filter_milestone=里程碑
issues.filter_milestone_no_select=所有里程碑
issues.filter_assignee=負責人
issues.filter_assginee_no_select=所有負責人
+issues.filter_poster=作者
+issues.filter_poster_no_select=所有作者
issues.filter_type=類型
issues.filter_type.all_issues=所有問題
issues.filter_type.assigned_to_you=指派給您的
@@ -1419,6 +1431,7 @@ issues.due_date_form_remove=移除
issues.due_date_not_writer=您需要儲存庫寫入權限來更改問題的截止日。
issues.due_date_not_set=未設定截止日期。
issues.due_date_added=新增了截止日期 %s %s
+issues.due_date_modified=將截止日期從 %[2]s 修改為 %[1]s %[3]s
issues.due_date_remove=移除了截止日期 %s %s
issues.due_date_overdue=逾期
issues.due_date_invalid=截止日期無效或超出範圍,請使用「yyyy-mm-dd」的格式。
@@ -1530,6 +1543,8 @@ pulls.remove_prefix=移除 %s 前綴
pulls.data_broken=此合併請求已損毀,因為遺失 Fork 資訊。
pulls.files_conflicted=此合併請求有變更和目標分支衝突。
pulls.is_checking=正在進行合併衝突檢查,請稍後再試。
+pulls.is_ancestor=這個分支已經合併到目標分支上。沒有可以合併的內容。
+pulls.is_empty=在這個分支上的更動都已經套用在目標分支上。這將會產生一個空的提交。
pulls.required_status_check_failed=未通過某些必要的檢查。
pulls.required_status_check_missing=遺失某些必要的檢查。
pulls.required_status_check_administrator=身為系統管理員,您依然可以進行合併。
@@ -1750,8 +1765,11 @@ activity.git_stats_deletion_n=刪除 %d 行
search=搜尋
search.search_repo=搜尋儲存庫
+search.type.tooltip=搜尋類型
search.fuzzy=模糊
+search.fuzzy.tooltip=包含近似關鍵字的結果
search.match=符合
+search.match.tooltip=只包含完全符合關鍵字的結果
search.results=在 %s 中搜尋 "%s" 的结果
search.code_no_results=找不到符合您關鍵字的原始碼。
search.code_search_unavailable=現在無法使用程式碼搜尋。請與網站管理員聯絡。
@@ -1780,10 +1798,6 @@ settings.mirror_settings.push_mirror.remote_url=Git 遠端儲存庫 URL
settings.mirror_settings.push_mirror.add=新增推送鏡像
settings.sync_mirror=立即同步
settings.mirror_sync_in_progress=鏡像同步正在進行中。 請稍後再回來看看。
-settings.email_notifications.enable=啟用郵件通知
-settings.email_notifications.onmention=只在被提到時傳送郵件通知
-settings.email_notifications.disable=關閉郵件通知
-settings.email_notifications.submit=套用郵件偏好設定
settings.site=網站
settings.update_settings=更新設定
settings.branches.update_default_branch=更新預設分支
@@ -1888,7 +1902,8 @@ settings.update_settings_success=已更新儲存庫的設定。
settings.confirm_delete=刪除儲存庫
settings.add_collaborator=增加協作者
settings.add_collaborator_success=成功增加協作者!
-settings.add_collaborator_inactive_user=無法加入未啟用的使用者為協作者。
+settings.add_collaborator_inactive_user=無法將未啟用的使用者加入為協作者。
+settings.add_collaborator_owner=無法將擁有者加入為協作者。
settings.add_collaborator_duplicate=此協作者早已被加入此儲存庫。
settings.delete_collaborator=移除
settings.collaborator_deletion=移除協作者
@@ -1947,6 +1962,8 @@ settings.event_delete=刪除
settings.event_delete_desc=刪除分支或標籤。
settings.event_fork=Fork
settings.event_fork_desc=儲存庫已被 fork。
+settings.event_wiki=Wiki
+settings.event_wiki_desc=建立、重新命名、編輯、刪除 Wiki 頁面。
settings.event_release=版本發布
settings.event_release_desc=在儲存庫中發布、更新或刪除版本。
settings.event_push=推送
@@ -2077,7 +2094,7 @@ settings.block_on_official_review_requests=有官方的審核請求時阻擋合
settings.block_on_official_review_requests_desc=如果有官方的審核請求時,即使有足夠的核可也不允許進行合併。
settings.block_outdated_branch=如果合併請求已經過時則阻擋合併
settings.block_outdated_branch_desc=當 head 分支落後於基礎分支時不得合併。
-settings.default_branch_desc=請選擇一個用來提交程式碼和合併請求的預設分支。
+settings.default_branch_desc=請選擇用來提交程式碼和合併請求的預設分支。
settings.default_merge_style_desc=合併請求的預設方式:
settings.choose_branch=選擇一個分支...
settings.no_protected_branch=沒有受保護的分支。
@@ -2214,7 +2231,7 @@ release.new_subheader=發布、整理專案的版本。
release.edit_subheader=發布、整理專案的版本。
release.tag_name=標籤名稱
release.target=目標分支
-release.tag_helper=新增或選擇一個既有的標籤。
+release.tag_helper=新增或選擇既有的標籤。
release.title=標題
release.content=內容
release.prerelease_desc=標記為 Pre-Release
@@ -2298,6 +2315,7 @@ create_org=建立組織
repo_updated=更新於
people=成員
teams=團隊
+code=程式碼
lower_members=名成員
lower_repositories=個儲存庫
create_new_team=建立團隊
@@ -2536,6 +2554,8 @@ users.delete_account=刪除使用者帳戶
users.cannot_delete_self=您無法刪除您自己
users.still_own_repo=這個使用者還擁有一個或更多的儲存庫。請先刪除或是轉移這些儲存庫。
users.still_has_org=此使用者是組織的成員。請先將他從組織中移除。
+users.purge=清除使用者
+users.purge_help=強制刪除使用者和他擁有的所有儲存庫、組織、套件,所有留言也會被刪除。
users.still_own_packages=此使用者擁有一個或多個套件,請先刪除這些套件。
users.deletion_success=使用者帳戶已被刪除。
users.reset_2fa=重設兩步驟驗證
@@ -2695,8 +2715,8 @@ auths.tips.oauth2.general.tip=註冊新的 OAuth2 認證時,callback/redirect
auths.tip.oauth2_provider=OAuth2 提供者
auths.tip.bitbucket=註冊新的 OAuth 客戶端並加入權限「Account - Read」。網址:https://bitbucket.org/account/user//oauth-consumers/new
auths.tip.nextcloud=在您的執行個體中,於選單「設定 -> 安全性 -> OAuth 2.0 客戶端」註冊新的 OAuth 客戶端
-auths.tip.dropbox=建立一個新的 App。網址:https://www.dropbox.com/developers/apps
-auths.tip.facebook=註冊一個新的應用程式並新增產品「Facebook 登入」。網址:https://developers.facebook.com/apps
+auths.tip.dropbox=建立新的 App。網址:https://www.dropbox.com/developers/apps
+auths.tip.facebook=註冊新的應用程式並新增產品「Facebook 登入」。網址:https://developers.facebook.com/apps
auths.tip.github=註冊新的 OAuth 應用程式。網址:https://github.com/settings/applications/new
auths.tip.gitlab=註冊新的應用程式。網址:https://gitlab.com/profile/applications
auths.tip.google_plus=從 Google API 控制台取得 OAuth2 用戶端憑證。網址:https://console.developers.google.com/
@@ -2792,16 +2812,19 @@ config.queue_length=佇列長度
config.deliver_timeout=傳送逾時
config.skip_tls_verify=略過 TLS 驗證
-config.mailer_config=SMTP 組態
+config.mailer_config=郵件程式組態
config.mailer_enabled=啟用服務
-config.mailer_disable_helo=停用 HELO 操作
+config.mailer_enable_helo=啟用 HELO
config.mailer_name=發送者名稱
-config.mailer_host=郵件主機地址
+config.mailer_protocol=協定
+config.mailer_smtp_addr=SMTP 位址
+config.mailer_smtp_port=SMTP 連接埠
config.mailer_user=發送者帳號
config.mailer_use_sendmail=使用 Sendmail
config.mailer_sendmail_path=Sendmail 路徑
config.mailer_sendmail_args=Sendmail 參數
config.mailer_sendmail_timeout=Sendmail 逾時
+config.mailer_use_dummy=Dummy
config.test_email_placeholder=電子信箱 (例:test@example.com)
config.send_test_mail=傳送測試郵件
config.test_mail_failed=傳送測試郵件到「%s」時失敗:%v
@@ -2854,6 +2877,7 @@ config.access_log_template=範本
config.xorm_log_mode=XORM 日誌模式
config.xorm_log_sql=記錄 SQL
+
monitor.cron=Cron 任務
monitor.name=任務名稱
monitor.schedule=任務安排
@@ -2886,6 +2910,7 @@ monitor.queue.nopool.title=沒有工作者集區
monitor.queue.nopool.desc=此佇列包裝著其他佇列,且本身沒有工作者集區。
monitor.queue.wrapped.desc=一個被包裝的佇列包裝著一個緩慢啟動的佇列,在 Channel 中緩衝請求。它本身沒有工作者集區。
monitor.queue.persistable-channel.desc=一個持久性的 Channel 包裝著兩個列隊。一個擁有自己工作者集區的 Channel 佇列,另一個是 Level 佇列用於先前關閉後持續的請求。它本身沒有工作者集區。
+monitor.queue.flush=清除者
monitor.queue.pool.timeout=逾時
monitor.queue.pool.addworkers.title=新增工作者
monitor.queue.pool.addworkers.submit=新增工作者
@@ -3016,6 +3041,9 @@ pin=固定通知
mark_as_read=標記為已讀
mark_as_unread=標記為未讀
mark_all_as_read=標記所有為已讀
+subscriptions=訂閱
+watching=正在關注
+no_subscriptions=沒有訂閱
[gpg]
default_key=使用預設金鑰簽署
@@ -3038,6 +3066,7 @@ title=套件
desc=管理儲存庫套件。
empty=目前還沒有套件。
empty.documentation=關於套件註冊中心的詳情請參閱說明文件。
+empty.repo=已經上傳了一個套件,但是沒有顯示在這裡嗎?打開套件設定並將其連結到這個儲存庫。
filter.type=類型
filter.type.all=所有
filter.no_result=沒有篩選結果。
@@ -3074,6 +3103,7 @@ container.details.platform=平台
container.details.repository_site=儲存庫網站
container.details.documentation_site=文件網站
container.pull=透過下列命令拉取映像檔:
+container.digest=摘要:
container.documentation=關於 Container registry 的詳情請參閱說明文件。
container.multi_arch=作業系統 / 架構
container.layers=映像檔 Layers
@@ -3103,6 +3133,10 @@ npm.dependencies.development=開發相依性
npm.dependencies.peer=Peer 相依性
npm.dependencies.optional=選用相依性
npm.details.tag=標籤
+pub.install=執行下列命令以使用 Dart 安裝此套件:
+pub.documentation=關於 Pub registry 的詳情請參閱說明文件。
+pub.details.repository_site=儲存庫網站
+pub.details.documentation_site=文件網站
pypi.requires=需要 Python
pypi.install=執行下列命令以使用 pip 安裝此套件:
pypi.documentation=關於 PyPI registry 的詳情請參閱說明文件。
@@ -3113,6 +3147,8 @@ rubygems.dependencies.development=開發相依性
rubygems.required.ruby=需要的 Ruby 版本
rubygems.required.rubygems=需要的 RubyGem 版本
rubygems.documentation=關於 RubyGems registry 的詳情請參閱說明文件。
+vagrant.install=執行下列命令以新增 Vagrant box:
+vagrant.documentation=關於 Vagrant registry 的詳情請參閱說明文件。
settings.link=連結此套件到儲存庫
settings.link.description=如果您將套件連結到儲存庫,該套件會顯示在儲存庫的套件清單。
settings.link.select=選擇儲存庫
diff --git a/package-lock.json b/package-lock.json
index cfd2a6ad2a..69f1ce409b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8,80 +8,71 @@
"license": "MIT",
"dependencies": {
"@claviska/jquery-minicolors": "2.3.6",
- "@primer/octicons": "17.3.0",
+ "@mcaptcha/vanilla-glue": "0.1.0-alpha-3",
+ "@primer/octicons": "17.7.0",
+ "@vue/compiler-sfc": "3.2.40",
"add-asset-webpack-plugin": "2.0.1",
"css-loader": "6.7.1",
"dropzone": "6.0.0-beta.2",
- "easymde": "2.16.1",
- "esbuild-loader": "2.19.0",
+ "easymde": "2.18.0",
+ "esbuild-loader": "2.20.0",
"escape-goat": "4.0.0",
- "fast-glob": "3.2.11",
+ "fast-glob": "3.2.12",
"font-awesome": "4.7.0",
- "jquery": "3.6.0",
+ "jquery": "3.6.1",
"jquery.are-you-sure": "1.9.0",
+ "katex": "0.16.2",
"less": "4.1.3",
- "less-loader": "11.0.0",
+ "less-loader": "11.1.0",
"license-checker-webpack-plugin": "0.2.1",
- "mermaid": "9.1.3",
+ "mermaid": "9.1.7",
"mini-css-extract-plugin": "2.6.1",
- "monaco-editor": "0.33.0",
+ "monaco-editor": "0.34.0",
"monaco-editor-webpack-plugin": "7.0.1",
"pretty-ms": "8.0.0",
"sortablejs": "1.15.0",
- "swagger-ui-dist": "4.13.0",
+ "swagger-ui-dist": "4.14.2",
"tippy.js": "6.3.7",
"tributejs": "5.1.3",
"uint8-to-base64": "0.2.0",
- "vue": "2.6.14",
- "vue-bar-graph": "1.3.1",
- "vue-calendar-heatmap": "0.8.4",
- "vue-loader": "15.9.8",
- "vue-template-compiler": "2.6.14",
- "webpack": "5.73.0",
+ "vue": "3.2.40",
+ "vue-bar-graph": "2.0.0",
+ "vue-loader": "17.0.0",
+ "vue3-calendar-heatmap": "2.0.0",
+ "webpack": "5.74.0",
"webpack-cli": "4.10.0",
- "workbox-routing": "6.5.3",
- "workbox-strategies": "6.5.3",
+ "workbox-routing": "6.5.4",
+ "workbox-strategies": "6.5.4",
"worker-loader": "3.0.8",
"wrap-ansi": "8.0.1"
},
"devDependencies": {
- "@happy-dom/jest-environment": "6.0.4",
- "@stoplight/spectral-cli": "6.4.1",
- "eslint": "8.20.0",
+ "@playwright/test": "1.27.0",
+ "@rollup/pluginutils": "5.0.1",
+ "@stoplight/spectral-cli": "6.5.1",
+ "eslint": "8.25.0",
"eslint-plugin-import": "2.26.0",
"eslint-plugin-jquery": "1.5.1",
- "eslint-plugin-sonarjs": "0.13.0",
- "eslint-plugin-unicorn": "43.0.2",
- "eslint-plugin-vue": "9.2.0",
- "jest": "28.1.3",
- "jest-extended": "3.0.1",
+ "eslint-plugin-sonarjs": "0.15.0",
+ "eslint-plugin-unicorn": "44.0.2",
+ "eslint-plugin-vue": "9.6.0",
+ "jsdom": "20.0.1",
+ "markdownlint-cli": "0.32.2",
"postcss-less": "6.0.0",
- "stylelint": "14.9.1",
- "stylelint-config-standard": "26.0.0",
+ "stylelint": "14.13.0",
+ "stylelint-config-standard": "28.0.0",
"svgo": "2.8.0",
- "updates": "13.1.2"
+ "updates": "13.1.8",
+ "vitest": "0.24.1"
},
"engines": {
"node": ">= 14.0.0"
}
},
- "node_modules/@ampproject/remapping": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz",
- "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==",
- "dev": true,
- "dependencies": {
- "@jridgewell/gen-mapping": "^0.1.0",
- "@jridgewell/trace-mapping": "^0.3.9"
- },
- "engines": {
- "node": ">=6.0.0"
- }
- },
"node_modules/@asyncapi/specs": {
- "version": "2.14.0",
- "resolved": "https://registry.npmjs.org/@asyncapi/specs/-/specs-2.14.0.tgz",
- "integrity": "sha512-hHsYF6XsYNIKb1P2rXaooF4H+uKKQ4b/Ljxrk3rZ3riEDiSxMshMEfb1fUlw9Yj4V4OmJhjXwkNvw8W59AXv1A==",
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/@asyncapi/specs/-/specs-3.2.1.tgz",
+ "integrity": "sha512-FO+EteK+Gk3zwumrBw6frpp9cJ4oQL5++hBBpfM81w16e9KaiA4sKrzvQsvVjifoZZHNvVEX4D2zoz9i8CLccQ==",
"dev": true
},
"node_modules/@babel/code-frame": {
@@ -96,239 +87,15 @@
"node": ">=6.9.0"
}
},
- "node_modules/@babel/compat-data": {
- "version": "7.18.8",
- "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.18.8.tgz",
- "integrity": "sha512-HSmX4WZPPK3FUxYp7g2T6EyO8j96HlZJlxmKPSh6KAcqwyDrfx7hKjXpAW/0FhFfTJsR0Yt4lAjLI2coMptIHQ==",
- "dev": true,
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/core": {
- "version": "7.18.9",
- "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.9.tgz",
- "integrity": "sha512-1LIb1eL8APMy91/IMW+31ckrfBM4yCoLaVzoDhZUKSM4cu1L1nIidyxkCgzPAgrC5WEz36IPEr/eSeSF9pIn+g==",
- "dev": true,
- "dependencies": {
- "@ampproject/remapping": "^2.1.0",
- "@babel/code-frame": "^7.18.6",
- "@babel/generator": "^7.18.9",
- "@babel/helper-compilation-targets": "^7.18.9",
- "@babel/helper-module-transforms": "^7.18.9",
- "@babel/helpers": "^7.18.9",
- "@babel/parser": "^7.18.9",
- "@babel/template": "^7.18.6",
- "@babel/traverse": "^7.18.9",
- "@babel/types": "^7.18.9",
- "convert-source-map": "^1.7.0",
- "debug": "^4.1.0",
- "gensync": "^1.0.0-beta.2",
- "json5": "^2.2.1",
- "semver": "^6.3.0"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/babel"
- }
- },
- "node_modules/@babel/core/node_modules/semver": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
- "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
- "dev": true,
- "bin": {
- "semver": "bin/semver.js"
- }
- },
- "node_modules/@babel/generator": {
- "version": "7.18.9",
- "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.9.tgz",
- "integrity": "sha512-wt5Naw6lJrL1/SGkipMiFxJjtyczUWTP38deiP1PO60HsBjDeKk08CGC3S8iVuvf0FmTdgKwU1KIXzSKL1G0Ug==",
- "dev": true,
- "dependencies": {
- "@babel/types": "^7.18.9",
- "@jridgewell/gen-mapping": "^0.3.2",
- "jsesc": "^2.5.1"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/generator/node_modules/@jridgewell/gen-mapping": {
- "version": "0.3.2",
- "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz",
- "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==",
- "dev": true,
- "dependencies": {
- "@jridgewell/set-array": "^1.0.1",
- "@jridgewell/sourcemap-codec": "^1.4.10",
- "@jridgewell/trace-mapping": "^0.3.9"
- },
- "engines": {
- "node": ">=6.0.0"
- }
- },
- "node_modules/@babel/helper-compilation-targets": {
- "version": "7.18.9",
- "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.9.tgz",
- "integrity": "sha512-tzLCyVmqUiFlcFoAPLA/gL9TeYrF61VLNtb+hvkuVaB5SUjW7jcfrglBIX1vUIoT7CLP3bBlIMeyEsIl2eFQNg==",
- "dev": true,
- "dependencies": {
- "@babel/compat-data": "^7.18.8",
- "@babel/helper-validator-option": "^7.18.6",
- "browserslist": "^4.20.2",
- "semver": "^6.3.0"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0"
- }
- },
- "node_modules/@babel/helper-compilation-targets/node_modules/semver": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
- "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
- "dev": true,
- "bin": {
- "semver": "bin/semver.js"
- }
- },
- "node_modules/@babel/helper-environment-visitor": {
- "version": "7.18.9",
- "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz",
- "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==",
- "dev": true,
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/helper-function-name": {
- "version": "7.18.9",
- "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.18.9.tgz",
- "integrity": "sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A==",
- "dev": true,
- "dependencies": {
- "@babel/template": "^7.18.6",
- "@babel/types": "^7.18.9"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/helper-hoist-variables": {
- "version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz",
- "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==",
- "dev": true,
- "dependencies": {
- "@babel/types": "^7.18.6"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/helper-module-imports": {
- "version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz",
- "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==",
- "dev": true,
- "dependencies": {
- "@babel/types": "^7.18.6"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/helper-module-transforms": {
- "version": "7.18.9",
- "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.18.9.tgz",
- "integrity": "sha512-KYNqY0ICwfv19b31XzvmI/mfcylOzbLtowkw+mfvGPAQ3kfCnMLYbED3YecL5tPd8nAYFQFAd6JHp2LxZk/J1g==",
- "dev": true,
- "dependencies": {
- "@babel/helper-environment-visitor": "^7.18.9",
- "@babel/helper-module-imports": "^7.18.6",
- "@babel/helper-simple-access": "^7.18.6",
- "@babel/helper-split-export-declaration": "^7.18.6",
- "@babel/helper-validator-identifier": "^7.18.6",
- "@babel/template": "^7.18.6",
- "@babel/traverse": "^7.18.9",
- "@babel/types": "^7.18.9"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/helper-plugin-utils": {
- "version": "7.18.9",
- "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.18.9.tgz",
- "integrity": "sha512-aBXPT3bmtLryXaoJLyYPXPlSD4p1ld9aYeR+sJNOZjJJGiOpb+fKfh3NkcCu7J54nUJwCERPBExCCpyCOHnu/w==",
- "dev": true,
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/helper-simple-access": {
- "version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz",
- "integrity": "sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g==",
- "dev": true,
- "dependencies": {
- "@babel/types": "^7.18.6"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/helper-split-export-declaration": {
- "version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz",
- "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==",
- "dev": true,
- "dependencies": {
- "@babel/types": "^7.18.6"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
"node_modules/@babel/helper-validator-identifier": {
- "version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz",
- "integrity": "sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==",
+ "version": "7.19.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz",
+ "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==",
"dev": true,
"engines": {
"node": ">=6.9.0"
}
},
- "node_modules/@babel/helper-validator-option": {
- "version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz",
- "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==",
- "dev": true,
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/helpers": {
- "version": "7.18.9",
- "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.18.9.tgz",
- "integrity": "sha512-Jf5a+rbrLoR4eNdUmnFu8cN5eNJT6qdTdOg5IHIzq87WwyRw9PwguLFOWYgktN/60IP4fgDUawJvs7PjQIzELQ==",
- "dev": true,
- "dependencies": {
- "@babel/template": "^7.18.6",
- "@babel/traverse": "^7.18.9",
- "@babel/types": "^7.18.9"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
"node_modules/@babel/highlight": {
"version": "7.18.6",
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz",
@@ -415,10 +182,9 @@
}
},
"node_modules/@babel/parser": {
- "version": "7.18.9",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.9.tgz",
- "integrity": "sha512-9uJveS9eY9DJ0t64YbIBZICtJy8a5QrDEVdiLCG97fVLpDTpGX7t8mMSb6OWw6Lrnjqj4O8zwjELX3dhoMgiBg==",
- "dev": true,
+ "version": "7.19.3",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.19.3.tgz",
+ "integrity": "sha512-pJ9xOlNWHiy9+FuFP09DEAFbAn4JskgRsVcc169w2xRBC3FRGuQEwjeIMMND9L2zc0iEhO/tGv4Zq+km+hxNpQ==",
"bin": {
"parser": "bin/babel-parser.js"
},
@@ -426,242 +192,6 @@
"node": ">=6.0.0"
}
},
- "node_modules/@babel/plugin-syntax-async-generators": {
- "version": "7.8.4",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz",
- "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==",
- "dev": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.8.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-syntax-bigint": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz",
- "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==",
- "dev": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.8.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-syntax-class-properties": {
- "version": "7.12.13",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz",
- "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==",
- "dev": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.12.13"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-syntax-import-meta": {
- "version": "7.10.4",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz",
- "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==",
- "dev": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.10.4"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-syntax-json-strings": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz",
- "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==",
- "dev": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.8.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-syntax-logical-assignment-operators": {
- "version": "7.10.4",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz",
- "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==",
- "dev": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.10.4"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz",
- "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==",
- "dev": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.8.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-syntax-numeric-separator": {
- "version": "7.10.4",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz",
- "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==",
- "dev": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.10.4"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-syntax-object-rest-spread": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz",
- "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==",
- "dev": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.8.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-syntax-optional-catch-binding": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz",
- "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==",
- "dev": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.8.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-syntax-optional-chaining": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz",
- "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==",
- "dev": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.8.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-syntax-top-level-await": {
- "version": "7.14.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz",
- "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==",
- "dev": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.14.5"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/plugin-syntax-typescript": {
- "version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.18.6.tgz",
- "integrity": "sha512-mAWAuq4rvOepWCBid55JuRNvpTNf2UGVgoz4JV0fXEKolsVZDzsa4NqCef758WZJj/GDu0gVGItjKFiClTAmZA==",
- "dev": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.18.6"
- },
- "engines": {
- "node": ">=6.9.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0-0"
- }
- },
- "node_modules/@babel/runtime": {
- "version": "7.18.9",
- "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.9.tgz",
- "integrity": "sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw==",
- "dependencies": {
- "regenerator-runtime": "^0.13.4"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/template": {
- "version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.6.tgz",
- "integrity": "sha512-JoDWzPe+wgBsTTgdnIma3iHNFC7YVJoPssVBDjiHfNlyt4YcunDtcDOUmfVDfCK5MfdsaIoX9PkijPhjH3nYUw==",
- "dev": true,
- "dependencies": {
- "@babel/code-frame": "^7.18.6",
- "@babel/parser": "^7.18.6",
- "@babel/types": "^7.18.6"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/traverse": {
- "version": "7.18.9",
- "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.18.9.tgz",
- "integrity": "sha512-LcPAnujXGwBgv3/WHv01pHtb2tihcyW1XuL9wd7jqh1Z8AQkTd+QVjMrMijrln0T7ED3UXLIy36P9Ao7W75rYg==",
- "dev": true,
- "dependencies": {
- "@babel/code-frame": "^7.18.6",
- "@babel/generator": "^7.18.9",
- "@babel/helper-environment-visitor": "^7.18.9",
- "@babel/helper-function-name": "^7.18.9",
- "@babel/helper-hoist-variables": "^7.18.6",
- "@babel/helper-split-export-declaration": "^7.18.6",
- "@babel/parser": "^7.18.9",
- "@babel/types": "^7.18.9",
- "debug": "^4.1.0",
- "globals": "^11.1.0"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@babel/traverse/node_modules/globals": {
- "version": "11.12.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
- "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
- "dev": true,
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/@babel/types": {
- "version": "7.18.9",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.9.tgz",
- "integrity": "sha512-WwMLAg2MvJmt/rKEVQBBhIVffMmnilX4oe0sRe7iPOHIGsqpruFHHdrfj4O1CMMtgMtCU4oPafZjDPCRgO57Wg==",
- "dev": true,
- "dependencies": {
- "@babel/helper-validator-identifier": "^7.18.6",
- "to-fast-properties": "^2.0.0"
- },
- "engines": {
- "node": ">=6.9.0"
- }
- },
- "node_modules/@bcoe/v8-coverage": {
- "version": "0.2.3",
- "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz",
- "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==",
- "dev": true
- },
"node_modules/@braintree/sanitize-url": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-6.0.0.tgz",
@@ -700,15 +230,45 @@
"node": ">=10.0.0"
}
},
+ "node_modules/@esbuild/android-arm": {
+ "version": "0.15.10",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.15.10.tgz",
+ "integrity": "sha512-FNONeQPy/ox+5NBkcSbYJxoXj9GWu8gVGJTVmUyoOCKQFDTrHVKgNSzChdNt0I8Aj/iKcsDf2r9BFwv+FSNUXg==",
+ "cpu": [
+ "arm"
+ ],
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-loong64": {
+ "version": "0.15.10",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.15.10.tgz",
+ "integrity": "sha512-w0Ou3Z83LOYEkwaui2M8VwIp+nLi/NA60lBLMvaJ+vXVMcsARYdEzLNE7RSm4+lSg4zq4d7fAVuzk7PNQ5JFgg==",
+ "cpu": [
+ "loong64"
+ ],
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
"node_modules/@eslint/eslintrc": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.0.tgz",
- "integrity": "sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw==",
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.3.tgz",
+ "integrity": "sha512-uj3pT6Mg+3t39fvLrj8iuCIJ38zKO9FpGtJ4BBJebJhEwjoT+KLVNCcHT5QC9NGRIEi7fZ0ZR8YRb884auB4Lg==",
"dev": true,
"dependencies": {
"ajv": "^6.12.4",
"debug": "^4.3.2",
- "espree": "^9.3.2",
+ "espree": "^9.4.0",
"globals": "^13.15.0",
"ignore": "^5.2.0",
"import-fresh": "^3.2.1",
@@ -718,6 +278,9 @@
},
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
}
},
"node_modules/@eslint/eslintrc/node_modules/ajv": {
@@ -742,24 +305,10 @@
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
"dev": true
},
- "node_modules/@happy-dom/jest-environment": {
- "version": "6.0.4",
- "resolved": "https://registry.npmjs.org/@happy-dom/jest-environment/-/jest-environment-6.0.4.tgz",
- "integrity": "sha512-3FEWODVN4H7Hnkbm7IEtRE8lU7uitxSsnVagkQbfjjwKSUfF6BT9MJ95VF7nLimdc5ctOIa7ZbDietRRIpgSHw==",
- "dev": true,
- "dependencies": {
- "@jest/environment": "^27.5.1",
- "@jest/fake-timers": "^27.5.1",
- "@jest/types": "^27.5.1",
- "happy-dom": "^6.0.4",
- "jest-mock": "^27.5.1",
- "jest-util": "^27.5.1"
- }
- },
"node_modules/@humanwhocodes/config-array": {
- "version": "0.9.5",
- "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz",
- "integrity": "sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==",
+ "version": "0.10.7",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.10.7.tgz",
+ "integrity": "sha512-MDl6D6sBsaV452/QSdX+4CXIjZhIcI0PELsxUjk4U828yd58vk3bTIvk/6w5FY+4hIy9sLW0sfrV7K7Kc++j/w==",
"dev": true,
"dependencies": {
"@humanwhocodes/object-schema": "^1.2.1",
@@ -770,795 +319,25 @@
"node": ">=10.10.0"
}
},
+ "node_modules/@humanwhocodes/module-importer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+ "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+ "dev": true,
+ "engines": {
+ "node": ">=12.22"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
"node_modules/@humanwhocodes/object-schema": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz",
"integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==",
"dev": true
},
- "node_modules/@istanbuljs/load-nyc-config": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz",
- "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==",
- "dev": true,
- "dependencies": {
- "camelcase": "^5.3.1",
- "find-up": "^4.1.0",
- "get-package-type": "^0.1.0",
- "js-yaml": "^3.13.1",
- "resolve-from": "^5.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": {
- "version": "1.0.10",
- "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
- "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
- "dev": true,
- "dependencies": {
- "sprintf-js": "~1.0.2"
- }
- },
- "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
- "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
- "dev": true,
- "dependencies": {
- "locate-path": "^5.0.0",
- "path-exists": "^4.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": {
- "version": "3.14.1",
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
- "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
- "dev": true,
- "dependencies": {
- "argparse": "^1.0.7",
- "esprima": "^4.0.0"
- },
- "bin": {
- "js-yaml": "bin/js-yaml.js"
- }
- },
- "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
- "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
- "dev": true,
- "dependencies": {
- "p-locate": "^4.1.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
- "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
- "dev": true,
- "dependencies": {
- "p-try": "^2.0.0"
- },
- "engines": {
- "node": ">=6"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
- "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
- "dev": true,
- "dependencies": {
- "p-limit": "^2.2.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/@istanbuljs/load-nyc-config/node_modules/path-exists": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
- "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
- "dev": true,
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
- "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
- "dev": true,
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/@istanbuljs/schema": {
- "version": "0.1.3",
- "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz",
- "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==",
- "dev": true,
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/@jest/console": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/console/-/console-28.1.3.tgz",
- "integrity": "sha512-QPAkP5EwKdK/bxIr6C1I4Vs0rm2nHiANzj/Z5X2JQkrZo6IqvC4ldZ9K95tF0HdidhA8Bo6egxSzUFPYKcEXLw==",
- "dev": true,
- "dependencies": {
- "@jest/types": "^28.1.3",
- "@types/node": "*",
- "chalk": "^4.0.0",
- "jest-message-util": "^28.1.3",
- "jest-util": "^28.1.3",
- "slash": "^3.0.0"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/@jest/console/node_modules/@jest/types": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz",
- "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==",
- "dev": true,
- "dependencies": {
- "@jest/schemas": "^28.1.3",
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^3.0.0",
- "@types/node": "*",
- "@types/yargs": "^17.0.8",
- "chalk": "^4.0.0"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/@jest/console/node_modules/@types/yargs": {
- "version": "17.0.10",
- "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.10.tgz",
- "integrity": "sha512-gmEaFwpj/7f/ROdtIlci1R1VYU1J4j95m8T+Tj3iBgiBFKg1foE/PSl93bBd5T9LDXNPo8UlNN6W0qwD8O5OaA==",
- "dev": true,
- "dependencies": {
- "@types/yargs-parser": "*"
- }
- },
- "node_modules/@jest/console/node_modules/jest-message-util": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-28.1.3.tgz",
- "integrity": "sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g==",
- "dev": true,
- "dependencies": {
- "@babel/code-frame": "^7.12.13",
- "@jest/types": "^28.1.3",
- "@types/stack-utils": "^2.0.0",
- "chalk": "^4.0.0",
- "graceful-fs": "^4.2.9",
- "micromatch": "^4.0.4",
- "pretty-format": "^28.1.3",
- "slash": "^3.0.0",
- "stack-utils": "^2.0.3"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/@jest/console/node_modules/jest-util": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz",
- "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==",
- "dev": true,
- "dependencies": {
- "@jest/types": "^28.1.3",
- "@types/node": "*",
- "chalk": "^4.0.0",
- "ci-info": "^3.2.0",
- "graceful-fs": "^4.2.9",
- "picomatch": "^2.2.3"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/@jest/core": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/core/-/core-28.1.3.tgz",
- "integrity": "sha512-CIKBrlaKOzA7YG19BEqCw3SLIsEwjZkeJzf5bdooVnW4bH5cktqe3JX+G2YV1aK5vP8N9na1IGWFzYaTp6k6NA==",
- "dev": true,
- "dependencies": {
- "@jest/console": "^28.1.3",
- "@jest/reporters": "^28.1.3",
- "@jest/test-result": "^28.1.3",
- "@jest/transform": "^28.1.3",
- "@jest/types": "^28.1.3",
- "@types/node": "*",
- "ansi-escapes": "^4.2.1",
- "chalk": "^4.0.0",
- "ci-info": "^3.2.0",
- "exit": "^0.1.2",
- "graceful-fs": "^4.2.9",
- "jest-changed-files": "^28.1.3",
- "jest-config": "^28.1.3",
- "jest-haste-map": "^28.1.3",
- "jest-message-util": "^28.1.3",
- "jest-regex-util": "^28.0.2",
- "jest-resolve": "^28.1.3",
- "jest-resolve-dependencies": "^28.1.3",
- "jest-runner": "^28.1.3",
- "jest-runtime": "^28.1.3",
- "jest-snapshot": "^28.1.3",
- "jest-util": "^28.1.3",
- "jest-validate": "^28.1.3",
- "jest-watcher": "^28.1.3",
- "micromatch": "^4.0.4",
- "pretty-format": "^28.1.3",
- "rimraf": "^3.0.0",
- "slash": "^3.0.0",
- "strip-ansi": "^6.0.0"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- },
- "peerDependencies": {
- "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0"
- },
- "peerDependenciesMeta": {
- "node-notifier": {
- "optional": true
- }
- }
- },
- "node_modules/@jest/core/node_modules/@jest/types": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz",
- "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==",
- "dev": true,
- "dependencies": {
- "@jest/schemas": "^28.1.3",
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^3.0.0",
- "@types/node": "*",
- "@types/yargs": "^17.0.8",
- "chalk": "^4.0.0"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/@jest/core/node_modules/@types/yargs": {
- "version": "17.0.10",
- "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.10.tgz",
- "integrity": "sha512-gmEaFwpj/7f/ROdtIlci1R1VYU1J4j95m8T+Tj3iBgiBFKg1foE/PSl93bBd5T9LDXNPo8UlNN6W0qwD8O5OaA==",
- "dev": true,
- "dependencies": {
- "@types/yargs-parser": "*"
- }
- },
- "node_modules/@jest/core/node_modules/jest-message-util": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-28.1.3.tgz",
- "integrity": "sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g==",
- "dev": true,
- "dependencies": {
- "@babel/code-frame": "^7.12.13",
- "@jest/types": "^28.1.3",
- "@types/stack-utils": "^2.0.0",
- "chalk": "^4.0.0",
- "graceful-fs": "^4.2.9",
- "micromatch": "^4.0.4",
- "pretty-format": "^28.1.3",
- "slash": "^3.0.0",
- "stack-utils": "^2.0.3"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/@jest/core/node_modules/jest-util": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz",
- "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==",
- "dev": true,
- "dependencies": {
- "@jest/types": "^28.1.3",
- "@types/node": "*",
- "chalk": "^4.0.0",
- "ci-info": "^3.2.0",
- "graceful-fs": "^4.2.9",
- "picomatch": "^2.2.3"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/@jest/environment": {
- "version": "27.5.1",
- "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-27.5.1.tgz",
- "integrity": "sha512-/WQjhPJe3/ghaol/4Bq480JKXV/Rfw8nQdN7f41fM8VDHLcxKXou6QyXAh3EFr9/bVG3x74z1NWDkP87EiY8gA==",
- "dev": true,
- "dependencies": {
- "@jest/fake-timers": "^27.5.1",
- "@jest/types": "^27.5.1",
- "@types/node": "*",
- "jest-mock": "^27.5.1"
- },
- "engines": {
- "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
- }
- },
- "node_modules/@jest/expect": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-28.1.3.tgz",
- "integrity": "sha512-lzc8CpUbSoE4dqT0U+g1qODQjBRHPpCPXissXD4mS9+sWQdmmpeJ9zSH1rS1HEkrsMN0fb7nKrJ9giAR1d3wBw==",
- "dev": true,
- "dependencies": {
- "expect": "^28.1.3",
- "jest-snapshot": "^28.1.3"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/@jest/expect-utils": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-28.1.3.tgz",
- "integrity": "sha512-wvbi9LUrHJLn3NlDW6wF2hvIMtd4JUl2QNVrjq+IBSHirgfrR3o9RnVtxzdEGO2n9JyIWwHnLfby5KzqBGg2YA==",
- "dev": true,
- "dependencies": {
- "jest-get-type": "^28.0.2"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/@jest/fake-timers": {
- "version": "27.5.1",
- "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-27.5.1.tgz",
- "integrity": "sha512-/aPowoolwa07k7/oM3aASneNeBGCmGQsc3ugN4u6s4C/+s5M64MFo/+djTdiwcbQlRfFElGuDXWzaWj6QgKObQ==",
- "dev": true,
- "dependencies": {
- "@jest/types": "^27.5.1",
- "@sinonjs/fake-timers": "^8.0.1",
- "@types/node": "*",
- "jest-message-util": "^27.5.1",
- "jest-mock": "^27.5.1",
- "jest-util": "^27.5.1"
- },
- "engines": {
- "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
- }
- },
- "node_modules/@jest/globals": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-28.1.3.tgz",
- "integrity": "sha512-XFU4P4phyryCXu1pbcqMO0GSQcYe1IsalYCDzRNyhetyeyxMcIxa11qPNDpVNLeretItNqEmYYQn1UYz/5x1NA==",
- "dev": true,
- "dependencies": {
- "@jest/environment": "^28.1.3",
- "@jest/expect": "^28.1.3",
- "@jest/types": "^28.1.3"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/@jest/globals/node_modules/@jest/environment": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-28.1.3.tgz",
- "integrity": "sha512-1bf40cMFTEkKyEf585R9Iz1WayDjHoHqvts0XFYEqyKM3cFWDpeMoqKKTAF9LSYQModPUlh8FKptoM2YcMWAXA==",
- "dev": true,
- "dependencies": {
- "@jest/fake-timers": "^28.1.3",
- "@jest/types": "^28.1.3",
- "@types/node": "*",
- "jest-mock": "^28.1.3"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/@jest/globals/node_modules/@jest/fake-timers": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-28.1.3.tgz",
- "integrity": "sha512-D/wOkL2POHv52h+ok5Oj/1gOG9HSywdoPtFsRCUmlCILXNn5eIWmcnd3DIiWlJnpGvQtmajqBP95Ei0EimxfLw==",
- "dev": true,
- "dependencies": {
- "@jest/types": "^28.1.3",
- "@sinonjs/fake-timers": "^9.1.2",
- "@types/node": "*",
- "jest-message-util": "^28.1.3",
- "jest-mock": "^28.1.3",
- "jest-util": "^28.1.3"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/@jest/globals/node_modules/@jest/types": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz",
- "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==",
- "dev": true,
- "dependencies": {
- "@jest/schemas": "^28.1.3",
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^3.0.0",
- "@types/node": "*",
- "@types/yargs": "^17.0.8",
- "chalk": "^4.0.0"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/@jest/globals/node_modules/@sinonjs/fake-timers": {
- "version": "9.1.2",
- "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz",
- "integrity": "sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw==",
- "dev": true,
- "dependencies": {
- "@sinonjs/commons": "^1.7.0"
- }
- },
- "node_modules/@jest/globals/node_modules/@types/yargs": {
- "version": "17.0.10",
- "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.10.tgz",
- "integrity": "sha512-gmEaFwpj/7f/ROdtIlci1R1VYU1J4j95m8T+Tj3iBgiBFKg1foE/PSl93bBd5T9LDXNPo8UlNN6W0qwD8O5OaA==",
- "dev": true,
- "dependencies": {
- "@types/yargs-parser": "*"
- }
- },
- "node_modules/@jest/globals/node_modules/jest-message-util": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-28.1.3.tgz",
- "integrity": "sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g==",
- "dev": true,
- "dependencies": {
- "@babel/code-frame": "^7.12.13",
- "@jest/types": "^28.1.3",
- "@types/stack-utils": "^2.0.0",
- "chalk": "^4.0.0",
- "graceful-fs": "^4.2.9",
- "micromatch": "^4.0.4",
- "pretty-format": "^28.1.3",
- "slash": "^3.0.0",
- "stack-utils": "^2.0.3"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/@jest/globals/node_modules/jest-mock": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-28.1.3.tgz",
- "integrity": "sha512-o3J2jr6dMMWYVH4Lh/NKmDXdosrsJgi4AviS8oXLujcjpCMBb1FMsblDnOXKZKfSiHLxYub1eS0IHuRXsio9eA==",
- "dev": true,
- "dependencies": {
- "@jest/types": "^28.1.3",
- "@types/node": "*"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/@jest/globals/node_modules/jest-util": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz",
- "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==",
- "dev": true,
- "dependencies": {
- "@jest/types": "^28.1.3",
- "@types/node": "*",
- "chalk": "^4.0.0",
- "ci-info": "^3.2.0",
- "graceful-fs": "^4.2.9",
- "picomatch": "^2.2.3"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/@jest/reporters": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-28.1.3.tgz",
- "integrity": "sha512-JuAy7wkxQZVNU/V6g9xKzCGC5LVXx9FDcABKsSXp5MiKPEE2144a/vXTEDoyzjUpZKfVwp08Wqg5A4WfTMAzjg==",
- "dev": true,
- "dependencies": {
- "@bcoe/v8-coverage": "^0.2.3",
- "@jest/console": "^28.1.3",
- "@jest/test-result": "^28.1.3",
- "@jest/transform": "^28.1.3",
- "@jest/types": "^28.1.3",
- "@jridgewell/trace-mapping": "^0.3.13",
- "@types/node": "*",
- "chalk": "^4.0.0",
- "collect-v8-coverage": "^1.0.0",
- "exit": "^0.1.2",
- "glob": "^7.1.3",
- "graceful-fs": "^4.2.9",
- "istanbul-lib-coverage": "^3.0.0",
- "istanbul-lib-instrument": "^5.1.0",
- "istanbul-lib-report": "^3.0.0",
- "istanbul-lib-source-maps": "^4.0.0",
- "istanbul-reports": "^3.1.3",
- "jest-message-util": "^28.1.3",
- "jest-util": "^28.1.3",
- "jest-worker": "^28.1.3",
- "slash": "^3.0.0",
- "string-length": "^4.0.1",
- "strip-ansi": "^6.0.0",
- "terminal-link": "^2.0.0",
- "v8-to-istanbul": "^9.0.1"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- },
- "peerDependencies": {
- "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0"
- },
- "peerDependenciesMeta": {
- "node-notifier": {
- "optional": true
- }
- }
- },
- "node_modules/@jest/reporters/node_modules/@jest/types": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz",
- "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==",
- "dev": true,
- "dependencies": {
- "@jest/schemas": "^28.1.3",
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^3.0.0",
- "@types/node": "*",
- "@types/yargs": "^17.0.8",
- "chalk": "^4.0.0"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/@jest/reporters/node_modules/@types/yargs": {
- "version": "17.0.10",
- "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.10.tgz",
- "integrity": "sha512-gmEaFwpj/7f/ROdtIlci1R1VYU1J4j95m8T+Tj3iBgiBFKg1foE/PSl93bBd5T9LDXNPo8UlNN6W0qwD8O5OaA==",
- "dev": true,
- "dependencies": {
- "@types/yargs-parser": "*"
- }
- },
- "node_modules/@jest/reporters/node_modules/jest-message-util": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-28.1.3.tgz",
- "integrity": "sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g==",
- "dev": true,
- "dependencies": {
- "@babel/code-frame": "^7.12.13",
- "@jest/types": "^28.1.3",
- "@types/stack-utils": "^2.0.0",
- "chalk": "^4.0.0",
- "graceful-fs": "^4.2.9",
- "micromatch": "^4.0.4",
- "pretty-format": "^28.1.3",
- "slash": "^3.0.0",
- "stack-utils": "^2.0.3"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/@jest/reporters/node_modules/jest-util": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz",
- "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==",
- "dev": true,
- "dependencies": {
- "@jest/types": "^28.1.3",
- "@types/node": "*",
- "chalk": "^4.0.0",
- "ci-info": "^3.2.0",
- "graceful-fs": "^4.2.9",
- "picomatch": "^2.2.3"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/@jest/schemas": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz",
- "integrity": "sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==",
- "dev": true,
- "dependencies": {
- "@sinclair/typebox": "^0.24.1"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/@jest/source-map": {
- "version": "28.1.2",
- "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-28.1.2.tgz",
- "integrity": "sha512-cV8Lx3BeStJb8ipPHnqVw/IM2VCMWO3crWZzYodSIkxXnRcXJipCdx1JCK0K5MsJJouZQTH73mzf4vgxRaH9ww==",
- "dev": true,
- "dependencies": {
- "@jridgewell/trace-mapping": "^0.3.13",
- "callsites": "^3.0.0",
- "graceful-fs": "^4.2.9"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/@jest/test-result": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-28.1.3.tgz",
- "integrity": "sha512-kZAkxnSE+FqE8YjW8gNuoVkkC9I7S1qmenl8sGcDOLropASP+BkcGKwhXoyqQuGOGeYY0y/ixjrd/iERpEXHNg==",
- "dev": true,
- "dependencies": {
- "@jest/console": "^28.1.3",
- "@jest/types": "^28.1.3",
- "@types/istanbul-lib-coverage": "^2.0.0",
- "collect-v8-coverage": "^1.0.0"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/@jest/test-result/node_modules/@jest/types": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz",
- "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==",
- "dev": true,
- "dependencies": {
- "@jest/schemas": "^28.1.3",
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^3.0.0",
- "@types/node": "*",
- "@types/yargs": "^17.0.8",
- "chalk": "^4.0.0"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/@jest/test-result/node_modules/@types/yargs": {
- "version": "17.0.10",
- "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.10.tgz",
- "integrity": "sha512-gmEaFwpj/7f/ROdtIlci1R1VYU1J4j95m8T+Tj3iBgiBFKg1foE/PSl93bBd5T9LDXNPo8UlNN6W0qwD8O5OaA==",
- "dev": true,
- "dependencies": {
- "@types/yargs-parser": "*"
- }
- },
- "node_modules/@jest/test-sequencer": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-28.1.3.tgz",
- "integrity": "sha512-NIMPEqqa59MWnDi1kvXXpYbqsfQmSJsIbnd85mdVGkiDfQ9WQQTXOLsvISUfonmnBT+w85WEgneCigEEdHDFxw==",
- "dev": true,
- "dependencies": {
- "@jest/test-result": "^28.1.3",
- "graceful-fs": "^4.2.9",
- "jest-haste-map": "^28.1.3",
- "slash": "^3.0.0"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/@jest/transform": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-28.1.3.tgz",
- "integrity": "sha512-u5dT5di+oFI6hfcLOHGTAfmUxFRrjK+vnaP0kkVow9Md/M7V/MxqQMOz/VV25UZO8pzeA9PjfTpOu6BDuwSPQA==",
- "dev": true,
- "dependencies": {
- "@babel/core": "^7.11.6",
- "@jest/types": "^28.1.3",
- "@jridgewell/trace-mapping": "^0.3.13",
- "babel-plugin-istanbul": "^6.1.1",
- "chalk": "^4.0.0",
- "convert-source-map": "^1.4.0",
- "fast-json-stable-stringify": "^2.0.0",
- "graceful-fs": "^4.2.9",
- "jest-haste-map": "^28.1.3",
- "jest-regex-util": "^28.0.2",
- "jest-util": "^28.1.3",
- "micromatch": "^4.0.4",
- "pirates": "^4.0.4",
- "slash": "^3.0.0",
- "write-file-atomic": "^4.0.1"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/@jest/transform/node_modules/@jest/types": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz",
- "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==",
- "dev": true,
- "dependencies": {
- "@jest/schemas": "^28.1.3",
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^3.0.0",
- "@types/node": "*",
- "@types/yargs": "^17.0.8",
- "chalk": "^4.0.0"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/@jest/transform/node_modules/@types/yargs": {
- "version": "17.0.10",
- "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.10.tgz",
- "integrity": "sha512-gmEaFwpj/7f/ROdtIlci1R1VYU1J4j95m8T+Tj3iBgiBFKg1foE/PSl93bBd5T9LDXNPo8UlNN6W0qwD8O5OaA==",
- "dev": true,
- "dependencies": {
- "@types/yargs-parser": "*"
- }
- },
- "node_modules/@jest/transform/node_modules/jest-util": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz",
- "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==",
- "dev": true,
- "dependencies": {
- "@jest/types": "^28.1.3",
- "@types/node": "*",
- "chalk": "^4.0.0",
- "ci-info": "^3.2.0",
- "graceful-fs": "^4.2.9",
- "picomatch": "^2.2.3"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/@jest/types": {
- "version": "27.5.1",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz",
- "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==",
- "dev": true,
- "dependencies": {
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^3.0.0",
- "@types/node": "*",
- "@types/yargs": "^16.0.0",
- "chalk": "^4.0.0"
- },
- "engines": {
- "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
- }
- },
- "node_modules/@jridgewell/gen-mapping": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz",
- "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==",
- "dev": true,
- "dependencies": {
- "@jridgewell/set-array": "^1.0.0",
- "@jridgewell/sourcemap-codec": "^1.4.10"
- },
- "engines": {
- "node": ">=6.0.0"
- }
- },
"node_modules/@jridgewell/resolve-uri": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
@@ -1603,18 +382,18 @@
"integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw=="
},
"node_modules/@jridgewell/trace-mapping": {
- "version": "0.3.14",
- "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz",
- "integrity": "sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ==",
+ "version": "0.3.16",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.16.tgz",
+ "integrity": "sha512-LCQ+NeThyJ4k1W2d+vIKdxuSt9R3pQSZ4P92m7EakaYuXcVWbHuT5bjNcqLd4Rdgi6xYWYDvBJZJLZSLanjDcA==",
"dependencies": {
- "@jridgewell/resolve-uri": "^3.0.3",
- "@jridgewell/sourcemap-codec": "^1.4.10"
+ "@jridgewell/resolve-uri": "3.1.0",
+ "@jridgewell/sourcemap-codec": "1.4.14"
}
},
"node_modules/@jsep-plugin/regex": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/@jsep-plugin/regex/-/regex-1.0.2.tgz",
- "integrity": "sha512-Nn/Bcaww8zOebMDqNmGlhAWPWhIr/8S8lGIgaB/fSqev5xaO5uKy5i4qvTh63GpR+VzKqimgxDdcxdcRuCJXSw==",
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@jsep-plugin/regex/-/regex-1.0.3.tgz",
+ "integrity": "sha512-XfZgry4DwEZvSFtS/6Y+R48D7qJYJK6R9/yJFyUFHCIUMEEHuJ4X95TDgJp5QkmzfLYvapMPzskV5HpIDrREug==",
"dev": true,
"engines": {
"node": ">= 10.16.0"
@@ -1624,9 +403,9 @@
}
},
"node_modules/@jsep-plugin/ternary": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/@jsep-plugin/ternary/-/ternary-1.1.2.tgz",
- "integrity": "sha512-gXguJc09uCrqWt1MD7L1+ChO32g4UH4BYGpHPoQRLhyU7pAPPRA7cvKbyjoqhnUlLutiXvLzB5hVVawPKax8jw==",
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/@jsep-plugin/ternary/-/ternary-1.1.3.tgz",
+ "integrity": "sha512-qtLGzCNzPVJ3kdH6/zoLWDPjauHIKiLSBAR71Wa0+PWvGA8wODUQvRgxtpUA5YqAYL3CQ8S4qXhd/9WuWTZirg==",
"dev": true,
"engines": {
"node": ">= 10.16.0"
@@ -1635,6 +414,55 @@
"jsep": "^0.4.0||^1.0.0"
}
},
+ "node_modules/@mcaptcha/core-glue": {
+ "version": "0.1.0-alpha-5",
+ "resolved": "https://registry.npmjs.org/@mcaptcha/core-glue/-/core-glue-0.1.0-alpha-5.tgz",
+ "integrity": "sha512-16qWm5O5X0Y9LXULULaAks8Vf9FNlUUBcR5KDt49aWhFhG5++JzxNmCwQM9EJSHNU7y0U+FdyAWcGmjfKlkRLA==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "http://mcaptcha.org/donate"
+ },
+ {
+ "type": "liberapay",
+ "url": "https://liberapay.com/mcaptcha"
+ },
+ {
+ "type": "individual",
+ "url": "http://batsense.net/donate"
+ },
+ {
+ "type": "liberapay",
+ "url": "https://liberapay.com/realaravinth"
+ }
+ ]
+ },
+ "node_modules/@mcaptcha/vanilla-glue": {
+ "version": "0.1.0-alpha-3",
+ "resolved": "https://registry.npmjs.org/@mcaptcha/vanilla-glue/-/vanilla-glue-0.1.0-alpha-3.tgz",
+ "integrity": "sha512-GT6TJBgmViGXcXiT5VOr+h/6iOnThSlZuCoOWncubyTZU9R3cgU5vWPkF7G6Ob6ee2CBe3yqBxxk24CFVGTVXw==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "http://mcaptcha.org/donate"
+ },
+ {
+ "type": "liberapay",
+ "url": "https://liberapay.com/mcaptcha"
+ },
+ {
+ "type": "individual",
+ "url": "http://batsense.net/donate"
+ },
+ {
+ "type": "liberapay",
+ "url": "https://liberapay.com/realaravinth"
+ }
+ ],
+ "dependencies": {
+ "@mcaptcha/core-glue": "^0.1.0-alpha-5"
+ }
+ },
"node_modules/@nodelib/fs.scandir": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@@ -1667,19 +495,35 @@
"node": ">= 8"
}
},
+ "node_modules/@playwright/test": {
+ "version": "1.27.0",
+ "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.27.0.tgz",
+ "integrity": "sha512-L4BswoJvGkFsEHhEgzVNHBnkFB1FbnBQn3QmvTl7+AouoJQ4a8tLwZKvytdovCsNi7B5cXuRo58yGvfM5PnExw==",
+ "dev": true,
+ "dependencies": {
+ "@types/node": "*",
+ "playwright-core": "1.27.0"
+ },
+ "bin": {
+ "playwright": "cli.js"
+ },
+ "engines": {
+ "node": ">=14"
+ }
+ },
"node_modules/@popperjs/core": {
- "version": "2.11.5",
- "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.5.tgz",
- "integrity": "sha512-9X2obfABZuDVLCgPK9aX0a/x4jaOEweTTWE2+9sr0Qqqevj2Uv5XorvusThmc9XGYpS9yI+fhh8RTafBtGposw==",
+ "version": "2.11.6",
+ "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.6.tgz",
+ "integrity": "sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/popperjs"
}
},
"node_modules/@primer/octicons": {
- "version": "17.3.0",
- "resolved": "https://registry.npmjs.org/@primer/octicons/-/octicons-17.3.0.tgz",
- "integrity": "sha512-4zPwwloYWdR6RznMafV7Fsw3n2CeDPp/+qEIQbaX/tBbPY1KmU0OAXmhRfhD5AzgB5kdV1aQ7KnQr1GeQXl9Dg==",
+ "version": "17.7.0",
+ "resolved": "https://registry.npmjs.org/@primer/octicons/-/octicons-17.7.0.tgz",
+ "integrity": "sha512-J5cVJDhExmqLGLWu8zHTOqcC8g1rQL7QzQZdbvHxW85u8ya82GtF5F68uHMDI5En3fsMlbkkF8Rz6dCaV3r+KA==",
"dependencies": {
"object-assign": "^4.1.1"
}
@@ -1705,7 +549,7 @@
"rollup": "^2.38.3"
}
},
- "node_modules/@rollup/pluginutils": {
+ "node_modules/@rollup/plugin-commonjs/node_modules/@rollup/pluginutils": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz",
"integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==",
@@ -1722,40 +566,44 @@
"rollup": "^1.20.0||^2.0.0"
}
},
- "node_modules/@rollup/pluginutils/node_modules/estree-walker": {
+ "node_modules/@rollup/plugin-commonjs/node_modules/@rollup/pluginutils/node_modules/estree-walker": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz",
"integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==",
"dev": true
},
- "node_modules/@sinclair/typebox": {
- "version": "0.24.20",
- "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.20.tgz",
- "integrity": "sha512-kVaO5aEFZb33nPMTZBxiPEkY+slxiPtqC7QX8f9B3eGOMBvEfuMfxp9DSTTCsRJPumPKjrge4yagyssO4q6qzQ==",
+ "node_modules/@rollup/pluginutils": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.0.1.tgz",
+ "integrity": "sha512-4HaCVEXXuObvcPUaUlLt4faHYHCeQOOWNj8NKFGaRSrw3ZLD0TWeAFZicV9vXjnE2nkNuaVTfTuwAnjR+6uc9A==",
+ "dev": true,
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "estree-walker": "^2.0.2",
+ "picomatch": "^2.3.1"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "rollup": "^1.20.0||^2.0.0||^3.0.0"
+ },
+ "peerDependenciesMeta": {
+ "rollup": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@rollup/pluginutils/node_modules/@types/estree": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.0.tgz",
+ "integrity": "sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==",
"dev": true
},
- "node_modules/@sinonjs/commons": {
- "version": "1.8.3",
- "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz",
- "integrity": "sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==",
- "dev": true,
- "dependencies": {
- "type-detect": "4.0.8"
- }
- },
- "node_modules/@sinonjs/fake-timers": {
- "version": "8.1.0",
- "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz",
- "integrity": "sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==",
- "dev": true,
- "dependencies": {
- "@sinonjs/commons": "^1.7.0"
- }
- },
"node_modules/@stoplight/better-ajv-errors": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/@stoplight/better-ajv-errors/-/better-ajv-errors-1.0.1.tgz",
- "integrity": "sha512-rgxT+ZMeZbYRiOLNk6Oy6e/Ig1iQKo0IL8v/Y9E/0FewzgtkGs/p5dMeUpIFZXWj3RZaEPmfL9yh0oUEmNXZjg==",
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@stoplight/better-ajv-errors/-/better-ajv-errors-1.0.3.tgz",
+ "integrity": "sha512-0p9uXkuB22qGdNfy3VeEhxkU5uwvp/KrBTAbrLBURv6ilxIVwanKwjMc41lQfIVgPGcOkmLbTolfFrSsueu7zA==",
"dev": true,
"dependencies": {
"jsonpointer": "^5.0.0",
@@ -1825,22 +673,10 @@
"node": ">=8.3.0"
}
},
- "node_modules/@stoplight/lifecycle": {
- "version": "2.3.2",
- "resolved": "https://registry.npmjs.org/@stoplight/lifecycle/-/lifecycle-2.3.2.tgz",
- "integrity": "sha512-v0u8p27FA/eg04b4z6QXw4s0NeeFcRzyvseBW0+k/q4jtpg7EhVCqy42EbbbU43NTNDpIeQ81OcvkFz+6CYshw==",
- "dev": true,
- "dependencies": {
- "wolfy87-eventemitter": "~5.2.8"
- },
- "engines": {
- "node": ">=8.3.0"
- }
- },
"node_modules/@stoplight/ordered-object-literal": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/@stoplight/ordered-object-literal/-/ordered-object-literal-1.0.3.tgz",
- "integrity": "sha512-cjJ7PPkhgTXNMTkevAlmyrx9xOOCaI3c6rEeYb6VitL1o1WcZtrz9KyFyISmTmUa7yYTiy2IS/ud9S8s2sn3+A==",
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/@stoplight/ordered-object-literal/-/ordered-object-literal-1.0.4.tgz",
+ "integrity": "sha512-OF8uib1jjDs5/cCU+iOVy+GJjU3X7vk/qJIkIJFqwmlJKrrtijFmqwbu8XToXrwTYLQTP+Hebws5gtZEmk9jag==",
"dev": true,
"engines": {
"node": ">=8"
@@ -1856,9 +692,9 @@
}
},
"node_modules/@stoplight/spectral-cli": {
- "version": "6.4.1",
- "resolved": "https://registry.npmjs.org/@stoplight/spectral-cli/-/spectral-cli-6.4.1.tgz",
- "integrity": "sha512-l5nWXy/6YEyk51VVrOurhupVScIqfK0ra8yIRSli+gnW5Kf5Nfw5PLci5GceQGaM5WE+wmqZ/iY95yOVFwHc+A==",
+ "version": "6.5.1",
+ "resolved": "https://registry.npmjs.org/@stoplight/spectral-cli/-/spectral-cli-6.5.1.tgz",
+ "integrity": "sha512-+qpwsDG2jQ4ULQmegBWonI3UnF6tUh351WDnV1GU8acl8eaeKbS+ZUNBgoP2f9tnMTfITdctVRFEGC3D6P7f9g==",
"dev": true,
"dependencies": {
"@rollup/plugin-commonjs": "^20.0.0",
@@ -1921,25 +757,26 @@
}
},
"node_modules/@stoplight/spectral-core": {
- "version": "1.12.3",
- "resolved": "https://registry.npmjs.org/@stoplight/spectral-core/-/spectral-core-1.12.3.tgz",
- "integrity": "sha512-+PhVzTD8q6kUZw4BcbM+ibVaH5/ELryKt5tlLitA8SJIaJ+5/9ZKaGN0AV3ExZQZGYvXwucPOQuJKYZYKA6mWg==",
+ "version": "1.14.2",
+ "resolved": "https://registry.npmjs.org/@stoplight/spectral-core/-/spectral-core-1.14.2.tgz",
+ "integrity": "sha512-W2Z31lasEICpZS50STFskOdkn4g0Va81XA1A88LIj9mvlctDFf4BfpjLgIjKkI4f2DEK5C4j3COcbbzV8y5Xig==",
"dev": true,
"dependencies": {
- "@stoplight/better-ajv-errors": "1.0.1",
- "@stoplight/json": "~3.18.1",
- "@stoplight/lifecycle": "2.3.2",
+ "@stoplight/better-ajv-errors": "1.0.3",
+ "@stoplight/json": "~3.20.1",
"@stoplight/path": "1.3.2",
"@stoplight/spectral-parsers": "^1.0.0",
"@stoplight/spectral-ref-resolver": "^1.0.0",
"@stoplight/spectral-runtime": "^1.0.0",
- "@stoplight/types": "~13.2.0",
+ "@stoplight/types": "~13.6.0",
+ "@types/es-aggregate-error": "^1.0.2",
"@types/json-schema": "^7.0.11",
"ajv": "^8.6.0",
"ajv-errors": "~3.0.0",
"ajv-formats": "~2.1.0",
"blueimp-md5": "2.18.0",
- "jsonpath-plus": "6.0.1",
+ "es-aggregate-error": "^1.0.7",
+ "jsonpath-plus": "7.1.0",
"lodash": "~4.17.21",
"lodash.topath": "^4.5.2",
"minimatch": "3.1.2",
@@ -1953,13 +790,14 @@
}
},
"node_modules/@stoplight/spectral-core/node_modules/@stoplight/json": {
- "version": "3.18.1",
- "resolved": "https://registry.npmjs.org/@stoplight/json/-/json-3.18.1.tgz",
- "integrity": "sha512-QmELAqBS8DC+8YuG7+OvDVP6RaUVi8bzN0KKW2UEcZg+0a1sqeeZgfW079AmJIZg8HEN7udAt4iozIB8Dm0t1Q==",
+ "version": "3.20.1",
+ "resolved": "https://registry.npmjs.org/@stoplight/json/-/json-3.20.1.tgz",
+ "integrity": "sha512-FXfud+uWgIj1xv6nUO9WnmgmnVikaxJcbtR4XQt4C42n5c2qua3U05Z/3B57hP5TJRSj+tpn9ID6/bFeyYYlEg==",
"dev": true,
"dependencies": {
- "@stoplight/ordered-object-literal": "^1.0.2",
- "@stoplight/types": "^13.0.0",
+ "@stoplight/ordered-object-literal": "^1.0.3",
+ "@stoplight/path": "^1.3.2",
+ "@stoplight/types": "^13.6.0",
"jsonc-parser": "~2.2.1",
"lodash": "^4.17.21",
"safe-stable-stringify": "^1.1"
@@ -1969,9 +807,9 @@
}
},
"node_modules/@stoplight/spectral-core/node_modules/@stoplight/types": {
- "version": "13.2.0",
- "resolved": "https://registry.npmjs.org/@stoplight/types/-/types-13.2.0.tgz",
- "integrity": "sha512-V3BRfzWEAcCpICGQh/WW2LX4rcB2KagQ7/msf0BDTCF5qpFMSwOxcjv25k1NUMVQSh3qwGfGoka/gYGA5m+NQA==",
+ "version": "13.6.0",
+ "resolved": "https://registry.npmjs.org/@stoplight/types/-/types-13.6.0.tgz",
+ "integrity": "sha512-dzyuzvUjv3m1wmhPfq82lCVYGcXG0xUYgqnWfCq3PCVR4BKFhjdkHrnJ+jIDoMKvXb05AZP/ObQF6+NpDo29IQ==",
"dev": true,
"dependencies": {
"@types/json-schema": "^7.0.4",
@@ -1982,9 +820,9 @@
}
},
"node_modules/@stoplight/spectral-formats": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/@stoplight/spectral-formats/-/spectral-formats-1.2.0.tgz",
- "integrity": "sha512-idvn7r8fvQjY/KeJpKgXQ5eJhce6N6/KoKWMPSh5yyvYDpn+bkU4pxAD79jOJaDnIyKJd1jjTPEJWnxbS0jj6A==",
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/@stoplight/spectral-formats/-/spectral-formats-1.4.0.tgz",
+ "integrity": "sha512-j9VQukDzgqDSi26rK9LqsbXrqtkeIsPSPgEf5/sxRsmeF2bwWUhSjYXgYin4flSZ7owFZjZWQ3o0Qq3iApi2JQ==",
"dev": true,
"dependencies": {
"@stoplight/json": "^3.17.0",
@@ -1997,17 +835,16 @@
}
},
"node_modules/@stoplight/spectral-functions": {
- "version": "1.6.1",
- "resolved": "https://registry.npmjs.org/@stoplight/spectral-functions/-/spectral-functions-1.6.1.tgz",
- "integrity": "sha512-f4cFtbI35bQtY0t4fYhKtS+/nMU3UsAeFlqm4tARGGG5WjOv4ieCFNFbgodKNiO3F4O+syMEjVQuXlBNPuY7jw==",
+ "version": "1.7.1",
+ "resolved": "https://registry.npmjs.org/@stoplight/spectral-functions/-/spectral-functions-1.7.1.tgz",
+ "integrity": "sha512-UWeUrxc1pu45ZNYKtK3OloMpkUNTPqwpmjbGUn4oEnbqrLEYu/B2oOg66EtGcadOBEsdOb7f5vaPlhUNNrpEpQ==",
"dev": true,
"dependencies": {
- "@stoplight/better-ajv-errors": "1.0.1",
- "@stoplight/json": "~3.17.1",
+ "@stoplight/better-ajv-errors": "1.0.3",
+ "@stoplight/json": "^3.17.1",
"@stoplight/spectral-core": "^1.7.0",
"@stoplight/spectral-formats": "^1.0.0",
"@stoplight/spectral-runtime": "^1.1.0",
- "@stoplight/types": "12.3.0",
"ajv": "^8.6.3",
"ajv-draft-04": "~1.0.0",
"ajv-errors": "~3.0.0",
@@ -2020,13 +857,14 @@
}
},
"node_modules/@stoplight/spectral-functions/node_modules/@stoplight/json": {
- "version": "3.17.2",
- "resolved": "https://registry.npmjs.org/@stoplight/json/-/json-3.17.2.tgz",
- "integrity": "sha512-NwIVzanXRUy291J5BMkncCZRMG1Lx+aq+VidGQgfkJjgo8vh1Y/PSAz7fSU8gVGSZBCcqmOkMI7R4zw7DlfTwA==",
+ "version": "3.20.1",
+ "resolved": "https://registry.npmjs.org/@stoplight/json/-/json-3.20.1.tgz",
+ "integrity": "sha512-FXfud+uWgIj1xv6nUO9WnmgmnVikaxJcbtR4XQt4C42n5c2qua3U05Z/3B57hP5TJRSj+tpn9ID6/bFeyYYlEg==",
"dev": true,
"dependencies": {
- "@stoplight/ordered-object-literal": "^1.0.2",
- "@stoplight/types": "^12.3.0",
+ "@stoplight/ordered-object-literal": "^1.0.3",
+ "@stoplight/path": "^1.3.2",
+ "@stoplight/types": "^13.6.0",
"jsonc-parser": "~2.2.1",
"lodash": "^4.17.21",
"safe-stable-stringify": "^1.1"
@@ -2035,21 +873,64 @@
"node": ">=8.3.0"
}
},
- "node_modules/@stoplight/spectral-parsers": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/@stoplight/spectral-parsers/-/spectral-parsers-1.0.1.tgz",
- "integrity": "sha512-JGKlrTxhjUzIGo2FOCf8Qp0WKTWXedoRNPovqYPE8pAp08epqU8DzHwl/i46BGH5yfTmouKMZgBN/PV2+Cr5jw==",
+ "node_modules/@stoplight/spectral-functions/node_modules/@stoplight/types": {
+ "version": "13.7.0",
+ "resolved": "https://registry.npmjs.org/@stoplight/types/-/types-13.7.0.tgz",
+ "integrity": "sha512-7ePIccfTxjEhruv8VrkDv5whP5qd9ijRzAWEbjYpUYnDfaqPTfq8/wMMjMCAKIecboxsAVD9LZy/3puXddGsDQ==",
"dev": true,
"dependencies": {
- "@stoplight/json": "3.17.0",
- "@stoplight/types": "12.3.0",
- "@stoplight/yaml": "4.2.2",
+ "@types/json-schema": "^7.0.4",
+ "utility-types": "^3.10.0"
+ },
+ "engines": {
+ "node": "^12.20 || >=14.13"
+ }
+ },
+ "node_modules/@stoplight/spectral-parsers": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@stoplight/spectral-parsers/-/spectral-parsers-1.0.2.tgz",
+ "integrity": "sha512-ZQXknJ+BM5Re4Opj4cgVlHgG2qyOk/wznKJq3Vf1qsBEg2CNzN0pJmSB0deRqW0kArqm44qpb8c+cz3F2rgMtw==",
+ "dev": true,
+ "dependencies": {
+ "@stoplight/json": "~3.20.1",
+ "@stoplight/types": "^13.6.0",
+ "@stoplight/yaml": "~4.2.3",
"tslib": "^2.3.1"
},
"engines": {
"node": ">=12"
}
},
+ "node_modules/@stoplight/spectral-parsers/node_modules/@stoplight/json": {
+ "version": "3.20.1",
+ "resolved": "https://registry.npmjs.org/@stoplight/json/-/json-3.20.1.tgz",
+ "integrity": "sha512-FXfud+uWgIj1xv6nUO9WnmgmnVikaxJcbtR4XQt4C42n5c2qua3U05Z/3B57hP5TJRSj+tpn9ID6/bFeyYYlEg==",
+ "dev": true,
+ "dependencies": {
+ "@stoplight/ordered-object-literal": "^1.0.3",
+ "@stoplight/path": "^1.3.2",
+ "@stoplight/types": "^13.6.0",
+ "jsonc-parser": "~2.2.1",
+ "lodash": "^4.17.21",
+ "safe-stable-stringify": "^1.1"
+ },
+ "engines": {
+ "node": ">=8.3.0"
+ }
+ },
+ "node_modules/@stoplight/spectral-parsers/node_modules/@stoplight/types": {
+ "version": "13.7.0",
+ "resolved": "https://registry.npmjs.org/@stoplight/types/-/types-13.7.0.tgz",
+ "integrity": "sha512-7ePIccfTxjEhruv8VrkDv5whP5qd9ijRzAWEbjYpUYnDfaqPTfq8/wMMjMCAKIecboxsAVD9LZy/3puXddGsDQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/json-schema": "^7.0.4",
+ "utility-types": "^3.10.0"
+ },
+ "engines": {
+ "node": "^12.20 || >=14.13"
+ }
+ },
"node_modules/@stoplight/spectral-ref-resolver": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/@stoplight/spectral-ref-resolver/-/spectral-ref-resolver-1.0.1.tgz",
@@ -2067,25 +948,25 @@
}
},
"node_modules/@stoplight/spectral-ruleset-bundler": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/@stoplight/spectral-ruleset-bundler/-/spectral-ruleset-bundler-1.3.0.tgz",
- "integrity": "sha512-6Tif7GQL18F0LN1+FhEmhFWgE/TiWudb/pFl4DC7oS1QRoutB7QJPqIfVFSmteToPidxlrIbC6VAXSyEhlpDVQ==",
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/@stoplight/spectral-ruleset-bundler/-/spectral-ruleset-bundler-1.3.2.tgz",
+ "integrity": "sha512-sy7mHVBwmo5/8dUlnWiel2UND1Mnu3x+okBAgLmkGcIpXz74rMmVY3h5vT6rjxw65WZ3/c3mtm1dRQuSe+q5fw==",
"dev": true,
"dependencies": {
- "@rollup/plugin-commonjs": "^21.0.1",
+ "@rollup/plugin-commonjs": "~22.0.2",
"@stoplight/path": "1.3.2",
"@stoplight/spectral-core": ">=1",
"@stoplight/spectral-formats": ">=1",
"@stoplight/spectral-functions": ">=1",
"@stoplight/spectral-parsers": ">=1",
"@stoplight/spectral-ref-resolver": ">=1",
- "@stoplight/spectral-ruleset-migrator": "^1.5.2",
+ "@stoplight/spectral-ruleset-migrator": "^1.7.4",
"@stoplight/spectral-rulesets": ">=1",
"@stoplight/spectral-runtime": "^1.1.0",
- "@stoplight/types": "^12.3.0",
+ "@stoplight/types": "^13.6.0",
"@types/node": "*",
"pony-cause": "1.1.1",
- "rollup": "~2.67.0",
+ "rollup": "~2.79.0",
"tslib": "^2.3.1",
"validate-npm-package-name": "3.0.0"
},
@@ -2094,9 +975,9 @@
}
},
"node_modules/@stoplight/spectral-ruleset-bundler/node_modules/@rollup/plugin-commonjs": {
- "version": "21.1.0",
- "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-21.1.0.tgz",
- "integrity": "sha512-6ZtHx3VHIp2ReNNDxHjuUml6ur+WcQ28N1yHgCQwsbNkQg2suhxGMDQGJOn/KuDxKtd1xuZP5xSTwBA4GQ8hbA==",
+ "version": "22.0.2",
+ "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-22.0.2.tgz",
+ "integrity": "sha512-//NdP6iIwPbMTcazYsiBMbJW7gfmpHom33u1beiIoHDEM0Q9clvtQB1T0efvMqHeKsGohiHo97BCPCkBXdscwg==",
"dev": true,
"dependencies": {
"@rollup/pluginutils": "^3.1.0",
@@ -2107,41 +988,62 @@
"magic-string": "^0.25.7",
"resolve": "^1.17.0"
},
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "peerDependencies": {
+ "rollup": "^2.68.0"
+ }
+ },
+ "node_modules/@stoplight/spectral-ruleset-bundler/node_modules/@rollup/pluginutils": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz",
+ "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==",
+ "dev": true,
+ "dependencies": {
+ "@types/estree": "0.0.39",
+ "estree-walker": "^1.0.1",
+ "picomatch": "^2.2.2"
+ },
"engines": {
"node": ">= 8.0.0"
},
"peerDependencies": {
- "rollup": "^2.38.3"
+ "rollup": "^1.20.0||^2.0.0"
}
},
- "node_modules/@stoplight/spectral-ruleset-bundler/node_modules/rollup": {
- "version": "2.67.3",
- "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.67.3.tgz",
- "integrity": "sha512-G/x1vUwbGtP6O5ZM8/sWr8+p7YfZhI18pPqMRtMYMWSbHjKZ/ajHGiM+GWNTlWyOR0EHIdT8LHU+Z4ciIZ1oBw==",
+ "node_modules/@stoplight/spectral-ruleset-bundler/node_modules/@rollup/pluginutils/node_modules/estree-walker": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz",
+ "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==",
+ "dev": true
+ },
+ "node_modules/@stoplight/spectral-ruleset-bundler/node_modules/@stoplight/types": {
+ "version": "13.7.0",
+ "resolved": "https://registry.npmjs.org/@stoplight/types/-/types-13.7.0.tgz",
+ "integrity": "sha512-7ePIccfTxjEhruv8VrkDv5whP5qd9ijRzAWEbjYpUYnDfaqPTfq8/wMMjMCAKIecboxsAVD9LZy/3puXddGsDQ==",
"dev": true,
- "bin": {
- "rollup": "dist/bin/rollup"
+ "dependencies": {
+ "@types/json-schema": "^7.0.4",
+ "utility-types": "^3.10.0"
},
"engines": {
- "node": ">=10.0.0"
- },
- "optionalDependencies": {
- "fsevents": "~2.3.2"
+ "node": "^12.20 || >=14.13"
}
},
"node_modules/@stoplight/spectral-ruleset-migrator": {
- "version": "1.7.3",
- "resolved": "https://registry.npmjs.org/@stoplight/spectral-ruleset-migrator/-/spectral-ruleset-migrator-1.7.3.tgz",
- "integrity": "sha512-1TlJgNxIqlcafzrH6gsGpQQcVkFhndib5piMNXVg9xshJ42l2yC6A0AUAixUC+ODJ5098DR7SjIYBVKk+CTQSw==",
+ "version": "1.7.4",
+ "resolved": "https://registry.npmjs.org/@stoplight/spectral-ruleset-migrator/-/spectral-ruleset-migrator-1.7.4.tgz",
+ "integrity": "sha512-QySMWSvGUC5D8cNDvXhrXEY0a4DB5hewHwjxXbwlH51fVNiVKJ4+KcaCW3s2yAT4T1p6/ij8NkLX9T81D4vSCg==",
"dev": true,
"dependencies": {
- "@stoplight/json": "~3.17.0",
- "@stoplight/ordered-object-literal": "1.0.2",
+ "@stoplight/json": "~3.20.1",
+ "@stoplight/ordered-object-literal": "~1.0.4",
"@stoplight/path": "1.3.2",
"@stoplight/spectral-functions": "^1.0.0",
"@stoplight/spectral-runtime": "^1.1.0",
- "@stoplight/types": "^12.3.0",
- "@stoplight/yaml": "4.2.2",
+ "@stoplight/types": "^13.6.0",
+ "@stoplight/yaml": "~4.2.3",
"@types/node": "*",
"ajv": "^8.6.0",
"ast-types": "0.14.2",
@@ -2154,29 +1056,50 @@
"node": ">=12"
}
},
- "node_modules/@stoplight/spectral-ruleset-migrator/node_modules/@stoplight/ordered-object-literal": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/@stoplight/ordered-object-literal/-/ordered-object-literal-1.0.2.tgz",
- "integrity": "sha512-0ZMS/9sNU3kVo/6RF3eAv7MK9DY8WLjiVJB/tVyfF2lhr2R4kqh534jZ0PlrFB9CRXrdndzn1DbX6ihKZXft2w==",
+ "node_modules/@stoplight/spectral-ruleset-migrator/node_modules/@stoplight/json": {
+ "version": "3.20.1",
+ "resolved": "https://registry.npmjs.org/@stoplight/json/-/json-3.20.1.tgz",
+ "integrity": "sha512-FXfud+uWgIj1xv6nUO9WnmgmnVikaxJcbtR4XQt4C42n5c2qua3U05Z/3B57hP5TJRSj+tpn9ID6/bFeyYYlEg==",
"dev": true,
+ "dependencies": {
+ "@stoplight/ordered-object-literal": "^1.0.3",
+ "@stoplight/path": "^1.3.2",
+ "@stoplight/types": "^13.6.0",
+ "jsonc-parser": "~2.2.1",
+ "lodash": "^4.17.21",
+ "safe-stable-stringify": "^1.1"
+ },
"engines": {
- "node": ">=8"
+ "node": ">=8.3.0"
+ }
+ },
+ "node_modules/@stoplight/spectral-ruleset-migrator/node_modules/@stoplight/types": {
+ "version": "13.7.0",
+ "resolved": "https://registry.npmjs.org/@stoplight/types/-/types-13.7.0.tgz",
+ "integrity": "sha512-7ePIccfTxjEhruv8VrkDv5whP5qd9ijRzAWEbjYpUYnDfaqPTfq8/wMMjMCAKIecboxsAVD9LZy/3puXddGsDQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/json-schema": "^7.0.4",
+ "utility-types": "^3.10.0"
+ },
+ "engines": {
+ "node": "^12.20 || >=14.13"
}
},
"node_modules/@stoplight/spectral-rulesets": {
- "version": "1.11.0",
- "resolved": "https://registry.npmjs.org/@stoplight/spectral-rulesets/-/spectral-rulesets-1.11.0.tgz",
- "integrity": "sha512-0zFbxIuoWmGrkl2txOuaEDF8o6aoKDpMAYOG2oDfmmX9FhXX3c3ivIy80hyb2tMKkIYuqqx/zwIiOuww5S8iUA==",
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/@stoplight/spectral-rulesets/-/spectral-rulesets-1.14.1.tgz",
+ "integrity": "sha512-tn6a5fYPFDwEY+/YyK/hcq2gcR5nSIBt7l+JGELb/2RdTzD5ikj2mfl2ua3uxbqOZytftFoOX5ewGZ0qQNrudw==",
"dev": true,
"dependencies": {
- "@asyncapi/specs": "^2.14.0",
- "@stoplight/better-ajv-errors": "1.0.1",
+ "@asyncapi/specs": "^3.2.0",
+ "@stoplight/better-ajv-errors": "1.0.3",
"@stoplight/json": "^3.17.0",
"@stoplight/spectral-core": "^1.8.1",
- "@stoplight/spectral-formats": "^1.2.0",
+ "@stoplight/spectral-formats": "^1.4.0",
"@stoplight/spectral-functions": "^1.5.1",
"@stoplight/spectral-runtime": "^1.1.1",
- "@stoplight/types": "^12.5.0",
+ "@stoplight/types": "^13.6.0",
"@types/json-schema": "^7.0.7",
"ajv": "^8.8.2",
"ajv-formats": "~2.1.0",
@@ -2189,16 +1112,16 @@
}
},
"node_modules/@stoplight/spectral-rulesets/node_modules/@stoplight/types": {
- "version": "12.5.0",
- "resolved": "https://registry.npmjs.org/@stoplight/types/-/types-12.5.0.tgz",
- "integrity": "sha512-dwqYcDrGmEyUv5TWrDam5TGOxU72ufyQ7hnOIIDdmW5ezOwZaBFoR5XQ9AsH49w7wgvOqB2Bmo799pJPWnpCbg==",
+ "version": "13.7.0",
+ "resolved": "https://registry.npmjs.org/@stoplight/types/-/types-13.7.0.tgz",
+ "integrity": "sha512-7ePIccfTxjEhruv8VrkDv5whP5qd9ijRzAWEbjYpUYnDfaqPTfq8/wMMjMCAKIecboxsAVD9LZy/3puXddGsDQ==",
"dev": true,
"dependencies": {
"@types/json-schema": "^7.0.4",
"utility-types": "^3.10.0"
},
"engines": {
- "node": ">=8"
+ "node": "^12.20 || >=14.13"
}
},
"node_modules/@stoplight/spectral-runtime": {
@@ -2233,13 +1156,13 @@
}
},
"node_modules/@stoplight/yaml": {
- "version": "4.2.2",
- "resolved": "https://registry.npmjs.org/@stoplight/yaml/-/yaml-4.2.2.tgz",
- "integrity": "sha512-N086FU8pmSpjc5TvMBjmlTniZVh3OXzmEh6SYljSLiuv6aMxgjyjf13YrAlUqgu0b4b6pQ5zmkjrfo9i0SiLsw==",
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/@stoplight/yaml/-/yaml-4.2.3.tgz",
+ "integrity": "sha512-Mx01wjRAR9C7yLMUyYFTfbUf5DimEpHMkRDQ1PKLe9dfNILbgdxyrncsOXM3vCpsQ1Hfj4bPiGl+u4u6e9Akqw==",
"dev": true,
"dependencies": {
"@stoplight/ordered-object-literal": "^1.0.1",
- "@stoplight/types": "^12.0.0",
+ "@stoplight/types": "^13.0.0",
"@stoplight/yaml-ast-parser": "0.0.48",
"tslib": "^2.2.0"
},
@@ -2253,18 +1176,31 @@
"integrity": "sha512-sV+51I7WYnLJnKPn2EMWgS4EUfoP4iWEbrWwbXsj0MZCB/xOK8j6+C9fntIdOM50kpx45ZLC3s6kwKivWuqvyg==",
"dev": true
},
+ "node_modules/@stoplight/yaml/node_modules/@stoplight/types": {
+ "version": "13.7.0",
+ "resolved": "https://registry.npmjs.org/@stoplight/types/-/types-13.7.0.tgz",
+ "integrity": "sha512-7ePIccfTxjEhruv8VrkDv5whP5qd9ijRzAWEbjYpUYnDfaqPTfq8/wMMjMCAKIecboxsAVD9LZy/3puXddGsDQ==",
+ "dev": true,
+ "dependencies": {
+ "@types/json-schema": "^7.0.4",
+ "utility-types": "^3.10.0"
+ },
+ "engines": {
+ "node": "^12.20 || >=14.13"
+ }
+ },
"node_modules/@swc/helpers": {
"version": "0.2.14",
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.2.14.tgz",
"integrity": "sha512-wpCQMhf5p5GhNg2MmGKXzUNwxe7zRiCsmqYsamez2beP7mKPCSiu+BjZcdN95yYSzO857kr0VfQewmGpS77nqA=="
},
"node_modules/@tootallnate/once": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz",
- "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==",
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz",
+ "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==",
"dev": true,
"engines": {
- "node": ">= 6"
+ "node": ">= 10"
}
},
"node_modules/@trysound/sax": {
@@ -2276,45 +1212,19 @@
"node": ">=10.13.0"
}
},
- "node_modules/@types/babel__core": {
- "version": "7.1.19",
- "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.19.tgz",
- "integrity": "sha512-WEOTgRsbYkvA/KCsDwVEGkd7WAr1e3g31VHQ8zy5gul/V1qKullU/BU5I68X5v7V3GnB9eotmom4v5a5gjxorw==",
- "dev": true,
- "dependencies": {
- "@babel/parser": "^7.1.0",
- "@babel/types": "^7.0.0",
- "@types/babel__generator": "*",
- "@types/babel__template": "*",
- "@types/babel__traverse": "*"
- }
+ "node_modules/@types/chai": {
+ "version": "4.3.3",
+ "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.3.tgz",
+ "integrity": "sha512-hC7OMnszpxhZPduX+m+nrx+uFoLkWOMiR4oa/AZF3MuSETYTZmFfJAHqZEM8MVlvfG7BEUcgvtwoCTxBp6hm3g==",
+ "dev": true
},
- "node_modules/@types/babel__generator": {
- "version": "7.6.4",
- "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz",
- "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==",
+ "node_modules/@types/chai-subset": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/@types/chai-subset/-/chai-subset-1.3.3.tgz",
+ "integrity": "sha512-frBecisrNGz+F4T6bcc+NLeolfiojh5FxW2klu669+8BARtyQv2C/GkNW6FUodVe4BroGMP/wER/YDGc7rEllw==",
"dev": true,
"dependencies": {
- "@babel/types": "^7.0.0"
- }
- },
- "node_modules/@types/babel__template": {
- "version": "7.4.1",
- "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz",
- "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==",
- "dev": true,
- "dependencies": {
- "@babel/parser": "^7.1.0",
- "@babel/types": "^7.0.0"
- }
- },
- "node_modules/@types/babel__traverse": {
- "version": "7.17.1",
- "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.17.1.tgz",
- "integrity": "sha512-kVzjari1s2YVi77D3w1yuvohV2idweYXMCDzqBiVNN63TcDWrIlTVOYpqVrvbbyOE/IyzBoTKF0fdnLPEORFxA==",
- "dev": true,
- "dependencies": {
- "@babel/types": "^7.3.0"
+ "@types/chai": "*"
}
},
"node_modules/@types/codemirror": {
@@ -2325,19 +1235,19 @@
"@types/tern": "*"
}
},
- "node_modules/@types/concat-stream": {
- "version": "1.6.1",
- "resolved": "https://registry.npmjs.org/@types/concat-stream/-/concat-stream-1.6.1.tgz",
- "integrity": "sha512-eHE4cQPoj6ngxBZMvVf6Hw7Mh4jMW4U9lpGmS5GBPB9RYxlFg+CHaVN7ErNY4W9XfLIEn20b4VDYaIrbq0q4uA==",
+ "node_modules/@types/es-aggregate-error": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@types/es-aggregate-error/-/es-aggregate-error-1.0.2.tgz",
+ "integrity": "sha512-erqUpFXksaeR2kejKnhnjZjbFxUpGZx4Z7ydNL9ie8tEhXPiZTsLeUDJ6aR1F8j5wWUAtOAQWUqkc7givBJbBA==",
"dev": true,
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/eslint": {
- "version": "8.4.5",
- "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.5.tgz",
- "integrity": "sha512-dhsC09y1gpJWnK+Ff4SGvCuSnk9DaU0BJZSzOwa6GVSg65XtTugLBITDAAzRU5duGBoXBHpdR/9jHGxJjNflJQ==",
+ "version": "8.4.6",
+ "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.6.tgz",
+ "integrity": "sha512-/fqTbjxyFUaYNO7VcW5g+4npmqVACz1bB7RTHYuLj+PRjw9hrCwrUXVQFpChUS0JsyEFvMZ7U/PfmvWgxJhI9g==",
"dependencies": {
"@types/estree": "*",
"@types/json-schema": "*"
@@ -2357,48 +1267,6 @@
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz",
"integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw=="
},
- "node_modules/@types/form-data": {
- "version": "0.0.33",
- "resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-0.0.33.tgz",
- "integrity": "sha512-8BSvG1kGm83cyJITQMZSulnl6QV8jqAGreJsc5tPu1Jq0vTSOiY/k24Wx82JRpWwZSqrala6sd5rWi6aNXvqcw==",
- "dev": true,
- "dependencies": {
- "@types/node": "*"
- }
- },
- "node_modules/@types/graceful-fs": {
- "version": "4.1.5",
- "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz",
- "integrity": "sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==",
- "dev": true,
- "dependencies": {
- "@types/node": "*"
- }
- },
- "node_modules/@types/istanbul-lib-coverage": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz",
- "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==",
- "dev": true
- },
- "node_modules/@types/istanbul-lib-report": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz",
- "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==",
- "dev": true,
- "dependencies": {
- "@types/istanbul-lib-coverage": "*"
- }
- },
- "node_modules/@types/istanbul-reports": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz",
- "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==",
- "dev": true,
- "dependencies": {
- "@types/istanbul-lib-report": "*"
- }
- },
"node_modules/@types/json-schema": {
"version": "7.0.11",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz",
@@ -2411,9 +1279,9 @@
"dev": true
},
"node_modules/@types/marked": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/@types/marked/-/marked-4.0.3.tgz",
- "integrity": "sha512-HnMWQkLJEf/PnxZIfbm0yGJRRZYYMhb++O9M36UCTA9z53uPvVoSlAwJr3XOpDEryb7Hwl1qAx/MV6YIW1RXxg=="
+ "version": "4.0.7",
+ "resolved": "https://registry.npmjs.org/@types/marked/-/marked-4.0.7.tgz",
+ "integrity": "sha512-eEAhnz21CwvKVW+YvRvcTuFKNU9CV1qH+opcgVK3pIMI6YZzDm6gc8o2vHjldFk6MGKt5pueSB7IOpvpx5Qekw=="
},
"node_modules/@types/minimist": {
"version": "1.2.2",
@@ -2422,9 +1290,9 @@
"dev": true
},
"node_modules/@types/node": {
- "version": "18.0.6",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-18.0.6.tgz",
- "integrity": "sha512-/xUq6H2aQm261exT6iZTMifUySEt4GR5KX8eYyY+C4MSNPqSh9oNIP7tz2GLKTlFaiBbgZNxffoR3CVRG+cljw=="
+ "version": "18.8.3",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-18.8.3.tgz",
+ "integrity": "sha512-0os9vz6BpGwxGe9LOhgP/ncvYN5Tx1fNcd2TM3rD/aCGBkysb+ZWpXEocG24h6ZzOi13+VB8HndAQFezsSOw1w=="
},
"node_modules/@types/normalize-package-data": {
"version": "2.4.1",
@@ -2438,24 +1306,6 @@
"integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==",
"dev": true
},
- "node_modules/@types/prettier": {
- "version": "2.6.3",
- "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.6.3.tgz",
- "integrity": "sha512-ymZk3LEC/fsut+/Q5qejp6R9O1rMxz3XaRHDV6kX8MrGAhOSPqVARbDi+EZvInBpw+BnCX3TD240byVkOfQsHg==",
- "dev": true
- },
- "node_modules/@types/qs": {
- "version": "6.9.7",
- "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz",
- "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==",
- "dev": true
- },
- "node_modules/@types/stack-utils": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz",
- "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==",
- "dev": true
- },
"node_modules/@types/tern": {
"version": "0.23.4",
"resolved": "https://registry.npmjs.org/@types/tern/-/tern-0.23.4.tgz",
@@ -2470,73 +1320,107 @@
"integrity": "sha512-FDJNkyhmKLw7uEvTxx5tSXfPeQpO0iy73Ry+PmYZJvQy0QIWX8a7kJ4kLWRf+EbTPJEPDSgPXHaM7pzr5lmvCg==",
"dev": true
},
- "node_modules/@types/yargs": {
- "version": "16.0.4",
- "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz",
- "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==",
- "dev": true,
+ "node_modules/@vue/compiler-core": {
+ "version": "3.2.40",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.40.tgz",
+ "integrity": "sha512-2Dc3Stk0J/VyQ4OUr2yEC53kU28614lZS+bnrCbFSAIftBJ40g/2yQzf4mPBiFuqguMB7hyHaujdgZAQ67kZYA==",
"dependencies": {
- "@types/yargs-parser": "*"
- }
- },
- "node_modules/@types/yargs-parser": {
- "version": "21.0.0",
- "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz",
- "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==",
- "dev": true
- },
- "node_modules/@vue/component-compiler-utils": {
- "version": "3.3.0",
- "resolved": "https://registry.npmjs.org/@vue/component-compiler-utils/-/component-compiler-utils-3.3.0.tgz",
- "integrity": "sha512-97sfH2mYNU+2PzGrmK2haqffDpVASuib9/w2/noxiFi31Z54hW+q3izKQXXQZSNhtiUpAI36uSuYepeBe4wpHQ==",
- "dependencies": {
- "consolidate": "^0.15.1",
- "hash-sum": "^1.0.2",
- "lru-cache": "^4.1.2",
- "merge-source-map": "^1.1.0",
- "postcss": "^7.0.36",
- "postcss-selector-parser": "^6.0.2",
- "source-map": "~0.6.1",
- "vue-template-es2015-compiler": "^1.9.0"
- },
- "optionalDependencies": {
- "prettier": "^1.18.2 || ^2.0.0"
- }
- },
- "node_modules/@vue/component-compiler-utils/node_modules/lru-cache": {
- "version": "4.1.5",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz",
- "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==",
- "dependencies": {
- "pseudomap": "^1.0.2",
- "yallist": "^2.1.2"
- }
- },
- "node_modules/@vue/component-compiler-utils/node_modules/picocolors": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz",
- "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA=="
- },
- "node_modules/@vue/component-compiler-utils/node_modules/postcss": {
- "version": "7.0.39",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz",
- "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==",
- "dependencies": {
- "picocolors": "^0.2.1",
+ "@babel/parser": "^7.16.4",
+ "@vue/shared": "3.2.40",
+ "estree-walker": "^2.0.2",
"source-map": "^0.6.1"
- },
- "engines": {
- "node": ">=6.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/postcss/"
}
},
- "node_modules/@vue/component-compiler-utils/node_modules/yallist": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
- "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A=="
+ "node_modules/@vue/compiler-dom": {
+ "version": "3.2.40",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.40.tgz",
+ "integrity": "sha512-OZCNyYVC2LQJy4H7h0o28rtk+4v+HMQygRTpmibGoG9wZyomQiS5otU7qo3Wlq5UfHDw2RFwxb9BJgKjVpjrQw==",
+ "dependencies": {
+ "@vue/compiler-core": "3.2.40",
+ "@vue/shared": "3.2.40"
+ }
+ },
+ "node_modules/@vue/compiler-sfc": {
+ "version": "3.2.40",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.40.tgz",
+ "integrity": "sha512-tzqwniIN1fu1PDHC3CpqY/dPCfN/RN1thpBC+g69kJcrl7mbGiHKNwbA6kJ3XKKy8R6JLKqcpVugqN4HkeBFFg==",
+ "dependencies": {
+ "@babel/parser": "^7.16.4",
+ "@vue/compiler-core": "3.2.40",
+ "@vue/compiler-dom": "3.2.40",
+ "@vue/compiler-ssr": "3.2.40",
+ "@vue/reactivity-transform": "3.2.40",
+ "@vue/shared": "3.2.40",
+ "estree-walker": "^2.0.2",
+ "magic-string": "^0.25.7",
+ "postcss": "^8.1.10",
+ "source-map": "^0.6.1"
+ }
+ },
+ "node_modules/@vue/compiler-ssr": {
+ "version": "3.2.40",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.40.tgz",
+ "integrity": "sha512-80cQcgasKjrPPuKcxwuCx7feq+wC6oFl5YaKSee9pV3DNq+6fmCVwEEC3vvkf/E2aI76rIJSOYHsWSEIxK74oQ==",
+ "dependencies": {
+ "@vue/compiler-dom": "3.2.40",
+ "@vue/shared": "3.2.40"
+ }
+ },
+ "node_modules/@vue/reactivity": {
+ "version": "3.2.40",
+ "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.2.40.tgz",
+ "integrity": "sha512-N9qgGLlZmtUBMHF9xDT4EkD9RdXde1Xbveb+niWMXuHVWQP5BzgRmE3SFyUBBcyayG4y1lhoz+lphGRRxxK4RA==",
+ "dependencies": {
+ "@vue/shared": "3.2.40"
+ }
+ },
+ "node_modules/@vue/reactivity-transform": {
+ "version": "3.2.40",
+ "resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.2.40.tgz",
+ "integrity": "sha512-HQUCVwEaacq6fGEsg2NUuGKIhUveMCjOk8jGHqLXPI2w6zFoPrlQhwWEaINTv5kkZDXKEnCijAp+4gNEHG03yw==",
+ "dependencies": {
+ "@babel/parser": "^7.16.4",
+ "@vue/compiler-core": "3.2.40",
+ "@vue/shared": "3.2.40",
+ "estree-walker": "^2.0.2",
+ "magic-string": "^0.25.7"
+ }
+ },
+ "node_modules/@vue/runtime-core": {
+ "version": "3.2.40",
+ "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.2.40.tgz",
+ "integrity": "sha512-U1+rWf0H8xK8aBUZhnrN97yoZfHbjgw/bGUzfgKPJl69/mXDuSg8CbdBYBn6VVQdR947vWneQBFzdhasyzMUKg==",
+ "dependencies": {
+ "@vue/reactivity": "3.2.40",
+ "@vue/shared": "3.2.40"
+ }
+ },
+ "node_modules/@vue/runtime-dom": {
+ "version": "3.2.40",
+ "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.2.40.tgz",
+ "integrity": "sha512-AO2HMQ+0s2+MCec8hXAhxMgWhFhOPJ/CyRXnmTJ6XIOnJFLrH5Iq3TNwvVcODGR295jy77I6dWPj+wvFoSYaww==",
+ "dependencies": {
+ "@vue/runtime-core": "3.2.40",
+ "@vue/shared": "3.2.40",
+ "csstype": "^2.6.8"
+ }
+ },
+ "node_modules/@vue/server-renderer": {
+ "version": "3.2.40",
+ "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.2.40.tgz",
+ "integrity": "sha512-gtUcpRwrXOJPJ4qyBpU3EyxQa4EkV8I4f8VrDePcGCPe4O/hd0BPS7v9OgjIQob6Ap8VDz9G+mGTKazE45/95w==",
+ "dependencies": {
+ "@vue/compiler-ssr": "3.2.40",
+ "@vue/shared": "3.2.40"
+ },
+ "peerDependencies": {
+ "vue": "3.2.40"
+ }
+ },
+ "node_modules/@vue/shared": {
+ "version": "3.2.40",
+ "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.40.tgz",
+ "integrity": "sha512-0PLQ6RUtZM0vO3teRfzGi4ltLUO5aO+kLgwh4Um3THSR03rpQWLTuRCkuO5A41ITzwdWeKdPHtSARuPkoo5pCQ=="
},
"node_modules/@webassemblyjs/ast": {
"version": "1.11.1",
@@ -2712,6 +1596,12 @@
"resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz",
"integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ=="
},
+ "node_modules/abab": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz",
+ "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==",
+ "dev": true
+ },
"node_modules/abort-controller": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
@@ -2725,9 +1615,9 @@
}
},
"node_modules/acorn": {
- "version": "8.7.1",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz",
- "integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==",
+ "version": "8.8.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz",
+ "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==",
"bin": {
"acorn": "bin/acorn"
},
@@ -2735,6 +1625,16 @@
"node": ">=0.4.0"
}
},
+ "node_modules/acorn-globals": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-7.0.1.tgz",
+ "integrity": "sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==",
+ "dev": true,
+ "dependencies": {
+ "acorn": "^8.1.0",
+ "acorn-walk": "^8.0.2"
+ }
+ },
"node_modules/acorn-import-assertions": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz",
@@ -2852,33 +1752,6 @@
"ajv": "^8.8.2"
}
},
- "node_modules/ansi-escapes": {
- "version": "4.3.2",
- "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz",
- "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==",
- "dev": true,
- "dependencies": {
- "type-fest": "^0.21.3"
- },
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/ansi-escapes/node_modules/type-fest": {
- "version": "0.21.3",
- "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz",
- "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==",
- "dev": true,
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/ansi-regex": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
@@ -2901,19 +1774,6 @@
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
- "node_modules/anymatch": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
- "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
- "dev": true,
- "dependencies": {
- "normalize-path": "^3.0.0",
- "picomatch": "^2.0.4"
- },
- "engines": {
- "node": ">= 8"
- }
- },
"node_modules/argparse": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
@@ -2992,11 +1852,14 @@
"printable-characters": "^1.0.42"
}
},
- "node_modules/asap": {
- "version": "2.0.6",
- "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
- "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==",
- "dev": true
+ "node_modules/assertion-error": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz",
+ "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==",
+ "dev": true,
+ "engines": {
+ "node": "*"
+ }
},
"node_modules/ast-types": {
"version": "0.14.2",
@@ -3034,97 +1897,6 @@
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
"dev": true
},
- "node_modules/babel-jest": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-28.1.3.tgz",
- "integrity": "sha512-epUaPOEWMk3cWX0M/sPvCHHCe9fMFAa/9hXEgKP8nFfNl/jlGkE9ucq9NqkZGXLDduCJYS0UvSlPUwC0S+rH6Q==",
- "dev": true,
- "dependencies": {
- "@jest/transform": "^28.1.3",
- "@types/babel__core": "^7.1.14",
- "babel-plugin-istanbul": "^6.1.1",
- "babel-preset-jest": "^28.1.3",
- "chalk": "^4.0.0",
- "graceful-fs": "^4.2.9",
- "slash": "^3.0.0"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.8.0"
- }
- },
- "node_modules/babel-plugin-istanbul": {
- "version": "6.1.1",
- "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz",
- "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==",
- "dev": true,
- "dependencies": {
- "@babel/helper-plugin-utils": "^7.0.0",
- "@istanbuljs/load-nyc-config": "^1.0.0",
- "@istanbuljs/schema": "^0.1.2",
- "istanbul-lib-instrument": "^5.0.4",
- "test-exclude": "^6.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/babel-plugin-jest-hoist": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-28.1.3.tgz",
- "integrity": "sha512-Ys3tUKAmfnkRUpPdpa98eYrAR0nV+sSFUZZEGuQ2EbFd1y4SOLtD5QDNHAq+bb9a+bbXvYQC4b+ID/THIMcU6Q==",
- "dev": true,
- "dependencies": {
- "@babel/template": "^7.3.3",
- "@babel/types": "^7.3.3",
- "@types/babel__core": "^7.1.14",
- "@types/babel__traverse": "^7.0.6"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/babel-preset-current-node-syntax": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz",
- "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==",
- "dev": true,
- "dependencies": {
- "@babel/plugin-syntax-async-generators": "^7.8.4",
- "@babel/plugin-syntax-bigint": "^7.8.3",
- "@babel/plugin-syntax-class-properties": "^7.8.3",
- "@babel/plugin-syntax-import-meta": "^7.8.3",
- "@babel/plugin-syntax-json-strings": "^7.8.3",
- "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3",
- "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3",
- "@babel/plugin-syntax-numeric-separator": "^7.8.3",
- "@babel/plugin-syntax-object-rest-spread": "^7.8.3",
- "@babel/plugin-syntax-optional-catch-binding": "^7.8.3",
- "@babel/plugin-syntax-optional-chaining": "^7.8.3",
- "@babel/plugin-syntax-top-level-await": "^7.8.3"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0"
- }
- },
- "node_modules/babel-preset-jest": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-28.1.3.tgz",
- "integrity": "sha512-L+fupJvlWAHbQfn74coNX3zf60LXMJsezNvvx8eIh7iOR1luJ1poxYgQk1F8PYtNq/6QODDHCqsSnTFSWC491A==",
- "dev": true,
- "dependencies": {
- "babel-plugin-jest-hoist": "^28.1.3",
- "babel-preset-current-node-syntax": "^1.0.0"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- },
- "peerDependencies": {
- "@babel/core": "^7.0.0"
- }
- },
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
@@ -3138,11 +1910,6 @@
"node": "*"
}
},
- "node_modules/bluebird": {
- "version": "3.7.2",
- "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
- "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg=="
- },
"node_modules/blueimp-md5": {
"version": "2.18.0",
"resolved": "https://registry.npmjs.org/blueimp-md5/-/blueimp-md5-2.18.0.tgz",
@@ -3176,9 +1943,9 @@
}
},
"node_modules/browserslist": {
- "version": "4.21.2",
- "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.2.tgz",
- "integrity": "sha512-MonuOgAtUB46uP5CezYbRaYKBNt2LxP0yX+Pmj4LkcDFGkn9Cbpi83d9sCjwQDErXsIJSzY5oKGDbgOlF/LPAA==",
+ "version": "4.21.4",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz",
+ "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==",
"funding": [
{
"type": "opencollective",
@@ -3190,10 +1957,10 @@
}
],
"dependencies": {
- "caniuse-lite": "^1.0.30001366",
- "electron-to-chromium": "^1.4.188",
+ "caniuse-lite": "^1.0.30001400",
+ "electron-to-chromium": "^1.4.251",
"node-releases": "^2.0.6",
- "update-browserslist-db": "^1.0.4"
+ "update-browserslist-db": "^1.0.9"
},
"bin": {
"browserslist": "cli.js"
@@ -3202,15 +1969,6 @@
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
}
},
- "node_modules/bser": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz",
- "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==",
- "dev": true,
- "dependencies": {
- "node-int64": "^0.4.0"
- }
- },
"node_modules/buffer-from": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
@@ -3292,9 +2050,9 @@
}
},
"node_modules/caniuse-lite": {
- "version": "1.0.30001367",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001367.tgz",
- "integrity": "sha512-XDgbeOHfifWV3GEES2B8rtsrADx4Jf+juKX2SICJcaUhjYBO3bR96kvEIHa15VU6ohtOhBZuPGGYGbXMRn0NCw==",
+ "version": "1.0.30001418",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001418.tgz",
+ "integrity": "sha512-oIs7+JL3K9JRQ3jPZjlH6qyYDp+nBTCais7hjh0s+fuBwufc7uZ7hPYMXrDOJhV360KGMTcczMRObk0/iMqZRg==",
"funding": [
{
"type": "opencollective",
@@ -3306,17 +2064,28 @@
}
]
},
- "node_modules/caseless": {
- "version": "0.12.0",
- "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
- "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==",
- "dev": true
+ "node_modules/chai": {
+ "version": "4.3.6",
+ "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.6.tgz",
+ "integrity": "sha512-bbcp3YfHCUzMOvKqsztczerVgBKSsEijCySNlHHbX3VG1nskvqjz5Rfso1gGwD6w6oOV3eI60pKuMOV5MV7p3Q==",
+ "dev": true,
+ "dependencies": {
+ "assertion-error": "^1.1.0",
+ "check-error": "^1.0.2",
+ "deep-eql": "^3.0.1",
+ "get-func-name": "^2.0.0",
+ "loupe": "^2.3.1",
+ "pathval": "^1.1.1",
+ "type-detect": "^4.0.5"
+ },
+ "engines": {
+ "node": ">=4"
+ }
},
"node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
- "dev": true,
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
@@ -3328,13 +2097,13 @@
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
- "node_modules/char-regex": {
+ "node_modules/check-error": {
"version": "1.0.2",
- "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz",
- "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==",
+ "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz",
+ "integrity": "sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==",
"dev": true,
"engines": {
- "node": ">=10"
+ "node": "*"
}
},
"node_modules/chrome-trace-event": {
@@ -3346,15 +2115,9 @@
}
},
"node_modules/ci-info": {
- "version": "3.3.2",
- "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.3.2.tgz",
- "integrity": "sha512-xmDt/QIAdeZ9+nfdPsaBCpMvHNLFiLdjj59qjqn+6iPe6YmHGQ35sBnQ8uslRBXFmXkiZQOJRjvQeoGppoTjjg==",
- "dev": true
- },
- "node_modules/cjs-module-lexer": {
- "version": "1.2.2",
- "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz",
- "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==",
+ "version": "3.5.0",
+ "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.5.0.tgz",
+ "integrity": "sha512-yH4RezKOGlOhxkmhbeNuC4eYZKAUsEaGtBuBzDDP1eFUKiccDWzBABxBfOx31IDwDIXMTxWuwAxUGModvkbuVw==",
"dev": true
},
"node_modules/clean-regexp": {
@@ -3430,32 +2193,10 @@
"node": ">=0.10.0"
}
},
- "node_modules/clone-regexp": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/clone-regexp/-/clone-regexp-2.2.0.tgz",
- "integrity": "sha512-beMpP7BOtTipFuW8hrJvREQ2DrRu3BE7by0ZpibtfBA+qfHYvMGTc2Yb1JMYPKg/JUw0CHYvpg796aNTSW9z7Q==",
- "dev": true,
- "dependencies": {
- "is-regexp": "^2.0.0"
- },
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/co": {
- "version": "4.6.0",
- "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
- "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==",
- "dev": true,
- "engines": {
- "iojs": ">= 1.0.0",
- "node": ">= 0.12.0"
- }
- },
"node_modules/codemirror": {
- "version": "5.65.6",
- "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.65.6.tgz",
- "integrity": "sha512-zNihMSMoDxK9Gqv9oEyDT8oM51rcRrQ+IEo2zyS48gJByBq5Fj8XuNEguMra+MuIOuh6lkpnLUJeL70DoTt6yw=="
+ "version": "5.65.9",
+ "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.65.9.tgz",
+ "integrity": "sha512-19Jox5sAKpusTDgqgKB5dawPpQcY+ipQK7xoEI+MVucEF9qqFaXpeqY1KaoyGBso/wHQoDa4HMMxMjdsS3Zzzw=="
},
"node_modules/codemirror-spell-checker": {
"version": "1.1.2",
@@ -3465,12 +2206,6 @@
"typo-js": "*"
}
},
- "node_modules/collect-v8-coverage": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz",
- "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==",
- "dev": true
- },
"node_modules/color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
@@ -3488,9 +2223,9 @@
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
},
"node_modules/colord": {
- "version": "2.9.2",
- "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.2.tgz",
- "integrity": "sha512-Uqbg+J445nc1TKn4FoDPS6ZZqAvEDnwrH42yo8B40JSOgSLxMZ/gt3h4nmCtPLQeXhjJJkqBx7SCY35WnIixaQ==",
+ "version": "2.9.3",
+ "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz",
+ "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==",
"dev": true
},
"node_modules/colorette": {
@@ -3511,11 +2246,11 @@
}
},
"node_modules/commander": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
- "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
+ "version": "8.3.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz",
+ "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==",
"engines": {
- "node": ">= 10"
+ "node": ">= 12"
}
},
"node_modules/commondir": {
@@ -3529,71 +2264,6 @@
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
},
- "node_modules/concat-stream": {
- "version": "1.6.2",
- "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
- "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
- "dev": true,
- "engines": [
- "node >= 0.8"
- ],
- "dependencies": {
- "buffer-from": "^1.0.0",
- "inherits": "^2.0.3",
- "readable-stream": "^2.2.2",
- "typedarray": "^0.0.6"
- }
- },
- "node_modules/concat-stream/node_modules/isarray": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
- "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
- "dev": true
- },
- "node_modules/concat-stream/node_modules/readable-stream": {
- "version": "2.3.7",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
- "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
- "dev": true,
- "dependencies": {
- "core-util-is": "~1.0.0",
- "inherits": "~2.0.3",
- "isarray": "~1.0.0",
- "process-nextick-args": "~2.0.0",
- "safe-buffer": "~5.1.1",
- "string_decoder": "~1.1.1",
- "util-deprecate": "~1.0.1"
- }
- },
- "node_modules/concat-stream/node_modules/string_decoder": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
- "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
- "dev": true,
- "dependencies": {
- "safe-buffer": "~5.1.0"
- }
- },
- "node_modules/consolidate": {
- "version": "0.15.1",
- "resolved": "https://registry.npmjs.org/consolidate/-/consolidate-0.15.1.tgz",
- "integrity": "sha512-DW46nrsMJgy9kqAbPt5rKaCr7uFtpo4mSUvLHIUbJEjm0vo+aY5QLwBUq3FK4tRnJr/X0Psc0C4jf/h+HtXSMw==",
- "dependencies": {
- "bluebird": "^3.1.1"
- },
- "engines": {
- "node": ">= 0.10.0"
- }
- },
- "node_modules/convert-source-map": {
- "version": "1.8.0",
- "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz",
- "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==",
- "dev": true,
- "dependencies": {
- "safe-buffer": "~5.1.1"
- }
- },
"node_modules/copy-anything": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-2.0.6.tgz",
@@ -3715,12 +2385,6 @@
"url": "https://github.com/sponsors/fb55"
}
},
- "node_modules/css.escape": {
- "version": "1.5.1",
- "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz",
- "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==",
- "dev": true
- },
"node_modules/cssesc": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
@@ -3744,6 +2408,35 @@
"node": ">=8.0.0"
}
},
+ "node_modules/cssom": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz",
+ "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==",
+ "dev": true
+ },
+ "node_modules/cssstyle": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz",
+ "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==",
+ "dev": true,
+ "dependencies": {
+ "cssom": "~0.3.6"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cssstyle/node_modules/cssom": {
+ "version": "0.3.8",
+ "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz",
+ "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==",
+ "dev": true
+ },
+ "node_modules/csstype": {
+ "version": "2.6.21",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.21.tgz",
+ "integrity": "sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w=="
+ },
"node_modules/d3": {
"version": "7.6.1",
"resolved": "https://registry.npmjs.org/d3/-/d3-7.6.1.tgz",
@@ -3908,6 +2601,14 @@
"node": ">=12"
}
},
+ "node_modules/d3-dsv/node_modules/commander": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
+ "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
+ "engines": {
+ "node": ">= 10"
+ }
+ },
"node_modules/d3-ease": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz",
@@ -4434,10 +3135,19 @@
"node": ">= 6"
}
},
- "node_modules/de-indent": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz",
- "integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg=="
+ "node_modules/data-urls": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz",
+ "integrity": "sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==",
+ "dev": true,
+ "dependencies": {
+ "abab": "^2.0.6",
+ "whatwg-mimetype": "^3.0.0",
+ "whatwg-url": "^11.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
},
"node_modules/debug": {
"version": "4.3.4",
@@ -4487,27 +3197,39 @@
"node": ">=0.10.0"
}
},
- "node_modules/dedent": {
- "version": "0.7.0",
- "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz",
- "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==",
+ "node_modules/decimal.js": {
+ "version": "10.4.1",
+ "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.1.tgz",
+ "integrity": "sha512-F29o+vci4DodHYT9UrR5IEbfBw9pE5eSapIJdTqXK5+6hq+t8VRxwQyKlW2i+KDKFkkJQRvFyI/QXD83h8LyQw==",
"dev": true
},
+ "node_modules/deep-eql": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz",
+ "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==",
+ "dev": true,
+ "dependencies": {
+ "type-detect": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=0.12"
+ }
+ },
+ "node_modules/deep-extend": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
+ "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
+ "dev": true,
+ "engines": {
+ "node": ">=4.0.0"
+ }
+ },
"node_modules/deep-is": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
"integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
"dev": true
},
- "node_modules/deepmerge": {
- "version": "4.2.2",
- "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz",
- "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==",
- "dev": true,
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/define-properties": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz",
@@ -4551,6 +3273,88 @@
"node": ">=4"
}
},
+ "node_modules/degenerator/node_modules/escodegen": {
+ "version": "1.14.3",
+ "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz",
+ "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==",
+ "dev": true,
+ "dependencies": {
+ "esprima": "^4.0.1",
+ "estraverse": "^4.2.0",
+ "esutils": "^2.0.2",
+ "optionator": "^0.8.1"
+ },
+ "bin": {
+ "escodegen": "bin/escodegen.js",
+ "esgenerate": "bin/esgenerate.js"
+ },
+ "engines": {
+ "node": ">=4.0"
+ },
+ "optionalDependencies": {
+ "source-map": "~0.6.1"
+ }
+ },
+ "node_modules/degenerator/node_modules/estraverse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
+ "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
+ "dev": true,
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/degenerator/node_modules/levn": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
+ "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==",
+ "dev": true,
+ "dependencies": {
+ "prelude-ls": "~1.1.2",
+ "type-check": "~0.3.2"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/degenerator/node_modules/optionator": {
+ "version": "0.8.3",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz",
+ "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==",
+ "dev": true,
+ "dependencies": {
+ "deep-is": "~0.1.3",
+ "fast-levenshtein": "~2.0.6",
+ "levn": "~0.3.0",
+ "prelude-ls": "~1.1.2",
+ "type-check": "~0.3.2",
+ "word-wrap": "~1.2.3"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/degenerator/node_modules/prelude-ls": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
+ "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/degenerator/node_modules/type-check": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
+ "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==",
+ "dev": true,
+ "dependencies": {
+ "prelude-ls": "~1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
"node_modules/delaunator": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.0.tgz",
@@ -4586,24 +3390,6 @@
"node": ">= 0.6.0"
}
},
- "node_modules/detect-newline": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz",
- "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==",
- "dev": true,
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/diff-sequences": {
- "version": "28.1.1",
- "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-28.1.1.tgz",
- "integrity": "sha512-FU0iFaH/E23a+a718l8Qa/19bF9p06kgE0KipMOMadwa3SjnaElKzPaUC0vnibs6/B/9ni97s61mcejk8W1fQw==",
- "dev": true,
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
"node_modules/dir-glob": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
@@ -4642,6 +3428,15 @@
"url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
}
},
+ "node_modules/dom-serializer/node_modules/entities": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
+ "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==",
+ "dev": true,
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
+ },
"node_modules/domelementtype": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
@@ -4654,6 +3449,18 @@
}
]
},
+ "node_modules/domexception": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz",
+ "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==",
+ "dev": true,
+ "dependencies": {
+ "webidl-conversions": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
"node_modules/domhandler": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz",
@@ -4670,9 +3477,9 @@
}
},
"node_modules/dompurify": {
- "version": "2.3.8",
- "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.3.8.tgz",
- "integrity": "sha512-eVhaWoVibIzqdGYjwsBWodIQIaXFSB+cKDf4cfxLMsK0xiud6SE+/WCVx/Xw/UwQsa4cS3T2eITcdtmTg2UKcw=="
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.4.0.tgz",
+ "integrity": "sha512-Be9tbQMZds4a3C6xTmz68NlMfeONA//4dOavl/1rNw50E+/QO0KVpbcU0PcaW0nsQxurXls9ZocqFxk8R2mWEA=="
},
"node_modules/domutils": {
"version": "2.8.0",
@@ -4703,33 +3510,21 @@
"integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="
},
"node_modules/easymde": {
- "version": "2.16.1",
- "resolved": "https://registry.npmjs.org/easymde/-/easymde-2.16.1.tgz",
- "integrity": "sha512-FihYgjRsKfhGNk89SHSqxKLC4aJ1kfybPWW6iAmtb5GnXu+tnFPSzSaGBmk1RRlCuhFSjhF0SnIMGVPjEzkr6g==",
+ "version": "2.18.0",
+ "resolved": "https://registry.npmjs.org/easymde/-/easymde-2.18.0.tgz",
+ "integrity": "sha512-IxVVUxNWIoXLeqtBU4BLc+eS/ScYhT1Dcb6yF5Wchoj1iXAV+TIIDWx+NCaZhY7RcSHqDPKllbYq7nwGKILnoA==",
"dependencies": {
"@types/codemirror": "^5.60.4",
- "@types/marked": "^4.0.1",
+ "@types/marked": "^4.0.7",
"codemirror": "^5.63.1",
"codemirror-spell-checker": "1.1.2",
- "marked": "^4.0.10"
+ "marked": "^4.1.0"
}
},
"node_modules/electron-to-chromium": {
- "version": "1.4.195",
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.195.tgz",
- "integrity": "sha512-vefjEh0sk871xNmR5whJf9TEngX+KTKS3hOHpjoMpauKkwlGwtMz1H8IaIjAT/GNnX0TbGwAdmVoXCAzXf+PPg=="
- },
- "node_modules/emittery": {
- "version": "0.10.2",
- "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.10.2.tgz",
- "integrity": "sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw==",
- "dev": true,
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/sindresorhus/emittery?sponsor=1"
- }
+ "version": "1.4.276",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.276.tgz",
+ "integrity": "sha512-EpuHPqu8YhonqLBXHoU6hDJCD98FCe6KDoet3/gY1qsQ6usjJoHqBH2YIVs8FXaAtHwVL8Uqa/fsYao/vq9VWQ=="
},
"node_modules/emoji-regex": {
"version": "8.0.0",
@@ -4757,10 +3552,13 @@
}
},
"node_modules/entities": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
- "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==",
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz",
+ "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==",
"dev": true,
+ "engines": {
+ "node": ">=0.12"
+ },
"funding": {
"url": "https://github.com/fb55/entities?sponsor=1"
}
@@ -4804,31 +3602,32 @@
}
},
"node_modules/es-abstract": {
- "version": "1.20.1",
- "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.1.tgz",
- "integrity": "sha512-WEm2oBhfoI2sImeM4OF2zE2V3BYdSF+KnSi9Sidz51fQHd7+JuF8Xgcj9/0o+OWeIeIS/MiuNnlruQrJf16GQA==",
+ "version": "1.20.4",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.4.tgz",
+ "integrity": "sha512-0UtvRN79eMe2L+UNEF1BwRe364sj/DXhQ/k5FmivgoSdpM90b8Jc0mDzKMGo7QS0BVbOP/bTwBKNnDc9rNzaPA==",
"dev": true,
"dependencies": {
"call-bind": "^1.0.2",
"es-to-primitive": "^1.2.1",
"function-bind": "^1.1.1",
"function.prototype.name": "^1.1.5",
- "get-intrinsic": "^1.1.1",
+ "get-intrinsic": "^1.1.3",
"get-symbol-description": "^1.0.0",
"has": "^1.0.3",
"has-property-descriptors": "^1.0.0",
"has-symbols": "^1.0.3",
"internal-slot": "^1.0.3",
- "is-callable": "^1.2.4",
+ "is-callable": "^1.2.7",
"is-negative-zero": "^2.0.2",
"is-regex": "^1.1.4",
"is-shared-array-buffer": "^1.0.2",
"is-string": "^1.0.7",
"is-weakref": "^1.0.2",
- "object-inspect": "^1.12.0",
+ "object-inspect": "^1.12.2",
"object-keys": "^1.1.1",
- "object.assign": "^4.1.2",
+ "object.assign": "^4.1.4",
"regexp.prototype.flags": "^1.4.3",
+ "safe-regex-test": "^1.0.0",
"string.prototype.trimend": "^1.0.5",
"string.prototype.trimstart": "^1.0.5",
"unbox-primitive": "^1.0.2"
@@ -4840,6 +3639,27 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/es-aggregate-error": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/es-aggregate-error/-/es-aggregate-error-1.0.8.tgz",
+ "integrity": "sha512-AKUb5MKLWMozPlFRHOKqWD7yta5uaEhH21qwtnf6FlKjNjTJOoqFi0/G14+FfSkIQhhu6X68Af4xgRC6y8qG4A==",
+ "dev": true,
+ "dependencies": {
+ "define-properties": "^1.1.4",
+ "es-abstract": "^1.19.5",
+ "function-bind": "^1.1.1",
+ "functions-have-names": "^1.2.3",
+ "get-intrinsic": "^1.1.1",
+ "globalthis": "^1.0.2",
+ "has-property-descriptors": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/es-module-lexer": {
"version": "0.9.3",
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz",
@@ -4872,9 +3692,9 @@
}
},
"node_modules/esbuild": {
- "version": "0.14.49",
- "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.49.tgz",
- "integrity": "sha512-/TlVHhOaq7Yz8N1OJrjqM3Auzo5wjvHFLk+T8pIue+fhnhIMpfAzsG6PLVMbFveVxqD2WOp3QHei+52IMUNmCw==",
+ "version": "0.15.10",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.10.tgz",
+ "integrity": "sha512-N7wBhfJ/E5fzn/SpNgX+oW2RLRjwaL8Y0ezqNqhjD6w0H2p0rDuEz2FKZqpqLnO8DCaWumKe8dsC/ljvVSSxng==",
"hasInstallScript": true,
"bin": {
"esbuild": "bin/esbuild"
@@ -4883,32 +3703,34 @@
"node": ">=12"
},
"optionalDependencies": {
- "esbuild-android-64": "0.14.49",
- "esbuild-android-arm64": "0.14.49",
- "esbuild-darwin-64": "0.14.49",
- "esbuild-darwin-arm64": "0.14.49",
- "esbuild-freebsd-64": "0.14.49",
- "esbuild-freebsd-arm64": "0.14.49",
- "esbuild-linux-32": "0.14.49",
- "esbuild-linux-64": "0.14.49",
- "esbuild-linux-arm": "0.14.49",
- "esbuild-linux-arm64": "0.14.49",
- "esbuild-linux-mips64le": "0.14.49",
- "esbuild-linux-ppc64le": "0.14.49",
- "esbuild-linux-riscv64": "0.14.49",
- "esbuild-linux-s390x": "0.14.49",
- "esbuild-netbsd-64": "0.14.49",
- "esbuild-openbsd-64": "0.14.49",
- "esbuild-sunos-64": "0.14.49",
- "esbuild-windows-32": "0.14.49",
- "esbuild-windows-64": "0.14.49",
- "esbuild-windows-arm64": "0.14.49"
+ "@esbuild/android-arm": "0.15.10",
+ "@esbuild/linux-loong64": "0.15.10",
+ "esbuild-android-64": "0.15.10",
+ "esbuild-android-arm64": "0.15.10",
+ "esbuild-darwin-64": "0.15.10",
+ "esbuild-darwin-arm64": "0.15.10",
+ "esbuild-freebsd-64": "0.15.10",
+ "esbuild-freebsd-arm64": "0.15.10",
+ "esbuild-linux-32": "0.15.10",
+ "esbuild-linux-64": "0.15.10",
+ "esbuild-linux-arm": "0.15.10",
+ "esbuild-linux-arm64": "0.15.10",
+ "esbuild-linux-mips64le": "0.15.10",
+ "esbuild-linux-ppc64le": "0.15.10",
+ "esbuild-linux-riscv64": "0.15.10",
+ "esbuild-linux-s390x": "0.15.10",
+ "esbuild-netbsd-64": "0.15.10",
+ "esbuild-openbsd-64": "0.15.10",
+ "esbuild-sunos-64": "0.15.10",
+ "esbuild-windows-32": "0.15.10",
+ "esbuild-windows-64": "0.15.10",
+ "esbuild-windows-arm64": "0.15.10"
}
},
"node_modules/esbuild-android-64": {
- "version": "0.14.49",
- "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.14.49.tgz",
- "integrity": "sha512-vYsdOTD+yi+kquhBiFWl3tyxnj2qZJsl4tAqwhT90ktUdnyTizgle7TjNx6Ar1bN7wcwWqZ9QInfdk2WVagSww==",
+ "version": "0.15.10",
+ "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.15.10.tgz",
+ "integrity": "sha512-UI7krF8OYO1N7JYTgLT9ML5j4+45ra3amLZKx7LO3lmLt1Ibn8t3aZbX5Pu4BjWiqDuJ3m/hsvhPhK/5Y/YpnA==",
"cpu": [
"x64"
],
@@ -4921,9 +3743,9 @@
}
},
"node_modules/esbuild-android-arm64": {
- "version": "0.14.49",
- "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.49.tgz",
- "integrity": "sha512-g2HGr/hjOXCgSsvQZ1nK4nW/ei8JUx04Li74qub9qWrStlysaVmadRyTVuW32FGIpLQyc5sUjjZopj49eGGM2g==",
+ "version": "0.15.10",
+ "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.15.10.tgz",
+ "integrity": "sha512-EOt55D6xBk5O05AK8brXUbZmoFj4chM8u3riGflLa6ziEoVvNjRdD7Cnp82NHQGfSHgYR06XsPI8/sMuA/cUwg==",
"cpu": [
"arm64"
],
@@ -4936,9 +3758,9 @@
}
},
"node_modules/esbuild-darwin-64": {
- "version": "0.14.49",
- "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.49.tgz",
- "integrity": "sha512-3rvqnBCtX9ywso5fCHixt2GBCUsogNp9DjGmvbBohh31Ces34BVzFltMSxJpacNki96+WIcX5s/vum+ckXiLYg==",
+ "version": "0.15.10",
+ "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.15.10.tgz",
+ "integrity": "sha512-hbDJugTicqIm+WKZgp208d7FcXcaK8j2c0l+fqSJ3d2AzQAfjEYDRM3Z2oMeqSJ9uFxyj/muSACLdix7oTstRA==",
"cpu": [
"x64"
],
@@ -4951,9 +3773,9 @@
}
},
"node_modules/esbuild-darwin-arm64": {
- "version": "0.14.49",
- "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.49.tgz",
- "integrity": "sha512-XMaqDxO846srnGlUSJnwbijV29MTKUATmOLyQSfswbK/2X5Uv28M9tTLUJcKKxzoo9lnkYPsx2o8EJcTYwCs/A==",
+ "version": "0.15.10",
+ "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.10.tgz",
+ "integrity": "sha512-M1t5+Kj4IgSbYmunf2BB6EKLkWUq+XlqaFRiGOk8bmBapu9bCDrxjf4kUnWn59Dka3I27EiuHBKd1rSO4osLFQ==",
"cpu": [
"arm64"
],
@@ -4966,9 +3788,9 @@
}
},
"node_modules/esbuild-freebsd-64": {
- "version": "0.14.49",
- "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.49.tgz",
- "integrity": "sha512-NJ5Q6AjV879mOHFri+5lZLTp5XsO2hQ+KSJYLbfY9DgCu8s6/Zl2prWXVANYTeCDLlrIlNNYw8y34xqyLDKOmQ==",
+ "version": "0.15.10",
+ "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.10.tgz",
+ "integrity": "sha512-KMBFMa7C8oc97nqDdoZwtDBX7gfpolkk6Bcmj6YFMrtCMVgoU/x2DI1p74DmYl7CSS6Ppa3xgemrLrr5IjIn0w==",
"cpu": [
"x64"
],
@@ -4981,9 +3803,9 @@
}
},
"node_modules/esbuild-freebsd-arm64": {
- "version": "0.14.49",
- "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.49.tgz",
- "integrity": "sha512-lFLtgXnAc3eXYqj5koPlBZvEbBSOSUbWO3gyY/0+4lBdRqELyz4bAuamHvmvHW5swJYL7kngzIZw6kdu25KGOA==",
+ "version": "0.15.10",
+ "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.10.tgz",
+ "integrity": "sha512-m2KNbuCX13yQqLlbSojFMHpewbn8wW5uDS6DxRpmaZKzyq8Dbsku6hHvh2U+BcLwWY4mpgXzFUoENEf7IcioGg==",
"cpu": [
"arm64"
],
@@ -4996,9 +3818,9 @@
}
},
"node_modules/esbuild-linux-32": {
- "version": "0.14.49",
- "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.49.tgz",
- "integrity": "sha512-zTTH4gr2Kb8u4QcOpTDVn7Z8q7QEIvFl/+vHrI3cF6XOJS7iEI1FWslTo3uofB2+mn6sIJEQD9PrNZKoAAMDiA==",
+ "version": "0.15.10",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.15.10.tgz",
+ "integrity": "sha512-guXrwSYFAvNkuQ39FNeV4sNkNms1bLlA5vF1H0cazZBOLdLFIny6BhT+TUbK/hdByMQhtWQ5jI9VAmPKbVPu1w==",
"cpu": [
"ia32"
],
@@ -5011,9 +3833,9 @@
}
},
"node_modules/esbuild-linux-64": {
- "version": "0.14.49",
- "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.49.tgz",
- "integrity": "sha512-hYmzRIDzFfLrB5c1SknkxzM8LdEUOusp6M2TnuQZJLRtxTgyPnZZVtyMeCLki0wKgYPXkFsAVhi8vzo2mBNeTg==",
+ "version": "0.15.10",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.15.10.tgz",
+ "integrity": "sha512-jd8XfaSJeucMpD63YNMO1JCrdJhckHWcMv6O233bL4l6ogQKQOxBYSRP/XLWP+6kVTu0obXovuckJDcA0DKtQA==",
"cpu": [
"x64"
],
@@ -5026,9 +3848,9 @@
}
},
"node_modules/esbuild-linux-arm": {
- "version": "0.14.49",
- "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.49.tgz",
- "integrity": "sha512-iE3e+ZVv1Qz1Sy0gifIsarJMQ89Rpm9mtLSRtG3AH0FPgAzQ5Z5oU6vYzhc/3gSPi2UxdCOfRhw2onXuFw/0lg==",
+ "version": "0.15.10",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.15.10.tgz",
+ "integrity": "sha512-6N8vThLL/Lysy9y4Ex8XoLQAlbZKUyExCWyayGi2KgTBelKpPgj6RZnUaKri0dHNPGgReJriKVU6+KDGQwn10A==",
"cpu": [
"arm"
],
@@ -5041,9 +3863,9 @@
}
},
"node_modules/esbuild-linux-arm64": {
- "version": "0.14.49",
- "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.49.tgz",
- "integrity": "sha512-KLQ+WpeuY+7bxukxLz5VgkAAVQxUv67Ft4DmHIPIW+2w3ObBPQhqNoeQUHxopoW/aiOn3m99NSmSV+bs4BSsdA==",
+ "version": "0.15.10",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.10.tgz",
+ "integrity": "sha512-GByBi4fgkvZFTHFDYNftu1DQ1GzR23jws0oWyCfhnI7eMOe+wgwWrc78dbNk709Ivdr/evefm2PJiUBMiusS1A==",
"cpu": [
"arm64"
],
@@ -5056,9 +3878,9 @@
}
},
"node_modules/esbuild-linux-mips64le": {
- "version": "0.14.49",
- "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.49.tgz",
- "integrity": "sha512-n+rGODfm8RSum5pFIqFQVQpYBw+AztL8s6o9kfx7tjfK0yIGF6tm5HlG6aRjodiiKkH2xAiIM+U4xtQVZYU4rA==",
+ "version": "0.15.10",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.10.tgz",
+ "integrity": "sha512-BxP+LbaGVGIdQNJUNF7qpYjEGWb0YyHVSKqYKrn+pTwH/SiHUxFyJYSP3pqkku61olQiSBnSmWZ+YUpj78Tw7Q==",
"cpu": [
"mips64el"
],
@@ -5071,9 +3893,9 @@
}
},
"node_modules/esbuild-linux-ppc64le": {
- "version": "0.14.49",
- "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.49.tgz",
- "integrity": "sha512-WP9zR4HX6iCBmMFH+XHHng2LmdoIeUmBpL4aL2TR8ruzXyT4dWrJ5BSbT8iNo6THN8lod6GOmYDLq/dgZLalGw==",
+ "version": "0.15.10",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.10.tgz",
+ "integrity": "sha512-LoSQCd6498PmninNgqd/BR7z3Bsk/mabImBWuQ4wQgmQEeanzWd5BQU2aNi9mBURCLgyheuZS6Xhrw5luw3OkQ==",
"cpu": [
"ppc64"
],
@@ -5086,9 +3908,9 @@
}
},
"node_modules/esbuild-linux-riscv64": {
- "version": "0.14.49",
- "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.49.tgz",
- "integrity": "sha512-h66ORBz+Dg+1KgLvzTVQEA1LX4XBd1SK0Fgbhhw4akpG/YkN8pS6OzYI/7SGENiN6ao5hETRDSkVcvU9NRtkMQ==",
+ "version": "0.15.10",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.10.tgz",
+ "integrity": "sha512-Lrl9Cr2YROvPV4wmZ1/g48httE8z/5SCiXIyebiB5N8VT7pX3t6meI7TQVHw/wQpqP/AF4SksDuFImPTM7Z32Q==",
"cpu": [
"riscv64"
],
@@ -5101,9 +3923,9 @@
}
},
"node_modules/esbuild-linux-s390x": {
- "version": "0.14.49",
- "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.49.tgz",
- "integrity": "sha512-DhrUoFVWD+XmKO1y7e4kNCqQHPs6twz6VV6Uezl/XHYGzM60rBewBF5jlZjG0nCk5W/Xy6y1xWeopkrhFFM0sQ==",
+ "version": "0.15.10",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.10.tgz",
+ "integrity": "sha512-ReP+6q3eLVVP2lpRrvl5EodKX7EZ1bS1/z5j6hsluAlZP5aHhk6ghT6Cq3IANvvDdscMMCB4QEbI+AjtvoOFpA==",
"cpu": [
"s390x"
],
@@ -5116,11 +3938,11 @@
}
},
"node_modules/esbuild-loader": {
- "version": "2.19.0",
- "resolved": "https://registry.npmjs.org/esbuild-loader/-/esbuild-loader-2.19.0.tgz",
- "integrity": "sha512-urGNVE6Tl2rqx92ElKi/LiExXjGvcH6HfDBFzJ9Ppwqh4n6Jmx8x7RKAyMzSM78b6CAaJLhDncG5sPrL0ROh5Q==",
+ "version": "2.20.0",
+ "resolved": "https://registry.npmjs.org/esbuild-loader/-/esbuild-loader-2.20.0.tgz",
+ "integrity": "sha512-dr+j8O4w5RvqZ7I4PPB4EIyVTd679EBQnMm+JBB7av+vu05Zpje2IpK5N3ld1VWa+WxrInIbNFAg093+E1aRsA==",
"dependencies": {
- "esbuild": "^0.14.39",
+ "esbuild": "^0.15.6",
"joycon": "^3.0.1",
"json5": "^2.2.0",
"loader-utils": "^2.0.0",
@@ -5135,9 +3957,9 @@
}
},
"node_modules/esbuild-netbsd-64": {
- "version": "0.14.49",
- "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.49.tgz",
- "integrity": "sha512-BXaUwFOfCy2T+hABtiPUIpWjAeWK9P8O41gR4Pg73hpzoygVGnj0nI3YK4SJhe52ELgtdgWP/ckIkbn2XaTxjQ==",
+ "version": "0.15.10",
+ "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.10.tgz",
+ "integrity": "sha512-iGDYtJCMCqldMskQ4eIV+QSS/CuT7xyy9i2/FjpKvxAuCzrESZXiA1L64YNj6/afuzfBe9i8m/uDkFHy257hTw==",
"cpu": [
"x64"
],
@@ -5150,9 +3972,9 @@
}
},
"node_modules/esbuild-openbsd-64": {
- "version": "0.14.49",
- "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.49.tgz",
- "integrity": "sha512-lP06UQeLDGmVPw9Rg437Btu6J9/BmyhdoefnQ4gDEJTtJvKtQaUcOQrhjTq455ouZN4EHFH1h28WOJVANK41kA==",
+ "version": "0.15.10",
+ "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.10.tgz",
+ "integrity": "sha512-ftMMIwHWrnrYnvuJQRJs/Smlcb28F9ICGde/P3FUTCgDDM0N7WA0o9uOR38f5Xe2/OhNCgkjNeb7QeaE3cyWkQ==",
"cpu": [
"x64"
],
@@ -5165,9 +3987,9 @@
}
},
"node_modules/esbuild-sunos-64": {
- "version": "0.14.49",
- "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.49.tgz",
- "integrity": "sha512-4c8Zowp+V3zIWje329BeLbGh6XI9c/rqARNaj5yPHdC61pHI9UNdDxT3rePPJeWcEZVKjkiAS6AP6kiITp7FSw==",
+ "version": "0.15.10",
+ "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.15.10.tgz",
+ "integrity": "sha512-mf7hBL9Uo2gcy2r3rUFMjVpTaGpFJJE5QTDDqUFf1632FxteYANffDZmKbqX0PfeQ2XjUDE604IcE7OJeoHiyg==",
"cpu": [
"x64"
],
@@ -5180,9 +4002,9 @@
}
},
"node_modules/esbuild-windows-32": {
- "version": "0.14.49",
- "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.49.tgz",
- "integrity": "sha512-q7Rb+J9yHTeKr9QTPDYkqfkEj8/kcKz9lOabDuvEXpXuIcosWCJgo5Z7h/L4r7rbtTH4a8U2FGKb6s1eeOHmJA==",
+ "version": "0.15.10",
+ "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.15.10.tgz",
+ "integrity": "sha512-ttFVo+Cg8b5+qHmZHbEc8Vl17kCleHhLzgT8X04y8zudEApo0PxPg9Mz8Z2cKH1bCYlve1XL8LkyXGFjtUYeGg==",
"cpu": [
"ia32"
],
@@ -5195,9 +4017,9 @@
}
},
"node_modules/esbuild-windows-64": {
- "version": "0.14.49",
- "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.49.tgz",
- "integrity": "sha512-+Cme7Ongv0UIUTniPqfTX6mJ8Deo7VXw9xN0yJEN1lQMHDppTNmKwAM3oGbD/Vqff+07K2gN0WfNkMohmG+dVw==",
+ "version": "0.15.10",
+ "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.15.10.tgz",
+ "integrity": "sha512-2H0gdsyHi5x+8lbng3hLbxDWR7mKHWh5BXZGKVG830KUmXOOWFE2YKJ4tHRkejRduOGDrBvHBriYsGtmTv3ntA==",
"cpu": [
"x64"
],
@@ -5210,9 +4032,9 @@
}
},
"node_modules/esbuild-windows-arm64": {
- "version": "0.14.49",
- "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.49.tgz",
- "integrity": "sha512-v+HYNAXzuANrCbbLFJ5nmO3m5y2PGZWLe3uloAkLt87aXiO2mZr3BTmacZdjwNkNEHuH3bNtN8cak+mzVjVPfA==",
+ "version": "0.15.10",
+ "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.10.tgz",
+ "integrity": "sha512-S+th4F+F8VLsHLR0zrUcG+Et4hx0RKgK1eyHc08kztmLOES8BWwMiaGdoW9hiXuzznXQ0I/Fg904MNbr11Nktw==",
"cpu": [
"arm64"
],
@@ -5256,13 +4078,13 @@
}
},
"node_modules/escodegen": {
- "version": "1.14.3",
- "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz",
- "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==",
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz",
+ "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==",
"dev": true,
"dependencies": {
"esprima": "^4.0.1",
- "estraverse": "^4.2.0",
+ "estraverse": "^5.2.0",
"esutils": "^2.0.2",
"optionator": "^0.8.1"
},
@@ -5271,21 +4093,12 @@
"esgenerate": "bin/esgenerate.js"
},
"engines": {
- "node": ">=4.0"
+ "node": ">=6.0"
},
"optionalDependencies": {
"source-map": "~0.6.1"
}
},
- "node_modules/escodegen/node_modules/estraverse": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
- "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
- "dev": true,
- "engines": {
- "node": ">=4.0"
- }
- },
"node_modules/escodegen/node_modules/levn": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
@@ -5338,13 +4151,14 @@
}
},
"node_modules/eslint": {
- "version": "8.20.0",
- "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.20.0.tgz",
- "integrity": "sha512-d4ixhz5SKCa1D6SCPrivP7yYVi7nyD6A4vs6HIAul9ujBzcEmZVM3/0NN/yu5nKhmO1wjp5xQ46iRfmDGlOviA==",
+ "version": "8.25.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.25.0.tgz",
+ "integrity": "sha512-DVlJOZ4Pn50zcKW5bYH7GQK/9MsoQG2d5eDH0ebEkE8PbgzTTmtt/VTH9GGJ4BfeZCpBLqFfvsjX35UacUL83A==",
"dev": true,
"dependencies": {
- "@eslint/eslintrc": "^1.3.0",
- "@humanwhocodes/config-array": "^0.9.2",
+ "@eslint/eslintrc": "^1.3.3",
+ "@humanwhocodes/config-array": "^0.10.5",
+ "@humanwhocodes/module-importer": "^1.0.1",
"ajv": "^6.10.0",
"chalk": "^4.0.0",
"cross-spawn": "^7.0.2",
@@ -5354,18 +4168,21 @@
"eslint-scope": "^7.1.1",
"eslint-utils": "^3.0.0",
"eslint-visitor-keys": "^3.3.0",
- "espree": "^9.3.2",
+ "espree": "^9.4.0",
"esquery": "^1.4.0",
"esutils": "^2.0.2",
"fast-deep-equal": "^3.1.3",
"file-entry-cache": "^6.0.1",
- "functional-red-black-tree": "^1.0.1",
+ "find-up": "^5.0.0",
"glob-parent": "^6.0.1",
"globals": "^13.15.0",
+ "globby": "^11.1.0",
+ "grapheme-splitter": "^1.0.4",
"ignore": "^5.2.0",
"import-fresh": "^3.0.0",
"imurmurhash": "^0.1.4",
"is-glob": "^4.0.0",
+ "js-sdsl": "^4.1.4",
"js-yaml": "^4.1.0",
"json-stable-stringify-without-jsonify": "^1.0.1",
"levn": "^0.4.1",
@@ -5376,8 +4193,7 @@
"regexpp": "^3.2.0",
"strip-ansi": "^6.0.1",
"strip-json-comments": "^3.1.0",
- "text-table": "^0.2.0",
- "v8-compile-cache": "^2.0.3"
+ "text-table": "^0.2.0"
},
"bin": {
"eslint": "bin/eslint.js"
@@ -5409,16 +4225,20 @@
}
},
"node_modules/eslint-module-utils": {
- "version": "2.7.3",
- "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.3.tgz",
- "integrity": "sha512-088JEC7O3lDZM9xGe0RerkOMd0EjFl+Yvd1jPWIkMT5u3H9+HC34mWWPnqPrN13gieT9pBOO+Qt07Nb/6TresQ==",
+ "version": "2.7.4",
+ "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.4.tgz",
+ "integrity": "sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==",
"dev": true,
"dependencies": {
- "debug": "^3.2.7",
- "find-up": "^2.1.0"
+ "debug": "^3.2.7"
},
"engines": {
"node": ">=4"
+ },
+ "peerDependenciesMeta": {
+ "eslint": {
+ "optional": true
+ }
}
},
"node_modules/eslint-module-utils/node_modules/debug": {
@@ -5494,9 +4314,9 @@
}
},
"node_modules/eslint-plugin-sonarjs": {
- "version": "0.13.0",
- "resolved": "https://registry.npmjs.org/eslint-plugin-sonarjs/-/eslint-plugin-sonarjs-0.13.0.tgz",
- "integrity": "sha512-t3m7ta0EspzDxSOZh3cEOJIJVZgN/TlJYaBGnQlK6W/PZNbWep8q4RQskkJkA7/zwNpX0BaoEOSUUrqaADVoqA==",
+ "version": "0.15.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-sonarjs/-/eslint-plugin-sonarjs-0.15.0.tgz",
+ "integrity": "sha512-LuxHdAe6VqSbi1phsUvNjbmXLuvlobmryQJJNyQYbdubCfz6K8tmgoqNiJPnz0pP2AbYDbtuPm0ajOMgMrC+dQ==",
"dev": true,
"engines": {
"node": ">=12"
@@ -5506,18 +4326,18 @@
}
},
"node_modules/eslint-plugin-unicorn": {
- "version": "43.0.2",
- "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-43.0.2.tgz",
- "integrity": "sha512-DtqZ5mf/GMlfWoz1abIjq5jZfaFuHzGBZYIeuJfEoKKGWRHr2JiJR+ea+BF7Wx2N1PPRoT/2fwgiK1NnmNE3Hg==",
+ "version": "44.0.2",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-44.0.2.tgz",
+ "integrity": "sha512-GLIDX1wmeEqpGaKcnMcqRvMVsoabeF0Ton0EX4Th5u6Kmf7RM9WBl705AXFEsns56ESkEs0uyelLuUTvz9Tr0w==",
"dev": true,
"dependencies": {
- "@babel/helper-validator-identifier": "^7.18.6",
- "ci-info": "^3.3.2",
+ "@babel/helper-validator-identifier": "^7.19.1",
+ "ci-info": "^3.4.0",
"clean-regexp": "^1.0.0",
"eslint-utils": "^3.0.0",
"esquery": "^1.4.0",
"indent-string": "^4.0.0",
- "is-builtin-module": "^3.1.0",
+ "is-builtin-module": "^3.2.0",
"lodash": "^4.17.21",
"pluralize": "^8.0.0",
"read-pkg-up": "^7.0.1",
@@ -5533,13 +4353,13 @@
"url": "https://github.com/sindresorhus/eslint-plugin-unicorn?sponsor=1"
},
"peerDependencies": {
- "eslint": ">=8.18.0"
+ "eslint": ">=8.23.1"
}
},
"node_modules/eslint-plugin-vue": {
- "version": "9.2.0",
- "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.2.0.tgz",
- "integrity": "sha512-W2hc+NUXoce8sZtWgZ45miQTy6jNyuSdub5aZ1IBune4JDeAyzucYX0TzkrQ1jMO52sNUDYlCIHDoaNePe0p5g==",
+ "version": "9.6.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.6.0.tgz",
+ "integrity": "sha512-zzySkJgVbFCylnG2+9MDF7N+2Rjze2y0bF8GyUNpFOnT8mCMfqqtLDJkHBuYu9N/psW1A6DVbQhPkP92E+qakA==",
"dev": true,
"dependencies": {
"eslint-utils": "^3.0.0",
@@ -5629,17 +4449,20 @@
"dev": true
},
"node_modules/espree": {
- "version": "9.3.2",
- "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.2.tgz",
- "integrity": "sha512-D211tC7ZwouTIuY5x9XnS0E9sWNChB7IYKX/Xp5eQj3nFXhqmiUDB9q27y76oFl8jTg3pXcQx/bpxMfs3CIZbA==",
+ "version": "9.4.0",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.0.tgz",
+ "integrity": "sha512-DQmnRpLj7f6TgN/NYb0MTzJXL+vJF9h3pHy4JhCIs3zwcgez8xmGg3sXHcEO97BrmO2OSvCwMdfdlyl+E9KjOw==",
"dev": true,
"dependencies": {
- "acorn": "^8.7.1",
+ "acorn": "^8.8.0",
"acorn-jsx": "^5.3.2",
"eslint-visitor-keys": "^3.3.0"
},
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
}
},
"node_modules/esprima": {
@@ -5689,8 +4512,7 @@
"node_modules/estree-walker": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
- "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
- "dev": true
+ "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
},
"node_modules/esutils": {
"version": "2.0.3",
@@ -5718,138 +4540,15 @@
"node": ">=0.8.x"
}
},
- "node_modules/execa": {
- "version": "5.1.1",
- "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
- "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==",
- "dev": true,
- "dependencies": {
- "cross-spawn": "^7.0.3",
- "get-stream": "^6.0.0",
- "human-signals": "^2.1.0",
- "is-stream": "^2.0.0",
- "merge-stream": "^2.0.0",
- "npm-run-path": "^4.0.1",
- "onetime": "^5.1.2",
- "signal-exit": "^3.0.3",
- "strip-final-newline": "^2.0.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sindresorhus/execa?sponsor=1"
- }
- },
- "node_modules/execall": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/execall/-/execall-2.0.0.tgz",
- "integrity": "sha512-0FU2hZ5Hh6iQnarpRtQurM/aAvp3RIbfvgLHrcqJYzhXyV2KFruhuChf9NC6waAhiUR7FFtlugkI4p7f2Fqlow==",
- "dev": true,
- "dependencies": {
- "clone-regexp": "^2.1.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/exit": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
- "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==",
- "dev": true,
- "engines": {
- "node": ">= 0.8.0"
- }
- },
- "node_modules/expect": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/expect/-/expect-28.1.3.tgz",
- "integrity": "sha512-eEh0xn8HlsuOBxFgIss+2mX85VAS4Qy3OSkjV7rlBWljtA4oWH37glVGyOZSZvErDT/yBywZdPGwCXuTvSG85g==",
- "dev": true,
- "dependencies": {
- "@jest/expect-utils": "^28.1.3",
- "jest-get-type": "^28.0.2",
- "jest-matcher-utils": "^28.1.3",
- "jest-message-util": "^28.1.3",
- "jest-util": "^28.1.3"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/expect/node_modules/@jest/types": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz",
- "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==",
- "dev": true,
- "dependencies": {
- "@jest/schemas": "^28.1.3",
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^3.0.0",
- "@types/node": "*",
- "@types/yargs": "^17.0.8",
- "chalk": "^4.0.0"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/expect/node_modules/@types/yargs": {
- "version": "17.0.10",
- "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.10.tgz",
- "integrity": "sha512-gmEaFwpj/7f/ROdtIlci1R1VYU1J4j95m8T+Tj3iBgiBFKg1foE/PSl93bBd5T9LDXNPo8UlNN6W0qwD8O5OaA==",
- "dev": true,
- "dependencies": {
- "@types/yargs-parser": "*"
- }
- },
- "node_modules/expect/node_modules/jest-message-util": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-28.1.3.tgz",
- "integrity": "sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g==",
- "dev": true,
- "dependencies": {
- "@babel/code-frame": "^7.12.13",
- "@jest/types": "^28.1.3",
- "@types/stack-utils": "^2.0.0",
- "chalk": "^4.0.0",
- "graceful-fs": "^4.2.9",
- "micromatch": "^4.0.4",
- "pretty-format": "^28.1.3",
- "slash": "^3.0.0",
- "stack-utils": "^2.0.3"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/expect/node_modules/jest-util": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz",
- "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==",
- "dev": true,
- "dependencies": {
- "@jest/types": "^28.1.3",
- "@types/node": "*",
- "chalk": "^4.0.0",
- "ci-info": "^3.2.0",
- "graceful-fs": "^4.2.9",
- "picomatch": "^2.2.3"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
"node_modules/fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
},
"node_modules/fast-glob": {
- "version": "3.2.11",
- "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz",
- "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==",
+ "version": "3.2.12",
+ "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz",
+ "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==",
"dependencies": {
"@nodelib/fs.stat": "^2.0.2",
"@nodelib/fs.walk": "^1.2.3",
@@ -5890,9 +4589,9 @@
"dev": true
},
"node_modules/fastest-levenshtein": {
- "version": "1.0.14",
- "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.14.tgz",
- "integrity": "sha512-tFfWHjnuUfKE186Tfgr+jtaFc0mZTApEgKDOeyN+FwOqRkO/zK/3h1AiRd8u8CY53owL3CUmGr/oI9p/RdyLTA==",
+ "version": "1.0.16",
+ "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz",
+ "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==",
"engines": {
"node": ">= 4.9.1"
}
@@ -5905,15 +4604,6 @@
"reusify": "^1.0.4"
}
},
- "node_modules/fb-watchman": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz",
- "integrity": "sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg==",
- "dev": true,
- "dependencies": {
- "bser": "2.1.1"
- }
- },
"node_modules/file-entry-cache": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
@@ -5947,15 +4637,19 @@
}
},
"node_modules/find-up": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
- "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==",
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+ "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
"dev": true,
"dependencies": {
- "locate-path": "^2.0.0"
+ "locate-path": "^6.0.0",
+ "path-exists": "^4.0.0"
},
"engines": {
- "node": ">=4"
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/flat-cache": {
@@ -5972,9 +4666,9 @@
}
},
"node_modules/flatted": {
- "version": "3.2.6",
- "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.6.tgz",
- "integrity": "sha512-0sQoMh9s0BYsm+12Huy/rkKxVu4R1+r96YX5cG44rHV0pQ6iC3Q+mkoMFaGWObMFYQxCVT+ssG1ksneA2MI9KQ==",
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz",
+ "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==",
"dev": true
},
"node_modules/font-awesome": {
@@ -5986,17 +4680,17 @@
}
},
"node_modules/form-data": {
- "version": "2.5.1",
- "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz",
- "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==",
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
+ "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
"dev": true,
"dependencies": {
"asynckit": "^0.4.0",
- "combined-stream": "^1.0.6",
+ "combined-stream": "^1.0.8",
"mime-types": "^2.1.12"
},
"engines": {
- "node": ">= 0.12"
+ "node": ">= 6"
}
},
"node_modules/fs-extra": {
@@ -6068,12 +4762,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/functional-red-black-tree": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
- "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==",
- "dev": true
- },
"node_modules/functions-have-names": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz",
@@ -6083,15 +4771,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/gensync": {
- "version": "1.0.0-beta.2",
- "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
- "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
- "dev": true,
- "engines": {
- "node": ">=6.9.0"
- }
- },
"node_modules/get-caller-file": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
@@ -6101,10 +4780,19 @@
"node": "6.* || 8.* || >= 10.*"
}
},
+ "node_modules/get-func-name": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz",
+ "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==",
+ "dev": true,
+ "engines": {
+ "node": "*"
+ }
+ },
"node_modules/get-intrinsic": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.2.tgz",
- "integrity": "sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA==",
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz",
+ "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==",
"dev": true,
"dependencies": {
"function-bind": "^1.1.1",
@@ -6115,24 +4803,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/get-package-type": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz",
- "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==",
- "dev": true,
- "engines": {
- "node": ">=8.0.0"
- }
- },
- "node_modules/get-port": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/get-port/-/get-port-3.2.0.tgz",
- "integrity": "sha512-x5UJKlgeUiNT8nyo/AcnwLnZuZNcSjSw0kogRB+Whd1fjjFq4B1hySFxSFWWSn4mIBzg3sRNUDFYc4g5gjPoLg==",
- "dev": true,
- "engines": {
- "node": ">=4"
- }
- },
"node_modules/get-source": {
"version": "2.0.12",
"resolved": "https://registry.npmjs.org/get-source/-/get-source-2.0.12.tgz",
@@ -6150,24 +4820,12 @@
"dev": true
},
"node_modules/get-stdin": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-8.0.0.tgz",
- "integrity": "sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==",
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-9.0.0.tgz",
+ "integrity": "sha512-dVKBjfWisLAicarI2Sf+JuBE/DghV4UzNAVe9yhEJuzeREd3JhOTE9cUaJTeSa77fsbQUK3pcOpJfM59+VKZaA==",
"dev": true,
"engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/get-stream": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
- "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
- "dev": true,
- "engines": {
- "node": ">=10"
+ "node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
@@ -6206,6 +4864,15 @@
"node": ">= 6"
}
},
+ "node_modules/get-uri/node_modules/@tootallnate/once": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz",
+ "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==",
+ "dev": true,
+ "engines": {
+ "node": ">= 6"
+ }
+ },
"node_modules/glob": {
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
@@ -6268,6 +4935,12 @@
"node": ">=6"
}
},
+ "node_modules/global-prefix/node_modules/ini": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
+ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
+ "dev": true
+ },
"node_modules/global-prefix/node_modules/which": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
@@ -6281,9 +4954,9 @@
}
},
"node_modules/globals": {
- "version": "13.16.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-13.16.0.tgz",
- "integrity": "sha512-A1lrQfpNF+McdPOnnFqY3kSN0AFTy485bTi1bkLk4mVPODIUEcSfhHgRqA+QdXPksrSTTztYXx37NFV+GpGk3Q==",
+ "version": "13.17.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz",
+ "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==",
"dev": true,
"dependencies": {
"type-fest": "^0.20.2"
@@ -6295,6 +4968,21 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/globalthis": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz",
+ "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==",
+ "dev": true,
+ "dependencies": {
+ "define-properties": "^1.1.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/globby": {
"version": "11.1.0",
"resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz",
@@ -6326,6 +5014,12 @@
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz",
"integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA=="
},
+ "node_modules/grapheme-splitter": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz",
+ "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==",
+ "dev": true
+ },
"node_modules/graphlib": {
"version": "2.1.8",
"resolved": "https://registry.npmjs.org/graphlib/-/graphlib-2.1.8.tgz",
@@ -6335,24 +5029,9 @@
}
},
"node_modules/gsap": {
- "version": "3.10.4",
- "resolved": "https://registry.npmjs.org/gsap/-/gsap-3.10.4.tgz",
- "integrity": "sha512-6QatdkKxXCMfvCW4rM++0RqyLQAzFX5nwl3yHS0XPgkZBkiSEY3VZVbMltrdtsbER/xZonLtyHt684wRp4erlQ=="
- },
- "node_modules/happy-dom": {
- "version": "6.0.4",
- "resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-6.0.4.tgz",
- "integrity": "sha512-b+ID23Ms0BY08UNLymsOMG7EI2jSlwEt4cbJs938GZfeNAg+fqgkSO3TokQMgSOFoHznpjWmpVjBUL5boJ9PWw==",
- "dev": true,
- "dependencies": {
- "css.escape": "^1.5.1",
- "he": "^1.2.0",
- "node-fetch": "^2.x.x",
- "sync-request": "^6.1.0",
- "webidl-conversions": "^7.0.0",
- "whatwg-encoding": "^2.0.0",
- "whatwg-mimetype": "^3.0.0"
- }
+ "version": "3.11.3",
+ "resolved": "https://registry.npmjs.org/gsap/-/gsap-3.11.3.tgz",
+ "integrity": "sha512-xc/iIJy+LWiMbRa4IdMtdnnKa/7PXEK6NNzV71gdOYUVeTZN7UWnLU0fB7Hi1iwiz4ZZoYkBZPPYGg+2+zzFHA=="
},
"node_modules/hard-rejection": {
"version": "2.1.0",
@@ -6431,17 +5110,9 @@
}
},
"node_modules/hash-sum": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-1.0.2.tgz",
- "integrity": "sha512-fUs4B4L+mlt8/XAtSOGMUO1TXmAelItBPtJG7CyHJfYTdDjwisntGO2JQz7oUsatOY9o68+57eziUVNw/mRHmA=="
- },
- "node_modules/he": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
- "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
- "bin": {
- "he": "bin/he"
- }
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-2.0.0.tgz",
+ "integrity": "sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg=="
},
"node_modules/hosted-git-info": {
"version": "2.8.9",
@@ -6449,11 +5120,17 @@
"integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==",
"dev": true
},
- "node_modules/html-escaper": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
- "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
- "dev": true
+ "node_modules/html-encoding-sniffer": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz",
+ "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==",
+ "dev": true,
+ "dependencies": {
+ "whatwg-encoding": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
},
"node_modules/html-tags": {
"version": "3.2.0",
@@ -6467,21 +5144,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/http-basic": {
- "version": "8.1.3",
- "resolved": "https://registry.npmjs.org/http-basic/-/http-basic-8.1.3.tgz",
- "integrity": "sha512-/EcDMwJZh3mABI2NhGfHOGOeOZITqfkEO4p/xK+l3NpyncIHUQBoMvCSF/b5GqvKtySC2srL/GGG3+EtlqlmCw==",
- "dev": true,
- "dependencies": {
- "caseless": "^0.12.0",
- "concat-stream": "^1.6.2",
- "http-response-object": "^3.0.1",
- "parse-cache-control": "^1.0.1"
- },
- "engines": {
- "node": ">=6.0.0"
- }
- },
"node_modules/http-errors": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
@@ -6499,12 +5161,12 @@
}
},
"node_modules/http-proxy-agent": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz",
- "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==",
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz",
+ "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==",
"dev": true,
"dependencies": {
- "@tootallnate/once": "1",
+ "@tootallnate/once": "2",
"agent-base": "6",
"debug": "4"
},
@@ -6512,21 +5174,6 @@
"node": ">= 6"
}
},
- "node_modules/http-response-object": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/http-response-object/-/http-response-object-3.0.2.tgz",
- "integrity": "sha512-bqX0XTF6fnXSQcEJ2Iuyr75yVakyjIDCqroJQ/aHfSdlM743Cwqoi2nDYMzLGWUcuTWGWy8AAvOKXTfiv6q9RA==",
- "dev": true,
- "dependencies": {
- "@types/node": "^10.0.3"
- }
- },
- "node_modules/http-response-object/node_modules/@types/node": {
- "version": "10.17.60",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz",
- "integrity": "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==",
- "dev": true
- },
"node_modules/https-proxy-agent": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
@@ -6540,15 +5187,6 @@
"node": ">= 6"
}
},
- "node_modules/human-signals": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
- "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==",
- "dev": true,
- "engines": {
- "node": ">=10.17.0"
- }
- },
"node_modules/iconv-lite": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
@@ -6678,10 +5316,13 @@
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"node_modules/ini": {
- "version": "1.3.8",
- "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
- "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
- "dev": true
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-3.0.1.tgz",
+ "integrity": "sha512-it4HyVAUTKBc6m8e1iXWvXSTdndF7HbdN713+kvLrymxTaU4AUBWrJ4vEooP+V7fexnVD3LKcBshjGGPefSMUQ==",
+ "dev": true,
+ "engines": {
+ "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+ }
},
"node_modules/internal-slot": {
"version": "1.0.3",
@@ -6754,21 +5395,24 @@
}
},
"node_modules/is-builtin-module": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.1.0.tgz",
- "integrity": "sha512-OV7JjAgOTfAFJmHZLvpSTb4qi0nIILDV1gWPYDnDJUTNFM5aGlRAhk4QcT8i7TuAleeEV5Fdkqn3t4mS+Q11fg==",
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.0.tgz",
+ "integrity": "sha512-phDA4oSGt7vl1n5tJvTWooWWAsXLY+2xCnxNqvKhGEzujg+A43wPlPOyDg3C8XQHN+6k/JTQWJ/j0dQh/qr+Hw==",
"dev": true,
"dependencies": {
- "builtin-modules": "^3.0.0"
+ "builtin-modules": "^3.3.0"
},
"engines": {
"node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/is-callable": {
- "version": "1.2.4",
- "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz",
- "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==",
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
+ "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
"dev": true,
"engines": {
"node": ">= 0.4"
@@ -6778,9 +5422,9 @@
}
},
"node_modules/is-core-module": {
- "version": "2.9.0",
- "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz",
- "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==",
+ "version": "2.10.0",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.10.0.tgz",
+ "integrity": "sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==",
"dependencies": {
"has": "^1.0.3"
},
@@ -6819,15 +5463,6 @@
"node": ">=8"
}
},
- "node_modules/is-generator-fn": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz",
- "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==",
- "dev": true,
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/is-glob": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
@@ -6892,6 +5527,12 @@
"node": ">=0.10.0"
}
},
+ "node_modules/is-potential-custom-element-name": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz",
+ "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==",
+ "dev": true
+ },
"node_modules/is-reference": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz",
@@ -6917,15 +5558,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/is-regexp": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-2.1.0.tgz",
- "integrity": "sha512-OZ4IlER3zmRIoB9AqNhEggVxqIH4ofDns5nRrPS6yQxXE1TPCUpFznBfRQmQa8uC+pXqjMnukiJBxCisIxiLGA==",
- "dev": true,
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/is-shared-array-buffer": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz",
@@ -6938,18 +5570,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/is-stream": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
- "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
- "dev": true,
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/is-string": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz",
@@ -7016,1525 +5636,6 @@
"node": ">=0.10.0"
}
},
- "node_modules/istanbul-lib-coverage": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz",
- "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==",
- "dev": true,
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/istanbul-lib-instrument": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.0.tgz",
- "integrity": "sha512-6Lthe1hqXHBNsqvgDzGO6l03XNeu3CrG4RqQ1KM9+l5+jNGpEJfIELx1NS3SEHmJQA8np/u+E4EPRKRiu6m19A==",
- "dev": true,
- "dependencies": {
- "@babel/core": "^7.12.3",
- "@babel/parser": "^7.14.7",
- "@istanbuljs/schema": "^0.1.2",
- "istanbul-lib-coverage": "^3.2.0",
- "semver": "^6.3.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/istanbul-lib-instrument/node_modules/semver": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
- "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
- "dev": true,
- "bin": {
- "semver": "bin/semver.js"
- }
- },
- "node_modules/istanbul-lib-report": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz",
- "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==",
- "dev": true,
- "dependencies": {
- "istanbul-lib-coverage": "^3.0.0",
- "make-dir": "^3.0.0",
- "supports-color": "^7.1.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/istanbul-lib-source-maps": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz",
- "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==",
- "dev": true,
- "dependencies": {
- "debug": "^4.1.1",
- "istanbul-lib-coverage": "^3.0.0",
- "source-map": "^0.6.1"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/istanbul-reports": {
- "version": "3.1.5",
- "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz",
- "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==",
- "dev": true,
- "dependencies": {
- "html-escaper": "^2.0.0",
- "istanbul-lib-report": "^3.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/jest": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest/-/jest-28.1.3.tgz",
- "integrity": "sha512-N4GT5on8UkZgH0O5LUavMRV1EDEhNTL0KEfRmDIeZHSV7p2XgLoY9t9VDUgL6o+yfdgYHVxuz81G8oB9VG5uyA==",
- "dev": true,
- "dependencies": {
- "@jest/core": "^28.1.3",
- "@jest/types": "^28.1.3",
- "import-local": "^3.0.2",
- "jest-cli": "^28.1.3"
- },
- "bin": {
- "jest": "bin/jest.js"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- },
- "peerDependencies": {
- "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0"
- },
- "peerDependenciesMeta": {
- "node-notifier": {
- "optional": true
- }
- }
- },
- "node_modules/jest-changed-files": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-28.1.3.tgz",
- "integrity": "sha512-esaOfUWJXk2nfZt9SPyC8gA1kNfdKLkQWyzsMlqq8msYSlNKfmZxfRgZn4Cd4MGVUF+7v6dBs0d5TOAKa7iIiA==",
- "dev": true,
- "dependencies": {
- "execa": "^5.0.0",
- "p-limit": "^3.1.0"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/jest-circus": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-28.1.3.tgz",
- "integrity": "sha512-cZ+eS5zc79MBwt+IhQhiEp0OeBddpc1n8MBo1nMB8A7oPMKEO+Sre+wHaLJexQUj9Ya/8NOBY0RESUgYjB6fow==",
- "dev": true,
- "dependencies": {
- "@jest/environment": "^28.1.3",
- "@jest/expect": "^28.1.3",
- "@jest/test-result": "^28.1.3",
- "@jest/types": "^28.1.3",
- "@types/node": "*",
- "chalk": "^4.0.0",
- "co": "^4.6.0",
- "dedent": "^0.7.0",
- "is-generator-fn": "^2.0.0",
- "jest-each": "^28.1.3",
- "jest-matcher-utils": "^28.1.3",
- "jest-message-util": "^28.1.3",
- "jest-runtime": "^28.1.3",
- "jest-snapshot": "^28.1.3",
- "jest-util": "^28.1.3",
- "p-limit": "^3.1.0",
- "pretty-format": "^28.1.3",
- "slash": "^3.0.0",
- "stack-utils": "^2.0.3"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/jest-circus/node_modules/@jest/environment": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-28.1.3.tgz",
- "integrity": "sha512-1bf40cMFTEkKyEf585R9Iz1WayDjHoHqvts0XFYEqyKM3cFWDpeMoqKKTAF9LSYQModPUlh8FKptoM2YcMWAXA==",
- "dev": true,
- "dependencies": {
- "@jest/fake-timers": "^28.1.3",
- "@jest/types": "^28.1.3",
- "@types/node": "*",
- "jest-mock": "^28.1.3"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/jest-circus/node_modules/@jest/fake-timers": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-28.1.3.tgz",
- "integrity": "sha512-D/wOkL2POHv52h+ok5Oj/1gOG9HSywdoPtFsRCUmlCILXNn5eIWmcnd3DIiWlJnpGvQtmajqBP95Ei0EimxfLw==",
- "dev": true,
- "dependencies": {
- "@jest/types": "^28.1.3",
- "@sinonjs/fake-timers": "^9.1.2",
- "@types/node": "*",
- "jest-message-util": "^28.1.3",
- "jest-mock": "^28.1.3",
- "jest-util": "^28.1.3"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/jest-circus/node_modules/@jest/types": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz",
- "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==",
- "dev": true,
- "dependencies": {
- "@jest/schemas": "^28.1.3",
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^3.0.0",
- "@types/node": "*",
- "@types/yargs": "^17.0.8",
- "chalk": "^4.0.0"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/jest-circus/node_modules/@sinonjs/fake-timers": {
- "version": "9.1.2",
- "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz",
- "integrity": "sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw==",
- "dev": true,
- "dependencies": {
- "@sinonjs/commons": "^1.7.0"
- }
- },
- "node_modules/jest-circus/node_modules/@types/yargs": {
- "version": "17.0.10",
- "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.10.tgz",
- "integrity": "sha512-gmEaFwpj/7f/ROdtIlci1R1VYU1J4j95m8T+Tj3iBgiBFKg1foE/PSl93bBd5T9LDXNPo8UlNN6W0qwD8O5OaA==",
- "dev": true,
- "dependencies": {
- "@types/yargs-parser": "*"
- }
- },
- "node_modules/jest-circus/node_modules/jest-message-util": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-28.1.3.tgz",
- "integrity": "sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g==",
- "dev": true,
- "dependencies": {
- "@babel/code-frame": "^7.12.13",
- "@jest/types": "^28.1.3",
- "@types/stack-utils": "^2.0.0",
- "chalk": "^4.0.0",
- "graceful-fs": "^4.2.9",
- "micromatch": "^4.0.4",
- "pretty-format": "^28.1.3",
- "slash": "^3.0.0",
- "stack-utils": "^2.0.3"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/jest-circus/node_modules/jest-mock": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-28.1.3.tgz",
- "integrity": "sha512-o3J2jr6dMMWYVH4Lh/NKmDXdosrsJgi4AviS8oXLujcjpCMBb1FMsblDnOXKZKfSiHLxYub1eS0IHuRXsio9eA==",
- "dev": true,
- "dependencies": {
- "@jest/types": "^28.1.3",
- "@types/node": "*"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/jest-circus/node_modules/jest-util": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz",
- "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==",
- "dev": true,
- "dependencies": {
- "@jest/types": "^28.1.3",
- "@types/node": "*",
- "chalk": "^4.0.0",
- "ci-info": "^3.2.0",
- "graceful-fs": "^4.2.9",
- "picomatch": "^2.2.3"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/jest-cli": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-28.1.3.tgz",
- "integrity": "sha512-roY3kvrv57Azn1yPgdTebPAXvdR2xfezaKKYzVxZ6It/5NCxzJym6tUI5P1zkdWhfUYkxEI9uZWcQdaFLo8mJQ==",
- "dev": true,
- "dependencies": {
- "@jest/core": "^28.1.3",
- "@jest/test-result": "^28.1.3",
- "@jest/types": "^28.1.3",
- "chalk": "^4.0.0",
- "exit": "^0.1.2",
- "graceful-fs": "^4.2.9",
- "import-local": "^3.0.2",
- "jest-config": "^28.1.3",
- "jest-util": "^28.1.3",
- "jest-validate": "^28.1.3",
- "prompts": "^2.0.1",
- "yargs": "^17.3.1"
- },
- "bin": {
- "jest": "bin/jest.js"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- },
- "peerDependencies": {
- "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0"
- },
- "peerDependenciesMeta": {
- "node-notifier": {
- "optional": true
- }
- }
- },
- "node_modules/jest-cli/node_modules/@jest/types": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz",
- "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==",
- "dev": true,
- "dependencies": {
- "@jest/schemas": "^28.1.3",
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^3.0.0",
- "@types/node": "*",
- "@types/yargs": "^17.0.8",
- "chalk": "^4.0.0"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/jest-cli/node_modules/@types/yargs": {
- "version": "17.0.10",
- "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.10.tgz",
- "integrity": "sha512-gmEaFwpj/7f/ROdtIlci1R1VYU1J4j95m8T+Tj3iBgiBFKg1foE/PSl93bBd5T9LDXNPo8UlNN6W0qwD8O5OaA==",
- "dev": true,
- "dependencies": {
- "@types/yargs-parser": "*"
- }
- },
- "node_modules/jest-cli/node_modules/jest-util": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz",
- "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==",
- "dev": true,
- "dependencies": {
- "@jest/types": "^28.1.3",
- "@types/node": "*",
- "chalk": "^4.0.0",
- "ci-info": "^3.2.0",
- "graceful-fs": "^4.2.9",
- "picomatch": "^2.2.3"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/jest-config": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-28.1.3.tgz",
- "integrity": "sha512-MG3INjByJ0J4AsNBm7T3hsuxKQqFIiRo/AUqb1q9LRKI5UU6Aar9JHbr9Ivn1TVwfUD9KirRoM/T6u8XlcQPHQ==",
- "dev": true,
- "dependencies": {
- "@babel/core": "^7.11.6",
- "@jest/test-sequencer": "^28.1.3",
- "@jest/types": "^28.1.3",
- "babel-jest": "^28.1.3",
- "chalk": "^4.0.0",
- "ci-info": "^3.2.0",
- "deepmerge": "^4.2.2",
- "glob": "^7.1.3",
- "graceful-fs": "^4.2.9",
- "jest-circus": "^28.1.3",
- "jest-environment-node": "^28.1.3",
- "jest-get-type": "^28.0.2",
- "jest-regex-util": "^28.0.2",
- "jest-resolve": "^28.1.3",
- "jest-runner": "^28.1.3",
- "jest-util": "^28.1.3",
- "jest-validate": "^28.1.3",
- "micromatch": "^4.0.4",
- "parse-json": "^5.2.0",
- "pretty-format": "^28.1.3",
- "slash": "^3.0.0",
- "strip-json-comments": "^3.1.1"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- },
- "peerDependencies": {
- "@types/node": "*",
- "ts-node": ">=9.0.0"
- },
- "peerDependenciesMeta": {
- "@types/node": {
- "optional": true
- },
- "ts-node": {
- "optional": true
- }
- }
- },
- "node_modules/jest-config/node_modules/@jest/types": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz",
- "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==",
- "dev": true,
- "dependencies": {
- "@jest/schemas": "^28.1.3",
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^3.0.0",
- "@types/node": "*",
- "@types/yargs": "^17.0.8",
- "chalk": "^4.0.0"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/jest-config/node_modules/@types/yargs": {
- "version": "17.0.10",
- "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.10.tgz",
- "integrity": "sha512-gmEaFwpj/7f/ROdtIlci1R1VYU1J4j95m8T+Tj3iBgiBFKg1foE/PSl93bBd5T9LDXNPo8UlNN6W0qwD8O5OaA==",
- "dev": true,
- "dependencies": {
- "@types/yargs-parser": "*"
- }
- },
- "node_modules/jest-config/node_modules/jest-util": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz",
- "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==",
- "dev": true,
- "dependencies": {
- "@jest/types": "^28.1.3",
- "@types/node": "*",
- "chalk": "^4.0.0",
- "ci-info": "^3.2.0",
- "graceful-fs": "^4.2.9",
- "picomatch": "^2.2.3"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/jest-diff": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-28.1.3.tgz",
- "integrity": "sha512-8RqP1B/OXzjjTWkqMX67iqgwBVJRgCyKD3L9nq+6ZqJMdvjE8RgHktqZ6jNrkdMT+dJuYNI3rhQpxaz7drJHfw==",
- "dev": true,
- "dependencies": {
- "chalk": "^4.0.0",
- "diff-sequences": "^28.1.1",
- "jest-get-type": "^28.0.2",
- "pretty-format": "^28.1.3"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/jest-docblock": {
- "version": "28.1.1",
- "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-28.1.1.tgz",
- "integrity": "sha512-3wayBVNiOYx0cwAbl9rwm5kKFP8yHH3d/fkEaL02NPTkDojPtheGB7HZSFY4wzX+DxyrvhXz0KSCVksmCknCuA==",
- "dev": true,
- "dependencies": {
- "detect-newline": "^3.0.0"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/jest-each": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-28.1.3.tgz",
- "integrity": "sha512-arT1z4sg2yABU5uogObVPvSlSMQlDA48owx07BDPAiasW0yYpYHYOo4HHLz9q0BVzDVU4hILFjzJw0So9aCL/g==",
- "dev": true,
- "dependencies": {
- "@jest/types": "^28.1.3",
- "chalk": "^4.0.0",
- "jest-get-type": "^28.0.2",
- "jest-util": "^28.1.3",
- "pretty-format": "^28.1.3"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/jest-each/node_modules/@jest/types": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz",
- "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==",
- "dev": true,
- "dependencies": {
- "@jest/schemas": "^28.1.3",
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^3.0.0",
- "@types/node": "*",
- "@types/yargs": "^17.0.8",
- "chalk": "^4.0.0"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/jest-each/node_modules/@types/yargs": {
- "version": "17.0.10",
- "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.10.tgz",
- "integrity": "sha512-gmEaFwpj/7f/ROdtIlci1R1VYU1J4j95m8T+Tj3iBgiBFKg1foE/PSl93bBd5T9LDXNPo8UlNN6W0qwD8O5OaA==",
- "dev": true,
- "dependencies": {
- "@types/yargs-parser": "*"
- }
- },
- "node_modules/jest-each/node_modules/jest-util": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz",
- "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==",
- "dev": true,
- "dependencies": {
- "@jest/types": "^28.1.3",
- "@types/node": "*",
- "chalk": "^4.0.0",
- "ci-info": "^3.2.0",
- "graceful-fs": "^4.2.9",
- "picomatch": "^2.2.3"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/jest-environment-node": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-28.1.3.tgz",
- "integrity": "sha512-ugP6XOhEpjAEhGYvp5Xj989ns5cB1K6ZdjBYuS30umT4CQEETaxSiPcZ/E1kFktX4GkrcM4qu07IIlDYX1gp+A==",
- "dev": true,
- "dependencies": {
- "@jest/environment": "^28.1.3",
- "@jest/fake-timers": "^28.1.3",
- "@jest/types": "^28.1.3",
- "@types/node": "*",
- "jest-mock": "^28.1.3",
- "jest-util": "^28.1.3"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/jest-environment-node/node_modules/@jest/environment": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-28.1.3.tgz",
- "integrity": "sha512-1bf40cMFTEkKyEf585R9Iz1WayDjHoHqvts0XFYEqyKM3cFWDpeMoqKKTAF9LSYQModPUlh8FKptoM2YcMWAXA==",
- "dev": true,
- "dependencies": {
- "@jest/fake-timers": "^28.1.3",
- "@jest/types": "^28.1.3",
- "@types/node": "*",
- "jest-mock": "^28.1.3"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/jest-environment-node/node_modules/@jest/fake-timers": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-28.1.3.tgz",
- "integrity": "sha512-D/wOkL2POHv52h+ok5Oj/1gOG9HSywdoPtFsRCUmlCILXNn5eIWmcnd3DIiWlJnpGvQtmajqBP95Ei0EimxfLw==",
- "dev": true,
- "dependencies": {
- "@jest/types": "^28.1.3",
- "@sinonjs/fake-timers": "^9.1.2",
- "@types/node": "*",
- "jest-message-util": "^28.1.3",
- "jest-mock": "^28.1.3",
- "jest-util": "^28.1.3"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/jest-environment-node/node_modules/@jest/types": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz",
- "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==",
- "dev": true,
- "dependencies": {
- "@jest/schemas": "^28.1.3",
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^3.0.0",
- "@types/node": "*",
- "@types/yargs": "^17.0.8",
- "chalk": "^4.0.0"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/jest-environment-node/node_modules/@sinonjs/fake-timers": {
- "version": "9.1.2",
- "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz",
- "integrity": "sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw==",
- "dev": true,
- "dependencies": {
- "@sinonjs/commons": "^1.7.0"
- }
- },
- "node_modules/jest-environment-node/node_modules/@types/yargs": {
- "version": "17.0.10",
- "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.10.tgz",
- "integrity": "sha512-gmEaFwpj/7f/ROdtIlci1R1VYU1J4j95m8T+Tj3iBgiBFKg1foE/PSl93bBd5T9LDXNPo8UlNN6W0qwD8O5OaA==",
- "dev": true,
- "dependencies": {
- "@types/yargs-parser": "*"
- }
- },
- "node_modules/jest-environment-node/node_modules/jest-message-util": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-28.1.3.tgz",
- "integrity": "sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g==",
- "dev": true,
- "dependencies": {
- "@babel/code-frame": "^7.12.13",
- "@jest/types": "^28.1.3",
- "@types/stack-utils": "^2.0.0",
- "chalk": "^4.0.0",
- "graceful-fs": "^4.2.9",
- "micromatch": "^4.0.4",
- "pretty-format": "^28.1.3",
- "slash": "^3.0.0",
- "stack-utils": "^2.0.3"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/jest-environment-node/node_modules/jest-mock": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-28.1.3.tgz",
- "integrity": "sha512-o3J2jr6dMMWYVH4Lh/NKmDXdosrsJgi4AviS8oXLujcjpCMBb1FMsblDnOXKZKfSiHLxYub1eS0IHuRXsio9eA==",
- "dev": true,
- "dependencies": {
- "@jest/types": "^28.1.3",
- "@types/node": "*"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/jest-environment-node/node_modules/jest-util": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz",
- "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==",
- "dev": true,
- "dependencies": {
- "@jest/types": "^28.1.3",
- "@types/node": "*",
- "chalk": "^4.0.0",
- "ci-info": "^3.2.0",
- "graceful-fs": "^4.2.9",
- "picomatch": "^2.2.3"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/jest-extended": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/jest-extended/-/jest-extended-3.0.1.tgz",
- "integrity": "sha512-OSGbKUhbjy7QikfQyK3ishFrAqLeRodBzeJk7SuuWGACAT7HHcGuJ4aUQ3ueLANx4KSv1Pa7r1LJWGtJ3eI0xA==",
- "dev": true,
- "dependencies": {
- "jest-diff": "^28.0.0",
- "jest-get-type": "^28.0.0"
- },
- "engines": {
- "node": "^14.15.0 || ^16.13.0 || >=18.0.0"
- },
- "peerDependencies": {
- "jest": ">=27.2.5"
- }
- },
- "node_modules/jest-get-type": {
- "version": "28.0.2",
- "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-28.0.2.tgz",
- "integrity": "sha512-ioj2w9/DxSYHfOm5lJKCdcAmPJzQXmbM/Url3rhlghrPvT3tt+7a/+oXc9azkKmLvoiXjtV83bEWqi+vs5nlPA==",
- "dev": true,
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/jest-haste-map": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-28.1.3.tgz",
- "integrity": "sha512-3S+RQWDXccXDKSWnkHa/dPwt+2qwA8CJzR61w3FoYCvoo3Pn8tvGcysmMF0Bj0EX5RYvAI2EIvC57OmotfdtKA==",
- "dev": true,
- "dependencies": {
- "@jest/types": "^28.1.3",
- "@types/graceful-fs": "^4.1.3",
- "@types/node": "*",
- "anymatch": "^3.0.3",
- "fb-watchman": "^2.0.0",
- "graceful-fs": "^4.2.9",
- "jest-regex-util": "^28.0.2",
- "jest-util": "^28.1.3",
- "jest-worker": "^28.1.3",
- "micromatch": "^4.0.4",
- "walker": "^1.0.8"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- },
- "optionalDependencies": {
- "fsevents": "^2.3.2"
- }
- },
- "node_modules/jest-haste-map/node_modules/@jest/types": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz",
- "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==",
- "dev": true,
- "dependencies": {
- "@jest/schemas": "^28.1.3",
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^3.0.0",
- "@types/node": "*",
- "@types/yargs": "^17.0.8",
- "chalk": "^4.0.0"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/jest-haste-map/node_modules/@types/yargs": {
- "version": "17.0.10",
- "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.10.tgz",
- "integrity": "sha512-gmEaFwpj/7f/ROdtIlci1R1VYU1J4j95m8T+Tj3iBgiBFKg1foE/PSl93bBd5T9LDXNPo8UlNN6W0qwD8O5OaA==",
- "dev": true,
- "dependencies": {
- "@types/yargs-parser": "*"
- }
- },
- "node_modules/jest-haste-map/node_modules/jest-util": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz",
- "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==",
- "dev": true,
- "dependencies": {
- "@jest/types": "^28.1.3",
- "@types/node": "*",
- "chalk": "^4.0.0",
- "ci-info": "^3.2.0",
- "graceful-fs": "^4.2.9",
- "picomatch": "^2.2.3"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/jest-leak-detector": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-28.1.3.tgz",
- "integrity": "sha512-WFVJhnQsiKtDEo5lG2mM0v40QWnBM+zMdHHyJs8AWZ7J0QZJS59MsyKeJHWhpBZBH32S48FOVvGyOFT1h0DlqA==",
- "dev": true,
- "dependencies": {
- "jest-get-type": "^28.0.2",
- "pretty-format": "^28.1.3"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/jest-matcher-utils": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-28.1.3.tgz",
- "integrity": "sha512-kQeJ7qHemKfbzKoGjHHrRKH6atgxMk8Enkk2iPQ3XwO6oE/KYD8lMYOziCkeSB9G4adPM4nR1DE8Tf5JeWH6Bw==",
- "dev": true,
- "dependencies": {
- "chalk": "^4.0.0",
- "jest-diff": "^28.1.3",
- "jest-get-type": "^28.0.2",
- "pretty-format": "^28.1.3"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/jest-message-util": {
- "version": "27.5.1",
- "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz",
- "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==",
- "dev": true,
- "dependencies": {
- "@babel/code-frame": "^7.12.13",
- "@jest/types": "^27.5.1",
- "@types/stack-utils": "^2.0.0",
- "chalk": "^4.0.0",
- "graceful-fs": "^4.2.9",
- "micromatch": "^4.0.4",
- "pretty-format": "^27.5.1",
- "slash": "^3.0.0",
- "stack-utils": "^2.0.3"
- },
- "engines": {
- "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
- }
- },
- "node_modules/jest-message-util/node_modules/ansi-styles": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
- "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
- "dev": true,
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/chalk/ansi-styles?sponsor=1"
- }
- },
- "node_modules/jest-message-util/node_modules/pretty-format": {
- "version": "27.5.1",
- "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz",
- "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==",
- "dev": true,
- "dependencies": {
- "ansi-regex": "^5.0.1",
- "ansi-styles": "^5.0.0",
- "react-is": "^17.0.1"
- },
- "engines": {
- "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
- }
- },
- "node_modules/jest-message-util/node_modules/react-is": {
- "version": "17.0.2",
- "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
- "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
- "dev": true
- },
- "node_modules/jest-mock": {
- "version": "27.5.1",
- "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-27.5.1.tgz",
- "integrity": "sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og==",
- "dev": true,
- "dependencies": {
- "@jest/types": "^27.5.1",
- "@types/node": "*"
- },
- "engines": {
- "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
- }
- },
- "node_modules/jest-pnp-resolver": {
- "version": "1.2.2",
- "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz",
- "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==",
- "dev": true,
- "engines": {
- "node": ">=6"
- },
- "peerDependencies": {
- "jest-resolve": "*"
- },
- "peerDependenciesMeta": {
- "jest-resolve": {
- "optional": true
- }
- }
- },
- "node_modules/jest-regex-util": {
- "version": "28.0.2",
- "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-28.0.2.tgz",
- "integrity": "sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw==",
- "dev": true,
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/jest-resolve": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-28.1.3.tgz",
- "integrity": "sha512-Z1W3tTjE6QaNI90qo/BJpfnvpxtaFTFw5CDgwpyE/Kz8U/06N1Hjf4ia9quUhCh39qIGWF1ZuxFiBiJQwSEYKQ==",
- "dev": true,
- "dependencies": {
- "chalk": "^4.0.0",
- "graceful-fs": "^4.2.9",
- "jest-haste-map": "^28.1.3",
- "jest-pnp-resolver": "^1.2.2",
- "jest-util": "^28.1.3",
- "jest-validate": "^28.1.3",
- "resolve": "^1.20.0",
- "resolve.exports": "^1.1.0",
- "slash": "^3.0.0"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/jest-resolve-dependencies": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-28.1.3.tgz",
- "integrity": "sha512-qa0QO2Q0XzQoNPouMbCc7Bvtsem8eQgVPNkwn9LnS+R2n8DaVDPL/U1gngC0LTl1RYXJU0uJa2BMC2DbTfFrHA==",
- "dev": true,
- "dependencies": {
- "jest-regex-util": "^28.0.2",
- "jest-snapshot": "^28.1.3"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/jest-resolve/node_modules/@jest/types": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz",
- "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==",
- "dev": true,
- "dependencies": {
- "@jest/schemas": "^28.1.3",
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^3.0.0",
- "@types/node": "*",
- "@types/yargs": "^17.0.8",
- "chalk": "^4.0.0"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/jest-resolve/node_modules/@types/yargs": {
- "version": "17.0.10",
- "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.10.tgz",
- "integrity": "sha512-gmEaFwpj/7f/ROdtIlci1R1VYU1J4j95m8T+Tj3iBgiBFKg1foE/PSl93bBd5T9LDXNPo8UlNN6W0qwD8O5OaA==",
- "dev": true,
- "dependencies": {
- "@types/yargs-parser": "*"
- }
- },
- "node_modules/jest-resolve/node_modules/jest-util": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz",
- "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==",
- "dev": true,
- "dependencies": {
- "@jest/types": "^28.1.3",
- "@types/node": "*",
- "chalk": "^4.0.0",
- "ci-info": "^3.2.0",
- "graceful-fs": "^4.2.9",
- "picomatch": "^2.2.3"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/jest-runner": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-28.1.3.tgz",
- "integrity": "sha512-GkMw4D/0USd62OVO0oEgjn23TM+YJa2U2Wu5zz9xsQB1MxWKDOlrnykPxnMsN0tnJllfLPinHTka61u0QhaxBA==",
- "dev": true,
- "dependencies": {
- "@jest/console": "^28.1.3",
- "@jest/environment": "^28.1.3",
- "@jest/test-result": "^28.1.3",
- "@jest/transform": "^28.1.3",
- "@jest/types": "^28.1.3",
- "@types/node": "*",
- "chalk": "^4.0.0",
- "emittery": "^0.10.2",
- "graceful-fs": "^4.2.9",
- "jest-docblock": "^28.1.1",
- "jest-environment-node": "^28.1.3",
- "jest-haste-map": "^28.1.3",
- "jest-leak-detector": "^28.1.3",
- "jest-message-util": "^28.1.3",
- "jest-resolve": "^28.1.3",
- "jest-runtime": "^28.1.3",
- "jest-util": "^28.1.3",
- "jest-watcher": "^28.1.3",
- "jest-worker": "^28.1.3",
- "p-limit": "^3.1.0",
- "source-map-support": "0.5.13"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/jest-runner/node_modules/@jest/environment": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-28.1.3.tgz",
- "integrity": "sha512-1bf40cMFTEkKyEf585R9Iz1WayDjHoHqvts0XFYEqyKM3cFWDpeMoqKKTAF9LSYQModPUlh8FKptoM2YcMWAXA==",
- "dev": true,
- "dependencies": {
- "@jest/fake-timers": "^28.1.3",
- "@jest/types": "^28.1.3",
- "@types/node": "*",
- "jest-mock": "^28.1.3"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/jest-runner/node_modules/@jest/fake-timers": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-28.1.3.tgz",
- "integrity": "sha512-D/wOkL2POHv52h+ok5Oj/1gOG9HSywdoPtFsRCUmlCILXNn5eIWmcnd3DIiWlJnpGvQtmajqBP95Ei0EimxfLw==",
- "dev": true,
- "dependencies": {
- "@jest/types": "^28.1.3",
- "@sinonjs/fake-timers": "^9.1.2",
- "@types/node": "*",
- "jest-message-util": "^28.1.3",
- "jest-mock": "^28.1.3",
- "jest-util": "^28.1.3"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/jest-runner/node_modules/@jest/types": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz",
- "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==",
- "dev": true,
- "dependencies": {
- "@jest/schemas": "^28.1.3",
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^3.0.0",
- "@types/node": "*",
- "@types/yargs": "^17.0.8",
- "chalk": "^4.0.0"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/jest-runner/node_modules/@sinonjs/fake-timers": {
- "version": "9.1.2",
- "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz",
- "integrity": "sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw==",
- "dev": true,
- "dependencies": {
- "@sinonjs/commons": "^1.7.0"
- }
- },
- "node_modules/jest-runner/node_modules/@types/yargs": {
- "version": "17.0.10",
- "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.10.tgz",
- "integrity": "sha512-gmEaFwpj/7f/ROdtIlci1R1VYU1J4j95m8T+Tj3iBgiBFKg1foE/PSl93bBd5T9LDXNPo8UlNN6W0qwD8O5OaA==",
- "dev": true,
- "dependencies": {
- "@types/yargs-parser": "*"
- }
- },
- "node_modules/jest-runner/node_modules/jest-message-util": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-28.1.3.tgz",
- "integrity": "sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g==",
- "dev": true,
- "dependencies": {
- "@babel/code-frame": "^7.12.13",
- "@jest/types": "^28.1.3",
- "@types/stack-utils": "^2.0.0",
- "chalk": "^4.0.0",
- "graceful-fs": "^4.2.9",
- "micromatch": "^4.0.4",
- "pretty-format": "^28.1.3",
- "slash": "^3.0.0",
- "stack-utils": "^2.0.3"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/jest-runner/node_modules/jest-mock": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-28.1.3.tgz",
- "integrity": "sha512-o3J2jr6dMMWYVH4Lh/NKmDXdosrsJgi4AviS8oXLujcjpCMBb1FMsblDnOXKZKfSiHLxYub1eS0IHuRXsio9eA==",
- "dev": true,
- "dependencies": {
- "@jest/types": "^28.1.3",
- "@types/node": "*"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/jest-runner/node_modules/jest-util": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz",
- "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==",
- "dev": true,
- "dependencies": {
- "@jest/types": "^28.1.3",
- "@types/node": "*",
- "chalk": "^4.0.0",
- "ci-info": "^3.2.0",
- "graceful-fs": "^4.2.9",
- "picomatch": "^2.2.3"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/jest-runtime": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-28.1.3.tgz",
- "integrity": "sha512-NU+881ScBQQLc1JHG5eJGU7Ui3kLKrmwCPPtYsJtBykixrM2OhVQlpMmFWJjMyDfdkGgBMNjXCGB/ebzsgNGQw==",
- "dev": true,
- "dependencies": {
- "@jest/environment": "^28.1.3",
- "@jest/fake-timers": "^28.1.3",
- "@jest/globals": "^28.1.3",
- "@jest/source-map": "^28.1.2",
- "@jest/test-result": "^28.1.3",
- "@jest/transform": "^28.1.3",
- "@jest/types": "^28.1.3",
- "chalk": "^4.0.0",
- "cjs-module-lexer": "^1.0.0",
- "collect-v8-coverage": "^1.0.0",
- "execa": "^5.0.0",
- "glob": "^7.1.3",
- "graceful-fs": "^4.2.9",
- "jest-haste-map": "^28.1.3",
- "jest-message-util": "^28.1.3",
- "jest-mock": "^28.1.3",
- "jest-regex-util": "^28.0.2",
- "jest-resolve": "^28.1.3",
- "jest-snapshot": "^28.1.3",
- "jest-util": "^28.1.3",
- "slash": "^3.0.0",
- "strip-bom": "^4.0.0"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/jest-runtime/node_modules/@jest/environment": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-28.1.3.tgz",
- "integrity": "sha512-1bf40cMFTEkKyEf585R9Iz1WayDjHoHqvts0XFYEqyKM3cFWDpeMoqKKTAF9LSYQModPUlh8FKptoM2YcMWAXA==",
- "dev": true,
- "dependencies": {
- "@jest/fake-timers": "^28.1.3",
- "@jest/types": "^28.1.3",
- "@types/node": "*",
- "jest-mock": "^28.1.3"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/jest-runtime/node_modules/@jest/fake-timers": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-28.1.3.tgz",
- "integrity": "sha512-D/wOkL2POHv52h+ok5Oj/1gOG9HSywdoPtFsRCUmlCILXNn5eIWmcnd3DIiWlJnpGvQtmajqBP95Ei0EimxfLw==",
- "dev": true,
- "dependencies": {
- "@jest/types": "^28.1.3",
- "@sinonjs/fake-timers": "^9.1.2",
- "@types/node": "*",
- "jest-message-util": "^28.1.3",
- "jest-mock": "^28.1.3",
- "jest-util": "^28.1.3"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/jest-runtime/node_modules/@jest/types": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz",
- "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==",
- "dev": true,
- "dependencies": {
- "@jest/schemas": "^28.1.3",
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^3.0.0",
- "@types/node": "*",
- "@types/yargs": "^17.0.8",
- "chalk": "^4.0.0"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/jest-runtime/node_modules/@sinonjs/fake-timers": {
- "version": "9.1.2",
- "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz",
- "integrity": "sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw==",
- "dev": true,
- "dependencies": {
- "@sinonjs/commons": "^1.7.0"
- }
- },
- "node_modules/jest-runtime/node_modules/@types/yargs": {
- "version": "17.0.10",
- "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.10.tgz",
- "integrity": "sha512-gmEaFwpj/7f/ROdtIlci1R1VYU1J4j95m8T+Tj3iBgiBFKg1foE/PSl93bBd5T9LDXNPo8UlNN6W0qwD8O5OaA==",
- "dev": true,
- "dependencies": {
- "@types/yargs-parser": "*"
- }
- },
- "node_modules/jest-runtime/node_modules/jest-message-util": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-28.1.3.tgz",
- "integrity": "sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g==",
- "dev": true,
- "dependencies": {
- "@babel/code-frame": "^7.12.13",
- "@jest/types": "^28.1.3",
- "@types/stack-utils": "^2.0.0",
- "chalk": "^4.0.0",
- "graceful-fs": "^4.2.9",
- "micromatch": "^4.0.4",
- "pretty-format": "^28.1.3",
- "slash": "^3.0.0",
- "stack-utils": "^2.0.3"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/jest-runtime/node_modules/jest-mock": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-28.1.3.tgz",
- "integrity": "sha512-o3J2jr6dMMWYVH4Lh/NKmDXdosrsJgi4AviS8oXLujcjpCMBb1FMsblDnOXKZKfSiHLxYub1eS0IHuRXsio9eA==",
- "dev": true,
- "dependencies": {
- "@jest/types": "^28.1.3",
- "@types/node": "*"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/jest-runtime/node_modules/jest-util": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz",
- "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==",
- "dev": true,
- "dependencies": {
- "@jest/types": "^28.1.3",
- "@types/node": "*",
- "chalk": "^4.0.0",
- "ci-info": "^3.2.0",
- "graceful-fs": "^4.2.9",
- "picomatch": "^2.2.3"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/jest-snapshot": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-28.1.3.tgz",
- "integrity": "sha512-4lzMgtiNlc3DU/8lZfmqxN3AYD6GGLbl+72rdBpXvcV+whX7mDrREzkPdp2RnmfIiWBg1YbuFSkXduF2JcafJg==",
- "dev": true,
- "dependencies": {
- "@babel/core": "^7.11.6",
- "@babel/generator": "^7.7.2",
- "@babel/plugin-syntax-typescript": "^7.7.2",
- "@babel/traverse": "^7.7.2",
- "@babel/types": "^7.3.3",
- "@jest/expect-utils": "^28.1.3",
- "@jest/transform": "^28.1.3",
- "@jest/types": "^28.1.3",
- "@types/babel__traverse": "^7.0.6",
- "@types/prettier": "^2.1.5",
- "babel-preset-current-node-syntax": "^1.0.0",
- "chalk": "^4.0.0",
- "expect": "^28.1.3",
- "graceful-fs": "^4.2.9",
- "jest-diff": "^28.1.3",
- "jest-get-type": "^28.0.2",
- "jest-haste-map": "^28.1.3",
- "jest-matcher-utils": "^28.1.3",
- "jest-message-util": "^28.1.3",
- "jest-util": "^28.1.3",
- "natural-compare": "^1.4.0",
- "pretty-format": "^28.1.3",
- "semver": "^7.3.5"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/jest-snapshot/node_modules/@jest/types": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz",
- "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==",
- "dev": true,
- "dependencies": {
- "@jest/schemas": "^28.1.3",
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^3.0.0",
- "@types/node": "*",
- "@types/yargs": "^17.0.8",
- "chalk": "^4.0.0"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/jest-snapshot/node_modules/@types/yargs": {
- "version": "17.0.10",
- "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.10.tgz",
- "integrity": "sha512-gmEaFwpj/7f/ROdtIlci1R1VYU1J4j95m8T+Tj3iBgiBFKg1foE/PSl93bBd5T9LDXNPo8UlNN6W0qwD8O5OaA==",
- "dev": true,
- "dependencies": {
- "@types/yargs-parser": "*"
- }
- },
- "node_modules/jest-snapshot/node_modules/jest-message-util": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-28.1.3.tgz",
- "integrity": "sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g==",
- "dev": true,
- "dependencies": {
- "@babel/code-frame": "^7.12.13",
- "@jest/types": "^28.1.3",
- "@types/stack-utils": "^2.0.0",
- "chalk": "^4.0.0",
- "graceful-fs": "^4.2.9",
- "micromatch": "^4.0.4",
- "pretty-format": "^28.1.3",
- "slash": "^3.0.0",
- "stack-utils": "^2.0.3"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/jest-snapshot/node_modules/jest-util": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz",
- "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==",
- "dev": true,
- "dependencies": {
- "@jest/types": "^28.1.3",
- "@types/node": "*",
- "chalk": "^4.0.0",
- "ci-info": "^3.2.0",
- "graceful-fs": "^4.2.9",
- "picomatch": "^2.2.3"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/jest-util": {
- "version": "27.5.1",
- "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz",
- "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==",
- "dev": true,
- "dependencies": {
- "@jest/types": "^27.5.1",
- "@types/node": "*",
- "chalk": "^4.0.0",
- "ci-info": "^3.2.0",
- "graceful-fs": "^4.2.9",
- "picomatch": "^2.2.3"
- },
- "engines": {
- "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0"
- }
- },
- "node_modules/jest-validate": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-28.1.3.tgz",
- "integrity": "sha512-SZbOGBWEsaTxBGCOpsRWlXlvNkvTkY0XxRfh7zYmvd8uL5Qzyg0CHAXiXKROflh801quA6+/DsT4ODDthOC/OA==",
- "dev": true,
- "dependencies": {
- "@jest/types": "^28.1.3",
- "camelcase": "^6.2.0",
- "chalk": "^4.0.0",
- "jest-get-type": "^28.0.2",
- "leven": "^3.1.0",
- "pretty-format": "^28.1.3"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/jest-validate/node_modules/@jest/types": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz",
- "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==",
- "dev": true,
- "dependencies": {
- "@jest/schemas": "^28.1.3",
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^3.0.0",
- "@types/node": "*",
- "@types/yargs": "^17.0.8",
- "chalk": "^4.0.0"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/jest-validate/node_modules/@types/yargs": {
- "version": "17.0.10",
- "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.10.tgz",
- "integrity": "sha512-gmEaFwpj/7f/ROdtIlci1R1VYU1J4j95m8T+Tj3iBgiBFKg1foE/PSl93bBd5T9LDXNPo8UlNN6W0qwD8O5OaA==",
- "dev": true,
- "dependencies": {
- "@types/yargs-parser": "*"
- }
- },
- "node_modules/jest-validate/node_modules/camelcase": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
- "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==",
- "dev": true,
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/jest-watcher": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-28.1.3.tgz",
- "integrity": "sha512-t4qcqj9hze+jviFPUN3YAtAEeFnr/azITXQEMARf5cMwKY2SMBRnCQTXLixTl20OR6mLh9KLMrgVJgJISym+1g==",
- "dev": true,
- "dependencies": {
- "@jest/test-result": "^28.1.3",
- "@jest/types": "^28.1.3",
- "@types/node": "*",
- "ansi-escapes": "^4.2.1",
- "chalk": "^4.0.0",
- "emittery": "^0.10.2",
- "jest-util": "^28.1.3",
- "string-length": "^4.0.1"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/jest-watcher/node_modules/@jest/types": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz",
- "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==",
- "dev": true,
- "dependencies": {
- "@jest/schemas": "^28.1.3",
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^3.0.0",
- "@types/node": "*",
- "@types/yargs": "^17.0.8",
- "chalk": "^4.0.0"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/jest-watcher/node_modules/@types/yargs": {
- "version": "17.0.10",
- "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.10.tgz",
- "integrity": "sha512-gmEaFwpj/7f/ROdtIlci1R1VYU1J4j95m8T+Tj3iBgiBFKg1foE/PSl93bBd5T9LDXNPo8UlNN6W0qwD8O5OaA==",
- "dev": true,
- "dependencies": {
- "@types/yargs-parser": "*"
- }
- },
- "node_modules/jest-watcher/node_modules/jest-util": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz",
- "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==",
- "dev": true,
- "dependencies": {
- "@jest/types": "^28.1.3",
- "@types/node": "*",
- "chalk": "^4.0.0",
- "ci-info": "^3.2.0",
- "graceful-fs": "^4.2.9",
- "picomatch": "^2.2.3"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/jest-worker": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-28.1.3.tgz",
- "integrity": "sha512-CqRA220YV/6jCo8VWvAt1KKx6eek1VIHMPeLEbpcfSfkEeWyBNppynM/o6q+Wmw+sOhos2ml34wZbSX3G13//g==",
- "dev": true,
- "dependencies": {
- "@types/node": "*",
- "merge-stream": "^2.0.0",
- "supports-color": "^8.0.0"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/jest-worker/node_modules/supports-color": {
- "version": "8.1.1",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
- "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
- "dev": true,
- "dependencies": {
- "has-flag": "^4.0.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/chalk/supports-color?sponsor=1"
- }
- },
- "node_modules/jest/node_modules/@jest/types": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz",
- "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==",
- "dev": true,
- "dependencies": {
- "@jest/schemas": "^28.1.3",
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^3.0.0",
- "@types/node": "*",
- "@types/yargs": "^17.0.8",
- "chalk": "^4.0.0"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/jest/node_modules/@types/yargs": {
- "version": "17.0.10",
- "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.10.tgz",
- "integrity": "sha512-gmEaFwpj/7f/ROdtIlci1R1VYU1J4j95m8T+Tj3iBgiBFKg1foE/PSl93bBd5T9LDXNPo8UlNN6W0qwD8O5OaA==",
- "dev": true,
- "dependencies": {
- "@types/yargs-parser": "*"
- }
- },
"node_modules/joycon": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz",
@@ -8544,9 +5645,9 @@
}
},
"node_modules/jquery": {
- "version": "3.6.0",
- "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.0.tgz",
- "integrity": "sha512-JVzAR/AjBvVt2BmYhxRCSYysDsPcssdmTFnzyLEts9qNwmjmu4JTAMYubEfwVOSwpQ1I1sKKFcxhZCI2buerfw=="
+ "version": "3.6.1",
+ "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.1.tgz",
+ "integrity": "sha512-opJeO4nCucVnsjiXOE+/PcCgYw9Gwpvs/a6B1LL/lQhwWwpbVEVYDZ1FokFr8PRc7ghYlrFPuyHuiiDNTQxmcw=="
},
"node_modules/jquery.are-you-sure": {
"version": "1.9.0",
@@ -8559,6 +5660,12 @@
"node": ">=0.8.0"
}
},
+ "node_modules/js-sdsl": {
+ "version": "4.1.5",
+ "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.1.5.tgz",
+ "integrity": "sha512-08bOAKweV2NUC1wqTtf3qZlnpOX/R2DU9ikpjOHs0H+ibQv3zpncVQg6um4uYtRtrwIX8M4Nh3ytK4HGlYAq7Q==",
+ "dev": true
+ },
"node_modules/js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@@ -8577,27 +5684,60 @@
"js-yaml": "bin/js-yaml.js"
}
},
+ "node_modules/jsdom": {
+ "version": "20.0.1",
+ "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-20.0.1.tgz",
+ "integrity": "sha512-pksjj7Rqoa+wdpkKcLzQRHhJCEE42qQhl/xLMUKHgoSejaKOdaXEAnqs6uDNwMl/fciHTzKeR8Wm8cw7N+g98A==",
+ "dev": true,
+ "dependencies": {
+ "abab": "^2.0.6",
+ "acorn": "^8.8.0",
+ "acorn-globals": "^7.0.0",
+ "cssom": "^0.5.0",
+ "cssstyle": "^2.3.0",
+ "data-urls": "^3.0.2",
+ "decimal.js": "^10.4.1",
+ "domexception": "^4.0.0",
+ "escodegen": "^2.0.0",
+ "form-data": "^4.0.0",
+ "html-encoding-sniffer": "^3.0.0",
+ "http-proxy-agent": "^5.0.0",
+ "https-proxy-agent": "^5.0.1",
+ "is-potential-custom-element-name": "^1.0.1",
+ "nwsapi": "^2.2.2",
+ "parse5": "^7.1.1",
+ "saxes": "^6.0.0",
+ "symbol-tree": "^3.2.4",
+ "tough-cookie": "^4.1.2",
+ "w3c-xmlserializer": "^3.0.0",
+ "webidl-conversions": "^7.0.0",
+ "whatwg-encoding": "^2.0.0",
+ "whatwg-mimetype": "^3.0.0",
+ "whatwg-url": "^11.0.0",
+ "ws": "^8.9.0",
+ "xml-name-validator": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "peerDependencies": {
+ "canvas": "^2.5.0"
+ },
+ "peerDependenciesMeta": {
+ "canvas": {
+ "optional": true
+ }
+ }
+ },
"node_modules/jsep": {
- "version": "1.3.6",
- "resolved": "https://registry.npmjs.org/jsep/-/jsep-1.3.6.tgz",
- "integrity": "sha512-o7fP1eZVROIChADx7HKiwGRVI0tUqgUUGhaok6DP7cMxpDeparuooREDBDeNk2G5KIB49MBSkRYsCOu4PmZ+1w==",
+ "version": "1.3.7",
+ "resolved": "https://registry.npmjs.org/jsep/-/jsep-1.3.7.tgz",
+ "integrity": "sha512-NFbZTr1t13fPKw53swmZFKwBkEDWDnno7uLJk+a+Rw9tGDTkGgnGdZJ8A/o3gR1+XaAXmSsbpfIBIBgqRBZWDA==",
"dev": true,
"engines": {
"node": ">= 10.16.0"
}
},
- "node_modules/jsesc": {
- "version": "2.5.2",
- "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
- "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
- "dev": true,
- "bin": {
- "jsesc": "bin/jsesc"
- },
- "engines": {
- "node": ">=4"
- }
- },
"node_modules/json-parse-even-better-errors": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
@@ -8641,12 +5781,12 @@
}
},
"node_modules/jsonpath-plus": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/jsonpath-plus/-/jsonpath-plus-6.0.1.tgz",
- "integrity": "sha512-EvGovdvau6FyLexFH2OeXfIITlgIbgZoAZe3usiySeaIDm5QS+A10DKNpaPBBqqRSZr2HN6HVNXxtwUAr2apEw==",
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/jsonpath-plus/-/jsonpath-plus-7.1.0.tgz",
+ "integrity": "sha512-gTaNRsPWO/K2KY6MrqaUFClF9kmuM6MFH5Dhg1VYDODgFbByw1yb7xu3hrViE/sz+dGOeMWgCzwUwQtAnCTE9g==",
"dev": true,
"engines": {
- "node": ">=10.0.0"
+ "node": ">=12.0.0"
}
},
"node_modules/jsonpointer": {
@@ -8663,6 +5803,21 @@
"resolved": "https://registry.npmjs.org/just-extend/-/just-extend-5.1.1.tgz",
"integrity": "sha512-b+z6yF1d4EOyDgylzQo5IminlUmzSeqR1hs/bzjBNjuGras4FXq/6TrzjxfN0j+TmI0ltJzTNlqXUMCniciwKQ=="
},
+ "node_modules/katex": {
+ "version": "0.16.2",
+ "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.2.tgz",
+ "integrity": "sha512-70DJdQAyh9EMsthw3AaQlDyFf54X7nWEUIa5W+rq8XOpEk//w5Th7/8SqFqpvi/KZ2t6MHUj4f9wLmztBmAYQA==",
+ "funding": [
+ "https://opencollective.com/katex",
+ "https://github.com/sponsors/katex"
+ ],
+ "dependencies": {
+ "commander": "^8.0.0"
+ },
+ "bin": {
+ "katex": "cli.js"
+ }
+ },
"node_modules/khroma": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/khroma/-/khroma-2.0.0.tgz",
@@ -8676,15 +5831,6 @@
"node": ">=0.10.0"
}
},
- "node_modules/kleur": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz",
- "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==",
- "dev": true,
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/klona": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/klona/-/klona-2.0.5.tgz",
@@ -8725,9 +5871,9 @@
}
},
"node_modules/less-loader": {
- "version": "11.0.0",
- "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-11.0.0.tgz",
- "integrity": "sha512-9+LOWWjuoectIEx3zrfN83NAGxSUB5pWEabbbidVQVgZhN+wN68pOvuyirVlH1IK4VT1f3TmlyvAnCXh8O5KEw==",
+ "version": "11.1.0",
+ "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-11.1.0.tgz",
+ "integrity": "sha512-C+uDBV7kS7W5fJlUjq5mPBeBVhYpTIm5gB09APT9o3n/ILeaXVsiSFTbZpTJCJwQ/Crczfn3DmfQFwxYusWFug==",
"dependencies": {
"klona": "^2.0.4"
},
@@ -8842,6 +5988,15 @@
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
"dev": true
},
+ "node_modules/linkify-it": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-4.0.1.tgz",
+ "integrity": "sha512-C7bfi1UZmoj8+PQx22XyeXCuBlokoyWQL5pWSP+EI6nzRylyThouddufc2c1NDIcP9k5agmN9fLpA7VNJfIiqw==",
+ "dev": true,
+ "dependencies": {
+ "uc.micro": "^1.0.1"
+ }
+ },
"node_modules/loader-runner": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz",
@@ -8863,17 +6018,31 @@
"node": ">=8.9.0"
}
},
+ "node_modules/local-pkg": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.4.2.tgz",
+ "integrity": "sha512-mlERgSPrbxU3BP4qBqAvvwlgW4MTg78iwJdGGnv7kibKjWcJksrG3t6LB5lXI93wXRDvG4NpUgJFmTG4T6rdrg==",
+ "dev": true,
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ }
+ },
"node_modules/locate-path": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz",
- "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==",
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+ "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
"dev": true,
"dependencies": {
- "p-locate": "^2.0.0",
- "path-exists": "^3.0.0"
+ "p-locate": "^5.0.0"
},
"engines": {
- "node": ">=4"
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/lodash": {
@@ -8933,6 +6102,15 @@
"integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==",
"dev": true
},
+ "node_modules/loupe": {
+ "version": "2.3.4",
+ "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.4.tgz",
+ "integrity": "sha512-OvKfgCC2Ndby6aSTREl5aCCPTNIzlDfQZvZxNUrBrihDhL3xcrYegTblhmEiCrg2kKQz4XsFIaemE5BF4ybSaQ==",
+ "dev": true,
+ "dependencies": {
+ "get-func-name": "^2.0.0"
+ }
+ },
"node_modules/lru-cache": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
@@ -8946,44 +6124,10 @@
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz",
"integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==",
- "dev": true,
"dependencies": {
"sourcemap-codec": "^1.4.8"
}
},
- "node_modules/make-dir": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
- "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
- "dev": true,
- "dependencies": {
- "semver": "^6.0.0"
- },
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/make-dir/node_modules/semver": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
- "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
- "dev": true,
- "bin": {
- "semver": "bin/semver.js"
- }
- },
- "node_modules/makeerror": {
- "version": "1.0.12",
- "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz",
- "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==",
- "dev": true,
- "dependencies": {
- "tmpl": "1.0.5"
- }
- },
"node_modules/map-obj": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz",
@@ -8996,10 +6140,126 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/markdown-it": {
+ "version": "13.0.1",
+ "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-13.0.1.tgz",
+ "integrity": "sha512-lTlxriVoy2criHP0JKRhO2VDG9c2ypWCsT237eDiLqi09rmbKoUetyGHq2uOIRoRS//kfoJckS0eUzzkDR+k2Q==",
+ "dev": true,
+ "dependencies": {
+ "argparse": "^2.0.1",
+ "entities": "~3.0.1",
+ "linkify-it": "^4.0.1",
+ "mdurl": "^1.0.1",
+ "uc.micro": "^1.0.5"
+ },
+ "bin": {
+ "markdown-it": "bin/markdown-it.js"
+ }
+ },
+ "node_modules/markdownlint": {
+ "version": "0.26.2",
+ "resolved": "https://registry.npmjs.org/markdownlint/-/markdownlint-0.26.2.tgz",
+ "integrity": "sha512-2Am42YX2Ex5SQhRq35HxYWDfz1NLEOZWWN25nqd2h3AHRKsGRE+Qg1gt1++exW792eXTrR4jCNHfShfWk9Nz8w==",
+ "dev": true,
+ "dependencies": {
+ "markdown-it": "13.0.1"
+ },
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/markdownlint-cli": {
+ "version": "0.32.2",
+ "resolved": "https://registry.npmjs.org/markdownlint-cli/-/markdownlint-cli-0.32.2.tgz",
+ "integrity": "sha512-xmJT1rGueUgT4yGNwk6D0oqQr90UJ7nMyakXtqjgswAkEhYYqjHew9RY8wDbOmh2R270IWjuKSeZzHDEGPAUkQ==",
+ "dev": true,
+ "dependencies": {
+ "commander": "~9.4.0",
+ "get-stdin": "~9.0.0",
+ "glob": "~8.0.3",
+ "ignore": "~5.2.0",
+ "js-yaml": "^4.1.0",
+ "jsonc-parser": "~3.1.0",
+ "markdownlint": "~0.26.2",
+ "markdownlint-rule-helpers": "~0.17.2",
+ "minimatch": "~5.1.0",
+ "run-con": "~1.2.11"
+ },
+ "bin": {
+ "markdownlint": "markdownlint.js"
+ },
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/markdownlint-cli/node_modules/brace-expansion": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "dev": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/markdownlint-cli/node_modules/commander": {
+ "version": "9.4.1",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-9.4.1.tgz",
+ "integrity": "sha512-5EEkTNyHNGFPD2H+c/dXXfQZYa/scCKasxWcXJaWnNJ99pnQN9Vnmqow+p+PlFPE63Q6mThaZws1T+HxfpgtPw==",
+ "dev": true,
+ "engines": {
+ "node": "^12.20.0 || >=14"
+ }
+ },
+ "node_modules/markdownlint-cli/node_modules/glob": {
+ "version": "8.0.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz",
+ "integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==",
+ "dev": true,
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^5.0.1",
+ "once": "^1.3.0"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/markdownlint-cli/node_modules/jsonc-parser": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.1.0.tgz",
+ "integrity": "sha512-DRf0QjnNeCUds3xTjKlQQ3DpJD51GvDjJfnxUVWg6PZTo2otSm+slzNAxU/35hF8/oJIKoG9slq30JYOsF2azg==",
+ "dev": true
+ },
+ "node_modules/markdownlint-cli/node_modules/minimatch": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz",
+ "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/markdownlint-rule-helpers": {
+ "version": "0.17.2",
+ "resolved": "https://registry.npmjs.org/markdownlint-rule-helpers/-/markdownlint-rule-helpers-0.17.2.tgz",
+ "integrity": "sha512-XaeoW2NYSlWxMCZM2B3H7YTG6nlaLfkEZWMBhr4hSPlq9MuY2sy83+Xr89jXOqZMZYjvi5nBCGoFh7hHoPKZmA==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ }
+ },
"node_modules/marked": {
- "version": "4.0.18",
- "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.18.tgz",
- "integrity": "sha512-wbLDJ7Zh0sqA0Vdg6aqlbT+yPxqLblpAZh1mK2+AO2twQkPywvvqQNfEPVwSSRjZ7dZcdeVBIAgiO7MMp3Dszw==",
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/marked/-/marked-4.1.1.tgz",
+ "integrity": "sha512-0cNMnTcUJPxbA6uWmCmjWz4NJRe/0Xfk2NhXCUHjew9qJzFN20krFnsUe7QynwqOwa5m1fZ4UDg0ycKFVC0ccw==",
"bin": {
"marked": "bin/marked.js"
},
@@ -9023,6 +6283,12 @@
"integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==",
"dev": true
},
+ "node_modules/mdurl": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz",
+ "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==",
+ "dev": true
+ },
"node_modules/meow": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/meow/-/meow-9.0.0.tgz",
@@ -9106,14 +6372,6 @@
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
"dev": true
},
- "node_modules/merge-source-map": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.1.0.tgz",
- "integrity": "sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==",
- "dependencies": {
- "source-map": "^0.6.1"
- }
- },
"node_modules/merge-stream": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
@@ -9128,18 +6386,18 @@
}
},
"node_modules/mermaid": {
- "version": "9.1.3",
- "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-9.1.3.tgz",
- "integrity": "sha512-jTIYiqKwsUXVCoxHUVkK8t0QN3zSKIdJlb9thT0J5jCnzXyc+gqTbZE2QmjRfavFTPPn5eRy5zaFp7V+6RhxYg==",
+ "version": "9.1.7",
+ "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-9.1.7.tgz",
+ "integrity": "sha512-MRVHXy5FLjnUQUG7YS3UN9jEN6FXCJbFCXVGJQjVIbiR6Vhw0j/6pLIjqsiah9xoHmQU6DEaKOvB3S1g/1nBPA==",
"dependencies": {
"@braintree/sanitize-url": "^6.0.0",
"d3": "^7.0.0",
"dagre": "^0.8.5",
"dagre-d3": "^0.6.4",
- "dompurify": "2.3.8",
+ "dompurify": "2.4.0",
"graphlib": "^2.1.8",
"khroma": "^2.0.0",
- "moment-mini": "^2.24.0",
+ "moment-mini": "2.24.0",
"stylis": "^4.0.10"
}
},
@@ -9186,15 +6444,6 @@
"node": ">= 0.6"
}
},
- "node_modules/mimic-fn": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
- "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
- "dev": true,
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/min-indent": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz",
@@ -9236,7 +6485,8 @@
"node_modules/minimist": {
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
- "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q=="
+ "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==",
+ "dev": true
},
"node_modules/minimist-options": {
"version": "4.1.0",
@@ -9258,9 +6508,9 @@
"integrity": "sha512-9ARkWHBs+6YJIvrIp0Ik5tyTTtP9PoV0Ssu2Ocq5y9v8+NOOpWiRshAp8c4rZVWTOe+157on/5G+zj5pwIQFEQ=="
},
"node_modules/monaco-editor": {
- "version": "0.33.0",
- "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.33.0.tgz",
- "integrity": "sha512-VcRWPSLIUEgQJQIE0pVT8FcGBIgFoxz7jtqctE+IiCxWugD0DwgyQBcZBhdSrdMC84eumoqMZsGl2GTreOzwqw=="
+ "version": "0.34.0",
+ "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.34.0.tgz",
+ "integrity": "sha512-VF+S5zG8wxfinLKLrWcl4WUizMx+LeJrG4PM/M78OhcwocpV0jiyhX/pG6Q9jIOhrb/ckYi6nHnaR5OojlOZCQ=="
},
"node_modules/monaco-editor-webpack-plugin": {
"version": "7.0.1",
@@ -9356,6 +6606,16 @@
"lodash.topath": "^4.5.2"
}
},
+ "node_modules/nimma/node_modules/jsonpath-plus": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/jsonpath-plus/-/jsonpath-plus-6.0.1.tgz",
+ "integrity": "sha512-EvGovdvau6FyLexFH2OeXfIITlgIbgZoAZe3usiySeaIDm5QS+A10DKNpaPBBqqRSZr2HN6HVNXxtwUAr2apEw==",
+ "dev": true,
+ "optional": true,
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
"node_modules/node-fetch": {
"version": "2.6.7",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
@@ -9376,12 +6636,28 @@
}
}
},
- "node_modules/node-int64": {
- "version": "0.4.0",
- "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz",
- "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==",
+ "node_modules/node-fetch/node_modules/tr46": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
+ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
"dev": true
},
+ "node_modules/node-fetch/node_modules/webidl-conversions": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
+ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
+ "dev": true
+ },
+ "node_modules/node-fetch/node_modules/whatwg-url": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
+ "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
+ "dev": true,
+ "dependencies": {
+ "tr46": "~0.0.3",
+ "webidl-conversions": "^3.0.0"
+ }
+ },
"node_modules/node-releases": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz",
@@ -9417,18 +6693,6 @@
"node": ">=0.10.0"
}
},
- "node_modules/npm-run-path": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
- "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
- "dev": true,
- "dependencies": {
- "path-key": "^3.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
"node_modules/nth-check": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
@@ -9441,6 +6705,12 @@
"url": "https://github.com/fb55/nth-check?sponsor=1"
}
},
+ "node_modules/nwsapi": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.2.tgz",
+ "integrity": "sha512-90yv+6538zuvUMnN+zCr8LuV6bPFdq50304114vJYJ8RDyK8D5O9Phpbd6SZWgI7PwzmmfN1upeOJlvybDSgCw==",
+ "dev": true
+ },
"node_modules/object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
@@ -9468,14 +6738,14 @@
}
},
"node_modules/object.assign": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz",
- "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==",
+ "version": "4.1.4",
+ "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz",
+ "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==",
"dev": true,
"dependencies": {
- "call-bind": "^1.0.0",
- "define-properties": "^1.1.3",
- "has-symbols": "^1.0.1",
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.1.4",
+ "has-symbols": "^1.0.3",
"object-keys": "^1.1.1"
},
"engines": {
@@ -9510,21 +6780,6 @@
"wrappy": "1"
}
},
- "node_modules/onetime": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
- "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
- "dev": true,
- "dependencies": {
- "mimic-fn": "^2.1.0"
- },
- "engines": {
- "node": ">=6"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/optionator": {
"version": "0.9.1",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz",
@@ -9558,36 +6813,18 @@
}
},
"node_modules/p-locate": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz",
- "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==",
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+ "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
"dev": true,
"dependencies": {
- "p-limit": "^1.1.0"
+ "p-limit": "^3.0.2"
},
"engines": {
- "node": ">=4"
- }
- },
- "node_modules/p-locate/node_modules/p-limit": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz",
- "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==",
- "dev": true,
- "dependencies": {
- "p-try": "^1.0.0"
+ "node": ">=10"
},
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/p-locate/node_modules/p-try": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz",
- "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==",
- "dev": true,
- "engines": {
- "node": ">=4"
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/p-try": {
@@ -9618,6 +6855,29 @@
"node": ">= 8"
}
},
+ "node_modules/pac-proxy-agent/node_modules/@tootallnate/once": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz",
+ "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==",
+ "dev": true,
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/pac-proxy-agent/node_modules/http-proxy-agent": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz",
+ "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==",
+ "dev": true,
+ "dependencies": {
+ "@tootallnate/once": "1",
+ "agent-base": "6",
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
"node_modules/pac-resolver": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-5.0.1.tgz",
@@ -9644,12 +6904,6 @@
"node": ">=6"
}
},
- "node_modules/parse-cache-control": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/parse-cache-control/-/parse-cache-control-1.0.1.tgz",
- "integrity": "sha512-60zvsJReQPX5/QP0Kzfd/VrpjScIQ7SHBW6bFCYfEP+fp0Eppr1SHhIO5nd1PjZtvclzSzES9D/p5nFJurwfWg==",
- "dev": true
- },
"node_modules/parse-json": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
@@ -9687,13 +6941,36 @@
"node": ">= 0.10"
}
},
- "node_modules/path-exists": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
- "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==",
+ "node_modules/parse5": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.1.tgz",
+ "integrity": "sha512-kwpuwzB+px5WUg9pyK0IcK/shltJN5/OVhQagxhCQNtT9Y9QRZqNY2e1cmbu/paRh5LMnz/oVTVLBpjFmMZhSg==",
+ "dev": true,
+ "dependencies": {
+ "entities": "^4.4.0"
+ },
+ "funding": {
+ "url": "https://github.com/inikulin/parse5?sponsor=1"
+ }
+ },
+ "node_modules/parse5/node_modules/entities": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz",
+ "integrity": "sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==",
"dev": true,
"engines": {
- "node": ">=4"
+ "node": ">=0.12"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
+ },
+ "node_modules/path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "engines": {
+ "node": ">=8"
}
},
"node_modules/path-is-absolute": {
@@ -9726,6 +7003,15 @@
"node": ">=8"
}
},
+ "node_modules/pathval": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz",
+ "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==",
+ "dev": true,
+ "engines": {
+ "node": "*"
+ }
+ },
"node_modules/picocolors": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
@@ -9751,15 +7037,6 @@
"node": ">=6"
}
},
- "node_modules/pirates": {
- "version": "4.0.5",
- "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz",
- "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==",
- "dev": true,
- "engines": {
- "node": ">= 6"
- }
- },
"node_modules/pkg-dir": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
@@ -9819,12 +7096,16 @@
"node": ">=8"
}
},
- "node_modules/pkg-dir/node_modules/path-exists": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
- "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "node_modules/playwright-core": {
+ "version": "1.27.0",
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.27.0.tgz",
+ "integrity": "sha512-VBKaaFUVKDo3akW+o4DwbK1ZyXh46tcSwQKPK3lruh8IJd5feu55XVZx4vOkbb2uqrNdIF51sgsadYT533SdpA==",
+ "dev": true,
+ "bin": {
+ "playwright": "cli.js"
+ },
"engines": {
- "node": ">=8"
+ "node": ">=14"
}
},
"node_modules/pluralize": {
@@ -9845,20 +7126,10 @@
"node": ">=12.0.0"
}
},
- "node_modules/popper.js": {
- "version": "1.16.1",
- "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz",
- "integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==",
- "deprecated": "You can find the new Popper v2 at @popperjs/core, this package is dedicated to the legacy v1",
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/popperjs"
- }
- },
"node_modules/postcss": {
- "version": "8.4.14",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz",
- "integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==",
+ "version": "8.4.17",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.17.tgz",
+ "integrity": "sha512-UNxNOLQydcOFi41yHNMcKRZ39NeXlr8AxGuZJsdub8vIb12fHzcq37DTU/QtbI6WLxNg2gF9Z+8qtRwTj1UI1Q==",
"funding": [
{
"type": "opencollective",
@@ -9999,48 +7270,6 @@
"node": ">= 0.8.0"
}
},
- "node_modules/prettier": {
- "version": "2.7.1",
- "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz",
- "integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==",
- "optional": true,
- "bin": {
- "prettier": "bin-prettier.js"
- },
- "engines": {
- "node": ">=10.13.0"
- },
- "funding": {
- "url": "https://github.com/prettier/prettier?sponsor=1"
- }
- },
- "node_modules/pretty-format": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz",
- "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==",
- "dev": true,
- "dependencies": {
- "@jest/schemas": "^28.1.3",
- "ansi-regex": "^5.0.1",
- "ansi-styles": "^5.0.0",
- "react-is": "^18.0.0"
- },
- "engines": {
- "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0"
- }
- },
- "node_modules/pretty-format/node_modules/ansi-styles": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
- "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
- "dev": true,
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/chalk/ansi-styles?sponsor=1"
- }
- },
"node_modules/pretty-ms": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-8.0.0.tgz",
@@ -10061,34 +7290,6 @@
"integrity": "sha512-dKp+C4iXWK4vVYZmYSd0KBH5F/h1HoZRsbJ82AVKRO3PEo8L4lBS/vLwhVtpwwuYcoIsVY+1JYKR268yn480uQ==",
"dev": true
},
- "node_modules/process-nextick-args": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
- "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
- "dev": true
- },
- "node_modules/promise": {
- "version": "8.1.0",
- "resolved": "https://registry.npmjs.org/promise/-/promise-8.1.0.tgz",
- "integrity": "sha512-W04AqnILOL/sPRXziNicCjSNRruLAuIHEOVBazepu0545DDNGYHz7ar9ZgZ1fMU8/MA4mVxp5rkBWRi6OXIy3Q==",
- "dev": true,
- "dependencies": {
- "asap": "~2.0.6"
- }
- },
- "node_modules/prompts": {
- "version": "2.4.2",
- "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz",
- "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==",
- "dev": true,
- "dependencies": {
- "kleur": "^3.0.3",
- "sisteransi": "^1.0.5"
- },
- "engines": {
- "node": ">= 6"
- }
- },
"node_modules/proxy-agent": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-5.0.0.tgz",
@@ -10108,6 +7309,29 @@
"node": ">= 8"
}
},
+ "node_modules/proxy-agent/node_modules/@tootallnate/once": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz",
+ "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==",
+ "dev": true,
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/proxy-agent/node_modules/http-proxy-agent": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz",
+ "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==",
+ "dev": true,
+ "dependencies": {
+ "@tootallnate/once": "1",
+ "agent-base": "6",
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
"node_modules/proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
@@ -10120,10 +7344,11 @@
"integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==",
"optional": true
},
- "node_modules/pseudomap": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
- "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ=="
+ "node_modules/psl": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz",
+ "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==",
+ "dev": true
},
"node_modules/punycode": {
"version": "2.1.1",
@@ -10133,20 +7358,11 @@
"node": ">=6"
}
},
- "node_modules/qs": {
- "version": "6.11.0",
- "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
- "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
- "dev": true,
- "dependencies": {
- "side-channel": "^1.0.4"
- },
- "engines": {
- "node": ">=0.6"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
+ "node_modules/querystringify": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
+ "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==",
+ "dev": true
},
"node_modules/queue-microtask": {
"version": "1.2.3",
@@ -10211,12 +7427,6 @@
"node": ">=0.10.0"
}
},
- "node_modules/react-is": {
- "version": "18.2.0",
- "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
- "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==",
- "dev": true
- },
"node_modules/read-pkg": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz",
@@ -10301,15 +7511,6 @@
"node": ">=8"
}
},
- "node_modules/read-pkg-up/node_modules/path-exists": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
- "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
- "dev": true,
- "engines": {
- "node": ">=8"
- }
- },
"node_modules/read-pkg-up/node_modules/type-fest": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz",
@@ -10364,11 +7565,6 @@
"node": ">=8"
}
},
- "node_modules/regenerator-runtime": {
- "version": "0.13.9",
- "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz",
- "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA=="
- },
"node_modules/regexp-tree": {
"version": "0.1.24",
"resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.24.tgz",
@@ -10424,6 +7620,12 @@
"node": ">=0.10.0"
}
},
+ "node_modules/requires-port": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
+ "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==",
+ "dev": true
+ },
"node_modules/reserved": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/reserved/-/reserved-0.1.2.tgz",
@@ -10477,15 +7679,6 @@
"node": ">=4"
}
},
- "node_modules/resolve.exports": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.0.tgz",
- "integrity": "sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ==",
- "dev": true,
- "engines": {
- "node": ">=10"
- }
- },
"node_modules/reusify": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
@@ -10516,11 +7709,10 @@
"integrity": "sha512-ndEIpszUHiG4HtDsQLeIuMvRsDnn8c8rYStabochtUeCvfuvNptb5TUbVD68LRAILPX7p9nqQGh4xJgn3EHS/g=="
},
"node_modules/rollup": {
- "version": "2.77.0",
- "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.77.0.tgz",
- "integrity": "sha512-vL8xjY4yOQEw79DvyXLijhnhh+R/O9zpF/LEgkCebZFtb6ELeN9H3/2T0r8+mp+fFTBHZ5qGpOpW2ela2zRt3g==",
+ "version": "2.79.1",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz",
+ "integrity": "sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==",
"dev": true,
- "peer": true,
"bin": {
"rollup": "dist/bin/rollup"
},
@@ -10531,6 +7723,21 @@
"fsevents": "~2.3.2"
}
},
+ "node_modules/run-con": {
+ "version": "1.2.11",
+ "resolved": "https://registry.npmjs.org/run-con/-/run-con-1.2.11.tgz",
+ "integrity": "sha512-NEMGsUT+cglWkzEr4IFK21P4Jca45HqiAbIIZIBdX5+UZTB24Mb/21iNGgz9xZa8tL6vbW7CXmq7MFN42+VjNQ==",
+ "dev": true,
+ "dependencies": {
+ "deep-extend": "^0.6.0",
+ "ini": "~3.0.0",
+ "minimist": "^1.2.6",
+ "strip-json-comments": "~3.1.1"
+ },
+ "bin": {
+ "run-con": "cli.js"
+ }
+ },
"node_modules/run-parallel": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
@@ -10572,6 +7779,20 @@
"regexp-tree": "~0.1.1"
}
},
+ "node_modules/safe-regex-test": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz",
+ "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==",
+ "dev": true,
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "get-intrinsic": "^1.1.3",
+ "is-regex": "^1.1.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/safe-stable-stringify": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-1.1.1.tgz",
@@ -10589,6 +7810,18 @@
"integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==",
"optional": true
},
+ "node_modules/saxes": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz",
+ "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==",
+ "dev": true,
+ "dependencies": {
+ "xmlchars": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=v12.22.7"
+ }
+ },
"node_modules/schema-utils": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz",
@@ -10608,9 +7841,9 @@
}
},
"node_modules/semver": {
- "version": "7.3.7",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz",
- "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==",
+ "version": "7.3.8",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz",
+ "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==",
"dependencies": {
"lru-cache": "^6.0.0"
},
@@ -10713,12 +7946,6 @@
"node": ">=12"
}
},
- "node_modules/sisteransi": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz",
- "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==",
- "dev": true
- },
"node_modules/slash": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
@@ -10756,9 +7983,9 @@
}
},
"node_modules/socks": {
- "version": "2.7.0",
- "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.0.tgz",
- "integrity": "sha512-scnOe9y4VuiNUULJN72GrM26BNOjVsfPXI+j+98PkyEfsIXroa5ofyjT+FzGvn/xHs73U2JtoBYAVx9Hl4quSA==",
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz",
+ "integrity": "sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==",
"dev": true,
"dependencies": {
"ip": "^2.0.0",
@@ -10815,21 +8042,10 @@
"node": ">=0.10.0"
}
},
- "node_modules/source-map-support": {
- "version": "0.5.13",
- "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz",
- "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==",
- "dev": true,
- "dependencies": {
- "buffer-from": "^1.0.0",
- "source-map": "^0.6.0"
- }
- },
"node_modules/sourcemap-codec": {
"version": "1.4.8",
"resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
- "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==",
- "dev": true
+ "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA=="
},
"node_modules/spdx-compare": {
"version": "1.0.0",
@@ -10874,9 +8090,9 @@
}
},
"node_modules/spdx-license-ids": {
- "version": "3.0.11",
- "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz",
- "integrity": "sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g=="
+ "version": "3.0.12",
+ "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.12.tgz",
+ "integrity": "sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA=="
},
"node_modules/spdx-ranges": {
"version": "2.1.1",
@@ -10893,12 +8109,6 @@
"spdx-ranges": "^2.0.0"
}
},
- "node_modules/sprintf-js": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
- "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",
- "dev": true
- },
"node_modules/stable": {
"version": "0.1.8",
"resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz",
@@ -10906,27 +8116,6 @@
"deprecated": "Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility",
"dev": true
},
- "node_modules/stack-utils": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.5.tgz",
- "integrity": "sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA==",
- "dev": true,
- "dependencies": {
- "escape-string-regexp": "^2.0.0"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/stack-utils/node_modules/escape-string-regexp": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz",
- "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==",
- "dev": true,
- "engines": {
- "node": ">=8"
- }
- },
"node_modules/stacktracey": {
"version": "2.1.8",
"resolved": "https://registry.npmjs.org/stacktracey/-/stacktracey-2.1.8.tgz",
@@ -10952,19 +8141,6 @@
"integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==",
"dev": true
},
- "node_modules/string-length": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz",
- "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==",
- "dev": true,
- "dependencies": {
- "char-regex": "^1.0.2",
- "strip-ansi": "^6.0.0"
- },
- "engines": {
- "node": ">=10"
- }
- },
"node_modules/string-width": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
@@ -11017,24 +8193,6 @@
"node": ">=8"
}
},
- "node_modules/strip-bom": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz",
- "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==",
- "dev": true,
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/strip-final-newline": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz",
- "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==",
- "dev": true,
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/strip-indent": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz",
@@ -11059,6 +8217,18 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/strip-literal": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-0.4.2.tgz",
+ "integrity": "sha512-pv48ybn4iE1O9RLgCAN0iU4Xv7RlBTiit6DKmMiErbs9x1wH6vXBs45tWc0H5wUIF6TLTrKweqkmYF/iraQKNw==",
+ "dev": true,
+ "dependencies": {
+ "acorn": "^8.8.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ }
+ },
"node_modules/style-search": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/style-search/-/style-search-0.1.0.tgz",
@@ -11066,22 +8236,20 @@
"dev": true
},
"node_modules/stylelint": {
- "version": "14.9.1",
- "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-14.9.1.tgz",
- "integrity": "sha512-RdAkJdPiLqHawCSnu21nE27MjNXaVd4WcOHA4vK5GtIGjScfhNnaOuWR2wWdfKFAvcWQPOYe311iveiVKSmwsA==",
+ "version": "14.13.0",
+ "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-14.13.0.tgz",
+ "integrity": "sha512-NJSAdloiAB/jgVJKxMR90mWlctvmeBFGFVUvyKngi9+j/qPSJ5ZB+u8jOmGbLTnS7OHrII9NFGehPRyar8U5vg==",
"dev": true,
"dependencies": {
- "@csstools/selector-specificity": "^2.0.1",
+ "@csstools/selector-specificity": "^2.0.2",
"balanced-match": "^2.0.0",
- "colord": "^2.9.2",
+ "colord": "^2.9.3",
"cosmiconfig": "^7.0.1",
"css-functions-list": "^3.1.0",
"debug": "^4.3.4",
- "execall": "^2.0.0",
- "fast-glob": "^3.2.11",
- "fastest-levenshtein": "^1.0.12",
+ "fast-glob": "^3.2.12",
+ "fastest-levenshtein": "^1.0.16",
"file-entry-cache": "^6.0.1",
- "get-stdin": "^8.0.0",
"global-modules": "^2.0.0",
"globby": "^11.1.0",
"globjoin": "^0.1.4",
@@ -11096,7 +8264,7 @@
"micromatch": "^4.0.5",
"normalize-path": "^3.0.0",
"picocolors": "^1.0.0",
- "postcss": "^8.4.14",
+ "postcss": "^8.4.16",
"postcss-media-query-parser": "^0.2.3",
"postcss-resolve-nested-selector": "^0.1.1",
"postcss-safe-parser": "^6.0.0",
@@ -11106,11 +8274,11 @@
"string-width": "^4.2.3",
"strip-ansi": "^6.0.1",
"style-search": "^0.1.0",
- "supports-hyperlinks": "^2.2.0",
+ "supports-hyperlinks": "^2.3.0",
"svg-tags": "^1.0.0",
"table": "^6.8.0",
"v8-compile-cache": "^2.3.0",
- "write-file-atomic": "^4.0.1"
+ "write-file-atomic": "^4.0.2"
},
"bin": {
"stylelint": "bin/stylelint.js"
@@ -11124,24 +8292,24 @@
}
},
"node_modules/stylelint-config-recommended": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-8.0.0.tgz",
- "integrity": "sha512-IK6dWvE000+xBv9jbnHOnBq01gt6HGVB2ZTsot+QsMpe82doDQ9hvplxfv4YnpEuUwVGGd9y6nbaAnhrjcxhZQ==",
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-9.0.0.tgz",
+ "integrity": "sha512-9YQSrJq4NvvRuTbzDsWX3rrFOzOlYBmZP+o513BJN/yfEmGSr0AxdvrWs0P/ilSpVV/wisamAHu5XSk8Rcf4CQ==",
"dev": true,
"peerDependencies": {
- "stylelint": "^14.8.0"
+ "stylelint": "^14.10.0"
}
},
"node_modules/stylelint-config-standard": {
- "version": "26.0.0",
- "resolved": "https://registry.npmjs.org/stylelint-config-standard/-/stylelint-config-standard-26.0.0.tgz",
- "integrity": "sha512-hUuB7LaaqM8abvkOO84wh5oYSkpXgTzHu2Zza6e7mY+aOmpNTjoFBRxSLlzY0uAOMWEFx0OMKzr+reG1BUtcqQ==",
+ "version": "28.0.0",
+ "resolved": "https://registry.npmjs.org/stylelint-config-standard/-/stylelint-config-standard-28.0.0.tgz",
+ "integrity": "sha512-q/StuowDdDmFCravzGHAwgS9pjX0bdOQUEBBDIkIWsQuYGgYz/xsO8CM6eepmIQ1fc5bKdDVimlJZ6MoOUcJ5Q==",
"dev": true,
"dependencies": {
- "stylelint-config-recommended": "^8.0.0"
+ "stylelint-config-recommended": "^9.0.0"
},
"peerDependencies": {
- "stylelint": "^14.9.0"
+ "stylelint": "^14.11.0"
}
},
"node_modules/stylelint/node_modules/balanced-match": {
@@ -11160,9 +8328,9 @@
}
},
"node_modules/stylis": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.1.1.tgz",
- "integrity": "sha512-lVrM/bNdhVX2OgBFNa2YJ9Lxj7kPzylieHd3TNjuGE0Re9JB7joL5VUKOVH1kdNNJTgGPpT8hmwIAPLaSyEVFQ=="
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.1.2.tgz",
+ "integrity": "sha512-Nn2CCrG2ZaFziDxaZPN43CXqn+j7tcdjPFCkRBkFue8QYXC2HdEwnw5TCBo4yQZ2WxKYeSi0fdoOrtEqgDrXbA=="
},
"node_modules/superstruct": {
"version": "0.10.13",
@@ -11173,7 +8341,6 @@
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
- "dev": true,
"dependencies": {
"has-flag": "^4.0.0"
},
@@ -11182,9 +8349,9 @@
}
},
"node_modules/supports-hyperlinks": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz",
- "integrity": "sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ==",
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz",
+ "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==",
"dev": true,
"dependencies": {
"has-flag": "^4.0.0",
@@ -11232,33 +8399,25 @@
"node": ">=10.13.0"
}
},
- "node_modules/swagger-ui-dist": {
- "version": "4.13.0",
- "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-4.13.0.tgz",
- "integrity": "sha512-5yqhkUU9uV5oT/MTMBeSgDGI0Vx6eCOU43AszQBs88poI8OB1v+FoXEFHv+NaBbEfTkXCMWlAJrH6iWyDzLETQ=="
- },
- "node_modules/sync-request": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/sync-request/-/sync-request-6.1.0.tgz",
- "integrity": "sha512-8fjNkrNlNCrVc/av+Jn+xxqfCjYaBoHqCsDz6mt030UMxJGr+GSfCV1dQt2gRtlL63+VPidwDVLr7V2OcTSdRw==",
+ "node_modules/svgo/node_modules/commander": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
+ "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
"dev": true,
- "dependencies": {
- "http-response-object": "^3.0.1",
- "sync-rpc": "^1.2.1",
- "then-request": "^6.0.0"
- },
"engines": {
- "node": ">=8.0.0"
+ "node": ">= 10"
}
},
- "node_modules/sync-rpc": {
- "version": "1.3.6",
- "resolved": "https://registry.npmjs.org/sync-rpc/-/sync-rpc-1.3.6.tgz",
- "integrity": "sha512-J8jTXuZzRlvU7HemDgHi3pGnh/rkoqR/OZSjhTyyZrEkkYQbk7Z33AXp37mkPfPpfdOuj7Ex3H/TJM1z48uPQw==",
- "dev": true,
- "dependencies": {
- "get-port": "^3.1.0"
- }
+ "node_modules/swagger-ui-dist": {
+ "version": "4.14.2",
+ "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-4.14.2.tgz",
+ "integrity": "sha512-kOIU7Ts3TrXDLb3/c9jRe4qGp8O3bRT19FFJA8wJfrRFkcK/4atPn3krhtBVJ57ZkNNofworXHxuYwmaisXBdg=="
+ },
+ "node_modules/symbol-tree": {
+ "version": "3.2.4",
+ "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
+ "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==",
+ "dev": true
},
"node_modules/table": {
"version": "6.8.0",
@@ -11284,26 +8443,10 @@
"node": ">=6"
}
},
- "node_modules/terminal-link": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz",
- "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==",
- "dev": true,
- "dependencies": {
- "ansi-escapes": "^4.2.1",
- "supports-hyperlinks": "^2.0.0"
- },
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/terser": {
- "version": "5.14.2",
- "resolved": "https://registry.npmjs.org/terser/-/terser-5.14.2.tgz",
- "integrity": "sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==",
+ "version": "5.15.1",
+ "resolved": "https://registry.npmjs.org/terser/-/terser-5.15.1.tgz",
+ "integrity": "sha512-K1faMUvpm/FBxjBXud0LWVAGxmvoPbZbfTCYbSgaaYQaIXI3/TdI7a7ZGA73Zrou6Q8Zmz3oeUTsp/dj+ag2Xw==",
"dependencies": {
"@jridgewell/source-map": "^0.3.2",
"acorn": "^8.5.0",
@@ -11318,15 +8461,15 @@
}
},
"node_modules/terser-webpack-plugin": {
- "version": "5.3.3",
- "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.3.tgz",
- "integrity": "sha512-Fx60G5HNYknNTNQnzQ1VePRuu89ZVYWfjRAeT5rITuCY/1b08s49e5kSQwHDirKZWuoKOBRFS98EUUoZ9kLEwQ==",
+ "version": "5.3.6",
+ "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.6.tgz",
+ "integrity": "sha512-kfLFk+PoLUQIbLmB1+PZDMRSZS99Mp+/MHqDNmMA6tOItzRt+Npe3E+fsMs5mfcM0wCtrrdU387UnV+vnSffXQ==",
"dependencies": {
- "@jridgewell/trace-mapping": "^0.3.7",
+ "@jridgewell/trace-mapping": "^0.3.14",
"jest-worker": "^27.4.5",
"schema-utils": "^3.1.1",
"serialize-javascript": "^6.0.0",
- "terser": "^5.7.2"
+ "terser": "^5.14.1"
},
"engines": {
"node": ">= 10.13.0"
@@ -11436,53 +8579,35 @@
"source-map": "^0.6.0"
}
},
- "node_modules/test-exclude": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz",
- "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==",
- "dev": true,
- "dependencies": {
- "@istanbuljs/schema": "^0.1.2",
- "glob": "^7.1.4",
- "minimatch": "^3.0.4"
- },
- "engines": {
- "node": ">=8"
- }
- },
"node_modules/text-table": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
"integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
"dev": true
},
- "node_modules/then-request": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/then-request/-/then-request-6.0.2.tgz",
- "integrity": "sha512-3ZBiG7JvP3wbDzA9iNY5zJQcHL4jn/0BWtXIkagfz7QgOL/LqjCEOBQuJNZfu0XYnv5JhKh+cDxCPM4ILrqruA==",
+ "node_modules/tinybench": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.3.0.tgz",
+ "integrity": "sha512-zs1gMVBwyyG2QbVchYIbnabRhMOCGvrwZz/q+SV+LIMa9q5YDQZi2kkI6ZRqV2Bz7ba1uvrc7ieUoE4KWnGeKg==",
+ "dev": true
+ },
+ "node_modules/tinypool": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.3.0.tgz",
+ "integrity": "sha512-NX5KeqHOBZU6Bc0xj9Vr5Szbb1j8tUHIeD18s41aDJaPeC5QTdEhK0SpdpUrZlj2nv5cctNcSjaKNanXlfcVEQ==",
"dev": true,
- "dependencies": {
- "@types/concat-stream": "^1.6.0",
- "@types/form-data": "0.0.33",
- "@types/node": "^8.0.0",
- "@types/qs": "^6.2.31",
- "caseless": "~0.12.0",
- "concat-stream": "^1.6.0",
- "form-data": "^2.2.0",
- "http-basic": "^8.1.1",
- "http-response-object": "^3.0.1",
- "promise": "^8.0.0",
- "qs": "^6.4.0"
- },
"engines": {
- "node": ">=6.0.0"
+ "node": ">=14.0.0"
}
},
- "node_modules/then-request/node_modules/@types/node": {
- "version": "8.10.66",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.66.tgz",
- "integrity": "sha512-tktOkFUA4kXx2hhhrB8bIFb5TbwzS4uOhKEmwiD+NoiL0qtP2OQ9mFldbgD4dV1djrlBYP6eBuQZiWjuHUpqFw==",
- "dev": true
+ "node_modules/tinyspy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-1.0.2.tgz",
+ "integrity": "sha512-bSGlgwLBYf7PnUsQ6WOc6SJ3pGOcd+d8AA6EUnLDDM0kWEstC1JIlSZA3UNliDXhd9ABoS7hiRBDCu+XP/sf1Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=14.0.0"
+ }
},
"node_modules/tippy.js": {
"version": "6.3.7",
@@ -11492,21 +8617,6 @@
"@popperjs/core": "^2.9.0"
}
},
- "node_modules/tmpl": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz",
- "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==",
- "dev": true
- },
- "node_modules/to-fast-properties": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
- "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==",
- "dev": true,
- "engines": {
- "node": ">=4"
- }
- },
"node_modules/to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
@@ -11527,11 +8637,41 @@
"node": ">=0.6"
}
},
+ "node_modules/tough-cookie": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.2.tgz",
+ "integrity": "sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ==",
+ "dev": true,
+ "dependencies": {
+ "psl": "^1.1.33",
+ "punycode": "^2.1.1",
+ "universalify": "^0.2.0",
+ "url-parse": "^1.5.3"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/tough-cookie/node_modules/universalify": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz",
+ "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==",
+ "dev": true,
+ "engines": {
+ "node": ">= 4.0.0"
+ }
+ },
"node_modules/tr46": {
- "version": "0.0.3",
- "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
- "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
- "dev": true
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz",
+ "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==",
+ "dev": true,
+ "dependencies": {
+ "punycode": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=12"
+ }
},
"node_modules/tributejs": {
"version": "5.1.3",
@@ -11618,16 +8758,16 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/typedarray": {
- "version": "0.0.6",
- "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
- "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==",
- "dev": true
- },
"node_modules/typo-js": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/typo-js/-/typo-js-1.2.1.tgz",
- "integrity": "sha512-bTGLjbD3WqZDR3CgEFkyi9Q/SS2oM29ipXrWfDb4M74ea69QwKAECVceYpaBu0GfdnASMg9Qfl67ttB23nePHg=="
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/typo-js/-/typo-js-1.2.2.tgz",
+ "integrity": "sha512-C7pYBQK17EjSg8tVNY91KHdUt5Nf6FMJ+c3js076quPmBML57PmNMzAcIq/2kf/hSYtFABNDIYNYlJRl5BJhGw=="
+ },
+ "node_modules/uc.micro": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz",
+ "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==",
+ "dev": true
},
"node_modules/uint8-to-base64": {
"version": "0.2.0",
@@ -11668,9 +8808,9 @@
}
},
"node_modules/update-browserslist-db": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.5.tgz",
- "integrity": "sha512-dteFFpCyvuDdr9S/ff1ISkKt/9YZxKjI9WlRR99c180GaztJtRa/fn18FdxGVKVsnPY7/a/FDN68mcvUmP4U7Q==",
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz",
+ "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==",
"funding": [
{
"type": "opencollective",
@@ -11693,9 +8833,9 @@
}
},
"node_modules/updates": {
- "version": "13.1.2",
- "resolved": "https://registry.npmjs.org/updates/-/updates-13.1.2.tgz",
- "integrity": "sha512-wixXdKufbYwxKFMqWmkjnf6vlkZ8Lpx8fWYFrkxawNO9j7xlGQHCtbqW7LHkl/+tl57fFlvgvQ5dAIrseqk3Qw==",
+ "version": "13.1.8",
+ "resolved": "https://registry.npmjs.org/updates/-/updates-13.1.8.tgz",
+ "integrity": "sha512-hcDCkif1i4MWArx1QkJjLHyPCr8taYyzoypQ7ST/vbmNJyH/cpiawQf+5frvH5uekiSSwtuQNWKdFWB3UH4sew==",
"dev": true,
"bin": {
"updates": "bin/updates.js"
@@ -11718,6 +8858,16 @@
"integrity": "sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ==",
"dev": true
},
+ "node_modules/url-parse": {
+ "version": "1.5.10",
+ "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
+ "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==",
+ "dev": true,
+ "dependencies": {
+ "querystringify": "^2.1.1",
+ "requires-port": "^1.0.0"
+ }
+ },
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
@@ -11732,37 +8882,12 @@
"node": ">= 4"
}
},
- "node_modules/v-tooltip": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/v-tooltip/-/v-tooltip-2.1.3.tgz",
- "integrity": "sha512-xXngyxLQTOx/yUEy50thb8te7Qo4XU6h4LZB6cvEfVd9mnysUxLEoYwGWDdqR+l69liKsy3IPkdYff3J1gAJ5w==",
- "dependencies": {
- "@babel/runtime": "^7.13.10",
- "lodash": "^4.17.21",
- "popper.js": "^1.16.1",
- "vue-resize": "^1.0.1"
- }
- },
"node_modules/v8-compile-cache": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz",
"integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==",
"dev": true
},
- "node_modules/v8-to-istanbul": {
- "version": "9.0.1",
- "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.0.1.tgz",
- "integrity": "sha512-74Y4LqY74kLE6IFyIjPtkSTWzUZmj8tdHT9Ii/26dvQ6K9Dl2NbEfj0XgU2sHCtKgt5VupqhlO/5aWuqS+IY1w==",
- "dev": true,
- "dependencies": {
- "@jridgewell/trace-mapping": "^0.3.12",
- "@types/istanbul-lib-coverage": "^2.0.1",
- "convert-source-map": "^1.6.0"
- },
- "engines": {
- "node": ">=10.12.0"
- }
- },
"node_modules/validate-npm-package-license": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
@@ -11782,10 +8907,118 @@
"builtins": "^1.0.3"
}
},
+ "node_modules/vite": {
+ "version": "3.1.8",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-3.1.8.tgz",
+ "integrity": "sha512-m7jJe3nufUbuOfotkntGFupinL/fmuTNuQmiVE7cH2IZMuf4UbfbGYMUT3jVWgGYuRVLY9j8NnrRqgw5rr5QTg==",
+ "dev": true,
+ "dependencies": {
+ "esbuild": "^0.15.9",
+ "postcss": "^8.4.16",
+ "resolve": "^1.22.1",
+ "rollup": "~2.78.0"
+ },
+ "bin": {
+ "vite": "bin/vite.js"
+ },
+ "engines": {
+ "node": "^14.18.0 || >=16.0.0"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.2"
+ },
+ "peerDependencies": {
+ "less": "*",
+ "sass": "*",
+ "stylus": "*",
+ "terser": "^5.4.0"
+ },
+ "peerDependenciesMeta": {
+ "less": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ },
+ "stylus": {
+ "optional": true
+ },
+ "terser": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vite/node_modules/rollup": {
+ "version": "2.78.1",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.78.1.tgz",
+ "integrity": "sha512-VeeCgtGi4P+o9hIg+xz4qQpRl6R401LWEXBmxYKOV4zlF82lyhgh2hTZnheFUbANE8l2A41F458iwj2vEYaXJg==",
+ "dev": true,
+ "bin": {
+ "rollup": "dist/bin/rollup"
+ },
+ "engines": {
+ "node": ">=10.0.0"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/vitest": {
+ "version": "0.24.1",
+ "resolved": "https://registry.npmjs.org/vitest/-/vitest-0.24.1.tgz",
+ "integrity": "sha512-NKkK1xnDIOOr42pKBfGQQl6b6IWdFVBpG6ZS1T+nUlJuqcOiZ7lxjVwHy9wrtTYpJ0BWww9y6bSGYXubD29Nag==",
+ "dev": true,
+ "dependencies": {
+ "@types/chai": "^4.3.3",
+ "@types/chai-subset": "^1.3.3",
+ "@types/node": "*",
+ "chai": "^4.3.6",
+ "debug": "^4.3.4",
+ "local-pkg": "^0.4.2",
+ "strip-literal": "^0.4.2",
+ "tinybench": "^2.3.0",
+ "tinypool": "^0.3.0",
+ "tinyspy": "^1.0.2",
+ "vite": "^3.0.0"
+ },
+ "bin": {
+ "vitest": "vitest.mjs"
+ },
+ "engines": {
+ "node": ">=v14.16.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ },
+ "peerDependencies": {
+ "@edge-runtime/vm": "*",
+ "@vitest/browser": "*",
+ "@vitest/ui": "*",
+ "happy-dom": "*",
+ "jsdom": "*"
+ },
+ "peerDependenciesMeta": {
+ "@edge-runtime/vm": {
+ "optional": true
+ },
+ "@vitest/browser": {
+ "optional": true
+ },
+ "@vitest/ui": {
+ "optional": true
+ },
+ "happy-dom": {
+ "optional": true
+ },
+ "jsdom": {
+ "optional": true
+ }
+ }
+ },
"node_modules/vm2": {
- "version": "3.9.10",
- "resolved": "https://registry.npmjs.org/vm2/-/vm2-3.9.10.tgz",
- "integrity": "sha512-AuECTSvwu2OHLAZYhG716YzwodKCIJxB6u1zG7PgSQwIgAlEaoXH52bxdcvT8GkGjnYK7r7yWDW0m0sOsPuBjQ==",
+ "version": "3.9.11",
+ "resolved": "https://registry.npmjs.org/vm2/-/vm2-3.9.11.tgz",
+ "integrity": "sha512-PFG8iJRSjvvBdisowQ7iVF580DXb1uCIiGaXgm7tynMR1uTBlv7UJlB1zdv5KJ+Tmq1f0Upnj3fayoEOPpCBKg==",
"dev": true,
"dependencies": {
"acorn": "^8.7.0",
@@ -11799,31 +9032,30 @@
}
},
"node_modules/vue": {
- "version": "2.6.14",
- "resolved": "https://registry.npmjs.org/vue/-/vue-2.6.14.tgz",
- "integrity": "sha512-x2284lgYvjOMj3Za7kqzRcUSxBboHqtgRE2zlos1qWaOye5yUmHn42LB1250NJBLRwEcdrB0JRwyPTEPhfQjiQ=="
- },
- "node_modules/vue-bar-graph": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/vue-bar-graph/-/vue-bar-graph-1.3.1.tgz",
- "integrity": "sha512-C0U594QoEI91PuXIrygfIRDRPDrpICrsJ0iYxuJJzDUENpWqahZGsqZZj8XRJGXsPUI8Ri1rIo91uaNI/ht79w==",
+ "version": "3.2.40",
+ "resolved": "https://registry.npmjs.org/vue/-/vue-3.2.40.tgz",
+ "integrity": "sha512-1mGHulzUbl2Nk3pfvI5aXYYyJUs1nm4kyvuz38u4xlQkLUn1i2R7nDbI4TufECmY8v1qNBHYy62bCaM+3cHP2A==",
"dependencies": {
- "gsap": "^3.6.1",
- "vue": "^2.6.12"
+ "@vue/compiler-dom": "3.2.40",
+ "@vue/compiler-sfc": "3.2.40",
+ "@vue/runtime-dom": "3.2.40",
+ "@vue/server-renderer": "3.2.40",
+ "@vue/shared": "3.2.40"
}
},
- "node_modules/vue-calendar-heatmap": {
- "version": "0.8.4",
- "resolved": "https://registry.npmjs.org/vue-calendar-heatmap/-/vue-calendar-heatmap-0.8.4.tgz",
- "integrity": "sha512-Hx7OYBY1ghUIxKmFIIzpLT4XlcrwnI3WpadJEj/sKj5quoxwEuSDKmf94v0zWOHeQ/2CrB1G66geaKR/O56+OQ==",
+ "node_modules/vue-bar-graph": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/vue-bar-graph/-/vue-bar-graph-2.0.0.tgz",
+ "integrity": "sha512-IoYP+r5Ggjys6QdUNYFPh7qD41wi/uDOJj9nMawvDgvV6niOz3Dw8O2/98ZnUgjTpcgcGFDaaAaK6qa9x1jgpw==",
"dependencies": {
- "v-tooltip": "^2.0.0-rc.32"
+ "gsap": "^3.10.4",
+ "vue": "^3.2.37"
}
},
"node_modules/vue-eslint-parser": {
- "version": "9.0.3",
- "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.0.3.tgz",
- "integrity": "sha512-yL+ZDb+9T0ELG4VIFo/2anAOz8SvBdlqEnQnvJ3M7Scq56DvtjY0VY88bByRZB0D4J0u8olBcfrXTVONXsh4og==",
+ "version": "9.1.0",
+ "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.1.0.tgz",
+ "integrity": "sha512-NGn/iQy8/Wb7RrRa4aRkokyCZfOUWk19OP5HP6JEozQFX5AoS/t+Z0ZN7FY4LlmWc4FNI922V7cvX28zctN8dQ==",
"dev": true,
"dependencies": {
"debug": "^4.3.4",
@@ -11844,124 +9076,43 @@
"eslint": ">=6.0.0"
}
},
- "node_modules/vue-hot-reload-api": {
- "version": "2.3.4",
- "resolved": "https://registry.npmjs.org/vue-hot-reload-api/-/vue-hot-reload-api-2.3.4.tgz",
- "integrity": "sha512-BXq3jwIagosjgNVae6tkHzzIk6a8MHFtzAdwhnV5VlvPTFxDCvIttgSiHWjdGoTJvXtmRu5HacExfdarRcFhog=="
- },
"node_modules/vue-loader": {
- "version": "15.9.8",
- "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-15.9.8.tgz",
- "integrity": "sha512-GwSkxPrihfLR69/dSV3+5CdMQ0D+jXg8Ma1S4nQXKJAznYFX14vHdc/NetQc34Dw+rBbIJyP7JOuVb9Fhprvog==",
+ "version": "17.0.0",
+ "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-17.0.0.tgz",
+ "integrity": "sha512-OWSXjrzIvbF2LtOUmxT3HYgwwubbfFelN8PAP9R9dwpIkj48TVioHhWWSx7W7fk+iF5cgg3CBJRxwTdtLU4Ecg==",
"dependencies": {
- "@vue/component-compiler-utils": "^3.1.0",
- "hash-sum": "^1.0.2",
- "loader-utils": "^1.1.0",
- "vue-hot-reload-api": "^2.3.0",
- "vue-style-loader": "^4.1.0"
+ "chalk": "^4.1.0",
+ "hash-sum": "^2.0.0",
+ "loader-utils": "^2.0.0"
},
"peerDependencies": {
- "css-loader": "*",
- "webpack": "^3.0.0 || ^4.1.0 || ^5.0.0-0"
- },
- "peerDependenciesMeta": {
- "cache-loader": {
- "optional": true
- },
- "vue-template-compiler": {
- "optional": true
- }
+ "webpack": "^4.1.0 || ^5.0.0-0"
}
},
- "node_modules/vue-loader/node_modules/json5": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
- "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
+ "node_modules/vue3-calendar-heatmap": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/vue3-calendar-heatmap/-/vue3-calendar-heatmap-2.0.0.tgz",
+ "integrity": "sha512-BchyC33WiZryYatFINj3LWqgyE6X82Huzf7abA23tsF/IbaRZVwZzie8SmGaYvezEBiPXhJogQ3dtxIuXFjkBw==",
"dependencies": {
- "minimist": "^1.2.0"
- },
- "bin": {
- "json5": "lib/cli.js"
- }
- },
- "node_modules/vue-loader/node_modules/loader-utils": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz",
- "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==",
- "dependencies": {
- "big.js": "^5.2.2",
- "emojis-list": "^3.0.0",
- "json5": "^1.0.1"
+ "tippy.js": "^6.3.7"
},
"engines": {
- "node": ">=4.0.0"
- }
- },
- "node_modules/vue-resize": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/vue-resize/-/vue-resize-1.0.1.tgz",
- "integrity": "sha512-z5M7lJs0QluJnaoMFTIeGx6dIkYxOwHThlZDeQnWZBizKblb99GSejPnK37ZbNE/rVwDcYcHY+Io+AxdpY952w==",
- "dependencies": {
- "@babel/runtime": "^7.13.10"
+ "node": ">=12"
},
"peerDependencies": {
- "vue": "^2.6.0"
+ "vue": "^3.2.24"
}
},
- "node_modules/vue-style-loader": {
- "version": "4.1.3",
- "resolved": "https://registry.npmjs.org/vue-style-loader/-/vue-style-loader-4.1.3.tgz",
- "integrity": "sha512-sFuh0xfbtpRlKfm39ss/ikqs9AbKCoXZBpHeVZ8Tx650o0k0q/YCM7FRvigtxpACezfq6af+a7JeqVTWvncqDg==",
- "dependencies": {
- "hash-sum": "^1.0.2",
- "loader-utils": "^1.0.2"
- }
- },
- "node_modules/vue-style-loader/node_modules/json5": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
- "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
- "dependencies": {
- "minimist": "^1.2.0"
- },
- "bin": {
- "json5": "lib/cli.js"
- }
- },
- "node_modules/vue-style-loader/node_modules/loader-utils": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz",
- "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==",
- "dependencies": {
- "big.js": "^5.2.2",
- "emojis-list": "^3.0.0",
- "json5": "^1.0.1"
- },
- "engines": {
- "node": ">=4.0.0"
- }
- },
- "node_modules/vue-template-compiler": {
- "version": "2.6.14",
- "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.6.14.tgz",
- "integrity": "sha512-ODQS1SyMbjKoO1JBJZojSw6FE4qnh9rIpUZn2EUT86FKizx9uH5z6uXiIrm4/Nb/gwxTi/o17ZDEGWAXHvtC7g==",
- "dependencies": {
- "de-indent": "^1.0.2",
- "he": "^1.1.0"
- }
- },
- "node_modules/vue-template-es2015-compiler": {
- "version": "1.9.1",
- "resolved": "https://registry.npmjs.org/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz",
- "integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw=="
- },
- "node_modules/walker": {
- "version": "1.0.8",
- "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz",
- "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==",
+ "node_modules/w3c-xmlserializer": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-3.0.0.tgz",
+ "integrity": "sha512-3WFqGEgSXIyGhOmAFtlicJNMjEps8b1MG31NCA0/vOF9+nKMUW1ckhi9cnNHmf88Rzw5V+dwIwsm2C7X8k9aQg==",
"dev": true,
"dependencies": {
- "makeerror": "1.0.12"
+ "xml-name-validator": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=12"
}
},
"node_modules/watchpack": {
@@ -11986,20 +9137,20 @@
}
},
"node_modules/webpack": {
- "version": "5.73.0",
- "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.73.0.tgz",
- "integrity": "sha512-svjudQRPPa0YiOYa2lM/Gacw0r6PvxptHj4FuEKQ2kX05ZLkjbVc5MnPs6its5j7IZljnIqSVo/OsY2X0IpHGA==",
+ "version": "5.74.0",
+ "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.74.0.tgz",
+ "integrity": "sha512-A2InDwnhhGN4LYctJj6M1JEaGL7Luj6LOmyBHjcI8529cm5p6VXiTIW2sn6ffvEAKmveLzvu4jrihwXtPojlAA==",
"dependencies": {
"@types/eslint-scope": "^3.7.3",
"@types/estree": "^0.0.51",
"@webassemblyjs/ast": "1.11.1",
"@webassemblyjs/wasm-edit": "1.11.1",
"@webassemblyjs/wasm-parser": "1.11.1",
- "acorn": "^8.4.1",
+ "acorn": "^8.7.1",
"acorn-import-assertions": "^1.7.6",
"browserslist": "^4.14.5",
"chrome-trace-event": "^1.0.2",
- "enhanced-resolve": "^5.9.3",
+ "enhanced-resolve": "^5.10.0",
"es-module-lexer": "^0.9.0",
"eslint-scope": "5.1.1",
"events": "^3.2.0",
@@ -12012,7 +9163,7 @@
"schema-utils": "^3.1.0",
"tapable": "^2.1.1",
"terser-webpack-plugin": "^5.1.3",
- "watchpack": "^2.3.1",
+ "watchpack": "^2.4.0",
"webpack-sources": "^3.2.3"
},
"bin": {
@@ -12077,6 +9228,14 @@
}
}
},
+ "node_modules/webpack-cli/node_modules/commander": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
+ "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
+ "engines": {
+ "node": ">= 10"
+ }
+ },
"node_modules/webpack-merge": {
"version": "5.8.0",
"resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz",
@@ -12201,21 +9360,18 @@
}
},
"node_modules/whatwg-url": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
- "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
+ "version": "11.0.0",
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz",
+ "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==",
"dev": true,
"dependencies": {
- "tr46": "~0.0.3",
- "webidl-conversions": "^3.0.0"
+ "tr46": "^3.0.0",
+ "webidl-conversions": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=12"
}
},
- "node_modules/whatwg-url/node_modules/webidl-conversions": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
- "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
- "dev": true
- },
"node_modules/which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
@@ -12251,12 +9407,6 @@
"resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz",
"integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw=="
},
- "node_modules/wolfy87-eventemitter": {
- "version": "5.2.9",
- "resolved": "https://registry.npmjs.org/wolfy87-eventemitter/-/wolfy87-eventemitter-5.2.9.tgz",
- "integrity": "sha512-P+6vtWyuDw+MB01X7UeF8TaHBvbCovf4HPEMF/SV7BdDc1SMTiBy13SRD71lQh4ExFTG1d/WNzDGDCyOKSMblw==",
- "dev": true
- },
"node_modules/word-wrap": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
@@ -12267,24 +9417,24 @@
}
},
"node_modules/workbox-core": {
- "version": "6.5.3",
- "resolved": "https://registry.npmjs.org/workbox-core/-/workbox-core-6.5.3.tgz",
- "integrity": "sha512-Bb9ey5n/M9x+l3fBTlLpHt9ASTzgSGj6vxni7pY72ilB/Pb3XtN+cZ9yueboVhD5+9cNQrC9n/E1fSrqWsUz7Q=="
+ "version": "6.5.4",
+ "resolved": "https://registry.npmjs.org/workbox-core/-/workbox-core-6.5.4.tgz",
+ "integrity": "sha512-OXYb+m9wZm8GrORlV2vBbE5EC1FKu71GGp0H4rjmxmF4/HLbMCoTFws87M3dFwgpmg0v00K++PImpNQ6J5NQ6Q=="
},
"node_modules/workbox-routing": {
- "version": "6.5.3",
- "resolved": "https://registry.npmjs.org/workbox-routing/-/workbox-routing-6.5.3.tgz",
- "integrity": "sha512-DFjxcuRAJjjt4T34RbMm3MCn+xnd36UT/2RfPRfa8VWJGItGJIn7tG+GwVTdHmvE54i/QmVTJepyAGWtoLPTmg==",
+ "version": "6.5.4",
+ "resolved": "https://registry.npmjs.org/workbox-routing/-/workbox-routing-6.5.4.tgz",
+ "integrity": "sha512-apQswLsbrrOsBUWtr9Lf80F+P1sHnQdYodRo32SjiByYi36IDyL2r7BH1lJtFX8fwNHDa1QOVY74WKLLS6o5Pg==",
"dependencies": {
- "workbox-core": "6.5.3"
+ "workbox-core": "6.5.4"
}
},
"node_modules/workbox-strategies": {
- "version": "6.5.3",
- "resolved": "https://registry.npmjs.org/workbox-strategies/-/workbox-strategies-6.5.3.tgz",
- "integrity": "sha512-MgmGRrDVXs7rtSCcetZgkSZyMpRGw8HqL2aguszOc3nUmzGZsT238z/NN9ZouCxSzDu3PQ3ZSKmovAacaIhu1w==",
+ "version": "6.5.4",
+ "resolved": "https://registry.npmjs.org/workbox-strategies/-/workbox-strategies-6.5.4.tgz",
+ "integrity": "sha512-DEtsxhx0LIYWkJBTQolRxG4EI0setTJkqR4m7r4YpBdxtWJH1Mbg01Cj8ZjNOO8etqfA3IZaOPHUxCs8cBsKLw==",
"dependencies": {
- "workbox-core": "6.5.3"
+ "workbox-core": "6.5.4"
}
},
"node_modules/worker-loader": {
@@ -12379,9 +9529,9 @@
}
},
"node_modules/wrap-ansi/node_modules/ansi-styles": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.1.0.tgz",
- "integrity": "sha512-VbqNsoz55SYGczauuup0MFUyXNQviSpFTj1RQtFzmQLk18qbVSpTFFGMT293rmDaQuKCT6InmbuEyUne4mTuxQ==",
+ "version": "6.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.1.1.tgz",
+ "integrity": "sha512-qDOv24WjnYuL+wbwHdlsYZFy+cgPtrYw0Tn7GLORicQp9BkQLzrgI3Pm4VyR9ERZ41YTn7KlMPuL1n05WdZvmg==",
"engines": {
"node": ">=12"
},
@@ -12430,16 +9580,37 @@
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
},
"node_modules/write-file-atomic": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.1.tgz",
- "integrity": "sha512-nSKUxgAbyioruk6hU87QzVbY279oYT6uiwgDoujth2ju4mJ+TZau7SQBhtbTmUyuNYTuXnSyRn66FV0+eCgcrQ==",
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz",
+ "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==",
"dev": true,
"dependencies": {
"imurmurhash": "^0.1.4",
"signal-exit": "^3.0.7"
},
"engines": {
- "node": "^12.13.0 || ^14.15.0 || >=16"
+ "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+ }
+ },
+ "node_modules/ws": {
+ "version": "8.9.0",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.9.0.tgz",
+ "integrity": "sha512-Ja7nszREasGaYUYCI2k4lCKIRTt+y7XuqVoHR44YpI49TtryyqbqvDMn5eqfW7e6HzTukDRIsXqzVHScqRcafg==",
+ "dev": true,
+ "engines": {
+ "node": ">=10.0.0"
+ },
+ "peerDependencies": {
+ "bufferutil": "^4.0.1",
+ "utf-8-validate": "^5.0.2"
+ },
+ "peerDependenciesMeta": {
+ "bufferutil": {
+ "optional": true
+ },
+ "utf-8-validate": {
+ "optional": true
+ }
}
},
"node_modules/xml-name-validator": {
@@ -12451,6 +9622,12 @@
"node": ">=12"
}
},
+ "node_modules/xmlchars": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
+ "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==",
+ "dev": true
+ },
"node_modules/xregexp": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/xregexp/-/xregexp-2.0.0.tgz",
@@ -12512,9 +9689,9 @@
}
},
"node_modules/yargs/node_modules/yargs-parser": {
- "version": "21.0.1",
- "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.1.tgz",
- "integrity": "sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg==",
+ "version": "21.1.1",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
+ "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
"dev": true,
"engines": {
"node": ">=12"
@@ -12534,20 +9711,10 @@
}
},
"dependencies": {
- "@ampproject/remapping": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz",
- "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==",
- "dev": true,
- "requires": {
- "@jridgewell/gen-mapping": "^0.1.0",
- "@jridgewell/trace-mapping": "^0.3.9"
- }
- },
"@asyncapi/specs": {
- "version": "2.14.0",
- "resolved": "https://registry.npmjs.org/@asyncapi/specs/-/specs-2.14.0.tgz",
- "integrity": "sha512-hHsYF6XsYNIKb1P2rXaooF4H+uKKQ4b/Ljxrk3rZ3riEDiSxMshMEfb1fUlw9Yj4V4OmJhjXwkNvw8W59AXv1A==",
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/@asyncapi/specs/-/specs-3.2.1.tgz",
+ "integrity": "sha512-FO+EteK+Gk3zwumrBw6frpp9cJ4oQL5++hBBpfM81w16e9KaiA4sKrzvQsvVjifoZZHNvVEX4D2zoz9i8CLccQ==",
"dev": true
},
"@babel/code-frame": {
@@ -12559,184 +9726,12 @@
"@babel/highlight": "^7.18.6"
}
},
- "@babel/compat-data": {
- "version": "7.18.8",
- "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.18.8.tgz",
- "integrity": "sha512-HSmX4WZPPK3FUxYp7g2T6EyO8j96HlZJlxmKPSh6KAcqwyDrfx7hKjXpAW/0FhFfTJsR0Yt4lAjLI2coMptIHQ==",
- "dev": true
- },
- "@babel/core": {
- "version": "7.18.9",
- "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.9.tgz",
- "integrity": "sha512-1LIb1eL8APMy91/IMW+31ckrfBM4yCoLaVzoDhZUKSM4cu1L1nIidyxkCgzPAgrC5WEz36IPEr/eSeSF9pIn+g==",
- "dev": true,
- "requires": {
- "@ampproject/remapping": "^2.1.0",
- "@babel/code-frame": "^7.18.6",
- "@babel/generator": "^7.18.9",
- "@babel/helper-compilation-targets": "^7.18.9",
- "@babel/helper-module-transforms": "^7.18.9",
- "@babel/helpers": "^7.18.9",
- "@babel/parser": "^7.18.9",
- "@babel/template": "^7.18.6",
- "@babel/traverse": "^7.18.9",
- "@babel/types": "^7.18.9",
- "convert-source-map": "^1.7.0",
- "debug": "^4.1.0",
- "gensync": "^1.0.0-beta.2",
- "json5": "^2.2.1",
- "semver": "^6.3.0"
- },
- "dependencies": {
- "semver": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
- "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
- "dev": true
- }
- }
- },
- "@babel/generator": {
- "version": "7.18.9",
- "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.9.tgz",
- "integrity": "sha512-wt5Naw6lJrL1/SGkipMiFxJjtyczUWTP38deiP1PO60HsBjDeKk08CGC3S8iVuvf0FmTdgKwU1KIXzSKL1G0Ug==",
- "dev": true,
- "requires": {
- "@babel/types": "^7.18.9",
- "@jridgewell/gen-mapping": "^0.3.2",
- "jsesc": "^2.5.1"
- },
- "dependencies": {
- "@jridgewell/gen-mapping": {
- "version": "0.3.2",
- "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz",
- "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==",
- "dev": true,
- "requires": {
- "@jridgewell/set-array": "^1.0.1",
- "@jridgewell/sourcemap-codec": "^1.4.10",
- "@jridgewell/trace-mapping": "^0.3.9"
- }
- }
- }
- },
- "@babel/helper-compilation-targets": {
- "version": "7.18.9",
- "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.9.tgz",
- "integrity": "sha512-tzLCyVmqUiFlcFoAPLA/gL9TeYrF61VLNtb+hvkuVaB5SUjW7jcfrglBIX1vUIoT7CLP3bBlIMeyEsIl2eFQNg==",
- "dev": true,
- "requires": {
- "@babel/compat-data": "^7.18.8",
- "@babel/helper-validator-option": "^7.18.6",
- "browserslist": "^4.20.2",
- "semver": "^6.3.0"
- },
- "dependencies": {
- "semver": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
- "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
- "dev": true
- }
- }
- },
- "@babel/helper-environment-visitor": {
- "version": "7.18.9",
- "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz",
- "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==",
- "dev": true
- },
- "@babel/helper-function-name": {
- "version": "7.18.9",
- "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.18.9.tgz",
- "integrity": "sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A==",
- "dev": true,
- "requires": {
- "@babel/template": "^7.18.6",
- "@babel/types": "^7.18.9"
- }
- },
- "@babel/helper-hoist-variables": {
- "version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz",
- "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==",
- "dev": true,
- "requires": {
- "@babel/types": "^7.18.6"
- }
- },
- "@babel/helper-module-imports": {
- "version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz",
- "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==",
- "dev": true,
- "requires": {
- "@babel/types": "^7.18.6"
- }
- },
- "@babel/helper-module-transforms": {
- "version": "7.18.9",
- "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.18.9.tgz",
- "integrity": "sha512-KYNqY0ICwfv19b31XzvmI/mfcylOzbLtowkw+mfvGPAQ3kfCnMLYbED3YecL5tPd8nAYFQFAd6JHp2LxZk/J1g==",
- "dev": true,
- "requires": {
- "@babel/helper-environment-visitor": "^7.18.9",
- "@babel/helper-module-imports": "^7.18.6",
- "@babel/helper-simple-access": "^7.18.6",
- "@babel/helper-split-export-declaration": "^7.18.6",
- "@babel/helper-validator-identifier": "^7.18.6",
- "@babel/template": "^7.18.6",
- "@babel/traverse": "^7.18.9",
- "@babel/types": "^7.18.9"
- }
- },
- "@babel/helper-plugin-utils": {
- "version": "7.18.9",
- "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.18.9.tgz",
- "integrity": "sha512-aBXPT3bmtLryXaoJLyYPXPlSD4p1ld9aYeR+sJNOZjJJGiOpb+fKfh3NkcCu7J54nUJwCERPBExCCpyCOHnu/w==",
- "dev": true
- },
- "@babel/helper-simple-access": {
- "version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz",
- "integrity": "sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g==",
- "dev": true,
- "requires": {
- "@babel/types": "^7.18.6"
- }
- },
- "@babel/helper-split-export-declaration": {
- "version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz",
- "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==",
- "dev": true,
- "requires": {
- "@babel/types": "^7.18.6"
- }
- },
"@babel/helper-validator-identifier": {
- "version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz",
- "integrity": "sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==",
+ "version": "7.19.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz",
+ "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==",
"dev": true
},
- "@babel/helper-validator-option": {
- "version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz",
- "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==",
- "dev": true
- },
- "@babel/helpers": {
- "version": "7.18.9",
- "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.18.9.tgz",
- "integrity": "sha512-Jf5a+rbrLoR4eNdUmnFu8cN5eNJT6qdTdOg5IHIzq87WwyRw9PwguLFOWYgktN/60IP4fgDUawJvs7PjQIzELQ==",
- "dev": true,
- "requires": {
- "@babel/template": "^7.18.6",
- "@babel/traverse": "^7.18.9",
- "@babel/types": "^7.18.9"
- }
- },
"@babel/highlight": {
"version": "7.18.6",
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz",
@@ -12807,188 +9802,9 @@
}
},
"@babel/parser": {
- "version": "7.18.9",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.9.tgz",
- "integrity": "sha512-9uJveS9eY9DJ0t64YbIBZICtJy8a5QrDEVdiLCG97fVLpDTpGX7t8mMSb6OWw6Lrnjqj4O8zwjELX3dhoMgiBg==",
- "dev": true
- },
- "@babel/plugin-syntax-async-generators": {
- "version": "7.8.4",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz",
- "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==",
- "dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.0"
- }
- },
- "@babel/plugin-syntax-bigint": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz",
- "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==",
- "dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.0"
- }
- },
- "@babel/plugin-syntax-class-properties": {
- "version": "7.12.13",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz",
- "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==",
- "dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.12.13"
- }
- },
- "@babel/plugin-syntax-import-meta": {
- "version": "7.10.4",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz",
- "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==",
- "dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.10.4"
- }
- },
- "@babel/plugin-syntax-json-strings": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz",
- "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==",
- "dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.0"
- }
- },
- "@babel/plugin-syntax-logical-assignment-operators": {
- "version": "7.10.4",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz",
- "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==",
- "dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.10.4"
- }
- },
- "@babel/plugin-syntax-nullish-coalescing-operator": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz",
- "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==",
- "dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.0"
- }
- },
- "@babel/plugin-syntax-numeric-separator": {
- "version": "7.10.4",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz",
- "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==",
- "dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.10.4"
- }
- },
- "@babel/plugin-syntax-object-rest-spread": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz",
- "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==",
- "dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.0"
- }
- },
- "@babel/plugin-syntax-optional-catch-binding": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz",
- "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==",
- "dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.0"
- }
- },
- "@babel/plugin-syntax-optional-chaining": {
- "version": "7.8.3",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz",
- "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==",
- "dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.8.0"
- }
- },
- "@babel/plugin-syntax-top-level-await": {
- "version": "7.14.5",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz",
- "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==",
- "dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.14.5"
- }
- },
- "@babel/plugin-syntax-typescript": {
- "version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.18.6.tgz",
- "integrity": "sha512-mAWAuq4rvOepWCBid55JuRNvpTNf2UGVgoz4JV0fXEKolsVZDzsa4NqCef758WZJj/GDu0gVGItjKFiClTAmZA==",
- "dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.18.6"
- }
- },
- "@babel/runtime": {
- "version": "7.18.9",
- "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.9.tgz",
- "integrity": "sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw==",
- "requires": {
- "regenerator-runtime": "^0.13.4"
- }
- },
- "@babel/template": {
- "version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.6.tgz",
- "integrity": "sha512-JoDWzPe+wgBsTTgdnIma3iHNFC7YVJoPssVBDjiHfNlyt4YcunDtcDOUmfVDfCK5MfdsaIoX9PkijPhjH3nYUw==",
- "dev": true,
- "requires": {
- "@babel/code-frame": "^7.18.6",
- "@babel/parser": "^7.18.6",
- "@babel/types": "^7.18.6"
- }
- },
- "@babel/traverse": {
- "version": "7.18.9",
- "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.18.9.tgz",
- "integrity": "sha512-LcPAnujXGwBgv3/WHv01pHtb2tihcyW1XuL9wd7jqh1Z8AQkTd+QVjMrMijrln0T7ED3UXLIy36P9Ao7W75rYg==",
- "dev": true,
- "requires": {
- "@babel/code-frame": "^7.18.6",
- "@babel/generator": "^7.18.9",
- "@babel/helper-environment-visitor": "^7.18.9",
- "@babel/helper-function-name": "^7.18.9",
- "@babel/helper-hoist-variables": "^7.18.6",
- "@babel/helper-split-export-declaration": "^7.18.6",
- "@babel/parser": "^7.18.9",
- "@babel/types": "^7.18.9",
- "debug": "^4.1.0",
- "globals": "^11.1.0"
- },
- "dependencies": {
- "globals": {
- "version": "11.12.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
- "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
- "dev": true
- }
- }
- },
- "@babel/types": {
- "version": "7.18.9",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.9.tgz",
- "integrity": "sha512-WwMLAg2MvJmt/rKEVQBBhIVffMmnilX4oe0sRe7iPOHIGsqpruFHHdrfj4O1CMMtgMtCU4oPafZjDPCRgO57Wg==",
- "dev": true,
- "requires": {
- "@babel/helper-validator-identifier": "^7.18.6",
- "to-fast-properties": "^2.0.0"
- }
- },
- "@bcoe/v8-coverage": {
- "version": "0.2.3",
- "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz",
- "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==",
- "dev": true
+ "version": "7.19.3",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.19.3.tgz",
+ "integrity": "sha512-pJ9xOlNWHiy9+FuFP09DEAFbAn4JskgRsVcc169w2xRBC3FRGuQEwjeIMMND9L2zc0iEhO/tGv4Zq+km+hxNpQ=="
},
"@braintree/sanitize-url": {
"version": "6.0.0",
@@ -13013,15 +9829,27 @@
"resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz",
"integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw=="
},
+ "@esbuild/android-arm": {
+ "version": "0.15.10",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.15.10.tgz",
+ "integrity": "sha512-FNONeQPy/ox+5NBkcSbYJxoXj9GWu8gVGJTVmUyoOCKQFDTrHVKgNSzChdNt0I8Aj/iKcsDf2r9BFwv+FSNUXg==",
+ "optional": true
+ },
+ "@esbuild/linux-loong64": {
+ "version": "0.15.10",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.15.10.tgz",
+ "integrity": "sha512-w0Ou3Z83LOYEkwaui2M8VwIp+nLi/NA60lBLMvaJ+vXVMcsARYdEzLNE7RSm4+lSg4zq4d7fAVuzk7PNQ5JFgg==",
+ "optional": true
+ },
"@eslint/eslintrc": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.0.tgz",
- "integrity": "sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw==",
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.3.tgz",
+ "integrity": "sha512-uj3pT6Mg+3t39fvLrj8iuCIJ38zKO9FpGtJ4BBJebJhEwjoT+KLVNCcHT5QC9NGRIEi7fZ0ZR8YRb884auB4Lg==",
"dev": true,
"requires": {
"ajv": "^6.12.4",
"debug": "^4.3.2",
- "espree": "^9.3.2",
+ "espree": "^9.4.0",
"globals": "^13.15.0",
"ignore": "^5.2.0",
"import-fresh": "^3.2.1",
@@ -13050,24 +9878,10 @@
}
}
},
- "@happy-dom/jest-environment": {
- "version": "6.0.4",
- "resolved": "https://registry.npmjs.org/@happy-dom/jest-environment/-/jest-environment-6.0.4.tgz",
- "integrity": "sha512-3FEWODVN4H7Hnkbm7IEtRE8lU7uitxSsnVagkQbfjjwKSUfF6BT9MJ95VF7nLimdc5ctOIa7ZbDietRRIpgSHw==",
- "dev": true,
- "requires": {
- "@jest/environment": "^27.5.1",
- "@jest/fake-timers": "^27.5.1",
- "@jest/types": "^27.5.1",
- "happy-dom": "^6.0.4",
- "jest-mock": "^27.5.1",
- "jest-util": "^27.5.1"
- }
- },
"@humanwhocodes/config-array": {
- "version": "0.9.5",
- "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz",
- "integrity": "sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==",
+ "version": "0.10.7",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.10.7.tgz",
+ "integrity": "sha512-MDl6D6sBsaV452/QSdX+4CXIjZhIcI0PELsxUjk4U828yd58vk3bTIvk/6w5FY+4hIy9sLW0sfrV7K7Kc++j/w==",
"dev": true,
"requires": {
"@humanwhocodes/object-schema": "^1.2.1",
@@ -13075,664 +9889,18 @@
"minimatch": "^3.0.4"
}
},
+ "@humanwhocodes/module-importer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+ "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+ "dev": true
+ },
"@humanwhocodes/object-schema": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz",
"integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==",
"dev": true
},
- "@istanbuljs/load-nyc-config": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz",
- "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==",
- "dev": true,
- "requires": {
- "camelcase": "^5.3.1",
- "find-up": "^4.1.0",
- "get-package-type": "^0.1.0",
- "js-yaml": "^3.13.1",
- "resolve-from": "^5.0.0"
- },
- "dependencies": {
- "argparse": {
- "version": "1.0.10",
- "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
- "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
- "dev": true,
- "requires": {
- "sprintf-js": "~1.0.2"
- }
- },
- "find-up": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
- "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
- "dev": true,
- "requires": {
- "locate-path": "^5.0.0",
- "path-exists": "^4.0.0"
- }
- },
- "js-yaml": {
- "version": "3.14.1",
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
- "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
- "dev": true,
- "requires": {
- "argparse": "^1.0.7",
- "esprima": "^4.0.0"
- }
- },
- "locate-path": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
- "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
- "dev": true,
- "requires": {
- "p-locate": "^4.1.0"
- }
- },
- "p-limit": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
- "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
- "dev": true,
- "requires": {
- "p-try": "^2.0.0"
- }
- },
- "p-locate": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
- "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
- "dev": true,
- "requires": {
- "p-limit": "^2.2.0"
- }
- },
- "path-exists": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
- "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
- "dev": true
- },
- "resolve-from": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
- "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
- "dev": true
- }
- }
- },
- "@istanbuljs/schema": {
- "version": "0.1.3",
- "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz",
- "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==",
- "dev": true
- },
- "@jest/console": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/console/-/console-28.1.3.tgz",
- "integrity": "sha512-QPAkP5EwKdK/bxIr6C1I4Vs0rm2nHiANzj/Z5X2JQkrZo6IqvC4ldZ9K95tF0HdidhA8Bo6egxSzUFPYKcEXLw==",
- "dev": true,
- "requires": {
- "@jest/types": "^28.1.3",
- "@types/node": "*",
- "chalk": "^4.0.0",
- "jest-message-util": "^28.1.3",
- "jest-util": "^28.1.3",
- "slash": "^3.0.0"
- },
- "dependencies": {
- "@jest/types": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz",
- "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==",
- "dev": true,
- "requires": {
- "@jest/schemas": "^28.1.3",
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^3.0.0",
- "@types/node": "*",
- "@types/yargs": "^17.0.8",
- "chalk": "^4.0.0"
- }
- },
- "@types/yargs": {
- "version": "17.0.10",
- "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.10.tgz",
- "integrity": "sha512-gmEaFwpj/7f/ROdtIlci1R1VYU1J4j95m8T+Tj3iBgiBFKg1foE/PSl93bBd5T9LDXNPo8UlNN6W0qwD8O5OaA==",
- "dev": true,
- "requires": {
- "@types/yargs-parser": "*"
- }
- },
- "jest-message-util": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-28.1.3.tgz",
- "integrity": "sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g==",
- "dev": true,
- "requires": {
- "@babel/code-frame": "^7.12.13",
- "@jest/types": "^28.1.3",
- "@types/stack-utils": "^2.0.0",
- "chalk": "^4.0.0",
- "graceful-fs": "^4.2.9",
- "micromatch": "^4.0.4",
- "pretty-format": "^28.1.3",
- "slash": "^3.0.0",
- "stack-utils": "^2.0.3"
- }
- },
- "jest-util": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz",
- "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==",
- "dev": true,
- "requires": {
- "@jest/types": "^28.1.3",
- "@types/node": "*",
- "chalk": "^4.0.0",
- "ci-info": "^3.2.0",
- "graceful-fs": "^4.2.9",
- "picomatch": "^2.2.3"
- }
- }
- }
- },
- "@jest/core": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/core/-/core-28.1.3.tgz",
- "integrity": "sha512-CIKBrlaKOzA7YG19BEqCw3SLIsEwjZkeJzf5bdooVnW4bH5cktqe3JX+G2YV1aK5vP8N9na1IGWFzYaTp6k6NA==",
- "dev": true,
- "requires": {
- "@jest/console": "^28.1.3",
- "@jest/reporters": "^28.1.3",
- "@jest/test-result": "^28.1.3",
- "@jest/transform": "^28.1.3",
- "@jest/types": "^28.1.3",
- "@types/node": "*",
- "ansi-escapes": "^4.2.1",
- "chalk": "^4.0.0",
- "ci-info": "^3.2.0",
- "exit": "^0.1.2",
- "graceful-fs": "^4.2.9",
- "jest-changed-files": "^28.1.3",
- "jest-config": "^28.1.3",
- "jest-haste-map": "^28.1.3",
- "jest-message-util": "^28.1.3",
- "jest-regex-util": "^28.0.2",
- "jest-resolve": "^28.1.3",
- "jest-resolve-dependencies": "^28.1.3",
- "jest-runner": "^28.1.3",
- "jest-runtime": "^28.1.3",
- "jest-snapshot": "^28.1.3",
- "jest-util": "^28.1.3",
- "jest-validate": "^28.1.3",
- "jest-watcher": "^28.1.3",
- "micromatch": "^4.0.4",
- "pretty-format": "^28.1.3",
- "rimraf": "^3.0.0",
- "slash": "^3.0.0",
- "strip-ansi": "^6.0.0"
- },
- "dependencies": {
- "@jest/types": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz",
- "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==",
- "dev": true,
- "requires": {
- "@jest/schemas": "^28.1.3",
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^3.0.0",
- "@types/node": "*",
- "@types/yargs": "^17.0.8",
- "chalk": "^4.0.0"
- }
- },
- "@types/yargs": {
- "version": "17.0.10",
- "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.10.tgz",
- "integrity": "sha512-gmEaFwpj/7f/ROdtIlci1R1VYU1J4j95m8T+Tj3iBgiBFKg1foE/PSl93bBd5T9LDXNPo8UlNN6W0qwD8O5OaA==",
- "dev": true,
- "requires": {
- "@types/yargs-parser": "*"
- }
- },
- "jest-message-util": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-28.1.3.tgz",
- "integrity": "sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g==",
- "dev": true,
- "requires": {
- "@babel/code-frame": "^7.12.13",
- "@jest/types": "^28.1.3",
- "@types/stack-utils": "^2.0.0",
- "chalk": "^4.0.0",
- "graceful-fs": "^4.2.9",
- "micromatch": "^4.0.4",
- "pretty-format": "^28.1.3",
- "slash": "^3.0.0",
- "stack-utils": "^2.0.3"
- }
- },
- "jest-util": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz",
- "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==",
- "dev": true,
- "requires": {
- "@jest/types": "^28.1.3",
- "@types/node": "*",
- "chalk": "^4.0.0",
- "ci-info": "^3.2.0",
- "graceful-fs": "^4.2.9",
- "picomatch": "^2.2.3"
- }
- }
- }
- },
- "@jest/environment": {
- "version": "27.5.1",
- "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-27.5.1.tgz",
- "integrity": "sha512-/WQjhPJe3/ghaol/4Bq480JKXV/Rfw8nQdN7f41fM8VDHLcxKXou6QyXAh3EFr9/bVG3x74z1NWDkP87EiY8gA==",
- "dev": true,
- "requires": {
- "@jest/fake-timers": "^27.5.1",
- "@jest/types": "^27.5.1",
- "@types/node": "*",
- "jest-mock": "^27.5.1"
- }
- },
- "@jest/expect": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-28.1.3.tgz",
- "integrity": "sha512-lzc8CpUbSoE4dqT0U+g1qODQjBRHPpCPXissXD4mS9+sWQdmmpeJ9zSH1rS1HEkrsMN0fb7nKrJ9giAR1d3wBw==",
- "dev": true,
- "requires": {
- "expect": "^28.1.3",
- "jest-snapshot": "^28.1.3"
- }
- },
- "@jest/expect-utils": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-28.1.3.tgz",
- "integrity": "sha512-wvbi9LUrHJLn3NlDW6wF2hvIMtd4JUl2QNVrjq+IBSHirgfrR3o9RnVtxzdEGO2n9JyIWwHnLfby5KzqBGg2YA==",
- "dev": true,
- "requires": {
- "jest-get-type": "^28.0.2"
- }
- },
- "@jest/fake-timers": {
- "version": "27.5.1",
- "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-27.5.1.tgz",
- "integrity": "sha512-/aPowoolwa07k7/oM3aASneNeBGCmGQsc3ugN4u6s4C/+s5M64MFo/+djTdiwcbQlRfFElGuDXWzaWj6QgKObQ==",
- "dev": true,
- "requires": {
- "@jest/types": "^27.5.1",
- "@sinonjs/fake-timers": "^8.0.1",
- "@types/node": "*",
- "jest-message-util": "^27.5.1",
- "jest-mock": "^27.5.1",
- "jest-util": "^27.5.1"
- }
- },
- "@jest/globals": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-28.1.3.tgz",
- "integrity": "sha512-XFU4P4phyryCXu1pbcqMO0GSQcYe1IsalYCDzRNyhetyeyxMcIxa11qPNDpVNLeretItNqEmYYQn1UYz/5x1NA==",
- "dev": true,
- "requires": {
- "@jest/environment": "^28.1.3",
- "@jest/expect": "^28.1.3",
- "@jest/types": "^28.1.3"
- },
- "dependencies": {
- "@jest/environment": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-28.1.3.tgz",
- "integrity": "sha512-1bf40cMFTEkKyEf585R9Iz1WayDjHoHqvts0XFYEqyKM3cFWDpeMoqKKTAF9LSYQModPUlh8FKptoM2YcMWAXA==",
- "dev": true,
- "requires": {
- "@jest/fake-timers": "^28.1.3",
- "@jest/types": "^28.1.3",
- "@types/node": "*",
- "jest-mock": "^28.1.3"
- }
- },
- "@jest/fake-timers": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-28.1.3.tgz",
- "integrity": "sha512-D/wOkL2POHv52h+ok5Oj/1gOG9HSywdoPtFsRCUmlCILXNn5eIWmcnd3DIiWlJnpGvQtmajqBP95Ei0EimxfLw==",
- "dev": true,
- "requires": {
- "@jest/types": "^28.1.3",
- "@sinonjs/fake-timers": "^9.1.2",
- "@types/node": "*",
- "jest-message-util": "^28.1.3",
- "jest-mock": "^28.1.3",
- "jest-util": "^28.1.3"
- }
- },
- "@jest/types": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz",
- "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==",
- "dev": true,
- "requires": {
- "@jest/schemas": "^28.1.3",
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^3.0.0",
- "@types/node": "*",
- "@types/yargs": "^17.0.8",
- "chalk": "^4.0.0"
- }
- },
- "@sinonjs/fake-timers": {
- "version": "9.1.2",
- "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz",
- "integrity": "sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw==",
- "dev": true,
- "requires": {
- "@sinonjs/commons": "^1.7.0"
- }
- },
- "@types/yargs": {
- "version": "17.0.10",
- "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.10.tgz",
- "integrity": "sha512-gmEaFwpj/7f/ROdtIlci1R1VYU1J4j95m8T+Tj3iBgiBFKg1foE/PSl93bBd5T9LDXNPo8UlNN6W0qwD8O5OaA==",
- "dev": true,
- "requires": {
- "@types/yargs-parser": "*"
- }
- },
- "jest-message-util": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-28.1.3.tgz",
- "integrity": "sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g==",
- "dev": true,
- "requires": {
- "@babel/code-frame": "^7.12.13",
- "@jest/types": "^28.1.3",
- "@types/stack-utils": "^2.0.0",
- "chalk": "^4.0.0",
- "graceful-fs": "^4.2.9",
- "micromatch": "^4.0.4",
- "pretty-format": "^28.1.3",
- "slash": "^3.0.0",
- "stack-utils": "^2.0.3"
- }
- },
- "jest-mock": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-28.1.3.tgz",
- "integrity": "sha512-o3J2jr6dMMWYVH4Lh/NKmDXdosrsJgi4AviS8oXLujcjpCMBb1FMsblDnOXKZKfSiHLxYub1eS0IHuRXsio9eA==",
- "dev": true,
- "requires": {
- "@jest/types": "^28.1.3",
- "@types/node": "*"
- }
- },
- "jest-util": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz",
- "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==",
- "dev": true,
- "requires": {
- "@jest/types": "^28.1.3",
- "@types/node": "*",
- "chalk": "^4.0.0",
- "ci-info": "^3.2.0",
- "graceful-fs": "^4.2.9",
- "picomatch": "^2.2.3"
- }
- }
- }
- },
- "@jest/reporters": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-28.1.3.tgz",
- "integrity": "sha512-JuAy7wkxQZVNU/V6g9xKzCGC5LVXx9FDcABKsSXp5MiKPEE2144a/vXTEDoyzjUpZKfVwp08Wqg5A4WfTMAzjg==",
- "dev": true,
- "requires": {
- "@bcoe/v8-coverage": "^0.2.3",
- "@jest/console": "^28.1.3",
- "@jest/test-result": "^28.1.3",
- "@jest/transform": "^28.1.3",
- "@jest/types": "^28.1.3",
- "@jridgewell/trace-mapping": "^0.3.13",
- "@types/node": "*",
- "chalk": "^4.0.0",
- "collect-v8-coverage": "^1.0.0",
- "exit": "^0.1.2",
- "glob": "^7.1.3",
- "graceful-fs": "^4.2.9",
- "istanbul-lib-coverage": "^3.0.0",
- "istanbul-lib-instrument": "^5.1.0",
- "istanbul-lib-report": "^3.0.0",
- "istanbul-lib-source-maps": "^4.0.0",
- "istanbul-reports": "^3.1.3",
- "jest-message-util": "^28.1.3",
- "jest-util": "^28.1.3",
- "jest-worker": "^28.1.3",
- "slash": "^3.0.0",
- "string-length": "^4.0.1",
- "strip-ansi": "^6.0.0",
- "terminal-link": "^2.0.0",
- "v8-to-istanbul": "^9.0.1"
- },
- "dependencies": {
- "@jest/types": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz",
- "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==",
- "dev": true,
- "requires": {
- "@jest/schemas": "^28.1.3",
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^3.0.0",
- "@types/node": "*",
- "@types/yargs": "^17.0.8",
- "chalk": "^4.0.0"
- }
- },
- "@types/yargs": {
- "version": "17.0.10",
- "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.10.tgz",
- "integrity": "sha512-gmEaFwpj/7f/ROdtIlci1R1VYU1J4j95m8T+Tj3iBgiBFKg1foE/PSl93bBd5T9LDXNPo8UlNN6W0qwD8O5OaA==",
- "dev": true,
- "requires": {
- "@types/yargs-parser": "*"
- }
- },
- "jest-message-util": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-28.1.3.tgz",
- "integrity": "sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g==",
- "dev": true,
- "requires": {
- "@babel/code-frame": "^7.12.13",
- "@jest/types": "^28.1.3",
- "@types/stack-utils": "^2.0.0",
- "chalk": "^4.0.0",
- "graceful-fs": "^4.2.9",
- "micromatch": "^4.0.4",
- "pretty-format": "^28.1.3",
- "slash": "^3.0.0",
- "stack-utils": "^2.0.3"
- }
- },
- "jest-util": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz",
- "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==",
- "dev": true,
- "requires": {
- "@jest/types": "^28.1.3",
- "@types/node": "*",
- "chalk": "^4.0.0",
- "ci-info": "^3.2.0",
- "graceful-fs": "^4.2.9",
- "picomatch": "^2.2.3"
- }
- }
- }
- },
- "@jest/schemas": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz",
- "integrity": "sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==",
- "dev": true,
- "requires": {
- "@sinclair/typebox": "^0.24.1"
- }
- },
- "@jest/source-map": {
- "version": "28.1.2",
- "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-28.1.2.tgz",
- "integrity": "sha512-cV8Lx3BeStJb8ipPHnqVw/IM2VCMWO3crWZzYodSIkxXnRcXJipCdx1JCK0K5MsJJouZQTH73mzf4vgxRaH9ww==",
- "dev": true,
- "requires": {
- "@jridgewell/trace-mapping": "^0.3.13",
- "callsites": "^3.0.0",
- "graceful-fs": "^4.2.9"
- }
- },
- "@jest/test-result": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-28.1.3.tgz",
- "integrity": "sha512-kZAkxnSE+FqE8YjW8gNuoVkkC9I7S1qmenl8sGcDOLropASP+BkcGKwhXoyqQuGOGeYY0y/ixjrd/iERpEXHNg==",
- "dev": true,
- "requires": {
- "@jest/console": "^28.1.3",
- "@jest/types": "^28.1.3",
- "@types/istanbul-lib-coverage": "^2.0.0",
- "collect-v8-coverage": "^1.0.0"
- },
- "dependencies": {
- "@jest/types": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz",
- "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==",
- "dev": true,
- "requires": {
- "@jest/schemas": "^28.1.3",
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^3.0.0",
- "@types/node": "*",
- "@types/yargs": "^17.0.8",
- "chalk": "^4.0.0"
- }
- },
- "@types/yargs": {
- "version": "17.0.10",
- "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.10.tgz",
- "integrity": "sha512-gmEaFwpj/7f/ROdtIlci1R1VYU1J4j95m8T+Tj3iBgiBFKg1foE/PSl93bBd5T9LDXNPo8UlNN6W0qwD8O5OaA==",
- "dev": true,
- "requires": {
- "@types/yargs-parser": "*"
- }
- }
- }
- },
- "@jest/test-sequencer": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-28.1.3.tgz",
- "integrity": "sha512-NIMPEqqa59MWnDi1kvXXpYbqsfQmSJsIbnd85mdVGkiDfQ9WQQTXOLsvISUfonmnBT+w85WEgneCigEEdHDFxw==",
- "dev": true,
- "requires": {
- "@jest/test-result": "^28.1.3",
- "graceful-fs": "^4.2.9",
- "jest-haste-map": "^28.1.3",
- "slash": "^3.0.0"
- }
- },
- "@jest/transform": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-28.1.3.tgz",
- "integrity": "sha512-u5dT5di+oFI6hfcLOHGTAfmUxFRrjK+vnaP0kkVow9Md/M7V/MxqQMOz/VV25UZO8pzeA9PjfTpOu6BDuwSPQA==",
- "dev": true,
- "requires": {
- "@babel/core": "^7.11.6",
- "@jest/types": "^28.1.3",
- "@jridgewell/trace-mapping": "^0.3.13",
- "babel-plugin-istanbul": "^6.1.1",
- "chalk": "^4.0.0",
- "convert-source-map": "^1.4.0",
- "fast-json-stable-stringify": "^2.0.0",
- "graceful-fs": "^4.2.9",
- "jest-haste-map": "^28.1.3",
- "jest-regex-util": "^28.0.2",
- "jest-util": "^28.1.3",
- "micromatch": "^4.0.4",
- "pirates": "^4.0.4",
- "slash": "^3.0.0",
- "write-file-atomic": "^4.0.1"
- },
- "dependencies": {
- "@jest/types": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz",
- "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==",
- "dev": true,
- "requires": {
- "@jest/schemas": "^28.1.3",
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^3.0.0",
- "@types/node": "*",
- "@types/yargs": "^17.0.8",
- "chalk": "^4.0.0"
- }
- },
- "@types/yargs": {
- "version": "17.0.10",
- "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.10.tgz",
- "integrity": "sha512-gmEaFwpj/7f/ROdtIlci1R1VYU1J4j95m8T+Tj3iBgiBFKg1foE/PSl93bBd5T9LDXNPo8UlNN6W0qwD8O5OaA==",
- "dev": true,
- "requires": {
- "@types/yargs-parser": "*"
- }
- },
- "jest-util": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz",
- "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==",
- "dev": true,
- "requires": {
- "@jest/types": "^28.1.3",
- "@types/node": "*",
- "chalk": "^4.0.0",
- "ci-info": "^3.2.0",
- "graceful-fs": "^4.2.9",
- "picomatch": "^2.2.3"
- }
- }
- }
- },
- "@jest/types": {
- "version": "27.5.1",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-27.5.1.tgz",
- "integrity": "sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==",
- "dev": true,
- "requires": {
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^3.0.0",
- "@types/node": "*",
- "@types/yargs": "^16.0.0",
- "chalk": "^4.0.0"
- }
- },
- "@jridgewell/gen-mapping": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz",
- "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==",
- "dev": true,
- "requires": {
- "@jridgewell/set-array": "^1.0.0",
- "@jridgewell/sourcemap-codec": "^1.4.10"
- }
- },
"@jridgewell/resolve-uri": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
@@ -13770,28 +9938,41 @@
"integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw=="
},
"@jridgewell/trace-mapping": {
- "version": "0.3.14",
- "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz",
- "integrity": "sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ==",
+ "version": "0.3.16",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.16.tgz",
+ "integrity": "sha512-LCQ+NeThyJ4k1W2d+vIKdxuSt9R3pQSZ4P92m7EakaYuXcVWbHuT5bjNcqLd4Rdgi6xYWYDvBJZJLZSLanjDcA==",
"requires": {
- "@jridgewell/resolve-uri": "^3.0.3",
- "@jridgewell/sourcemap-codec": "^1.4.10"
+ "@jridgewell/resolve-uri": "3.1.0",
+ "@jridgewell/sourcemap-codec": "1.4.14"
}
},
"@jsep-plugin/regex": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/@jsep-plugin/regex/-/regex-1.0.2.tgz",
- "integrity": "sha512-Nn/Bcaww8zOebMDqNmGlhAWPWhIr/8S8lGIgaB/fSqev5xaO5uKy5i4qvTh63GpR+VzKqimgxDdcxdcRuCJXSw==",
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@jsep-plugin/regex/-/regex-1.0.3.tgz",
+ "integrity": "sha512-XfZgry4DwEZvSFtS/6Y+R48D7qJYJK6R9/yJFyUFHCIUMEEHuJ4X95TDgJp5QkmzfLYvapMPzskV5HpIDrREug==",
"dev": true,
"requires": {}
},
"@jsep-plugin/ternary": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/@jsep-plugin/ternary/-/ternary-1.1.2.tgz",
- "integrity": "sha512-gXguJc09uCrqWt1MD7L1+ChO32g4UH4BYGpHPoQRLhyU7pAPPRA7cvKbyjoqhnUlLutiXvLzB5hVVawPKax8jw==",
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/@jsep-plugin/ternary/-/ternary-1.1.3.tgz",
+ "integrity": "sha512-qtLGzCNzPVJ3kdH6/zoLWDPjauHIKiLSBAR71Wa0+PWvGA8wODUQvRgxtpUA5YqAYL3CQ8S4qXhd/9WuWTZirg==",
"dev": true,
"requires": {}
},
+ "@mcaptcha/core-glue": {
+ "version": "0.1.0-alpha-5",
+ "resolved": "https://registry.npmjs.org/@mcaptcha/core-glue/-/core-glue-0.1.0-alpha-5.tgz",
+ "integrity": "sha512-16qWm5O5X0Y9LXULULaAks8Vf9FNlUUBcR5KDt49aWhFhG5++JzxNmCwQM9EJSHNU7y0U+FdyAWcGmjfKlkRLA=="
+ },
+ "@mcaptcha/vanilla-glue": {
+ "version": "0.1.0-alpha-3",
+ "resolved": "https://registry.npmjs.org/@mcaptcha/vanilla-glue/-/vanilla-glue-0.1.0-alpha-3.tgz",
+ "integrity": "sha512-GT6TJBgmViGXcXiT5VOr+h/6iOnThSlZuCoOWncubyTZU9R3cgU5vWPkF7G6Ob6ee2CBe3yqBxxk24CFVGTVXw==",
+ "requires": {
+ "@mcaptcha/core-glue": "^0.1.0-alpha-5"
+ }
+ },
"@nodelib/fs.scandir": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@@ -13815,15 +9996,25 @@
"fastq": "^1.6.0"
}
},
+ "@playwright/test": {
+ "version": "1.27.0",
+ "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.27.0.tgz",
+ "integrity": "sha512-L4BswoJvGkFsEHhEgzVNHBnkFB1FbnBQn3QmvTl7+AouoJQ4a8tLwZKvytdovCsNi7B5cXuRo58yGvfM5PnExw==",
+ "dev": true,
+ "requires": {
+ "@types/node": "*",
+ "playwright-core": "1.27.0"
+ }
+ },
"@popperjs/core": {
- "version": "2.11.5",
- "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.5.tgz",
- "integrity": "sha512-9X2obfABZuDVLCgPK9aX0a/x4jaOEweTTWE2+9sr0Qqqevj2Uv5XorvusThmc9XGYpS9yI+fhh8RTafBtGposw=="
+ "version": "2.11.6",
+ "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.6.tgz",
+ "integrity": "sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw=="
},
"@primer/octicons": {
- "version": "17.3.0",
- "resolved": "https://registry.npmjs.org/@primer/octicons/-/octicons-17.3.0.tgz",
- "integrity": "sha512-4zPwwloYWdR6RznMafV7Fsw3n2CeDPp/+qEIQbaX/tBbPY1KmU0OAXmhRfhD5AzgB5kdV1aQ7KnQr1GeQXl9Dg==",
+ "version": "17.7.0",
+ "resolved": "https://registry.npmjs.org/@primer/octicons/-/octicons-17.7.0.tgz",
+ "integrity": "sha512-J5cVJDhExmqLGLWu8zHTOqcC8g1rQL7QzQZdbvHxW85u8ya82GtF5F68uHMDI5En3fsMlbkkF8Rz6dCaV3r+KA==",
"requires": {
"object-assign": "^4.1.1"
}
@@ -13841,55 +10032,52 @@
"is-reference": "^1.2.1",
"magic-string": "^0.25.7",
"resolve": "^1.17.0"
+ },
+ "dependencies": {
+ "@rollup/pluginutils": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz",
+ "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==",
+ "dev": true,
+ "requires": {
+ "@types/estree": "0.0.39",
+ "estree-walker": "^1.0.1",
+ "picomatch": "^2.2.2"
+ },
+ "dependencies": {
+ "estree-walker": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz",
+ "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==",
+ "dev": true
+ }
+ }
+ }
}
},
"@rollup/pluginutils": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz",
- "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==",
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.0.1.tgz",
+ "integrity": "sha512-4HaCVEXXuObvcPUaUlLt4faHYHCeQOOWNj8NKFGaRSrw3ZLD0TWeAFZicV9vXjnE2nkNuaVTfTuwAnjR+6uc9A==",
"dev": true,
"requires": {
- "@types/estree": "0.0.39",
- "estree-walker": "^1.0.1",
- "picomatch": "^2.2.2"
+ "@types/estree": "^1.0.0",
+ "estree-walker": "^2.0.2",
+ "picomatch": "^2.3.1"
},
"dependencies": {
- "estree-walker": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz",
- "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==",
+ "@types/estree": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.0.tgz",
+ "integrity": "sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==",
"dev": true
}
}
},
- "@sinclair/typebox": {
- "version": "0.24.20",
- "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.20.tgz",
- "integrity": "sha512-kVaO5aEFZb33nPMTZBxiPEkY+slxiPtqC7QX8f9B3eGOMBvEfuMfxp9DSTTCsRJPumPKjrge4yagyssO4q6qzQ==",
- "dev": true
- },
- "@sinonjs/commons": {
- "version": "1.8.3",
- "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz",
- "integrity": "sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==",
- "dev": true,
- "requires": {
- "type-detect": "4.0.8"
- }
- },
- "@sinonjs/fake-timers": {
- "version": "8.1.0",
- "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz",
- "integrity": "sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==",
- "dev": true,
- "requires": {
- "@sinonjs/commons": "^1.7.0"
- }
- },
"@stoplight/better-ajv-errors": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/@stoplight/better-ajv-errors/-/better-ajv-errors-1.0.1.tgz",
- "integrity": "sha512-rgxT+ZMeZbYRiOLNk6Oy6e/Ig1iQKo0IL8v/Y9E/0FewzgtkGs/p5dMeUpIFZXWj3RZaEPmfL9yh0oUEmNXZjg==",
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@stoplight/better-ajv-errors/-/better-ajv-errors-1.0.3.tgz",
+ "integrity": "sha512-0p9uXkuB22qGdNfy3VeEhxkU5uwvp/KrBTAbrLBURv6ilxIVwanKwjMc41lQfIVgPGcOkmLbTolfFrSsueu7zA==",
"dev": true,
"requires": {
"jsonpointer": "^5.0.0",
@@ -13946,19 +10134,10 @@
"urijs": "^1.19.6"
}
},
- "@stoplight/lifecycle": {
- "version": "2.3.2",
- "resolved": "https://registry.npmjs.org/@stoplight/lifecycle/-/lifecycle-2.3.2.tgz",
- "integrity": "sha512-v0u8p27FA/eg04b4z6QXw4s0NeeFcRzyvseBW0+k/q4jtpg7EhVCqy42EbbbU43NTNDpIeQ81OcvkFz+6CYshw==",
- "dev": true,
- "requires": {
- "wolfy87-eventemitter": "~5.2.8"
- }
- },
"@stoplight/ordered-object-literal": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/@stoplight/ordered-object-literal/-/ordered-object-literal-1.0.3.tgz",
- "integrity": "sha512-cjJ7PPkhgTXNMTkevAlmyrx9xOOCaI3c6rEeYb6VitL1o1WcZtrz9KyFyISmTmUa7yYTiy2IS/ud9S8s2sn3+A==",
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/@stoplight/ordered-object-literal/-/ordered-object-literal-1.0.4.tgz",
+ "integrity": "sha512-OF8uib1jjDs5/cCU+iOVy+GJjU3X7vk/qJIkIJFqwmlJKrrtijFmqwbu8XToXrwTYLQTP+Hebws5gtZEmk9jag==",
"dev": true
},
"@stoplight/path": {
@@ -13968,9 +10147,9 @@
"dev": true
},
"@stoplight/spectral-cli": {
- "version": "6.4.1",
- "resolved": "https://registry.npmjs.org/@stoplight/spectral-cli/-/spectral-cli-6.4.1.tgz",
- "integrity": "sha512-l5nWXy/6YEyk51VVrOurhupVScIqfK0ra8yIRSli+gnW5Kf5Nfw5PLci5GceQGaM5WE+wmqZ/iY95yOVFwHc+A==",
+ "version": "6.5.1",
+ "resolved": "https://registry.npmjs.org/@stoplight/spectral-cli/-/spectral-cli-6.5.1.tgz",
+ "integrity": "sha512-+qpwsDG2jQ4ULQmegBWonI3UnF6tUh351WDnV1GU8acl8eaeKbS+ZUNBgoP2f9tnMTfITdctVRFEGC3D6P7f9g==",
"dev": true,
"requires": {
"@rollup/plugin-commonjs": "^20.0.0",
@@ -14023,25 +10202,26 @@
}
},
"@stoplight/spectral-core": {
- "version": "1.12.3",
- "resolved": "https://registry.npmjs.org/@stoplight/spectral-core/-/spectral-core-1.12.3.tgz",
- "integrity": "sha512-+PhVzTD8q6kUZw4BcbM+ibVaH5/ELryKt5tlLitA8SJIaJ+5/9ZKaGN0AV3ExZQZGYvXwucPOQuJKYZYKA6mWg==",
+ "version": "1.14.2",
+ "resolved": "https://registry.npmjs.org/@stoplight/spectral-core/-/spectral-core-1.14.2.tgz",
+ "integrity": "sha512-W2Z31lasEICpZS50STFskOdkn4g0Va81XA1A88LIj9mvlctDFf4BfpjLgIjKkI4f2DEK5C4j3COcbbzV8y5Xig==",
"dev": true,
"requires": {
- "@stoplight/better-ajv-errors": "1.0.1",
- "@stoplight/json": "~3.18.1",
- "@stoplight/lifecycle": "2.3.2",
+ "@stoplight/better-ajv-errors": "1.0.3",
+ "@stoplight/json": "~3.20.1",
"@stoplight/path": "1.3.2",
"@stoplight/spectral-parsers": "^1.0.0",
"@stoplight/spectral-ref-resolver": "^1.0.0",
"@stoplight/spectral-runtime": "^1.0.0",
- "@stoplight/types": "~13.2.0",
+ "@stoplight/types": "~13.6.0",
+ "@types/es-aggregate-error": "^1.0.2",
"@types/json-schema": "^7.0.11",
"ajv": "^8.6.0",
"ajv-errors": "~3.0.0",
"ajv-formats": "~2.1.0",
"blueimp-md5": "2.18.0",
- "jsonpath-plus": "6.0.1",
+ "es-aggregate-error": "^1.0.7",
+ "jsonpath-plus": "7.1.0",
"lodash": "~4.17.21",
"lodash.topath": "^4.5.2",
"minimatch": "3.1.2",
@@ -14052,22 +10232,23 @@
},
"dependencies": {
"@stoplight/json": {
- "version": "3.18.1",
- "resolved": "https://registry.npmjs.org/@stoplight/json/-/json-3.18.1.tgz",
- "integrity": "sha512-QmELAqBS8DC+8YuG7+OvDVP6RaUVi8bzN0KKW2UEcZg+0a1sqeeZgfW079AmJIZg8HEN7udAt4iozIB8Dm0t1Q==",
+ "version": "3.20.1",
+ "resolved": "https://registry.npmjs.org/@stoplight/json/-/json-3.20.1.tgz",
+ "integrity": "sha512-FXfud+uWgIj1xv6nUO9WnmgmnVikaxJcbtR4XQt4C42n5c2qua3U05Z/3B57hP5TJRSj+tpn9ID6/bFeyYYlEg==",
"dev": true,
"requires": {
- "@stoplight/ordered-object-literal": "^1.0.2",
- "@stoplight/types": "^13.0.0",
+ "@stoplight/ordered-object-literal": "^1.0.3",
+ "@stoplight/path": "^1.3.2",
+ "@stoplight/types": "^13.6.0",
"jsonc-parser": "~2.2.1",
"lodash": "^4.17.21",
"safe-stable-stringify": "^1.1"
}
},
"@stoplight/types": {
- "version": "13.2.0",
- "resolved": "https://registry.npmjs.org/@stoplight/types/-/types-13.2.0.tgz",
- "integrity": "sha512-V3BRfzWEAcCpICGQh/WW2LX4rcB2KagQ7/msf0BDTCF5qpFMSwOxcjv25k1NUMVQSh3qwGfGoka/gYGA5m+NQA==",
+ "version": "13.6.0",
+ "resolved": "https://registry.npmjs.org/@stoplight/types/-/types-13.6.0.tgz",
+ "integrity": "sha512-dzyuzvUjv3m1wmhPfq82lCVYGcXG0xUYgqnWfCq3PCVR4BKFhjdkHrnJ+jIDoMKvXb05AZP/ObQF6+NpDo29IQ==",
"dev": true,
"requires": {
"@types/json-schema": "^7.0.4",
@@ -14077,9 +10258,9 @@
}
},
"@stoplight/spectral-formats": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/@stoplight/spectral-formats/-/spectral-formats-1.2.0.tgz",
- "integrity": "sha512-idvn7r8fvQjY/KeJpKgXQ5eJhce6N6/KoKWMPSh5yyvYDpn+bkU4pxAD79jOJaDnIyKJd1jjTPEJWnxbS0jj6A==",
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/@stoplight/spectral-formats/-/spectral-formats-1.4.0.tgz",
+ "integrity": "sha512-j9VQukDzgqDSi26rK9LqsbXrqtkeIsPSPgEf5/sxRsmeF2bwWUhSjYXgYin4flSZ7owFZjZWQ3o0Qq3iApi2JQ==",
"dev": true,
"requires": {
"@stoplight/json": "^3.17.0",
@@ -14089,17 +10270,16 @@
}
},
"@stoplight/spectral-functions": {
- "version": "1.6.1",
- "resolved": "https://registry.npmjs.org/@stoplight/spectral-functions/-/spectral-functions-1.6.1.tgz",
- "integrity": "sha512-f4cFtbI35bQtY0t4fYhKtS+/nMU3UsAeFlqm4tARGGG5WjOv4ieCFNFbgodKNiO3F4O+syMEjVQuXlBNPuY7jw==",
+ "version": "1.7.1",
+ "resolved": "https://registry.npmjs.org/@stoplight/spectral-functions/-/spectral-functions-1.7.1.tgz",
+ "integrity": "sha512-UWeUrxc1pu45ZNYKtK3OloMpkUNTPqwpmjbGUn4oEnbqrLEYu/B2oOg66EtGcadOBEsdOb7f5vaPlhUNNrpEpQ==",
"dev": true,
"requires": {
- "@stoplight/better-ajv-errors": "1.0.1",
- "@stoplight/json": "~3.17.1",
+ "@stoplight/better-ajv-errors": "1.0.3",
+ "@stoplight/json": "^3.17.1",
"@stoplight/spectral-core": "^1.7.0",
"@stoplight/spectral-formats": "^1.0.0",
"@stoplight/spectral-runtime": "^1.1.0",
- "@stoplight/types": "12.3.0",
"ajv": "^8.6.3",
"ajv-draft-04": "~1.0.0",
"ajv-errors": "~3.0.0",
@@ -14109,30 +10289,67 @@
},
"dependencies": {
"@stoplight/json": {
- "version": "3.17.2",
- "resolved": "https://registry.npmjs.org/@stoplight/json/-/json-3.17.2.tgz",
- "integrity": "sha512-NwIVzanXRUy291J5BMkncCZRMG1Lx+aq+VidGQgfkJjgo8vh1Y/PSAz7fSU8gVGSZBCcqmOkMI7R4zw7DlfTwA==",
+ "version": "3.20.1",
+ "resolved": "https://registry.npmjs.org/@stoplight/json/-/json-3.20.1.tgz",
+ "integrity": "sha512-FXfud+uWgIj1xv6nUO9WnmgmnVikaxJcbtR4XQt4C42n5c2qua3U05Z/3B57hP5TJRSj+tpn9ID6/bFeyYYlEg==",
"dev": true,
"requires": {
- "@stoplight/ordered-object-literal": "^1.0.2",
- "@stoplight/types": "^12.3.0",
+ "@stoplight/ordered-object-literal": "^1.0.3",
+ "@stoplight/path": "^1.3.2",
+ "@stoplight/types": "^13.6.0",
"jsonc-parser": "~2.2.1",
"lodash": "^4.17.21",
"safe-stable-stringify": "^1.1"
}
+ },
+ "@stoplight/types": {
+ "version": "13.7.0",
+ "resolved": "https://registry.npmjs.org/@stoplight/types/-/types-13.7.0.tgz",
+ "integrity": "sha512-7ePIccfTxjEhruv8VrkDv5whP5qd9ijRzAWEbjYpUYnDfaqPTfq8/wMMjMCAKIecboxsAVD9LZy/3puXddGsDQ==",
+ "dev": true,
+ "requires": {
+ "@types/json-schema": "^7.0.4",
+ "utility-types": "^3.10.0"
+ }
}
}
},
"@stoplight/spectral-parsers": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/@stoplight/spectral-parsers/-/spectral-parsers-1.0.1.tgz",
- "integrity": "sha512-JGKlrTxhjUzIGo2FOCf8Qp0WKTWXedoRNPovqYPE8pAp08epqU8DzHwl/i46BGH5yfTmouKMZgBN/PV2+Cr5jw==",
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@stoplight/spectral-parsers/-/spectral-parsers-1.0.2.tgz",
+ "integrity": "sha512-ZQXknJ+BM5Re4Opj4cgVlHgG2qyOk/wznKJq3Vf1qsBEg2CNzN0pJmSB0deRqW0kArqm44qpb8c+cz3F2rgMtw==",
"dev": true,
"requires": {
- "@stoplight/json": "3.17.0",
- "@stoplight/types": "12.3.0",
- "@stoplight/yaml": "4.2.2",
+ "@stoplight/json": "~3.20.1",
+ "@stoplight/types": "^13.6.0",
+ "@stoplight/yaml": "~4.2.3",
"tslib": "^2.3.1"
+ },
+ "dependencies": {
+ "@stoplight/json": {
+ "version": "3.20.1",
+ "resolved": "https://registry.npmjs.org/@stoplight/json/-/json-3.20.1.tgz",
+ "integrity": "sha512-FXfud+uWgIj1xv6nUO9WnmgmnVikaxJcbtR4XQt4C42n5c2qua3U05Z/3B57hP5TJRSj+tpn9ID6/bFeyYYlEg==",
+ "dev": true,
+ "requires": {
+ "@stoplight/ordered-object-literal": "^1.0.3",
+ "@stoplight/path": "^1.3.2",
+ "@stoplight/types": "^13.6.0",
+ "jsonc-parser": "~2.2.1",
+ "lodash": "^4.17.21",
+ "safe-stable-stringify": "^1.1"
+ }
+ },
+ "@stoplight/types": {
+ "version": "13.7.0",
+ "resolved": "https://registry.npmjs.org/@stoplight/types/-/types-13.7.0.tgz",
+ "integrity": "sha512-7ePIccfTxjEhruv8VrkDv5whP5qd9ijRzAWEbjYpUYnDfaqPTfq8/wMMjMCAKIecboxsAVD9LZy/3puXddGsDQ==",
+ "dev": true,
+ "requires": {
+ "@types/json-schema": "^7.0.4",
+ "utility-types": "^3.10.0"
+ }
+ }
}
},
"@stoplight/spectral-ref-resolver": {
@@ -14149,33 +10366,33 @@
}
},
"@stoplight/spectral-ruleset-bundler": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/@stoplight/spectral-ruleset-bundler/-/spectral-ruleset-bundler-1.3.0.tgz",
- "integrity": "sha512-6Tif7GQL18F0LN1+FhEmhFWgE/TiWudb/pFl4DC7oS1QRoutB7QJPqIfVFSmteToPidxlrIbC6VAXSyEhlpDVQ==",
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/@stoplight/spectral-ruleset-bundler/-/spectral-ruleset-bundler-1.3.2.tgz",
+ "integrity": "sha512-sy7mHVBwmo5/8dUlnWiel2UND1Mnu3x+okBAgLmkGcIpXz74rMmVY3h5vT6rjxw65WZ3/c3mtm1dRQuSe+q5fw==",
"dev": true,
"requires": {
- "@rollup/plugin-commonjs": "^21.0.1",
+ "@rollup/plugin-commonjs": "~22.0.2",
"@stoplight/path": "1.3.2",
"@stoplight/spectral-core": ">=1",
"@stoplight/spectral-formats": ">=1",
"@stoplight/spectral-functions": ">=1",
"@stoplight/spectral-parsers": ">=1",
"@stoplight/spectral-ref-resolver": ">=1",
- "@stoplight/spectral-ruleset-migrator": "^1.5.2",
+ "@stoplight/spectral-ruleset-migrator": "^1.7.4",
"@stoplight/spectral-rulesets": ">=1",
"@stoplight/spectral-runtime": "^1.1.0",
- "@stoplight/types": "^12.3.0",
+ "@stoplight/types": "^13.6.0",
"@types/node": "*",
"pony-cause": "1.1.1",
- "rollup": "~2.67.0",
+ "rollup": "~2.79.0",
"tslib": "^2.3.1",
"validate-npm-package-name": "3.0.0"
},
"dependencies": {
"@rollup/plugin-commonjs": {
- "version": "21.1.0",
- "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-21.1.0.tgz",
- "integrity": "sha512-6ZtHx3VHIp2ReNNDxHjuUml6ur+WcQ28N1yHgCQwsbNkQg2suhxGMDQGJOn/KuDxKtd1xuZP5xSTwBA4GQ8hbA==",
+ "version": "22.0.2",
+ "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-22.0.2.tgz",
+ "integrity": "sha512-//NdP6iIwPbMTcazYsiBMbJW7gfmpHom33u1beiIoHDEM0Q9clvtQB1T0efvMqHeKsGohiHo97BCPCkBXdscwg==",
"dev": true,
"requires": {
"@rollup/pluginutils": "^3.1.0",
@@ -14187,30 +10404,50 @@
"resolve": "^1.17.0"
}
},
- "rollup": {
- "version": "2.67.3",
- "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.67.3.tgz",
- "integrity": "sha512-G/x1vUwbGtP6O5ZM8/sWr8+p7YfZhI18pPqMRtMYMWSbHjKZ/ajHGiM+GWNTlWyOR0EHIdT8LHU+Z4ciIZ1oBw==",
+ "@rollup/pluginutils": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz",
+ "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==",
"dev": true,
"requires": {
- "fsevents": "~2.3.2"
+ "@types/estree": "0.0.39",
+ "estree-walker": "^1.0.1",
+ "picomatch": "^2.2.2"
+ },
+ "dependencies": {
+ "estree-walker": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz",
+ "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==",
+ "dev": true
+ }
+ }
+ },
+ "@stoplight/types": {
+ "version": "13.7.0",
+ "resolved": "https://registry.npmjs.org/@stoplight/types/-/types-13.7.0.tgz",
+ "integrity": "sha512-7ePIccfTxjEhruv8VrkDv5whP5qd9ijRzAWEbjYpUYnDfaqPTfq8/wMMjMCAKIecboxsAVD9LZy/3puXddGsDQ==",
+ "dev": true,
+ "requires": {
+ "@types/json-schema": "^7.0.4",
+ "utility-types": "^3.10.0"
}
}
}
},
"@stoplight/spectral-ruleset-migrator": {
- "version": "1.7.3",
- "resolved": "https://registry.npmjs.org/@stoplight/spectral-ruleset-migrator/-/spectral-ruleset-migrator-1.7.3.tgz",
- "integrity": "sha512-1TlJgNxIqlcafzrH6gsGpQQcVkFhndib5piMNXVg9xshJ42l2yC6A0AUAixUC+ODJ5098DR7SjIYBVKk+CTQSw==",
+ "version": "1.7.4",
+ "resolved": "https://registry.npmjs.org/@stoplight/spectral-ruleset-migrator/-/spectral-ruleset-migrator-1.7.4.tgz",
+ "integrity": "sha512-QySMWSvGUC5D8cNDvXhrXEY0a4DB5hewHwjxXbwlH51fVNiVKJ4+KcaCW3s2yAT4T1p6/ij8NkLX9T81D4vSCg==",
"dev": true,
"requires": {
- "@stoplight/json": "~3.17.0",
- "@stoplight/ordered-object-literal": "1.0.2",
+ "@stoplight/json": "~3.20.1",
+ "@stoplight/ordered-object-literal": "~1.0.4",
"@stoplight/path": "1.3.2",
"@stoplight/spectral-functions": "^1.0.0",
"@stoplight/spectral-runtime": "^1.1.0",
- "@stoplight/types": "^12.3.0",
- "@stoplight/yaml": "4.2.2",
+ "@stoplight/types": "^13.6.0",
+ "@stoplight/yaml": "~4.2.3",
"@types/node": "*",
"ajv": "^8.6.0",
"ast-types": "0.14.2",
@@ -14220,28 +10457,46 @@
"validate-npm-package-name": "3.0.0"
},
"dependencies": {
- "@stoplight/ordered-object-literal": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/@stoplight/ordered-object-literal/-/ordered-object-literal-1.0.2.tgz",
- "integrity": "sha512-0ZMS/9sNU3kVo/6RF3eAv7MK9DY8WLjiVJB/tVyfF2lhr2R4kqh534jZ0PlrFB9CRXrdndzn1DbX6ihKZXft2w==",
- "dev": true
+ "@stoplight/json": {
+ "version": "3.20.1",
+ "resolved": "https://registry.npmjs.org/@stoplight/json/-/json-3.20.1.tgz",
+ "integrity": "sha512-FXfud+uWgIj1xv6nUO9WnmgmnVikaxJcbtR4XQt4C42n5c2qua3U05Z/3B57hP5TJRSj+tpn9ID6/bFeyYYlEg==",
+ "dev": true,
+ "requires": {
+ "@stoplight/ordered-object-literal": "^1.0.3",
+ "@stoplight/path": "^1.3.2",
+ "@stoplight/types": "^13.6.0",
+ "jsonc-parser": "~2.2.1",
+ "lodash": "^4.17.21",
+ "safe-stable-stringify": "^1.1"
+ }
+ },
+ "@stoplight/types": {
+ "version": "13.7.0",
+ "resolved": "https://registry.npmjs.org/@stoplight/types/-/types-13.7.0.tgz",
+ "integrity": "sha512-7ePIccfTxjEhruv8VrkDv5whP5qd9ijRzAWEbjYpUYnDfaqPTfq8/wMMjMCAKIecboxsAVD9LZy/3puXddGsDQ==",
+ "dev": true,
+ "requires": {
+ "@types/json-schema": "^7.0.4",
+ "utility-types": "^3.10.0"
+ }
}
}
},
"@stoplight/spectral-rulesets": {
- "version": "1.11.0",
- "resolved": "https://registry.npmjs.org/@stoplight/spectral-rulesets/-/spectral-rulesets-1.11.0.tgz",
- "integrity": "sha512-0zFbxIuoWmGrkl2txOuaEDF8o6aoKDpMAYOG2oDfmmX9FhXX3c3ivIy80hyb2tMKkIYuqqx/zwIiOuww5S8iUA==",
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/@stoplight/spectral-rulesets/-/spectral-rulesets-1.14.1.tgz",
+ "integrity": "sha512-tn6a5fYPFDwEY+/YyK/hcq2gcR5nSIBt7l+JGELb/2RdTzD5ikj2mfl2ua3uxbqOZytftFoOX5ewGZ0qQNrudw==",
"dev": true,
"requires": {
- "@asyncapi/specs": "^2.14.0",
- "@stoplight/better-ajv-errors": "1.0.1",
+ "@asyncapi/specs": "^3.2.0",
+ "@stoplight/better-ajv-errors": "1.0.3",
"@stoplight/json": "^3.17.0",
"@stoplight/spectral-core": "^1.8.1",
- "@stoplight/spectral-formats": "^1.2.0",
+ "@stoplight/spectral-formats": "^1.4.0",
"@stoplight/spectral-functions": "^1.5.1",
"@stoplight/spectral-runtime": "^1.1.1",
- "@stoplight/types": "^12.5.0",
+ "@stoplight/types": "^13.6.0",
"@types/json-schema": "^7.0.7",
"ajv": "^8.8.2",
"ajv-formats": "~2.1.0",
@@ -14251,9 +10506,9 @@
},
"dependencies": {
"@stoplight/types": {
- "version": "12.5.0",
- "resolved": "https://registry.npmjs.org/@stoplight/types/-/types-12.5.0.tgz",
- "integrity": "sha512-dwqYcDrGmEyUv5TWrDam5TGOxU72ufyQ7hnOIIDdmW5ezOwZaBFoR5XQ9AsH49w7wgvOqB2Bmo799pJPWnpCbg==",
+ "version": "13.7.0",
+ "resolved": "https://registry.npmjs.org/@stoplight/types/-/types-13.7.0.tgz",
+ "integrity": "sha512-7ePIccfTxjEhruv8VrkDv5whP5qd9ijRzAWEbjYpUYnDfaqPTfq8/wMMjMCAKIecboxsAVD9LZy/3puXddGsDQ==",
"dev": true,
"requires": {
"@types/json-schema": "^7.0.4",
@@ -14288,15 +10543,27 @@
}
},
"@stoplight/yaml": {
- "version": "4.2.2",
- "resolved": "https://registry.npmjs.org/@stoplight/yaml/-/yaml-4.2.2.tgz",
- "integrity": "sha512-N086FU8pmSpjc5TvMBjmlTniZVh3OXzmEh6SYljSLiuv6aMxgjyjf13YrAlUqgu0b4b6pQ5zmkjrfo9i0SiLsw==",
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/@stoplight/yaml/-/yaml-4.2.3.tgz",
+ "integrity": "sha512-Mx01wjRAR9C7yLMUyYFTfbUf5DimEpHMkRDQ1PKLe9dfNILbgdxyrncsOXM3vCpsQ1Hfj4bPiGl+u4u6e9Akqw==",
"dev": true,
"requires": {
"@stoplight/ordered-object-literal": "^1.0.1",
- "@stoplight/types": "^12.0.0",
+ "@stoplight/types": "^13.0.0",
"@stoplight/yaml-ast-parser": "0.0.48",
"tslib": "^2.2.0"
+ },
+ "dependencies": {
+ "@stoplight/types": {
+ "version": "13.7.0",
+ "resolved": "https://registry.npmjs.org/@stoplight/types/-/types-13.7.0.tgz",
+ "integrity": "sha512-7ePIccfTxjEhruv8VrkDv5whP5qd9ijRzAWEbjYpUYnDfaqPTfq8/wMMjMCAKIecboxsAVD9LZy/3puXddGsDQ==",
+ "dev": true,
+ "requires": {
+ "@types/json-schema": "^7.0.4",
+ "utility-types": "^3.10.0"
+ }
+ }
}
},
"@stoplight/yaml-ast-parser": {
@@ -14311,9 +10578,9 @@
"integrity": "sha512-wpCQMhf5p5GhNg2MmGKXzUNwxe7zRiCsmqYsamez2beP7mKPCSiu+BjZcdN95yYSzO857kr0VfQewmGpS77nqA=="
},
"@tootallnate/once": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz",
- "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==",
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz",
+ "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==",
"dev": true
},
"@trysound/sax": {
@@ -14322,45 +10589,19 @@
"integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==",
"dev": true
},
- "@types/babel__core": {
- "version": "7.1.19",
- "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.19.tgz",
- "integrity": "sha512-WEOTgRsbYkvA/KCsDwVEGkd7WAr1e3g31VHQ8zy5gul/V1qKullU/BU5I68X5v7V3GnB9eotmom4v5a5gjxorw==",
- "dev": true,
- "requires": {
- "@babel/parser": "^7.1.0",
- "@babel/types": "^7.0.0",
- "@types/babel__generator": "*",
- "@types/babel__template": "*",
- "@types/babel__traverse": "*"
- }
+ "@types/chai": {
+ "version": "4.3.3",
+ "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.3.tgz",
+ "integrity": "sha512-hC7OMnszpxhZPduX+m+nrx+uFoLkWOMiR4oa/AZF3MuSETYTZmFfJAHqZEM8MVlvfG7BEUcgvtwoCTxBp6hm3g==",
+ "dev": true
},
- "@types/babel__generator": {
- "version": "7.6.4",
- "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz",
- "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==",
+ "@types/chai-subset": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/@types/chai-subset/-/chai-subset-1.3.3.tgz",
+ "integrity": "sha512-frBecisrNGz+F4T6bcc+NLeolfiojh5FxW2klu669+8BARtyQv2C/GkNW6FUodVe4BroGMP/wER/YDGc7rEllw==",
"dev": true,
"requires": {
- "@babel/types": "^7.0.0"
- }
- },
- "@types/babel__template": {
- "version": "7.4.1",
- "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz",
- "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==",
- "dev": true,
- "requires": {
- "@babel/parser": "^7.1.0",
- "@babel/types": "^7.0.0"
- }
- },
- "@types/babel__traverse": {
- "version": "7.17.1",
- "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.17.1.tgz",
- "integrity": "sha512-kVzjari1s2YVi77D3w1yuvohV2idweYXMCDzqBiVNN63TcDWrIlTVOYpqVrvbbyOE/IyzBoTKF0fdnLPEORFxA==",
- "dev": true,
- "requires": {
- "@babel/types": "^7.3.0"
+ "@types/chai": "*"
}
},
"@types/codemirror": {
@@ -14371,19 +10612,19 @@
"@types/tern": "*"
}
},
- "@types/concat-stream": {
- "version": "1.6.1",
- "resolved": "https://registry.npmjs.org/@types/concat-stream/-/concat-stream-1.6.1.tgz",
- "integrity": "sha512-eHE4cQPoj6ngxBZMvVf6Hw7Mh4jMW4U9lpGmS5GBPB9RYxlFg+CHaVN7ErNY4W9XfLIEn20b4VDYaIrbq0q4uA==",
+ "@types/es-aggregate-error": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@types/es-aggregate-error/-/es-aggregate-error-1.0.2.tgz",
+ "integrity": "sha512-erqUpFXksaeR2kejKnhnjZjbFxUpGZx4Z7ydNL9ie8tEhXPiZTsLeUDJ6aR1F8j5wWUAtOAQWUqkc7givBJbBA==",
"dev": true,
"requires": {
"@types/node": "*"
}
},
"@types/eslint": {
- "version": "8.4.5",
- "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.5.tgz",
- "integrity": "sha512-dhsC09y1gpJWnK+Ff4SGvCuSnk9DaU0BJZSzOwa6GVSg65XtTugLBITDAAzRU5duGBoXBHpdR/9jHGxJjNflJQ==",
+ "version": "8.4.6",
+ "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.6.tgz",
+ "integrity": "sha512-/fqTbjxyFUaYNO7VcW5g+4npmqVACz1bB7RTHYuLj+PRjw9hrCwrUXVQFpChUS0JsyEFvMZ7U/PfmvWgxJhI9g==",
"requires": {
"@types/estree": "*",
"@types/json-schema": "*"
@@ -14403,48 +10644,6 @@
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz",
"integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw=="
},
- "@types/form-data": {
- "version": "0.0.33",
- "resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-0.0.33.tgz",
- "integrity": "sha512-8BSvG1kGm83cyJITQMZSulnl6QV8jqAGreJsc5tPu1Jq0vTSOiY/k24Wx82JRpWwZSqrala6sd5rWi6aNXvqcw==",
- "dev": true,
- "requires": {
- "@types/node": "*"
- }
- },
- "@types/graceful-fs": {
- "version": "4.1.5",
- "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz",
- "integrity": "sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==",
- "dev": true,
- "requires": {
- "@types/node": "*"
- }
- },
- "@types/istanbul-lib-coverage": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz",
- "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==",
- "dev": true
- },
- "@types/istanbul-lib-report": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz",
- "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==",
- "dev": true,
- "requires": {
- "@types/istanbul-lib-coverage": "*"
- }
- },
- "@types/istanbul-reports": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz",
- "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==",
- "dev": true,
- "requires": {
- "@types/istanbul-lib-report": "*"
- }
- },
"@types/json-schema": {
"version": "7.0.11",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz",
@@ -14457,9 +10656,9 @@
"dev": true
},
"@types/marked": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/@types/marked/-/marked-4.0.3.tgz",
- "integrity": "sha512-HnMWQkLJEf/PnxZIfbm0yGJRRZYYMhb++O9M36UCTA9z53uPvVoSlAwJr3XOpDEryb7Hwl1qAx/MV6YIW1RXxg=="
+ "version": "4.0.7",
+ "resolved": "https://registry.npmjs.org/@types/marked/-/marked-4.0.7.tgz",
+ "integrity": "sha512-eEAhnz21CwvKVW+YvRvcTuFKNU9CV1qH+opcgVK3pIMI6YZzDm6gc8o2vHjldFk6MGKt5pueSB7IOpvpx5Qekw=="
},
"@types/minimist": {
"version": "1.2.2",
@@ -14468,9 +10667,9 @@
"dev": true
},
"@types/node": {
- "version": "18.0.6",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-18.0.6.tgz",
- "integrity": "sha512-/xUq6H2aQm261exT6iZTMifUySEt4GR5KX8eYyY+C4MSNPqSh9oNIP7tz2GLKTlFaiBbgZNxffoR3CVRG+cljw=="
+ "version": "18.8.3",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-18.8.3.tgz",
+ "integrity": "sha512-0os9vz6BpGwxGe9LOhgP/ncvYN5Tx1fNcd2TM3rD/aCGBkysb+ZWpXEocG24h6ZzOi13+VB8HndAQFezsSOw1w=="
},
"@types/normalize-package-data": {
"version": "2.4.1",
@@ -14484,24 +10683,6 @@
"integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==",
"dev": true
},
- "@types/prettier": {
- "version": "2.6.3",
- "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.6.3.tgz",
- "integrity": "sha512-ymZk3LEC/fsut+/Q5qejp6R9O1rMxz3XaRHDV6kX8MrGAhOSPqVARbDi+EZvInBpw+BnCX3TD240byVkOfQsHg==",
- "dev": true
- },
- "@types/qs": {
- "version": "6.9.7",
- "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz",
- "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==",
- "dev": true
- },
- "@types/stack-utils": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz",
- "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==",
- "dev": true
- },
"@types/tern": {
"version": "0.23.4",
"resolved": "https://registry.npmjs.org/@types/tern/-/tern-0.23.4.tgz",
@@ -14516,67 +10697,105 @@
"integrity": "sha512-FDJNkyhmKLw7uEvTxx5tSXfPeQpO0iy73Ry+PmYZJvQy0QIWX8a7kJ4kLWRf+EbTPJEPDSgPXHaM7pzr5lmvCg==",
"dev": true
},
- "@types/yargs": {
- "version": "16.0.4",
- "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz",
- "integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==",
- "dev": true,
+ "@vue/compiler-core": {
+ "version": "3.2.40",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.40.tgz",
+ "integrity": "sha512-2Dc3Stk0J/VyQ4OUr2yEC53kU28614lZS+bnrCbFSAIftBJ40g/2yQzf4mPBiFuqguMB7hyHaujdgZAQ67kZYA==",
"requires": {
- "@types/yargs-parser": "*"
+ "@babel/parser": "^7.16.4",
+ "@vue/shared": "3.2.40",
+ "estree-walker": "^2.0.2",
+ "source-map": "^0.6.1"
}
},
- "@types/yargs-parser": {
- "version": "21.0.0",
- "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz",
- "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==",
- "dev": true
- },
- "@vue/component-compiler-utils": {
- "version": "3.3.0",
- "resolved": "https://registry.npmjs.org/@vue/component-compiler-utils/-/component-compiler-utils-3.3.0.tgz",
- "integrity": "sha512-97sfH2mYNU+2PzGrmK2haqffDpVASuib9/w2/noxiFi31Z54hW+q3izKQXXQZSNhtiUpAI36uSuYepeBe4wpHQ==",
+ "@vue/compiler-dom": {
+ "version": "3.2.40",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.40.tgz",
+ "integrity": "sha512-OZCNyYVC2LQJy4H7h0o28rtk+4v+HMQygRTpmibGoG9wZyomQiS5otU7qo3Wlq5UfHDw2RFwxb9BJgKjVpjrQw==",
"requires": {
- "consolidate": "^0.15.1",
- "hash-sum": "^1.0.2",
- "lru-cache": "^4.1.2",
- "merge-source-map": "^1.1.0",
- "postcss": "^7.0.36",
- "postcss-selector-parser": "^6.0.2",
- "prettier": "^1.18.2 || ^2.0.0",
- "source-map": "~0.6.1",
- "vue-template-es2015-compiler": "^1.9.0"
- },
- "dependencies": {
- "lru-cache": {
- "version": "4.1.5",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz",
- "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==",
- "requires": {
- "pseudomap": "^1.0.2",
- "yallist": "^2.1.2"
- }
- },
- "picocolors": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz",
- "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA=="
- },
- "postcss": {
- "version": "7.0.39",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz",
- "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==",
- "requires": {
- "picocolors": "^0.2.1",
- "source-map": "^0.6.1"
- }
- },
- "yallist": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
- "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A=="
- }
+ "@vue/compiler-core": "3.2.40",
+ "@vue/shared": "3.2.40"
}
},
+ "@vue/compiler-sfc": {
+ "version": "3.2.40",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.40.tgz",
+ "integrity": "sha512-tzqwniIN1fu1PDHC3CpqY/dPCfN/RN1thpBC+g69kJcrl7mbGiHKNwbA6kJ3XKKy8R6JLKqcpVugqN4HkeBFFg==",
+ "requires": {
+ "@babel/parser": "^7.16.4",
+ "@vue/compiler-core": "3.2.40",
+ "@vue/compiler-dom": "3.2.40",
+ "@vue/compiler-ssr": "3.2.40",
+ "@vue/reactivity-transform": "3.2.40",
+ "@vue/shared": "3.2.40",
+ "estree-walker": "^2.0.2",
+ "magic-string": "^0.25.7",
+ "postcss": "^8.1.10",
+ "source-map": "^0.6.1"
+ }
+ },
+ "@vue/compiler-ssr": {
+ "version": "3.2.40",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.40.tgz",
+ "integrity": "sha512-80cQcgasKjrPPuKcxwuCx7feq+wC6oFl5YaKSee9pV3DNq+6fmCVwEEC3vvkf/E2aI76rIJSOYHsWSEIxK74oQ==",
+ "requires": {
+ "@vue/compiler-dom": "3.2.40",
+ "@vue/shared": "3.2.40"
+ }
+ },
+ "@vue/reactivity": {
+ "version": "3.2.40",
+ "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.2.40.tgz",
+ "integrity": "sha512-N9qgGLlZmtUBMHF9xDT4EkD9RdXde1Xbveb+niWMXuHVWQP5BzgRmE3SFyUBBcyayG4y1lhoz+lphGRRxxK4RA==",
+ "requires": {
+ "@vue/shared": "3.2.40"
+ }
+ },
+ "@vue/reactivity-transform": {
+ "version": "3.2.40",
+ "resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.2.40.tgz",
+ "integrity": "sha512-HQUCVwEaacq6fGEsg2NUuGKIhUveMCjOk8jGHqLXPI2w6zFoPrlQhwWEaINTv5kkZDXKEnCijAp+4gNEHG03yw==",
+ "requires": {
+ "@babel/parser": "^7.16.4",
+ "@vue/compiler-core": "3.2.40",
+ "@vue/shared": "3.2.40",
+ "estree-walker": "^2.0.2",
+ "magic-string": "^0.25.7"
+ }
+ },
+ "@vue/runtime-core": {
+ "version": "3.2.40",
+ "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.2.40.tgz",
+ "integrity": "sha512-U1+rWf0H8xK8aBUZhnrN97yoZfHbjgw/bGUzfgKPJl69/mXDuSg8CbdBYBn6VVQdR947vWneQBFzdhasyzMUKg==",
+ "requires": {
+ "@vue/reactivity": "3.2.40",
+ "@vue/shared": "3.2.40"
+ }
+ },
+ "@vue/runtime-dom": {
+ "version": "3.2.40",
+ "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.2.40.tgz",
+ "integrity": "sha512-AO2HMQ+0s2+MCec8hXAhxMgWhFhOPJ/CyRXnmTJ6XIOnJFLrH5Iq3TNwvVcODGR295jy77I6dWPj+wvFoSYaww==",
+ "requires": {
+ "@vue/runtime-core": "3.2.40",
+ "@vue/shared": "3.2.40",
+ "csstype": "^2.6.8"
+ }
+ },
+ "@vue/server-renderer": {
+ "version": "3.2.40",
+ "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.2.40.tgz",
+ "integrity": "sha512-gtUcpRwrXOJPJ4qyBpU3EyxQa4EkV8I4f8VrDePcGCPe4O/hd0BPS7v9OgjIQob6Ap8VDz9G+mGTKazE45/95w==",
+ "requires": {
+ "@vue/compiler-ssr": "3.2.40",
+ "@vue/shared": "3.2.40"
+ }
+ },
+ "@vue/shared": {
+ "version": "3.2.40",
+ "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.40.tgz",
+ "integrity": "sha512-0PLQ6RUtZM0vO3teRfzGi4ltLUO5aO+kLgwh4Um3THSR03rpQWLTuRCkuO5A41ITzwdWeKdPHtSARuPkoo5pCQ=="
+ },
"@webassemblyjs/ast": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz",
@@ -14738,6 +10957,12 @@
"resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz",
"integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ=="
},
+ "abab": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz",
+ "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==",
+ "dev": true
+ },
"abort-controller": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
@@ -14748,9 +10973,19 @@
}
},
"acorn": {
- "version": "8.7.1",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz",
- "integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A=="
+ "version": "8.8.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz",
+ "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w=="
+ },
+ "acorn-globals": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-7.0.1.tgz",
+ "integrity": "sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==",
+ "dev": true,
+ "requires": {
+ "acorn": "^8.1.0",
+ "acorn-walk": "^8.0.2"
+ }
},
"acorn-import-assertions": {
"version": "1.8.0",
@@ -14827,23 +11062,6 @@
"fast-deep-equal": "^3.1.3"
}
},
- "ansi-escapes": {
- "version": "4.3.2",
- "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz",
- "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==",
- "dev": true,
- "requires": {
- "type-fest": "^0.21.3"
- },
- "dependencies": {
- "type-fest": {
- "version": "0.21.3",
- "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz",
- "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==",
- "dev": true
- }
- }
- },
"ansi-regex": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
@@ -14857,16 +11075,6 @@
"color-convert": "^2.0.1"
}
},
- "anymatch": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
- "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
- "dev": true,
- "requires": {
- "normalize-path": "^3.0.0",
- "picomatch": "^2.0.4"
- }
- },
"argparse": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
@@ -14924,10 +11132,10 @@
"printable-characters": "^1.0.42"
}
},
- "asap": {
- "version": "2.0.6",
- "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
- "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==",
+ "assertion-error": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz",
+ "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==",
"dev": true
},
"ast-types": {
@@ -14957,76 +11165,6 @@
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
"dev": true
},
- "babel-jest": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-28.1.3.tgz",
- "integrity": "sha512-epUaPOEWMk3cWX0M/sPvCHHCe9fMFAa/9hXEgKP8nFfNl/jlGkE9ucq9NqkZGXLDduCJYS0UvSlPUwC0S+rH6Q==",
- "dev": true,
- "requires": {
- "@jest/transform": "^28.1.3",
- "@types/babel__core": "^7.1.14",
- "babel-plugin-istanbul": "^6.1.1",
- "babel-preset-jest": "^28.1.3",
- "chalk": "^4.0.0",
- "graceful-fs": "^4.2.9",
- "slash": "^3.0.0"
- }
- },
- "babel-plugin-istanbul": {
- "version": "6.1.1",
- "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz",
- "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==",
- "dev": true,
- "requires": {
- "@babel/helper-plugin-utils": "^7.0.0",
- "@istanbuljs/load-nyc-config": "^1.0.0",
- "@istanbuljs/schema": "^0.1.2",
- "istanbul-lib-instrument": "^5.0.4",
- "test-exclude": "^6.0.0"
- }
- },
- "babel-plugin-jest-hoist": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-28.1.3.tgz",
- "integrity": "sha512-Ys3tUKAmfnkRUpPdpa98eYrAR0nV+sSFUZZEGuQ2EbFd1y4SOLtD5QDNHAq+bb9a+bbXvYQC4b+ID/THIMcU6Q==",
- "dev": true,
- "requires": {
- "@babel/template": "^7.3.3",
- "@babel/types": "^7.3.3",
- "@types/babel__core": "^7.1.14",
- "@types/babel__traverse": "^7.0.6"
- }
- },
- "babel-preset-current-node-syntax": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz",
- "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==",
- "dev": true,
- "requires": {
- "@babel/plugin-syntax-async-generators": "^7.8.4",
- "@babel/plugin-syntax-bigint": "^7.8.3",
- "@babel/plugin-syntax-class-properties": "^7.8.3",
- "@babel/plugin-syntax-import-meta": "^7.8.3",
- "@babel/plugin-syntax-json-strings": "^7.8.3",
- "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3",
- "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3",
- "@babel/plugin-syntax-numeric-separator": "^7.8.3",
- "@babel/plugin-syntax-object-rest-spread": "^7.8.3",
- "@babel/plugin-syntax-optional-catch-binding": "^7.8.3",
- "@babel/plugin-syntax-optional-chaining": "^7.8.3",
- "@babel/plugin-syntax-top-level-await": "^7.8.3"
- }
- },
- "babel-preset-jest": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-28.1.3.tgz",
- "integrity": "sha512-L+fupJvlWAHbQfn74coNX3zf60LXMJsezNvvx8eIh7iOR1luJ1poxYgQk1F8PYtNq/6QODDHCqsSnTFSWC491A==",
- "dev": true,
- "requires": {
- "babel-plugin-jest-hoist": "^28.1.3",
- "babel-preset-current-node-syntax": "^1.0.0"
- }
- },
"balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
@@ -15037,11 +11175,6 @@
"resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
"integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ=="
},
- "bluebird": {
- "version": "3.7.2",
- "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
- "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg=="
- },
"blueimp-md5": {
"version": "2.18.0",
"resolved": "https://registry.npmjs.org/blueimp-md5/-/blueimp-md5-2.18.0.tgz",
@@ -15072,23 +11205,14 @@
}
},
"browserslist": {
- "version": "4.21.2",
- "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.2.tgz",
- "integrity": "sha512-MonuOgAtUB46uP5CezYbRaYKBNt2LxP0yX+Pmj4LkcDFGkn9Cbpi83d9sCjwQDErXsIJSzY5oKGDbgOlF/LPAA==",
+ "version": "4.21.4",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.4.tgz",
+ "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==",
"requires": {
- "caniuse-lite": "^1.0.30001366",
- "electron-to-chromium": "^1.4.188",
+ "caniuse-lite": "^1.0.30001400",
+ "electron-to-chromium": "^1.4.251",
"node-releases": "^2.0.6",
- "update-browserslist-db": "^1.0.4"
- }
- },
- "bser": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz",
- "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==",
- "dev": true,
- "requires": {
- "node-int64": "^0.4.0"
+ "update-browserslist-db": "^1.0.9"
}
},
"buffer-from": {
@@ -15148,30 +11272,38 @@
}
},
"caniuse-lite": {
- "version": "1.0.30001367",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001367.tgz",
- "integrity": "sha512-XDgbeOHfifWV3GEES2B8rtsrADx4Jf+juKX2SICJcaUhjYBO3bR96kvEIHa15VU6ohtOhBZuPGGYGbXMRn0NCw=="
+ "version": "1.0.30001418",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001418.tgz",
+ "integrity": "sha512-oIs7+JL3K9JRQ3jPZjlH6qyYDp+nBTCais7hjh0s+fuBwufc7uZ7hPYMXrDOJhV360KGMTcczMRObk0/iMqZRg=="
},
- "caseless": {
- "version": "0.12.0",
- "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
- "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==",
- "dev": true
+ "chai": {
+ "version": "4.3.6",
+ "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.6.tgz",
+ "integrity": "sha512-bbcp3YfHCUzMOvKqsztczerVgBKSsEijCySNlHHbX3VG1nskvqjz5Rfso1gGwD6w6oOV3eI60pKuMOV5MV7p3Q==",
+ "dev": true,
+ "requires": {
+ "assertion-error": "^1.1.0",
+ "check-error": "^1.0.2",
+ "deep-eql": "^3.0.1",
+ "get-func-name": "^2.0.0",
+ "loupe": "^2.3.1",
+ "pathval": "^1.1.1",
+ "type-detect": "^4.0.5"
+ }
},
"chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
- "dev": true,
"requires": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
}
},
- "char-regex": {
+ "check-error": {
"version": "1.0.2",
- "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz",
- "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==",
+ "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz",
+ "integrity": "sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==",
"dev": true
},
"chrome-trace-event": {
@@ -15180,15 +11312,9 @@
"integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg=="
},
"ci-info": {
- "version": "3.3.2",
- "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.3.2.tgz",
- "integrity": "sha512-xmDt/QIAdeZ9+nfdPsaBCpMvHNLFiLdjj59qjqn+6iPe6YmHGQ35sBnQ8uslRBXFmXkiZQOJRjvQeoGppoTjjg==",
- "dev": true
- },
- "cjs-module-lexer": {
- "version": "1.2.2",
- "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz",
- "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==",
+ "version": "3.5.0",
+ "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.5.0.tgz",
+ "integrity": "sha512-yH4RezKOGlOhxkmhbeNuC4eYZKAUsEaGtBuBzDDP1eFUKiccDWzBABxBfOx31IDwDIXMTxWuwAxUGModvkbuVw==",
"dev": true
},
"clean-regexp": {
@@ -15252,25 +11378,10 @@
}
}
},
- "clone-regexp": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/clone-regexp/-/clone-regexp-2.2.0.tgz",
- "integrity": "sha512-beMpP7BOtTipFuW8hrJvREQ2DrRu3BE7by0ZpibtfBA+qfHYvMGTc2Yb1JMYPKg/JUw0CHYvpg796aNTSW9z7Q==",
- "dev": true,
- "requires": {
- "is-regexp": "^2.0.0"
- }
- },
- "co": {
- "version": "4.6.0",
- "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
- "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==",
- "dev": true
- },
"codemirror": {
- "version": "5.65.6",
- "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.65.6.tgz",
- "integrity": "sha512-zNihMSMoDxK9Gqv9oEyDT8oM51rcRrQ+IEo2zyS48gJByBq5Fj8XuNEguMra+MuIOuh6lkpnLUJeL70DoTt6yw=="
+ "version": "5.65.9",
+ "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.65.9.tgz",
+ "integrity": "sha512-19Jox5sAKpusTDgqgKB5dawPpQcY+ipQK7xoEI+MVucEF9qqFaXpeqY1KaoyGBso/wHQoDa4HMMxMjdsS3Zzzw=="
},
"codemirror-spell-checker": {
"version": "1.1.2",
@@ -15280,12 +11391,6 @@
"typo-js": "*"
}
},
- "collect-v8-coverage": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz",
- "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==",
- "dev": true
- },
"color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
@@ -15300,9 +11405,9 @@
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
},
"colord": {
- "version": "2.9.2",
- "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.2.tgz",
- "integrity": "sha512-Uqbg+J445nc1TKn4FoDPS6ZZqAvEDnwrH42yo8B40JSOgSLxMZ/gt3h4nmCtPLQeXhjJJkqBx7SCY35WnIixaQ==",
+ "version": "2.9.3",
+ "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz",
+ "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==",
"dev": true
},
"colorette": {
@@ -15320,9 +11425,9 @@
}
},
"commander": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
- "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw=="
+ "version": "8.3.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz",
+ "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww=="
},
"commondir": {
"version": "1.0.1",
@@ -15335,67 +11440,6 @@
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
},
- "concat-stream": {
- "version": "1.6.2",
- "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
- "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
- "dev": true,
- "requires": {
- "buffer-from": "^1.0.0",
- "inherits": "^2.0.3",
- "readable-stream": "^2.2.2",
- "typedarray": "^0.0.6"
- },
- "dependencies": {
- "isarray": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
- "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
- "dev": true
- },
- "readable-stream": {
- "version": "2.3.7",
- "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
- "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
- "dev": true,
- "requires": {
- "core-util-is": "~1.0.0",
- "inherits": "~2.0.3",
- "isarray": "~1.0.0",
- "process-nextick-args": "~2.0.0",
- "safe-buffer": "~5.1.1",
- "string_decoder": "~1.1.1",
- "util-deprecate": "~1.0.1"
- }
- },
- "string_decoder": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
- "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
- "dev": true,
- "requires": {
- "safe-buffer": "~5.1.0"
- }
- }
- }
- },
- "consolidate": {
- "version": "0.15.1",
- "resolved": "https://registry.npmjs.org/consolidate/-/consolidate-0.15.1.tgz",
- "integrity": "sha512-DW46nrsMJgy9kqAbPt5rKaCr7uFtpo4mSUvLHIUbJEjm0vo+aY5QLwBUq3FK4tRnJr/X0Psc0C4jf/h+HtXSMw==",
- "requires": {
- "bluebird": "^3.1.1"
- }
- },
- "convert-source-map": {
- "version": "1.8.0",
- "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz",
- "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==",
- "dev": true,
- "requires": {
- "safe-buffer": "~5.1.1"
- }
- },
"copy-anything": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-2.0.6.tgz",
@@ -15483,12 +11527,6 @@
"integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==",
"dev": true
},
- "css.escape": {
- "version": "1.5.1",
- "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz",
- "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==",
- "dev": true
- },
"cssesc": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
@@ -15503,6 +11541,34 @@
"css-tree": "^1.1.2"
}
},
+ "cssom": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz",
+ "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==",
+ "dev": true
+ },
+ "cssstyle": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz",
+ "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==",
+ "dev": true,
+ "requires": {
+ "cssom": "~0.3.6"
+ },
+ "dependencies": {
+ "cssom": {
+ "version": "0.3.8",
+ "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz",
+ "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==",
+ "dev": true
+ }
+ }
+ },
+ "csstype": {
+ "version": "2.6.21",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.21.tgz",
+ "integrity": "sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w=="
+ },
"d3": {
"version": "7.6.1",
"resolved": "https://registry.npmjs.org/d3/-/d3-7.6.1.tgz",
@@ -15621,6 +11687,13 @@
"commander": "7",
"iconv-lite": "0.6",
"rw": "1"
+ },
+ "dependencies": {
+ "commander": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
+ "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw=="
+ }
}
},
"d3-ease": {
@@ -16071,10 +12144,16 @@
"integrity": "sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og==",
"dev": true
},
- "de-indent": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz",
- "integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg=="
+ "data-urls": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz",
+ "integrity": "sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==",
+ "dev": true,
+ "requires": {
+ "abab": "^2.0.6",
+ "whatwg-mimetype": "^3.0.0",
+ "whatwg-url": "^11.0.0"
+ }
},
"debug": {
"version": "4.3.4",
@@ -16109,10 +12188,25 @@
}
}
},
- "dedent": {
- "version": "0.7.0",
- "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz",
- "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==",
+ "decimal.js": {
+ "version": "10.4.1",
+ "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.1.tgz",
+ "integrity": "sha512-F29o+vci4DodHYT9UrR5IEbfBw9pE5eSapIJdTqXK5+6hq+t8VRxwQyKlW2i+KDKFkkJQRvFyI/QXD83h8LyQw==",
+ "dev": true
+ },
+ "deep-eql": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz",
+ "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==",
+ "dev": true,
+ "requires": {
+ "type-detect": "^4.0.0"
+ }
+ },
+ "deep-extend": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
+ "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
"dev": true
},
"deep-is": {
@@ -16121,12 +12215,6 @@
"integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
"dev": true
},
- "deepmerge": {
- "version": "4.2.2",
- "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz",
- "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==",
- "dev": true
- },
"define-properties": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz",
@@ -16157,443 +12245,20 @@
"requires": {
"tslib": "^2.0.1"
}
- }
- }
- },
- "delaunator": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.0.tgz",
- "integrity": "sha512-AyLvtyJdbv/U1GkiS6gUUzclRoAY4Gs75qkMygJJhU75LW4DNuSF2RMzpxs9jw9Oz1BobHjTdkG3zdP55VxAqw==",
- "requires": {
- "robust-predicates": "^3.0.0"
- }
- },
- "delayed-stream": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
- "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
- "dev": true
- },
- "depd": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
- "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
- "dev": true
- },
- "dependency-graph": {
- "version": "0.11.0",
- "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.11.0.tgz",
- "integrity": "sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg==",
- "dev": true
- },
- "detect-newline": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz",
- "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==",
- "dev": true
- },
- "diff-sequences": {
- "version": "28.1.1",
- "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-28.1.1.tgz",
- "integrity": "sha512-FU0iFaH/E23a+a718l8Qa/19bF9p06kgE0KipMOMadwa3SjnaElKzPaUC0vnibs6/B/9ni97s61mcejk8W1fQw==",
- "dev": true
- },
- "dir-glob": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
- "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==",
- "dev": true,
- "requires": {
- "path-type": "^4.0.0"
- }
- },
- "doctrine": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
- "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
- "dev": true,
- "requires": {
- "esutils": "^2.0.2"
- }
- },
- "dom-serializer": {
- "version": "1.4.1",
- "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz",
- "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==",
- "dev": true,
- "requires": {
- "domelementtype": "^2.0.1",
- "domhandler": "^4.2.0",
- "entities": "^2.0.0"
- }
- },
- "domelementtype": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
- "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
- "dev": true
- },
- "domhandler": {
- "version": "4.3.1",
- "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz",
- "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==",
- "dev": true,
- "requires": {
- "domelementtype": "^2.2.0"
- }
- },
- "dompurify": {
- "version": "2.3.8",
- "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.3.8.tgz",
- "integrity": "sha512-eVhaWoVibIzqdGYjwsBWodIQIaXFSB+cKDf4cfxLMsK0xiud6SE+/WCVx/Xw/UwQsa4cS3T2eITcdtmTg2UKcw=="
- },
- "domutils": {
- "version": "2.8.0",
- "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
- "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==",
- "dev": true,
- "requires": {
- "dom-serializer": "^1.0.1",
- "domelementtype": "^2.2.0",
- "domhandler": "^4.2.0"
- }
- },
- "dropzone": {
- "version": "6.0.0-beta.2",
- "resolved": "https://registry.npmjs.org/dropzone/-/dropzone-6.0.0-beta.2.tgz",
- "integrity": "sha512-k44yLuFFhRk53M8zP71FaaNzJYIzr99SKmpbO/oZKNslDjNXQsBTdfLs+iONd0U0L94zzlFzRnFdqbLcs7h9fQ==",
- "requires": {
- "@swc/helpers": "^0.2.13",
- "just-extend": "^5.0.0"
- }
- },
- "eastasianwidth": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
- "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="
- },
- "easymde": {
- "version": "2.16.1",
- "resolved": "https://registry.npmjs.org/easymde/-/easymde-2.16.1.tgz",
- "integrity": "sha512-FihYgjRsKfhGNk89SHSqxKLC4aJ1kfybPWW6iAmtb5GnXu+tnFPSzSaGBmk1RRlCuhFSjhF0SnIMGVPjEzkr6g==",
- "requires": {
- "@types/codemirror": "^5.60.4",
- "@types/marked": "^4.0.1",
- "codemirror": "^5.63.1",
- "codemirror-spell-checker": "1.1.2",
- "marked": "^4.0.10"
- }
- },
- "electron-to-chromium": {
- "version": "1.4.195",
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.195.tgz",
- "integrity": "sha512-vefjEh0sk871xNmR5whJf9TEngX+KTKS3hOHpjoMpauKkwlGwtMz1H8IaIjAT/GNnX0TbGwAdmVoXCAzXf+PPg=="
- },
- "emittery": {
- "version": "0.10.2",
- "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.10.2.tgz",
- "integrity": "sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw==",
- "dev": true
- },
- "emoji-regex": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
- "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
- },
- "emojis-list": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz",
- "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q=="
- },
- "enhanced-resolve": {
- "version": "5.10.0",
- "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.10.0.tgz",
- "integrity": "sha512-T0yTFjdpldGY8PmuXXR0PyQ1ufZpEGiHVrp7zHKB7jdR4qlmZHhONVM5AQOAWXuF/w3dnHbEQVrNptJgt7F+cQ==",
- "requires": {
- "graceful-fs": "^4.2.4",
- "tapable": "^2.2.0"
- }
- },
- "entities": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
- "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==",
- "dev": true
- },
- "envinfo": {
- "version": "7.8.1",
- "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz",
- "integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw=="
- },
- "eol": {
- "version": "0.9.1",
- "resolved": "https://registry.npmjs.org/eol/-/eol-0.9.1.tgz",
- "integrity": "sha512-Ds/TEoZjwggRoz/Q2O7SE3i4Jm66mqTDfmdHdq/7DKVk3bro9Q8h6WdXKdPqFLMoqxrDK5SVRzHVPOS6uuGtrg==",
- "dev": true
- },
- "errno": {
- "version": "0.1.8",
- "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz",
- "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==",
- "optional": true,
- "requires": {
- "prr": "~1.0.1"
- }
- },
- "error-ex": {
- "version": "1.3.2",
- "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
- "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
- "dev": true,
- "requires": {
- "is-arrayish": "^0.2.1"
- }
- },
- "es-abstract": {
- "version": "1.20.1",
- "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.1.tgz",
- "integrity": "sha512-WEm2oBhfoI2sImeM4OF2zE2V3BYdSF+KnSi9Sidz51fQHd7+JuF8Xgcj9/0o+OWeIeIS/MiuNnlruQrJf16GQA==",
- "dev": true,
- "requires": {
- "call-bind": "^1.0.2",
- "es-to-primitive": "^1.2.1",
- "function-bind": "^1.1.1",
- "function.prototype.name": "^1.1.5",
- "get-intrinsic": "^1.1.1",
- "get-symbol-description": "^1.0.0",
- "has": "^1.0.3",
- "has-property-descriptors": "^1.0.0",
- "has-symbols": "^1.0.3",
- "internal-slot": "^1.0.3",
- "is-callable": "^1.2.4",
- "is-negative-zero": "^2.0.2",
- "is-regex": "^1.1.4",
- "is-shared-array-buffer": "^1.0.2",
- "is-string": "^1.0.7",
- "is-weakref": "^1.0.2",
- "object-inspect": "^1.12.0",
- "object-keys": "^1.1.1",
- "object.assign": "^4.1.2",
- "regexp.prototype.flags": "^1.4.3",
- "string.prototype.trimend": "^1.0.5",
- "string.prototype.trimstart": "^1.0.5",
- "unbox-primitive": "^1.0.2"
- }
- },
- "es-module-lexer": {
- "version": "0.9.3",
- "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz",
- "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ=="
- },
- "es-shim-unscopables": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz",
- "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==",
- "dev": true,
- "requires": {
- "has": "^1.0.3"
- }
- },
- "es-to-primitive": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
- "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
- "dev": true,
- "requires": {
- "is-callable": "^1.1.4",
- "is-date-object": "^1.0.1",
- "is-symbol": "^1.0.2"
- }
- },
- "esbuild": {
- "version": "0.14.49",
- "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.49.tgz",
- "integrity": "sha512-/TlVHhOaq7Yz8N1OJrjqM3Auzo5wjvHFLk+T8pIue+fhnhIMpfAzsG6PLVMbFveVxqD2WOp3QHei+52IMUNmCw==",
- "requires": {
- "esbuild-android-64": "0.14.49",
- "esbuild-android-arm64": "0.14.49",
- "esbuild-darwin-64": "0.14.49",
- "esbuild-darwin-arm64": "0.14.49",
- "esbuild-freebsd-64": "0.14.49",
- "esbuild-freebsd-arm64": "0.14.49",
- "esbuild-linux-32": "0.14.49",
- "esbuild-linux-64": "0.14.49",
- "esbuild-linux-arm": "0.14.49",
- "esbuild-linux-arm64": "0.14.49",
- "esbuild-linux-mips64le": "0.14.49",
- "esbuild-linux-ppc64le": "0.14.49",
- "esbuild-linux-riscv64": "0.14.49",
- "esbuild-linux-s390x": "0.14.49",
- "esbuild-netbsd-64": "0.14.49",
- "esbuild-openbsd-64": "0.14.49",
- "esbuild-sunos-64": "0.14.49",
- "esbuild-windows-32": "0.14.49",
- "esbuild-windows-64": "0.14.49",
- "esbuild-windows-arm64": "0.14.49"
- }
- },
- "esbuild-android-64": {
- "version": "0.14.49",
- "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.14.49.tgz",
- "integrity": "sha512-vYsdOTD+yi+kquhBiFWl3tyxnj2qZJsl4tAqwhT90ktUdnyTizgle7TjNx6Ar1bN7wcwWqZ9QInfdk2WVagSww==",
- "optional": true
- },
- "esbuild-android-arm64": {
- "version": "0.14.49",
- "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.49.tgz",
- "integrity": "sha512-g2HGr/hjOXCgSsvQZ1nK4nW/ei8JUx04Li74qub9qWrStlysaVmadRyTVuW32FGIpLQyc5sUjjZopj49eGGM2g==",
- "optional": true
- },
- "esbuild-darwin-64": {
- "version": "0.14.49",
- "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.49.tgz",
- "integrity": "sha512-3rvqnBCtX9ywso5fCHixt2GBCUsogNp9DjGmvbBohh31Ces34BVzFltMSxJpacNki96+WIcX5s/vum+ckXiLYg==",
- "optional": true
- },
- "esbuild-darwin-arm64": {
- "version": "0.14.49",
- "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.49.tgz",
- "integrity": "sha512-XMaqDxO846srnGlUSJnwbijV29MTKUATmOLyQSfswbK/2X5Uv28M9tTLUJcKKxzoo9lnkYPsx2o8EJcTYwCs/A==",
- "optional": true
- },
- "esbuild-freebsd-64": {
- "version": "0.14.49",
- "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.49.tgz",
- "integrity": "sha512-NJ5Q6AjV879mOHFri+5lZLTp5XsO2hQ+KSJYLbfY9DgCu8s6/Zl2prWXVANYTeCDLlrIlNNYw8y34xqyLDKOmQ==",
- "optional": true
- },
- "esbuild-freebsd-arm64": {
- "version": "0.14.49",
- "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.49.tgz",
- "integrity": "sha512-lFLtgXnAc3eXYqj5koPlBZvEbBSOSUbWO3gyY/0+4lBdRqELyz4bAuamHvmvHW5swJYL7kngzIZw6kdu25KGOA==",
- "optional": true
- },
- "esbuild-linux-32": {
- "version": "0.14.49",
- "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.49.tgz",
- "integrity": "sha512-zTTH4gr2Kb8u4QcOpTDVn7Z8q7QEIvFl/+vHrI3cF6XOJS7iEI1FWslTo3uofB2+mn6sIJEQD9PrNZKoAAMDiA==",
- "optional": true
- },
- "esbuild-linux-64": {
- "version": "0.14.49",
- "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.49.tgz",
- "integrity": "sha512-hYmzRIDzFfLrB5c1SknkxzM8LdEUOusp6M2TnuQZJLRtxTgyPnZZVtyMeCLki0wKgYPXkFsAVhi8vzo2mBNeTg==",
- "optional": true
- },
- "esbuild-linux-arm": {
- "version": "0.14.49",
- "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.49.tgz",
- "integrity": "sha512-iE3e+ZVv1Qz1Sy0gifIsarJMQ89Rpm9mtLSRtG3AH0FPgAzQ5Z5oU6vYzhc/3gSPi2UxdCOfRhw2onXuFw/0lg==",
- "optional": true
- },
- "esbuild-linux-arm64": {
- "version": "0.14.49",
- "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.49.tgz",
- "integrity": "sha512-KLQ+WpeuY+7bxukxLz5VgkAAVQxUv67Ft4DmHIPIW+2w3ObBPQhqNoeQUHxopoW/aiOn3m99NSmSV+bs4BSsdA==",
- "optional": true
- },
- "esbuild-linux-mips64le": {
- "version": "0.14.49",
- "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.49.tgz",
- "integrity": "sha512-n+rGODfm8RSum5pFIqFQVQpYBw+AztL8s6o9kfx7tjfK0yIGF6tm5HlG6aRjodiiKkH2xAiIM+U4xtQVZYU4rA==",
- "optional": true
- },
- "esbuild-linux-ppc64le": {
- "version": "0.14.49",
- "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.49.tgz",
- "integrity": "sha512-WP9zR4HX6iCBmMFH+XHHng2LmdoIeUmBpL4aL2TR8ruzXyT4dWrJ5BSbT8iNo6THN8lod6GOmYDLq/dgZLalGw==",
- "optional": true
- },
- "esbuild-linux-riscv64": {
- "version": "0.14.49",
- "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.49.tgz",
- "integrity": "sha512-h66ORBz+Dg+1KgLvzTVQEA1LX4XBd1SK0Fgbhhw4akpG/YkN8pS6OzYI/7SGENiN6ao5hETRDSkVcvU9NRtkMQ==",
- "optional": true
- },
- "esbuild-linux-s390x": {
- "version": "0.14.49",
- "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.49.tgz",
- "integrity": "sha512-DhrUoFVWD+XmKO1y7e4kNCqQHPs6twz6VV6Uezl/XHYGzM60rBewBF5jlZjG0nCk5W/Xy6y1xWeopkrhFFM0sQ==",
- "optional": true
- },
- "esbuild-loader": {
- "version": "2.19.0",
- "resolved": "https://registry.npmjs.org/esbuild-loader/-/esbuild-loader-2.19.0.tgz",
- "integrity": "sha512-urGNVE6Tl2rqx92ElKi/LiExXjGvcH6HfDBFzJ9Ppwqh4n6Jmx8x7RKAyMzSM78b6CAaJLhDncG5sPrL0ROh5Q==",
- "requires": {
- "esbuild": "^0.14.39",
- "joycon": "^3.0.1",
- "json5": "^2.2.0",
- "loader-utils": "^2.0.0",
- "tapable": "^2.2.0",
- "webpack-sources": "^2.2.0"
- }
- },
- "esbuild-netbsd-64": {
- "version": "0.14.49",
- "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.49.tgz",
- "integrity": "sha512-BXaUwFOfCy2T+hABtiPUIpWjAeWK9P8O41gR4Pg73hpzoygVGnj0nI3YK4SJhe52ELgtdgWP/ckIkbn2XaTxjQ==",
- "optional": true
- },
- "esbuild-openbsd-64": {
- "version": "0.14.49",
- "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.49.tgz",
- "integrity": "sha512-lP06UQeLDGmVPw9Rg437Btu6J9/BmyhdoefnQ4gDEJTtJvKtQaUcOQrhjTq455ouZN4EHFH1h28WOJVANK41kA==",
- "optional": true
- },
- "esbuild-sunos-64": {
- "version": "0.14.49",
- "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.49.tgz",
- "integrity": "sha512-4c8Zowp+V3zIWje329BeLbGh6XI9c/rqARNaj5yPHdC61pHI9UNdDxT3rePPJeWcEZVKjkiAS6AP6kiITp7FSw==",
- "optional": true
- },
- "esbuild-windows-32": {
- "version": "0.14.49",
- "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.49.tgz",
- "integrity": "sha512-q7Rb+J9yHTeKr9QTPDYkqfkEj8/kcKz9lOabDuvEXpXuIcosWCJgo5Z7h/L4r7rbtTH4a8U2FGKb6s1eeOHmJA==",
- "optional": true
- },
- "esbuild-windows-64": {
- "version": "0.14.49",
- "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.49.tgz",
- "integrity": "sha512-+Cme7Ongv0UIUTniPqfTX6mJ8Deo7VXw9xN0yJEN1lQMHDppTNmKwAM3oGbD/Vqff+07K2gN0WfNkMohmG+dVw==",
- "optional": true
- },
- "esbuild-windows-arm64": {
- "version": "0.14.49",
- "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.49.tgz",
- "integrity": "sha512-v+HYNAXzuANrCbbLFJ5nmO3m5y2PGZWLe3uloAkLt87aXiO2mZr3BTmacZdjwNkNEHuH3bNtN8cak+mzVjVPfA==",
- "optional": true
- },
- "escalade": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
- "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw=="
- },
- "escape-goat": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-4.0.0.tgz",
- "integrity": "sha512-2Sd4ShcWxbx6OY1IHyla/CVNwvg7XwZVoXZHcSu9w9SReNP1EzzD5T8NWKIR38fIqEns9kDWKUQTXXAmlDrdPg=="
- },
- "escape-string-regexp": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
- "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
- "dev": true
- },
- "escodegen": {
- "version": "1.14.3",
- "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz",
- "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==",
- "dev": true,
- "requires": {
- "esprima": "^4.0.1",
- "estraverse": "^4.2.0",
- "esutils": "^2.0.2",
- "optionator": "^0.8.1",
- "source-map": "~0.6.1"
- },
- "dependencies": {
+ },
+ "escodegen": {
+ "version": "1.14.3",
+ "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz",
+ "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==",
+ "dev": true,
+ "requires": {
+ "esprima": "^4.0.1",
+ "estraverse": "^4.2.0",
+ "esutils": "^2.0.2",
+ "optionator": "^0.8.1",
+ "source-map": "~0.6.1"
+ }
+ },
"estraverse": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
@@ -16641,14 +12306,507 @@
}
}
},
- "eslint": {
- "version": "8.20.0",
- "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.20.0.tgz",
- "integrity": "sha512-d4ixhz5SKCa1D6SCPrivP7yYVi7nyD6A4vs6HIAul9ujBzcEmZVM3/0NN/yu5nKhmO1wjp5xQ46iRfmDGlOviA==",
+ "delaunator": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.0.tgz",
+ "integrity": "sha512-AyLvtyJdbv/U1GkiS6gUUzclRoAY4Gs75qkMygJJhU75LW4DNuSF2RMzpxs9jw9Oz1BobHjTdkG3zdP55VxAqw==",
+ "requires": {
+ "robust-predicates": "^3.0.0"
+ }
+ },
+ "delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+ "dev": true
+ },
+ "depd": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
+ "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
+ "dev": true
+ },
+ "dependency-graph": {
+ "version": "0.11.0",
+ "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.11.0.tgz",
+ "integrity": "sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg==",
+ "dev": true
+ },
+ "dir-glob": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
+ "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==",
"dev": true,
"requires": {
- "@eslint/eslintrc": "^1.3.0",
- "@humanwhocodes/config-array": "^0.9.2",
+ "path-type": "^4.0.0"
+ }
+ },
+ "doctrine": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
+ "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
+ "dev": true,
+ "requires": {
+ "esutils": "^2.0.2"
+ }
+ },
+ "dom-serializer": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz",
+ "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==",
+ "dev": true,
+ "requires": {
+ "domelementtype": "^2.0.1",
+ "domhandler": "^4.2.0",
+ "entities": "^2.0.0"
+ },
+ "dependencies": {
+ "entities": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
+ "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==",
+ "dev": true
+ }
+ }
+ },
+ "domelementtype": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
+ "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
+ "dev": true
+ },
+ "domexception": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz",
+ "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==",
+ "dev": true,
+ "requires": {
+ "webidl-conversions": "^7.0.0"
+ }
+ },
+ "domhandler": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz",
+ "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==",
+ "dev": true,
+ "requires": {
+ "domelementtype": "^2.2.0"
+ }
+ },
+ "dompurify": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.4.0.tgz",
+ "integrity": "sha512-Be9tbQMZds4a3C6xTmz68NlMfeONA//4dOavl/1rNw50E+/QO0KVpbcU0PcaW0nsQxurXls9ZocqFxk8R2mWEA=="
+ },
+ "domutils": {
+ "version": "2.8.0",
+ "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
+ "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==",
+ "dev": true,
+ "requires": {
+ "dom-serializer": "^1.0.1",
+ "domelementtype": "^2.2.0",
+ "domhandler": "^4.2.0"
+ }
+ },
+ "dropzone": {
+ "version": "6.0.0-beta.2",
+ "resolved": "https://registry.npmjs.org/dropzone/-/dropzone-6.0.0-beta.2.tgz",
+ "integrity": "sha512-k44yLuFFhRk53M8zP71FaaNzJYIzr99SKmpbO/oZKNslDjNXQsBTdfLs+iONd0U0L94zzlFzRnFdqbLcs7h9fQ==",
+ "requires": {
+ "@swc/helpers": "^0.2.13",
+ "just-extend": "^5.0.0"
+ }
+ },
+ "eastasianwidth": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
+ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="
+ },
+ "easymde": {
+ "version": "2.18.0",
+ "resolved": "https://registry.npmjs.org/easymde/-/easymde-2.18.0.tgz",
+ "integrity": "sha512-IxVVUxNWIoXLeqtBU4BLc+eS/ScYhT1Dcb6yF5Wchoj1iXAV+TIIDWx+NCaZhY7RcSHqDPKllbYq7nwGKILnoA==",
+ "requires": {
+ "@types/codemirror": "^5.60.4",
+ "@types/marked": "^4.0.7",
+ "codemirror": "^5.63.1",
+ "codemirror-spell-checker": "1.1.2",
+ "marked": "^4.1.0"
+ }
+ },
+ "electron-to-chromium": {
+ "version": "1.4.276",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.276.tgz",
+ "integrity": "sha512-EpuHPqu8YhonqLBXHoU6hDJCD98FCe6KDoet3/gY1qsQ6usjJoHqBH2YIVs8FXaAtHwVL8Uqa/fsYao/vq9VWQ=="
+ },
+ "emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
+ },
+ "emojis-list": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz",
+ "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q=="
+ },
+ "enhanced-resolve": {
+ "version": "5.10.0",
+ "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.10.0.tgz",
+ "integrity": "sha512-T0yTFjdpldGY8PmuXXR0PyQ1ufZpEGiHVrp7zHKB7jdR4qlmZHhONVM5AQOAWXuF/w3dnHbEQVrNptJgt7F+cQ==",
+ "requires": {
+ "graceful-fs": "^4.2.4",
+ "tapable": "^2.2.0"
+ }
+ },
+ "entities": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz",
+ "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==",
+ "dev": true
+ },
+ "envinfo": {
+ "version": "7.8.1",
+ "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz",
+ "integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw=="
+ },
+ "eol": {
+ "version": "0.9.1",
+ "resolved": "https://registry.npmjs.org/eol/-/eol-0.9.1.tgz",
+ "integrity": "sha512-Ds/TEoZjwggRoz/Q2O7SE3i4Jm66mqTDfmdHdq/7DKVk3bro9Q8h6WdXKdPqFLMoqxrDK5SVRzHVPOS6uuGtrg==",
+ "dev": true
+ },
+ "errno": {
+ "version": "0.1.8",
+ "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz",
+ "integrity": "sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==",
+ "optional": true,
+ "requires": {
+ "prr": "~1.0.1"
+ }
+ },
+ "error-ex": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
+ "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
+ "dev": true,
+ "requires": {
+ "is-arrayish": "^0.2.1"
+ }
+ },
+ "es-abstract": {
+ "version": "1.20.4",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.4.tgz",
+ "integrity": "sha512-0UtvRN79eMe2L+UNEF1BwRe364sj/DXhQ/k5FmivgoSdpM90b8Jc0mDzKMGo7QS0BVbOP/bTwBKNnDc9rNzaPA==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.2",
+ "es-to-primitive": "^1.2.1",
+ "function-bind": "^1.1.1",
+ "function.prototype.name": "^1.1.5",
+ "get-intrinsic": "^1.1.3",
+ "get-symbol-description": "^1.0.0",
+ "has": "^1.0.3",
+ "has-property-descriptors": "^1.0.0",
+ "has-symbols": "^1.0.3",
+ "internal-slot": "^1.0.3",
+ "is-callable": "^1.2.7",
+ "is-negative-zero": "^2.0.2",
+ "is-regex": "^1.1.4",
+ "is-shared-array-buffer": "^1.0.2",
+ "is-string": "^1.0.7",
+ "is-weakref": "^1.0.2",
+ "object-inspect": "^1.12.2",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.4",
+ "regexp.prototype.flags": "^1.4.3",
+ "safe-regex-test": "^1.0.0",
+ "string.prototype.trimend": "^1.0.5",
+ "string.prototype.trimstart": "^1.0.5",
+ "unbox-primitive": "^1.0.2"
+ }
+ },
+ "es-aggregate-error": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/es-aggregate-error/-/es-aggregate-error-1.0.8.tgz",
+ "integrity": "sha512-AKUb5MKLWMozPlFRHOKqWD7yta5uaEhH21qwtnf6FlKjNjTJOoqFi0/G14+FfSkIQhhu6X68Af4xgRC6y8qG4A==",
+ "dev": true,
+ "requires": {
+ "define-properties": "^1.1.4",
+ "es-abstract": "^1.19.5",
+ "function-bind": "^1.1.1",
+ "functions-have-names": "^1.2.3",
+ "get-intrinsic": "^1.1.1",
+ "globalthis": "^1.0.2",
+ "has-property-descriptors": "^1.0.0"
+ }
+ },
+ "es-module-lexer": {
+ "version": "0.9.3",
+ "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz",
+ "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ=="
+ },
+ "es-shim-unscopables": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz",
+ "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==",
+ "dev": true,
+ "requires": {
+ "has": "^1.0.3"
+ }
+ },
+ "es-to-primitive": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
+ "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
+ "dev": true,
+ "requires": {
+ "is-callable": "^1.1.4",
+ "is-date-object": "^1.0.1",
+ "is-symbol": "^1.0.2"
+ }
+ },
+ "esbuild": {
+ "version": "0.15.10",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.10.tgz",
+ "integrity": "sha512-N7wBhfJ/E5fzn/SpNgX+oW2RLRjwaL8Y0ezqNqhjD6w0H2p0rDuEz2FKZqpqLnO8DCaWumKe8dsC/ljvVSSxng==",
+ "requires": {
+ "@esbuild/android-arm": "0.15.10",
+ "@esbuild/linux-loong64": "0.15.10",
+ "esbuild-android-64": "0.15.10",
+ "esbuild-android-arm64": "0.15.10",
+ "esbuild-darwin-64": "0.15.10",
+ "esbuild-darwin-arm64": "0.15.10",
+ "esbuild-freebsd-64": "0.15.10",
+ "esbuild-freebsd-arm64": "0.15.10",
+ "esbuild-linux-32": "0.15.10",
+ "esbuild-linux-64": "0.15.10",
+ "esbuild-linux-arm": "0.15.10",
+ "esbuild-linux-arm64": "0.15.10",
+ "esbuild-linux-mips64le": "0.15.10",
+ "esbuild-linux-ppc64le": "0.15.10",
+ "esbuild-linux-riscv64": "0.15.10",
+ "esbuild-linux-s390x": "0.15.10",
+ "esbuild-netbsd-64": "0.15.10",
+ "esbuild-openbsd-64": "0.15.10",
+ "esbuild-sunos-64": "0.15.10",
+ "esbuild-windows-32": "0.15.10",
+ "esbuild-windows-64": "0.15.10",
+ "esbuild-windows-arm64": "0.15.10"
+ }
+ },
+ "esbuild-android-64": {
+ "version": "0.15.10",
+ "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.15.10.tgz",
+ "integrity": "sha512-UI7krF8OYO1N7JYTgLT9ML5j4+45ra3amLZKx7LO3lmLt1Ibn8t3aZbX5Pu4BjWiqDuJ3m/hsvhPhK/5Y/YpnA==",
+ "optional": true
+ },
+ "esbuild-android-arm64": {
+ "version": "0.15.10",
+ "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.15.10.tgz",
+ "integrity": "sha512-EOt55D6xBk5O05AK8brXUbZmoFj4chM8u3riGflLa6ziEoVvNjRdD7Cnp82NHQGfSHgYR06XsPI8/sMuA/cUwg==",
+ "optional": true
+ },
+ "esbuild-darwin-64": {
+ "version": "0.15.10",
+ "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.15.10.tgz",
+ "integrity": "sha512-hbDJugTicqIm+WKZgp208d7FcXcaK8j2c0l+fqSJ3d2AzQAfjEYDRM3Z2oMeqSJ9uFxyj/muSACLdix7oTstRA==",
+ "optional": true
+ },
+ "esbuild-darwin-arm64": {
+ "version": "0.15.10",
+ "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.10.tgz",
+ "integrity": "sha512-M1t5+Kj4IgSbYmunf2BB6EKLkWUq+XlqaFRiGOk8bmBapu9bCDrxjf4kUnWn59Dka3I27EiuHBKd1rSO4osLFQ==",
+ "optional": true
+ },
+ "esbuild-freebsd-64": {
+ "version": "0.15.10",
+ "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.10.tgz",
+ "integrity": "sha512-KMBFMa7C8oc97nqDdoZwtDBX7gfpolkk6Bcmj6YFMrtCMVgoU/x2DI1p74DmYl7CSS6Ppa3xgemrLrr5IjIn0w==",
+ "optional": true
+ },
+ "esbuild-freebsd-arm64": {
+ "version": "0.15.10",
+ "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.10.tgz",
+ "integrity": "sha512-m2KNbuCX13yQqLlbSojFMHpewbn8wW5uDS6DxRpmaZKzyq8Dbsku6hHvh2U+BcLwWY4mpgXzFUoENEf7IcioGg==",
+ "optional": true
+ },
+ "esbuild-linux-32": {
+ "version": "0.15.10",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.15.10.tgz",
+ "integrity": "sha512-guXrwSYFAvNkuQ39FNeV4sNkNms1bLlA5vF1H0cazZBOLdLFIny6BhT+TUbK/hdByMQhtWQ5jI9VAmPKbVPu1w==",
+ "optional": true
+ },
+ "esbuild-linux-64": {
+ "version": "0.15.10",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.15.10.tgz",
+ "integrity": "sha512-jd8XfaSJeucMpD63YNMO1JCrdJhckHWcMv6O233bL4l6ogQKQOxBYSRP/XLWP+6kVTu0obXovuckJDcA0DKtQA==",
+ "optional": true
+ },
+ "esbuild-linux-arm": {
+ "version": "0.15.10",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.15.10.tgz",
+ "integrity": "sha512-6N8vThLL/Lysy9y4Ex8XoLQAlbZKUyExCWyayGi2KgTBelKpPgj6RZnUaKri0dHNPGgReJriKVU6+KDGQwn10A==",
+ "optional": true
+ },
+ "esbuild-linux-arm64": {
+ "version": "0.15.10",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.10.tgz",
+ "integrity": "sha512-GByBi4fgkvZFTHFDYNftu1DQ1GzR23jws0oWyCfhnI7eMOe+wgwWrc78dbNk709Ivdr/evefm2PJiUBMiusS1A==",
+ "optional": true
+ },
+ "esbuild-linux-mips64le": {
+ "version": "0.15.10",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.10.tgz",
+ "integrity": "sha512-BxP+LbaGVGIdQNJUNF7qpYjEGWb0YyHVSKqYKrn+pTwH/SiHUxFyJYSP3pqkku61olQiSBnSmWZ+YUpj78Tw7Q==",
+ "optional": true
+ },
+ "esbuild-linux-ppc64le": {
+ "version": "0.15.10",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.10.tgz",
+ "integrity": "sha512-LoSQCd6498PmninNgqd/BR7z3Bsk/mabImBWuQ4wQgmQEeanzWd5BQU2aNi9mBURCLgyheuZS6Xhrw5luw3OkQ==",
+ "optional": true
+ },
+ "esbuild-linux-riscv64": {
+ "version": "0.15.10",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.10.tgz",
+ "integrity": "sha512-Lrl9Cr2YROvPV4wmZ1/g48httE8z/5SCiXIyebiB5N8VT7pX3t6meI7TQVHw/wQpqP/AF4SksDuFImPTM7Z32Q==",
+ "optional": true
+ },
+ "esbuild-linux-s390x": {
+ "version": "0.15.10",
+ "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.10.tgz",
+ "integrity": "sha512-ReP+6q3eLVVP2lpRrvl5EodKX7EZ1bS1/z5j6hsluAlZP5aHhk6ghT6Cq3IANvvDdscMMCB4QEbI+AjtvoOFpA==",
+ "optional": true
+ },
+ "esbuild-loader": {
+ "version": "2.20.0",
+ "resolved": "https://registry.npmjs.org/esbuild-loader/-/esbuild-loader-2.20.0.tgz",
+ "integrity": "sha512-dr+j8O4w5RvqZ7I4PPB4EIyVTd679EBQnMm+JBB7av+vu05Zpje2IpK5N3ld1VWa+WxrInIbNFAg093+E1aRsA==",
+ "requires": {
+ "esbuild": "^0.15.6",
+ "joycon": "^3.0.1",
+ "json5": "^2.2.0",
+ "loader-utils": "^2.0.0",
+ "tapable": "^2.2.0",
+ "webpack-sources": "^2.2.0"
+ }
+ },
+ "esbuild-netbsd-64": {
+ "version": "0.15.10",
+ "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.10.tgz",
+ "integrity": "sha512-iGDYtJCMCqldMskQ4eIV+QSS/CuT7xyy9i2/FjpKvxAuCzrESZXiA1L64YNj6/afuzfBe9i8m/uDkFHy257hTw==",
+ "optional": true
+ },
+ "esbuild-openbsd-64": {
+ "version": "0.15.10",
+ "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.10.tgz",
+ "integrity": "sha512-ftMMIwHWrnrYnvuJQRJs/Smlcb28F9ICGde/P3FUTCgDDM0N7WA0o9uOR38f5Xe2/OhNCgkjNeb7QeaE3cyWkQ==",
+ "optional": true
+ },
+ "esbuild-sunos-64": {
+ "version": "0.15.10",
+ "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.15.10.tgz",
+ "integrity": "sha512-mf7hBL9Uo2gcy2r3rUFMjVpTaGpFJJE5QTDDqUFf1632FxteYANffDZmKbqX0PfeQ2XjUDE604IcE7OJeoHiyg==",
+ "optional": true
+ },
+ "esbuild-windows-32": {
+ "version": "0.15.10",
+ "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.15.10.tgz",
+ "integrity": "sha512-ttFVo+Cg8b5+qHmZHbEc8Vl17kCleHhLzgT8X04y8zudEApo0PxPg9Mz8Z2cKH1bCYlve1XL8LkyXGFjtUYeGg==",
+ "optional": true
+ },
+ "esbuild-windows-64": {
+ "version": "0.15.10",
+ "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.15.10.tgz",
+ "integrity": "sha512-2H0gdsyHi5x+8lbng3hLbxDWR7mKHWh5BXZGKVG830KUmXOOWFE2YKJ4tHRkejRduOGDrBvHBriYsGtmTv3ntA==",
+ "optional": true
+ },
+ "esbuild-windows-arm64": {
+ "version": "0.15.10",
+ "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.10.tgz",
+ "integrity": "sha512-S+th4F+F8VLsHLR0zrUcG+Et4hx0RKgK1eyHc08kztmLOES8BWwMiaGdoW9hiXuzznXQ0I/Fg904MNbr11Nktw==",
+ "optional": true
+ },
+ "escalade": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
+ "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw=="
+ },
+ "escape-goat": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-4.0.0.tgz",
+ "integrity": "sha512-2Sd4ShcWxbx6OY1IHyla/CVNwvg7XwZVoXZHcSu9w9SReNP1EzzD5T8NWKIR38fIqEns9kDWKUQTXXAmlDrdPg=="
+ },
+ "escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "dev": true
+ },
+ "escodegen": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz",
+ "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==",
+ "dev": true,
+ "requires": {
+ "esprima": "^4.0.1",
+ "estraverse": "^5.2.0",
+ "esutils": "^2.0.2",
+ "optionator": "^0.8.1",
+ "source-map": "~0.6.1"
+ },
+ "dependencies": {
+ "levn": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
+ "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==",
+ "dev": true,
+ "requires": {
+ "prelude-ls": "~1.1.2",
+ "type-check": "~0.3.2"
+ }
+ },
+ "optionator": {
+ "version": "0.8.3",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz",
+ "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==",
+ "dev": true,
+ "requires": {
+ "deep-is": "~0.1.3",
+ "fast-levenshtein": "~2.0.6",
+ "levn": "~0.3.0",
+ "prelude-ls": "~1.1.2",
+ "type-check": "~0.3.2",
+ "word-wrap": "~1.2.3"
+ }
+ },
+ "prelude-ls": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
+ "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==",
+ "dev": true
+ },
+ "type-check": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
+ "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==",
+ "dev": true,
+ "requires": {
+ "prelude-ls": "~1.1.2"
+ }
+ }
+ }
+ },
+ "eslint": {
+ "version": "8.25.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.25.0.tgz",
+ "integrity": "sha512-DVlJOZ4Pn50zcKW5bYH7GQK/9MsoQG2d5eDH0ebEkE8PbgzTTmtt/VTH9GGJ4BfeZCpBLqFfvsjX35UacUL83A==",
+ "dev": true,
+ "requires": {
+ "@eslint/eslintrc": "^1.3.3",
+ "@humanwhocodes/config-array": "^0.10.5",
+ "@humanwhocodes/module-importer": "^1.0.1",
"ajv": "^6.10.0",
"chalk": "^4.0.0",
"cross-spawn": "^7.0.2",
@@ -16658,18 +12816,21 @@
"eslint-scope": "^7.1.1",
"eslint-utils": "^3.0.0",
"eslint-visitor-keys": "^3.3.0",
- "espree": "^9.3.2",
+ "espree": "^9.4.0",
"esquery": "^1.4.0",
"esutils": "^2.0.2",
"fast-deep-equal": "^3.1.3",
"file-entry-cache": "^6.0.1",
- "functional-red-black-tree": "^1.0.1",
+ "find-up": "^5.0.0",
"glob-parent": "^6.0.1",
"globals": "^13.15.0",
+ "globby": "^11.1.0",
+ "grapheme-splitter": "^1.0.4",
"ignore": "^5.2.0",
"import-fresh": "^3.0.0",
"imurmurhash": "^0.1.4",
"is-glob": "^4.0.0",
+ "js-sdsl": "^4.1.4",
"js-yaml": "^4.1.0",
"json-stable-stringify-without-jsonify": "^1.0.1",
"levn": "^0.4.1",
@@ -16680,8 +12841,7 @@
"regexpp": "^3.2.0",
"strip-ansi": "^6.0.1",
"strip-json-comments": "^3.1.0",
- "text-table": "^0.2.0",
- "v8-compile-cache": "^2.0.3"
+ "text-table": "^0.2.0"
},
"dependencies": {
"ajv": {
@@ -16726,13 +12886,12 @@
}
},
"eslint-module-utils": {
- "version": "2.7.3",
- "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.3.tgz",
- "integrity": "sha512-088JEC7O3lDZM9xGe0RerkOMd0EjFl+Yvd1jPWIkMT5u3H9+HC34mWWPnqPrN13gieT9pBOO+Qt07Nb/6TresQ==",
+ "version": "2.7.4",
+ "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.4.tgz",
+ "integrity": "sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==",
"dev": true,
"requires": {
- "debug": "^3.2.7",
- "find-up": "^2.1.0"
+ "debug": "^3.2.7"
},
"dependencies": {
"debug": {
@@ -16801,25 +12960,25 @@
"requires": {}
},
"eslint-plugin-sonarjs": {
- "version": "0.13.0",
- "resolved": "https://registry.npmjs.org/eslint-plugin-sonarjs/-/eslint-plugin-sonarjs-0.13.0.tgz",
- "integrity": "sha512-t3m7ta0EspzDxSOZh3cEOJIJVZgN/TlJYaBGnQlK6W/PZNbWep8q4RQskkJkA7/zwNpX0BaoEOSUUrqaADVoqA==",
+ "version": "0.15.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-sonarjs/-/eslint-plugin-sonarjs-0.15.0.tgz",
+ "integrity": "sha512-LuxHdAe6VqSbi1phsUvNjbmXLuvlobmryQJJNyQYbdubCfz6K8tmgoqNiJPnz0pP2AbYDbtuPm0ajOMgMrC+dQ==",
"dev": true,
"requires": {}
},
"eslint-plugin-unicorn": {
- "version": "43.0.2",
- "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-43.0.2.tgz",
- "integrity": "sha512-DtqZ5mf/GMlfWoz1abIjq5jZfaFuHzGBZYIeuJfEoKKGWRHr2JiJR+ea+BF7Wx2N1PPRoT/2fwgiK1NnmNE3Hg==",
+ "version": "44.0.2",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-44.0.2.tgz",
+ "integrity": "sha512-GLIDX1wmeEqpGaKcnMcqRvMVsoabeF0Ton0EX4Th5u6Kmf7RM9WBl705AXFEsns56ESkEs0uyelLuUTvz9Tr0w==",
"dev": true,
"requires": {
- "@babel/helper-validator-identifier": "^7.18.6",
- "ci-info": "^3.3.2",
+ "@babel/helper-validator-identifier": "^7.19.1",
+ "ci-info": "^3.4.0",
"clean-regexp": "^1.0.0",
"eslint-utils": "^3.0.0",
"esquery": "^1.4.0",
"indent-string": "^4.0.0",
- "is-builtin-module": "^3.1.0",
+ "is-builtin-module": "^3.2.0",
"lodash": "^4.17.21",
"pluralize": "^8.0.0",
"read-pkg-up": "^7.0.1",
@@ -16830,9 +12989,9 @@
}
},
"eslint-plugin-vue": {
- "version": "9.2.0",
- "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.2.0.tgz",
- "integrity": "sha512-W2hc+NUXoce8sZtWgZ45miQTy6jNyuSdub5aZ1IBune4JDeAyzucYX0TzkrQ1jMO52sNUDYlCIHDoaNePe0p5g==",
+ "version": "9.6.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.6.0.tgz",
+ "integrity": "sha512-zzySkJgVbFCylnG2+9MDF7N+2Rjze2y0bF8GyUNpFOnT8mCMfqqtLDJkHBuYu9N/psW1A6DVbQhPkP92E+qakA==",
"dev": true,
"requires": {
"eslint-utils": "^3.0.0",
@@ -16878,12 +13037,12 @@
"dev": true
},
"espree": {
- "version": "9.3.2",
- "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.2.tgz",
- "integrity": "sha512-D211tC7ZwouTIuY5x9XnS0E9sWNChB7IYKX/Xp5eQj3nFXhqmiUDB9q27y76oFl8jTg3pXcQx/bpxMfs3CIZbA==",
+ "version": "9.4.0",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.0.tgz",
+ "integrity": "sha512-DQmnRpLj7f6TgN/NYb0MTzJXL+vJF9h3pHy4JhCIs3zwcgez8xmGg3sXHcEO97BrmO2OSvCwMdfdlyl+E9KjOw==",
"dev": true,
"requires": {
- "acorn": "^8.7.1",
+ "acorn": "^8.8.0",
"acorn-jsx": "^5.3.2",
"eslint-visitor-keys": "^3.3.0"
}
@@ -16919,8 +13078,7 @@
"estree-walker": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
- "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
- "dev": true
+ "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
},
"esutils": {
"version": "2.0.3",
@@ -16939,116 +13097,15 @@
"resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
"integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q=="
},
- "execa": {
- "version": "5.1.1",
- "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
- "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==",
- "dev": true,
- "requires": {
- "cross-spawn": "^7.0.3",
- "get-stream": "^6.0.0",
- "human-signals": "^2.1.0",
- "is-stream": "^2.0.0",
- "merge-stream": "^2.0.0",
- "npm-run-path": "^4.0.1",
- "onetime": "^5.1.2",
- "signal-exit": "^3.0.3",
- "strip-final-newline": "^2.0.0"
- }
- },
- "execall": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/execall/-/execall-2.0.0.tgz",
- "integrity": "sha512-0FU2hZ5Hh6iQnarpRtQurM/aAvp3RIbfvgLHrcqJYzhXyV2KFruhuChf9NC6waAhiUR7FFtlugkI4p7f2Fqlow==",
- "dev": true,
- "requires": {
- "clone-regexp": "^2.1.0"
- }
- },
- "exit": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
- "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==",
- "dev": true
- },
- "expect": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/expect/-/expect-28.1.3.tgz",
- "integrity": "sha512-eEh0xn8HlsuOBxFgIss+2mX85VAS4Qy3OSkjV7rlBWljtA4oWH37glVGyOZSZvErDT/yBywZdPGwCXuTvSG85g==",
- "dev": true,
- "requires": {
- "@jest/expect-utils": "^28.1.3",
- "jest-get-type": "^28.0.2",
- "jest-matcher-utils": "^28.1.3",
- "jest-message-util": "^28.1.3",
- "jest-util": "^28.1.3"
- },
- "dependencies": {
- "@jest/types": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz",
- "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==",
- "dev": true,
- "requires": {
- "@jest/schemas": "^28.1.3",
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^3.0.0",
- "@types/node": "*",
- "@types/yargs": "^17.0.8",
- "chalk": "^4.0.0"
- }
- },
- "@types/yargs": {
- "version": "17.0.10",
- "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.10.tgz",
- "integrity": "sha512-gmEaFwpj/7f/ROdtIlci1R1VYU1J4j95m8T+Tj3iBgiBFKg1foE/PSl93bBd5T9LDXNPo8UlNN6W0qwD8O5OaA==",
- "dev": true,
- "requires": {
- "@types/yargs-parser": "*"
- }
- },
- "jest-message-util": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-28.1.3.tgz",
- "integrity": "sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g==",
- "dev": true,
- "requires": {
- "@babel/code-frame": "^7.12.13",
- "@jest/types": "^28.1.3",
- "@types/stack-utils": "^2.0.0",
- "chalk": "^4.0.0",
- "graceful-fs": "^4.2.9",
- "micromatch": "^4.0.4",
- "pretty-format": "^28.1.3",
- "slash": "^3.0.0",
- "stack-utils": "^2.0.3"
- }
- },
- "jest-util": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz",
- "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==",
- "dev": true,
- "requires": {
- "@jest/types": "^28.1.3",
- "@types/node": "*",
- "chalk": "^4.0.0",
- "ci-info": "^3.2.0",
- "graceful-fs": "^4.2.9",
- "picomatch": "^2.2.3"
- }
- }
- }
- },
"fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
},
"fast-glob": {
- "version": "3.2.11",
- "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz",
- "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==",
+ "version": "3.2.12",
+ "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz",
+ "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==",
"requires": {
"@nodelib/fs.stat": "^2.0.2",
"@nodelib/fs.walk": "^1.2.3",
@@ -17085,9 +13142,9 @@
"dev": true
},
"fastest-levenshtein": {
- "version": "1.0.14",
- "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.14.tgz",
- "integrity": "sha512-tFfWHjnuUfKE186Tfgr+jtaFc0mZTApEgKDOeyN+FwOqRkO/zK/3h1AiRd8u8CY53owL3CUmGr/oI9p/RdyLTA=="
+ "version": "1.0.16",
+ "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz",
+ "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg=="
},
"fastq": {
"version": "1.13.0",
@@ -17097,15 +13154,6 @@
"reusify": "^1.0.4"
}
},
- "fb-watchman": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz",
- "integrity": "sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg==",
- "dev": true,
- "requires": {
- "bser": "2.1.1"
- }
- },
"file-entry-cache": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
@@ -17130,12 +13178,13 @@
}
},
"find-up": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
- "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==",
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+ "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
"dev": true,
"requires": {
- "locate-path": "^2.0.0"
+ "locate-path": "^6.0.0",
+ "path-exists": "^4.0.0"
}
},
"flat-cache": {
@@ -17149,9 +13198,9 @@
}
},
"flatted": {
- "version": "3.2.6",
- "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.6.tgz",
- "integrity": "sha512-0sQoMh9s0BYsm+12Huy/rkKxVu4R1+r96YX5cG44rHV0pQ6iC3Q+mkoMFaGWObMFYQxCVT+ssG1ksneA2MI9KQ==",
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz",
+ "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==",
"dev": true
},
"font-awesome": {
@@ -17160,13 +13209,13 @@
"integrity": "sha512-U6kGnykA/6bFmg1M/oT9EkFeIYv7JlX3bozwQJWiiLz6L0w3F5vBVPxHlwyX/vtNq1ckcpRKOB9f2Qal/VtFpg=="
},
"form-data": {
- "version": "2.5.1",
- "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz",
- "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==",
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
+ "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
"dev": true,
"requires": {
"asynckit": "^0.4.0",
- "combined-stream": "^1.0.6",
+ "combined-stream": "^1.0.8",
"mime-types": "^2.1.12"
}
},
@@ -17220,34 +13269,28 @@
"functions-have-names": "^1.2.2"
}
},
- "functional-red-black-tree": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
- "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==",
- "dev": true
- },
"functions-have-names": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz",
"integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==",
"dev": true
},
- "gensync": {
- "version": "1.0.0-beta.2",
- "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
- "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
- "dev": true
- },
"get-caller-file": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
"dev": true
},
+ "get-func-name": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz",
+ "integrity": "sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig==",
+ "dev": true
+ },
"get-intrinsic": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.2.tgz",
- "integrity": "sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA==",
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz",
+ "integrity": "sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==",
"dev": true,
"requires": {
"function-bind": "^1.1.1",
@@ -17255,18 +13298,6 @@
"has-symbols": "^1.0.3"
}
},
- "get-package-type": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz",
- "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==",
- "dev": true
- },
- "get-port": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/get-port/-/get-port-3.2.0.tgz",
- "integrity": "sha512-x5UJKlgeUiNT8nyo/AcnwLnZuZNcSjSw0kogRB+Whd1fjjFq4B1hySFxSFWWSn4mIBzg3sRNUDFYc4g5gjPoLg==",
- "dev": true
- },
"get-source": {
"version": "2.0.12",
"resolved": "https://registry.npmjs.org/get-source/-/get-source-2.0.12.tgz",
@@ -17286,15 +13317,9 @@
}
},
"get-stdin": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-8.0.0.tgz",
- "integrity": "sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==",
- "dev": true
- },
- "get-stream": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
- "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-9.0.0.tgz",
+ "integrity": "sha512-dVKBjfWisLAicarI2Sf+JuBE/DghV4UzNAVe9yhEJuzeREd3JhOTE9cUaJTeSa77fsbQUK3pcOpJfM59+VKZaA==",
"dev": true
},
"get-symbol-description": {
@@ -17319,6 +13344,14 @@
"file-uri-to-path": "2",
"fs-extra": "^8.1.0",
"ftp": "^0.3.10"
+ },
+ "dependencies": {
+ "@tootallnate/once": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz",
+ "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==",
+ "dev": true
+ }
}
},
"glob": {
@@ -17368,6 +13401,12 @@
"which": "^1.3.1"
},
"dependencies": {
+ "ini": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
+ "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
+ "dev": true
+ },
"which": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
@@ -17380,14 +13419,23 @@
}
},
"globals": {
- "version": "13.16.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-13.16.0.tgz",
- "integrity": "sha512-A1lrQfpNF+McdPOnnFqY3kSN0AFTy485bTi1bkLk4mVPODIUEcSfhHgRqA+QdXPksrSTTztYXx37NFV+GpGk3Q==",
+ "version": "13.17.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz",
+ "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==",
"dev": true,
"requires": {
"type-fest": "^0.20.2"
}
},
+ "globalthis": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz",
+ "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==",
+ "dev": true,
+ "requires": {
+ "define-properties": "^1.1.3"
+ }
+ },
"globby": {
"version": "11.1.0",
"resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz",
@@ -17413,6 +13461,12 @@
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz",
"integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA=="
},
+ "grapheme-splitter": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz",
+ "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==",
+ "dev": true
+ },
"graphlib": {
"version": "2.1.8",
"resolved": "https://registry.npmjs.org/graphlib/-/graphlib-2.1.8.tgz",
@@ -17422,24 +13476,9 @@
}
},
"gsap": {
- "version": "3.10.4",
- "resolved": "https://registry.npmjs.org/gsap/-/gsap-3.10.4.tgz",
- "integrity": "sha512-6QatdkKxXCMfvCW4rM++0RqyLQAzFX5nwl3yHS0XPgkZBkiSEY3VZVbMltrdtsbER/xZonLtyHt684wRp4erlQ=="
- },
- "happy-dom": {
- "version": "6.0.4",
- "resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-6.0.4.tgz",
- "integrity": "sha512-b+ID23Ms0BY08UNLymsOMG7EI2jSlwEt4cbJs938GZfeNAg+fqgkSO3TokQMgSOFoHznpjWmpVjBUL5boJ9PWw==",
- "dev": true,
- "requires": {
- "css.escape": "^1.5.1",
- "he": "^1.2.0",
- "node-fetch": "^2.x.x",
- "sync-request": "^6.1.0",
- "webidl-conversions": "^7.0.0",
- "whatwg-encoding": "^2.0.0",
- "whatwg-mimetype": "^3.0.0"
- }
+ "version": "3.11.3",
+ "resolved": "https://registry.npmjs.org/gsap/-/gsap-3.11.3.tgz",
+ "integrity": "sha512-xc/iIJy+LWiMbRa4IdMtdnnKa/7PXEK6NNzV71gdOYUVeTZN7UWnLU0fB7Hi1iwiz4ZZoYkBZPPYGg+2+zzFHA=="
},
"hard-rejection": {
"version": "2.1.0",
@@ -17491,14 +13530,9 @@
}
},
"hash-sum": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-1.0.2.tgz",
- "integrity": "sha512-fUs4B4L+mlt8/XAtSOGMUO1TXmAelItBPtJG7CyHJfYTdDjwisntGO2JQz7oUsatOY9o68+57eziUVNw/mRHmA=="
- },
- "he": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
- "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw=="
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-2.0.0.tgz",
+ "integrity": "sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg=="
},
"hosted-git-info": {
"version": "2.8.9",
@@ -17506,11 +13540,14 @@
"integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==",
"dev": true
},
- "html-escaper": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
- "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
- "dev": true
+ "html-encoding-sniffer": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz",
+ "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==",
+ "dev": true,
+ "requires": {
+ "whatwg-encoding": "^2.0.0"
+ }
},
"html-tags": {
"version": "3.2.0",
@@ -17518,18 +13555,6 @@
"integrity": "sha512-vy7ClnArOZwCnqZgvv+ddgHgJiAFXe3Ge9ML5/mBctVJoUoYPCdxVucOywjDARn6CVoh3dRSFdPHy2sX80L0Wg==",
"dev": true
},
- "http-basic": {
- "version": "8.1.3",
- "resolved": "https://registry.npmjs.org/http-basic/-/http-basic-8.1.3.tgz",
- "integrity": "sha512-/EcDMwJZh3mABI2NhGfHOGOeOZITqfkEO4p/xK+l3NpyncIHUQBoMvCSF/b5GqvKtySC2srL/GGG3+EtlqlmCw==",
- "dev": true,
- "requires": {
- "caseless": "^0.12.0",
- "concat-stream": "^1.6.2",
- "http-response-object": "^3.0.1",
- "parse-cache-control": "^1.0.1"
- }
- },
"http-errors": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
@@ -17544,33 +13569,16 @@
}
},
"http-proxy-agent": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz",
- "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==",
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz",
+ "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==",
"dev": true,
"requires": {
- "@tootallnate/once": "1",
+ "@tootallnate/once": "2",
"agent-base": "6",
"debug": "4"
}
},
- "http-response-object": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/http-response-object/-/http-response-object-3.0.2.tgz",
- "integrity": "sha512-bqX0XTF6fnXSQcEJ2Iuyr75yVakyjIDCqroJQ/aHfSdlM743Cwqoi2nDYMzLGWUcuTWGWy8AAvOKXTfiv6q9RA==",
- "dev": true,
- "requires": {
- "@types/node": "^10.0.3"
- },
- "dependencies": {
- "@types/node": {
- "version": "10.17.60",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz",
- "integrity": "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==",
- "dev": true
- }
- }
- },
"https-proxy-agent": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
@@ -17581,12 +13589,6 @@
"debug": "4"
}
},
- "human-signals": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
- "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==",
- "dev": true
- },
"iconv-lite": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
@@ -17671,9 +13673,9 @@
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"ini": {
- "version": "1.3.8",
- "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz",
- "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==",
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-3.0.1.tgz",
+ "integrity": "sha512-it4HyVAUTKBc6m8e1iXWvXSTdndF7HbdN713+kvLrymxTaU4AUBWrJ4vEooP+V7fexnVD3LKcBshjGGPefSMUQ==",
"dev": true
},
"internal-slot": {
@@ -17729,24 +13731,24 @@
}
},
"is-builtin-module": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.1.0.tgz",
- "integrity": "sha512-OV7JjAgOTfAFJmHZLvpSTb4qi0nIILDV1gWPYDnDJUTNFM5aGlRAhk4QcT8i7TuAleeEV5Fdkqn3t4mS+Q11fg==",
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.0.tgz",
+ "integrity": "sha512-phDA4oSGt7vl1n5tJvTWooWWAsXLY+2xCnxNqvKhGEzujg+A43wPlPOyDg3C8XQHN+6k/JTQWJ/j0dQh/qr+Hw==",
"dev": true,
"requires": {
- "builtin-modules": "^3.0.0"
+ "builtin-modules": "^3.3.0"
}
},
"is-callable": {
- "version": "1.2.4",
- "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz",
- "integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==",
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
+ "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
"dev": true
},
"is-core-module": {
- "version": "2.9.0",
- "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz",
- "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==",
+ "version": "2.10.0",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.10.0.tgz",
+ "integrity": "sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==",
"requires": {
"has": "^1.0.3"
}
@@ -17770,12 +13772,6 @@
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="
},
- "is-generator-fn": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz",
- "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==",
- "dev": true
- },
"is-glob": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
@@ -17816,6 +13812,12 @@
"integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==",
"dev": true
},
+ "is-potential-custom-element-name": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz",
+ "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==",
+ "dev": true
+ },
"is-reference": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz",
@@ -17835,12 +13837,6 @@
"has-tostringtag": "^1.0.0"
}
},
- "is-regexp": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-2.1.0.tgz",
- "integrity": "sha512-OZ4IlER3zmRIoB9AqNhEggVxqIH4ofDns5nRrPS6yQxXE1TPCUpFznBfRQmQa8uC+pXqjMnukiJBxCisIxiLGA==",
- "dev": true
- },
"is-shared-array-buffer": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz",
@@ -17850,12 +13846,6 @@
"call-bind": "^1.0.2"
}
},
- "is-stream": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
- "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
- "dev": true
- },
"is-string": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz",
@@ -17904,1277 +13894,15 @@
"resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
"integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg=="
},
- "istanbul-lib-coverage": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz",
- "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==",
- "dev": true
- },
- "istanbul-lib-instrument": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.0.tgz",
- "integrity": "sha512-6Lthe1hqXHBNsqvgDzGO6l03XNeu3CrG4RqQ1KM9+l5+jNGpEJfIELx1NS3SEHmJQA8np/u+E4EPRKRiu6m19A==",
- "dev": true,
- "requires": {
- "@babel/core": "^7.12.3",
- "@babel/parser": "^7.14.7",
- "@istanbuljs/schema": "^0.1.2",
- "istanbul-lib-coverage": "^3.2.0",
- "semver": "^6.3.0"
- },
- "dependencies": {
- "semver": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
- "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
- "dev": true
- }
- }
- },
- "istanbul-lib-report": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz",
- "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==",
- "dev": true,
- "requires": {
- "istanbul-lib-coverage": "^3.0.0",
- "make-dir": "^3.0.0",
- "supports-color": "^7.1.0"
- }
- },
- "istanbul-lib-source-maps": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz",
- "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==",
- "dev": true,
- "requires": {
- "debug": "^4.1.1",
- "istanbul-lib-coverage": "^3.0.0",
- "source-map": "^0.6.1"
- }
- },
- "istanbul-reports": {
- "version": "3.1.5",
- "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz",
- "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==",
- "dev": true,
- "requires": {
- "html-escaper": "^2.0.0",
- "istanbul-lib-report": "^3.0.0"
- }
- },
- "jest": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest/-/jest-28.1.3.tgz",
- "integrity": "sha512-N4GT5on8UkZgH0O5LUavMRV1EDEhNTL0KEfRmDIeZHSV7p2XgLoY9t9VDUgL6o+yfdgYHVxuz81G8oB9VG5uyA==",
- "dev": true,
- "requires": {
- "@jest/core": "^28.1.3",
- "@jest/types": "^28.1.3",
- "import-local": "^3.0.2",
- "jest-cli": "^28.1.3"
- },
- "dependencies": {
- "@jest/types": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz",
- "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==",
- "dev": true,
- "requires": {
- "@jest/schemas": "^28.1.3",
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^3.0.0",
- "@types/node": "*",
- "@types/yargs": "^17.0.8",
- "chalk": "^4.0.0"
- }
- },
- "@types/yargs": {
- "version": "17.0.10",
- "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.10.tgz",
- "integrity": "sha512-gmEaFwpj/7f/ROdtIlci1R1VYU1J4j95m8T+Tj3iBgiBFKg1foE/PSl93bBd5T9LDXNPo8UlNN6W0qwD8O5OaA==",
- "dev": true,
- "requires": {
- "@types/yargs-parser": "*"
- }
- }
- }
- },
- "jest-changed-files": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-28.1.3.tgz",
- "integrity": "sha512-esaOfUWJXk2nfZt9SPyC8gA1kNfdKLkQWyzsMlqq8msYSlNKfmZxfRgZn4Cd4MGVUF+7v6dBs0d5TOAKa7iIiA==",
- "dev": true,
- "requires": {
- "execa": "^5.0.0",
- "p-limit": "^3.1.0"
- }
- },
- "jest-circus": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-28.1.3.tgz",
- "integrity": "sha512-cZ+eS5zc79MBwt+IhQhiEp0OeBddpc1n8MBo1nMB8A7oPMKEO+Sre+wHaLJexQUj9Ya/8NOBY0RESUgYjB6fow==",
- "dev": true,
- "requires": {
- "@jest/environment": "^28.1.3",
- "@jest/expect": "^28.1.3",
- "@jest/test-result": "^28.1.3",
- "@jest/types": "^28.1.3",
- "@types/node": "*",
- "chalk": "^4.0.0",
- "co": "^4.6.0",
- "dedent": "^0.7.0",
- "is-generator-fn": "^2.0.0",
- "jest-each": "^28.1.3",
- "jest-matcher-utils": "^28.1.3",
- "jest-message-util": "^28.1.3",
- "jest-runtime": "^28.1.3",
- "jest-snapshot": "^28.1.3",
- "jest-util": "^28.1.3",
- "p-limit": "^3.1.0",
- "pretty-format": "^28.1.3",
- "slash": "^3.0.0",
- "stack-utils": "^2.0.3"
- },
- "dependencies": {
- "@jest/environment": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-28.1.3.tgz",
- "integrity": "sha512-1bf40cMFTEkKyEf585R9Iz1WayDjHoHqvts0XFYEqyKM3cFWDpeMoqKKTAF9LSYQModPUlh8FKptoM2YcMWAXA==",
- "dev": true,
- "requires": {
- "@jest/fake-timers": "^28.1.3",
- "@jest/types": "^28.1.3",
- "@types/node": "*",
- "jest-mock": "^28.1.3"
- }
- },
- "@jest/fake-timers": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-28.1.3.tgz",
- "integrity": "sha512-D/wOkL2POHv52h+ok5Oj/1gOG9HSywdoPtFsRCUmlCILXNn5eIWmcnd3DIiWlJnpGvQtmajqBP95Ei0EimxfLw==",
- "dev": true,
- "requires": {
- "@jest/types": "^28.1.3",
- "@sinonjs/fake-timers": "^9.1.2",
- "@types/node": "*",
- "jest-message-util": "^28.1.3",
- "jest-mock": "^28.1.3",
- "jest-util": "^28.1.3"
- }
- },
- "@jest/types": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz",
- "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==",
- "dev": true,
- "requires": {
- "@jest/schemas": "^28.1.3",
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^3.0.0",
- "@types/node": "*",
- "@types/yargs": "^17.0.8",
- "chalk": "^4.0.0"
- }
- },
- "@sinonjs/fake-timers": {
- "version": "9.1.2",
- "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz",
- "integrity": "sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw==",
- "dev": true,
- "requires": {
- "@sinonjs/commons": "^1.7.0"
- }
- },
- "@types/yargs": {
- "version": "17.0.10",
- "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.10.tgz",
- "integrity": "sha512-gmEaFwpj/7f/ROdtIlci1R1VYU1J4j95m8T+Tj3iBgiBFKg1foE/PSl93bBd5T9LDXNPo8UlNN6W0qwD8O5OaA==",
- "dev": true,
- "requires": {
- "@types/yargs-parser": "*"
- }
- },
- "jest-message-util": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-28.1.3.tgz",
- "integrity": "sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g==",
- "dev": true,
- "requires": {
- "@babel/code-frame": "^7.12.13",
- "@jest/types": "^28.1.3",
- "@types/stack-utils": "^2.0.0",
- "chalk": "^4.0.0",
- "graceful-fs": "^4.2.9",
- "micromatch": "^4.0.4",
- "pretty-format": "^28.1.3",
- "slash": "^3.0.0",
- "stack-utils": "^2.0.3"
- }
- },
- "jest-mock": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-28.1.3.tgz",
- "integrity": "sha512-o3J2jr6dMMWYVH4Lh/NKmDXdosrsJgi4AviS8oXLujcjpCMBb1FMsblDnOXKZKfSiHLxYub1eS0IHuRXsio9eA==",
- "dev": true,
- "requires": {
- "@jest/types": "^28.1.3",
- "@types/node": "*"
- }
- },
- "jest-util": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz",
- "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==",
- "dev": true,
- "requires": {
- "@jest/types": "^28.1.3",
- "@types/node": "*",
- "chalk": "^4.0.0",
- "ci-info": "^3.2.0",
- "graceful-fs": "^4.2.9",
- "picomatch": "^2.2.3"
- }
- }
- }
- },
- "jest-cli": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-28.1.3.tgz",
- "integrity": "sha512-roY3kvrv57Azn1yPgdTebPAXvdR2xfezaKKYzVxZ6It/5NCxzJym6tUI5P1zkdWhfUYkxEI9uZWcQdaFLo8mJQ==",
- "dev": true,
- "requires": {
- "@jest/core": "^28.1.3",
- "@jest/test-result": "^28.1.3",
- "@jest/types": "^28.1.3",
- "chalk": "^4.0.0",
- "exit": "^0.1.2",
- "graceful-fs": "^4.2.9",
- "import-local": "^3.0.2",
- "jest-config": "^28.1.3",
- "jest-util": "^28.1.3",
- "jest-validate": "^28.1.3",
- "prompts": "^2.0.1",
- "yargs": "^17.3.1"
- },
- "dependencies": {
- "@jest/types": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz",
- "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==",
- "dev": true,
- "requires": {
- "@jest/schemas": "^28.1.3",
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^3.0.0",
- "@types/node": "*",
- "@types/yargs": "^17.0.8",
- "chalk": "^4.0.0"
- }
- },
- "@types/yargs": {
- "version": "17.0.10",
- "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.10.tgz",
- "integrity": "sha512-gmEaFwpj/7f/ROdtIlci1R1VYU1J4j95m8T+Tj3iBgiBFKg1foE/PSl93bBd5T9LDXNPo8UlNN6W0qwD8O5OaA==",
- "dev": true,
- "requires": {
- "@types/yargs-parser": "*"
- }
- },
- "jest-util": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz",
- "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==",
- "dev": true,
- "requires": {
- "@jest/types": "^28.1.3",
- "@types/node": "*",
- "chalk": "^4.0.0",
- "ci-info": "^3.2.0",
- "graceful-fs": "^4.2.9",
- "picomatch": "^2.2.3"
- }
- }
- }
- },
- "jest-config": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-28.1.3.tgz",
- "integrity": "sha512-MG3INjByJ0J4AsNBm7T3hsuxKQqFIiRo/AUqb1q9LRKI5UU6Aar9JHbr9Ivn1TVwfUD9KirRoM/T6u8XlcQPHQ==",
- "dev": true,
- "requires": {
- "@babel/core": "^7.11.6",
- "@jest/test-sequencer": "^28.1.3",
- "@jest/types": "^28.1.3",
- "babel-jest": "^28.1.3",
- "chalk": "^4.0.0",
- "ci-info": "^3.2.0",
- "deepmerge": "^4.2.2",
- "glob": "^7.1.3",
- "graceful-fs": "^4.2.9",
- "jest-circus": "^28.1.3",
- "jest-environment-node": "^28.1.3",
- "jest-get-type": "^28.0.2",
- "jest-regex-util": "^28.0.2",
- "jest-resolve": "^28.1.3",
- "jest-runner": "^28.1.3",
- "jest-util": "^28.1.3",
- "jest-validate": "^28.1.3",
- "micromatch": "^4.0.4",
- "parse-json": "^5.2.0",
- "pretty-format": "^28.1.3",
- "slash": "^3.0.0",
- "strip-json-comments": "^3.1.1"
- },
- "dependencies": {
- "@jest/types": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz",
- "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==",
- "dev": true,
- "requires": {
- "@jest/schemas": "^28.1.3",
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^3.0.0",
- "@types/node": "*",
- "@types/yargs": "^17.0.8",
- "chalk": "^4.0.0"
- }
- },
- "@types/yargs": {
- "version": "17.0.10",
- "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.10.tgz",
- "integrity": "sha512-gmEaFwpj/7f/ROdtIlci1R1VYU1J4j95m8T+Tj3iBgiBFKg1foE/PSl93bBd5T9LDXNPo8UlNN6W0qwD8O5OaA==",
- "dev": true,
- "requires": {
- "@types/yargs-parser": "*"
- }
- },
- "jest-util": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz",
- "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==",
- "dev": true,
- "requires": {
- "@jest/types": "^28.1.3",
- "@types/node": "*",
- "chalk": "^4.0.0",
- "ci-info": "^3.2.0",
- "graceful-fs": "^4.2.9",
- "picomatch": "^2.2.3"
- }
- }
- }
- },
- "jest-diff": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-28.1.3.tgz",
- "integrity": "sha512-8RqP1B/OXzjjTWkqMX67iqgwBVJRgCyKD3L9nq+6ZqJMdvjE8RgHktqZ6jNrkdMT+dJuYNI3rhQpxaz7drJHfw==",
- "dev": true,
- "requires": {
- "chalk": "^4.0.0",
- "diff-sequences": "^28.1.1",
- "jest-get-type": "^28.0.2",
- "pretty-format": "^28.1.3"
- }
- },
- "jest-docblock": {
- "version": "28.1.1",
- "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-28.1.1.tgz",
- "integrity": "sha512-3wayBVNiOYx0cwAbl9rwm5kKFP8yHH3d/fkEaL02NPTkDojPtheGB7HZSFY4wzX+DxyrvhXz0KSCVksmCknCuA==",
- "dev": true,
- "requires": {
- "detect-newline": "^3.0.0"
- }
- },
- "jest-each": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-28.1.3.tgz",
- "integrity": "sha512-arT1z4sg2yABU5uogObVPvSlSMQlDA48owx07BDPAiasW0yYpYHYOo4HHLz9q0BVzDVU4hILFjzJw0So9aCL/g==",
- "dev": true,
- "requires": {
- "@jest/types": "^28.1.3",
- "chalk": "^4.0.0",
- "jest-get-type": "^28.0.2",
- "jest-util": "^28.1.3",
- "pretty-format": "^28.1.3"
- },
- "dependencies": {
- "@jest/types": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz",
- "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==",
- "dev": true,
- "requires": {
- "@jest/schemas": "^28.1.3",
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^3.0.0",
- "@types/node": "*",
- "@types/yargs": "^17.0.8",
- "chalk": "^4.0.0"
- }
- },
- "@types/yargs": {
- "version": "17.0.10",
- "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.10.tgz",
- "integrity": "sha512-gmEaFwpj/7f/ROdtIlci1R1VYU1J4j95m8T+Tj3iBgiBFKg1foE/PSl93bBd5T9LDXNPo8UlNN6W0qwD8O5OaA==",
- "dev": true,
- "requires": {
- "@types/yargs-parser": "*"
- }
- },
- "jest-util": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz",
- "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==",
- "dev": true,
- "requires": {
- "@jest/types": "^28.1.3",
- "@types/node": "*",
- "chalk": "^4.0.0",
- "ci-info": "^3.2.0",
- "graceful-fs": "^4.2.9",
- "picomatch": "^2.2.3"
- }
- }
- }
- },
- "jest-environment-node": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-28.1.3.tgz",
- "integrity": "sha512-ugP6XOhEpjAEhGYvp5Xj989ns5cB1K6ZdjBYuS30umT4CQEETaxSiPcZ/E1kFktX4GkrcM4qu07IIlDYX1gp+A==",
- "dev": true,
- "requires": {
- "@jest/environment": "^28.1.3",
- "@jest/fake-timers": "^28.1.3",
- "@jest/types": "^28.1.3",
- "@types/node": "*",
- "jest-mock": "^28.1.3",
- "jest-util": "^28.1.3"
- },
- "dependencies": {
- "@jest/environment": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-28.1.3.tgz",
- "integrity": "sha512-1bf40cMFTEkKyEf585R9Iz1WayDjHoHqvts0XFYEqyKM3cFWDpeMoqKKTAF9LSYQModPUlh8FKptoM2YcMWAXA==",
- "dev": true,
- "requires": {
- "@jest/fake-timers": "^28.1.3",
- "@jest/types": "^28.1.3",
- "@types/node": "*",
- "jest-mock": "^28.1.3"
- }
- },
- "@jest/fake-timers": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-28.1.3.tgz",
- "integrity": "sha512-D/wOkL2POHv52h+ok5Oj/1gOG9HSywdoPtFsRCUmlCILXNn5eIWmcnd3DIiWlJnpGvQtmajqBP95Ei0EimxfLw==",
- "dev": true,
- "requires": {
- "@jest/types": "^28.1.3",
- "@sinonjs/fake-timers": "^9.1.2",
- "@types/node": "*",
- "jest-message-util": "^28.1.3",
- "jest-mock": "^28.1.3",
- "jest-util": "^28.1.3"
- }
- },
- "@jest/types": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz",
- "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==",
- "dev": true,
- "requires": {
- "@jest/schemas": "^28.1.3",
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^3.0.0",
- "@types/node": "*",
- "@types/yargs": "^17.0.8",
- "chalk": "^4.0.0"
- }
- },
- "@sinonjs/fake-timers": {
- "version": "9.1.2",
- "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz",
- "integrity": "sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw==",
- "dev": true,
- "requires": {
- "@sinonjs/commons": "^1.7.0"
- }
- },
- "@types/yargs": {
- "version": "17.0.10",
- "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.10.tgz",
- "integrity": "sha512-gmEaFwpj/7f/ROdtIlci1R1VYU1J4j95m8T+Tj3iBgiBFKg1foE/PSl93bBd5T9LDXNPo8UlNN6W0qwD8O5OaA==",
- "dev": true,
- "requires": {
- "@types/yargs-parser": "*"
- }
- },
- "jest-message-util": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-28.1.3.tgz",
- "integrity": "sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g==",
- "dev": true,
- "requires": {
- "@babel/code-frame": "^7.12.13",
- "@jest/types": "^28.1.3",
- "@types/stack-utils": "^2.0.0",
- "chalk": "^4.0.0",
- "graceful-fs": "^4.2.9",
- "micromatch": "^4.0.4",
- "pretty-format": "^28.1.3",
- "slash": "^3.0.0",
- "stack-utils": "^2.0.3"
- }
- },
- "jest-mock": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-28.1.3.tgz",
- "integrity": "sha512-o3J2jr6dMMWYVH4Lh/NKmDXdosrsJgi4AviS8oXLujcjpCMBb1FMsblDnOXKZKfSiHLxYub1eS0IHuRXsio9eA==",
- "dev": true,
- "requires": {
- "@jest/types": "^28.1.3",
- "@types/node": "*"
- }
- },
- "jest-util": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz",
- "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==",
- "dev": true,
- "requires": {
- "@jest/types": "^28.1.3",
- "@types/node": "*",
- "chalk": "^4.0.0",
- "ci-info": "^3.2.0",
- "graceful-fs": "^4.2.9",
- "picomatch": "^2.2.3"
- }
- }
- }
- },
- "jest-extended": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/jest-extended/-/jest-extended-3.0.1.tgz",
- "integrity": "sha512-OSGbKUhbjy7QikfQyK3ishFrAqLeRodBzeJk7SuuWGACAT7HHcGuJ4aUQ3ueLANx4KSv1Pa7r1LJWGtJ3eI0xA==",
- "dev": true,
- "requires": {
- "jest-diff": "^28.0.0",
- "jest-get-type": "^28.0.0"
- }
- },
- "jest-get-type": {
- "version": "28.0.2",
- "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-28.0.2.tgz",
- "integrity": "sha512-ioj2w9/DxSYHfOm5lJKCdcAmPJzQXmbM/Url3rhlghrPvT3tt+7a/+oXc9azkKmLvoiXjtV83bEWqi+vs5nlPA==",
- "dev": true
- },
- "jest-haste-map": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-28.1.3.tgz",
- "integrity": "sha512-3S+RQWDXccXDKSWnkHa/dPwt+2qwA8CJzR61w3FoYCvoo3Pn8tvGcysmMF0Bj0EX5RYvAI2EIvC57OmotfdtKA==",
- "dev": true,
- "requires": {
- "@jest/types": "^28.1.3",
- "@types/graceful-fs": "^4.1.3",
- "@types/node": "*",
- "anymatch": "^3.0.3",
- "fb-watchman": "^2.0.0",
- "fsevents": "^2.3.2",
- "graceful-fs": "^4.2.9",
- "jest-regex-util": "^28.0.2",
- "jest-util": "^28.1.3",
- "jest-worker": "^28.1.3",
- "micromatch": "^4.0.4",
- "walker": "^1.0.8"
- },
- "dependencies": {
- "@jest/types": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz",
- "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==",
- "dev": true,
- "requires": {
- "@jest/schemas": "^28.1.3",
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^3.0.0",
- "@types/node": "*",
- "@types/yargs": "^17.0.8",
- "chalk": "^4.0.0"
- }
- },
- "@types/yargs": {
- "version": "17.0.10",
- "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.10.tgz",
- "integrity": "sha512-gmEaFwpj/7f/ROdtIlci1R1VYU1J4j95m8T+Tj3iBgiBFKg1foE/PSl93bBd5T9LDXNPo8UlNN6W0qwD8O5OaA==",
- "dev": true,
- "requires": {
- "@types/yargs-parser": "*"
- }
- },
- "jest-util": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz",
- "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==",
- "dev": true,
- "requires": {
- "@jest/types": "^28.1.3",
- "@types/node": "*",
- "chalk": "^4.0.0",
- "ci-info": "^3.2.0",
- "graceful-fs": "^4.2.9",
- "picomatch": "^2.2.3"
- }
- }
- }
- },
- "jest-leak-detector": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-28.1.3.tgz",
- "integrity": "sha512-WFVJhnQsiKtDEo5lG2mM0v40QWnBM+zMdHHyJs8AWZ7J0QZJS59MsyKeJHWhpBZBH32S48FOVvGyOFT1h0DlqA==",
- "dev": true,
- "requires": {
- "jest-get-type": "^28.0.2",
- "pretty-format": "^28.1.3"
- }
- },
- "jest-matcher-utils": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-28.1.3.tgz",
- "integrity": "sha512-kQeJ7qHemKfbzKoGjHHrRKH6atgxMk8Enkk2iPQ3XwO6oE/KYD8lMYOziCkeSB9G4adPM4nR1DE8Tf5JeWH6Bw==",
- "dev": true,
- "requires": {
- "chalk": "^4.0.0",
- "jest-diff": "^28.1.3",
- "jest-get-type": "^28.0.2",
- "pretty-format": "^28.1.3"
- }
- },
- "jest-message-util": {
- "version": "27.5.1",
- "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-27.5.1.tgz",
- "integrity": "sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==",
- "dev": true,
- "requires": {
- "@babel/code-frame": "^7.12.13",
- "@jest/types": "^27.5.1",
- "@types/stack-utils": "^2.0.0",
- "chalk": "^4.0.0",
- "graceful-fs": "^4.2.9",
- "micromatch": "^4.0.4",
- "pretty-format": "^27.5.1",
- "slash": "^3.0.0",
- "stack-utils": "^2.0.3"
- },
- "dependencies": {
- "ansi-styles": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
- "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
- "dev": true
- },
- "pretty-format": {
- "version": "27.5.1",
- "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz",
- "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==",
- "dev": true,
- "requires": {
- "ansi-regex": "^5.0.1",
- "ansi-styles": "^5.0.0",
- "react-is": "^17.0.1"
- }
- },
- "react-is": {
- "version": "17.0.2",
- "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
- "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
- "dev": true
- }
- }
- },
- "jest-mock": {
- "version": "27.5.1",
- "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-27.5.1.tgz",
- "integrity": "sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og==",
- "dev": true,
- "requires": {
- "@jest/types": "^27.5.1",
- "@types/node": "*"
- }
- },
- "jest-pnp-resolver": {
- "version": "1.2.2",
- "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz",
- "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==",
- "dev": true,
- "requires": {}
- },
- "jest-regex-util": {
- "version": "28.0.2",
- "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-28.0.2.tgz",
- "integrity": "sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw==",
- "dev": true
- },
- "jest-resolve": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-28.1.3.tgz",
- "integrity": "sha512-Z1W3tTjE6QaNI90qo/BJpfnvpxtaFTFw5CDgwpyE/Kz8U/06N1Hjf4ia9quUhCh39qIGWF1ZuxFiBiJQwSEYKQ==",
- "dev": true,
- "requires": {
- "chalk": "^4.0.0",
- "graceful-fs": "^4.2.9",
- "jest-haste-map": "^28.1.3",
- "jest-pnp-resolver": "^1.2.2",
- "jest-util": "^28.1.3",
- "jest-validate": "^28.1.3",
- "resolve": "^1.20.0",
- "resolve.exports": "^1.1.0",
- "slash": "^3.0.0"
- },
- "dependencies": {
- "@jest/types": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz",
- "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==",
- "dev": true,
- "requires": {
- "@jest/schemas": "^28.1.3",
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^3.0.0",
- "@types/node": "*",
- "@types/yargs": "^17.0.8",
- "chalk": "^4.0.0"
- }
- },
- "@types/yargs": {
- "version": "17.0.10",
- "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.10.tgz",
- "integrity": "sha512-gmEaFwpj/7f/ROdtIlci1R1VYU1J4j95m8T+Tj3iBgiBFKg1foE/PSl93bBd5T9LDXNPo8UlNN6W0qwD8O5OaA==",
- "dev": true,
- "requires": {
- "@types/yargs-parser": "*"
- }
- },
- "jest-util": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz",
- "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==",
- "dev": true,
- "requires": {
- "@jest/types": "^28.1.3",
- "@types/node": "*",
- "chalk": "^4.0.0",
- "ci-info": "^3.2.0",
- "graceful-fs": "^4.2.9",
- "picomatch": "^2.2.3"
- }
- }
- }
- },
- "jest-resolve-dependencies": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-28.1.3.tgz",
- "integrity": "sha512-qa0QO2Q0XzQoNPouMbCc7Bvtsem8eQgVPNkwn9LnS+R2n8DaVDPL/U1gngC0LTl1RYXJU0uJa2BMC2DbTfFrHA==",
- "dev": true,
- "requires": {
- "jest-regex-util": "^28.0.2",
- "jest-snapshot": "^28.1.3"
- }
- },
- "jest-runner": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-28.1.3.tgz",
- "integrity": "sha512-GkMw4D/0USd62OVO0oEgjn23TM+YJa2U2Wu5zz9xsQB1MxWKDOlrnykPxnMsN0tnJllfLPinHTka61u0QhaxBA==",
- "dev": true,
- "requires": {
- "@jest/console": "^28.1.3",
- "@jest/environment": "^28.1.3",
- "@jest/test-result": "^28.1.3",
- "@jest/transform": "^28.1.3",
- "@jest/types": "^28.1.3",
- "@types/node": "*",
- "chalk": "^4.0.0",
- "emittery": "^0.10.2",
- "graceful-fs": "^4.2.9",
- "jest-docblock": "^28.1.1",
- "jest-environment-node": "^28.1.3",
- "jest-haste-map": "^28.1.3",
- "jest-leak-detector": "^28.1.3",
- "jest-message-util": "^28.1.3",
- "jest-resolve": "^28.1.3",
- "jest-runtime": "^28.1.3",
- "jest-util": "^28.1.3",
- "jest-watcher": "^28.1.3",
- "jest-worker": "^28.1.3",
- "p-limit": "^3.1.0",
- "source-map-support": "0.5.13"
- },
- "dependencies": {
- "@jest/environment": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-28.1.3.tgz",
- "integrity": "sha512-1bf40cMFTEkKyEf585R9Iz1WayDjHoHqvts0XFYEqyKM3cFWDpeMoqKKTAF9LSYQModPUlh8FKptoM2YcMWAXA==",
- "dev": true,
- "requires": {
- "@jest/fake-timers": "^28.1.3",
- "@jest/types": "^28.1.3",
- "@types/node": "*",
- "jest-mock": "^28.1.3"
- }
- },
- "@jest/fake-timers": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-28.1.3.tgz",
- "integrity": "sha512-D/wOkL2POHv52h+ok5Oj/1gOG9HSywdoPtFsRCUmlCILXNn5eIWmcnd3DIiWlJnpGvQtmajqBP95Ei0EimxfLw==",
- "dev": true,
- "requires": {
- "@jest/types": "^28.1.3",
- "@sinonjs/fake-timers": "^9.1.2",
- "@types/node": "*",
- "jest-message-util": "^28.1.3",
- "jest-mock": "^28.1.3",
- "jest-util": "^28.1.3"
- }
- },
- "@jest/types": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz",
- "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==",
- "dev": true,
- "requires": {
- "@jest/schemas": "^28.1.3",
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^3.0.0",
- "@types/node": "*",
- "@types/yargs": "^17.0.8",
- "chalk": "^4.0.0"
- }
- },
- "@sinonjs/fake-timers": {
- "version": "9.1.2",
- "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz",
- "integrity": "sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw==",
- "dev": true,
- "requires": {
- "@sinonjs/commons": "^1.7.0"
- }
- },
- "@types/yargs": {
- "version": "17.0.10",
- "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.10.tgz",
- "integrity": "sha512-gmEaFwpj/7f/ROdtIlci1R1VYU1J4j95m8T+Tj3iBgiBFKg1foE/PSl93bBd5T9LDXNPo8UlNN6W0qwD8O5OaA==",
- "dev": true,
- "requires": {
- "@types/yargs-parser": "*"
- }
- },
- "jest-message-util": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-28.1.3.tgz",
- "integrity": "sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g==",
- "dev": true,
- "requires": {
- "@babel/code-frame": "^7.12.13",
- "@jest/types": "^28.1.3",
- "@types/stack-utils": "^2.0.0",
- "chalk": "^4.0.0",
- "graceful-fs": "^4.2.9",
- "micromatch": "^4.0.4",
- "pretty-format": "^28.1.3",
- "slash": "^3.0.0",
- "stack-utils": "^2.0.3"
- }
- },
- "jest-mock": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-28.1.3.tgz",
- "integrity": "sha512-o3J2jr6dMMWYVH4Lh/NKmDXdosrsJgi4AviS8oXLujcjpCMBb1FMsblDnOXKZKfSiHLxYub1eS0IHuRXsio9eA==",
- "dev": true,
- "requires": {
- "@jest/types": "^28.1.3",
- "@types/node": "*"
- }
- },
- "jest-util": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz",
- "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==",
- "dev": true,
- "requires": {
- "@jest/types": "^28.1.3",
- "@types/node": "*",
- "chalk": "^4.0.0",
- "ci-info": "^3.2.0",
- "graceful-fs": "^4.2.9",
- "picomatch": "^2.2.3"
- }
- }
- }
- },
- "jest-runtime": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-28.1.3.tgz",
- "integrity": "sha512-NU+881ScBQQLc1JHG5eJGU7Ui3kLKrmwCPPtYsJtBykixrM2OhVQlpMmFWJjMyDfdkGgBMNjXCGB/ebzsgNGQw==",
- "dev": true,
- "requires": {
- "@jest/environment": "^28.1.3",
- "@jest/fake-timers": "^28.1.3",
- "@jest/globals": "^28.1.3",
- "@jest/source-map": "^28.1.2",
- "@jest/test-result": "^28.1.3",
- "@jest/transform": "^28.1.3",
- "@jest/types": "^28.1.3",
- "chalk": "^4.0.0",
- "cjs-module-lexer": "^1.0.0",
- "collect-v8-coverage": "^1.0.0",
- "execa": "^5.0.0",
- "glob": "^7.1.3",
- "graceful-fs": "^4.2.9",
- "jest-haste-map": "^28.1.3",
- "jest-message-util": "^28.1.3",
- "jest-mock": "^28.1.3",
- "jest-regex-util": "^28.0.2",
- "jest-resolve": "^28.1.3",
- "jest-snapshot": "^28.1.3",
- "jest-util": "^28.1.3",
- "slash": "^3.0.0",
- "strip-bom": "^4.0.0"
- },
- "dependencies": {
- "@jest/environment": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-28.1.3.tgz",
- "integrity": "sha512-1bf40cMFTEkKyEf585R9Iz1WayDjHoHqvts0XFYEqyKM3cFWDpeMoqKKTAF9LSYQModPUlh8FKptoM2YcMWAXA==",
- "dev": true,
- "requires": {
- "@jest/fake-timers": "^28.1.3",
- "@jest/types": "^28.1.3",
- "@types/node": "*",
- "jest-mock": "^28.1.3"
- }
- },
- "@jest/fake-timers": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-28.1.3.tgz",
- "integrity": "sha512-D/wOkL2POHv52h+ok5Oj/1gOG9HSywdoPtFsRCUmlCILXNn5eIWmcnd3DIiWlJnpGvQtmajqBP95Ei0EimxfLw==",
- "dev": true,
- "requires": {
- "@jest/types": "^28.1.3",
- "@sinonjs/fake-timers": "^9.1.2",
- "@types/node": "*",
- "jest-message-util": "^28.1.3",
- "jest-mock": "^28.1.3",
- "jest-util": "^28.1.3"
- }
- },
- "@jest/types": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz",
- "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==",
- "dev": true,
- "requires": {
- "@jest/schemas": "^28.1.3",
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^3.0.0",
- "@types/node": "*",
- "@types/yargs": "^17.0.8",
- "chalk": "^4.0.0"
- }
- },
- "@sinonjs/fake-timers": {
- "version": "9.1.2",
- "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz",
- "integrity": "sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw==",
- "dev": true,
- "requires": {
- "@sinonjs/commons": "^1.7.0"
- }
- },
- "@types/yargs": {
- "version": "17.0.10",
- "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.10.tgz",
- "integrity": "sha512-gmEaFwpj/7f/ROdtIlci1R1VYU1J4j95m8T+Tj3iBgiBFKg1foE/PSl93bBd5T9LDXNPo8UlNN6W0qwD8O5OaA==",
- "dev": true,
- "requires": {
- "@types/yargs-parser": "*"
- }
- },
- "jest-message-util": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-28.1.3.tgz",
- "integrity": "sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g==",
- "dev": true,
- "requires": {
- "@babel/code-frame": "^7.12.13",
- "@jest/types": "^28.1.3",
- "@types/stack-utils": "^2.0.0",
- "chalk": "^4.0.0",
- "graceful-fs": "^4.2.9",
- "micromatch": "^4.0.4",
- "pretty-format": "^28.1.3",
- "slash": "^3.0.0",
- "stack-utils": "^2.0.3"
- }
- },
- "jest-mock": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-28.1.3.tgz",
- "integrity": "sha512-o3J2jr6dMMWYVH4Lh/NKmDXdosrsJgi4AviS8oXLujcjpCMBb1FMsblDnOXKZKfSiHLxYub1eS0IHuRXsio9eA==",
- "dev": true,
- "requires": {
- "@jest/types": "^28.1.3",
- "@types/node": "*"
- }
- },
- "jest-util": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz",
- "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==",
- "dev": true,
- "requires": {
- "@jest/types": "^28.1.3",
- "@types/node": "*",
- "chalk": "^4.0.0",
- "ci-info": "^3.2.0",
- "graceful-fs": "^4.2.9",
- "picomatch": "^2.2.3"
- }
- }
- }
- },
- "jest-snapshot": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-28.1.3.tgz",
- "integrity": "sha512-4lzMgtiNlc3DU/8lZfmqxN3AYD6GGLbl+72rdBpXvcV+whX7mDrREzkPdp2RnmfIiWBg1YbuFSkXduF2JcafJg==",
- "dev": true,
- "requires": {
- "@babel/core": "^7.11.6",
- "@babel/generator": "^7.7.2",
- "@babel/plugin-syntax-typescript": "^7.7.2",
- "@babel/traverse": "^7.7.2",
- "@babel/types": "^7.3.3",
- "@jest/expect-utils": "^28.1.3",
- "@jest/transform": "^28.1.3",
- "@jest/types": "^28.1.3",
- "@types/babel__traverse": "^7.0.6",
- "@types/prettier": "^2.1.5",
- "babel-preset-current-node-syntax": "^1.0.0",
- "chalk": "^4.0.0",
- "expect": "^28.1.3",
- "graceful-fs": "^4.2.9",
- "jest-diff": "^28.1.3",
- "jest-get-type": "^28.0.2",
- "jest-haste-map": "^28.1.3",
- "jest-matcher-utils": "^28.1.3",
- "jest-message-util": "^28.1.3",
- "jest-util": "^28.1.3",
- "natural-compare": "^1.4.0",
- "pretty-format": "^28.1.3",
- "semver": "^7.3.5"
- },
- "dependencies": {
- "@jest/types": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz",
- "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==",
- "dev": true,
- "requires": {
- "@jest/schemas": "^28.1.3",
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^3.0.0",
- "@types/node": "*",
- "@types/yargs": "^17.0.8",
- "chalk": "^4.0.0"
- }
- },
- "@types/yargs": {
- "version": "17.0.10",
- "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.10.tgz",
- "integrity": "sha512-gmEaFwpj/7f/ROdtIlci1R1VYU1J4j95m8T+Tj3iBgiBFKg1foE/PSl93bBd5T9LDXNPo8UlNN6W0qwD8O5OaA==",
- "dev": true,
- "requires": {
- "@types/yargs-parser": "*"
- }
- },
- "jest-message-util": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-28.1.3.tgz",
- "integrity": "sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g==",
- "dev": true,
- "requires": {
- "@babel/code-frame": "^7.12.13",
- "@jest/types": "^28.1.3",
- "@types/stack-utils": "^2.0.0",
- "chalk": "^4.0.0",
- "graceful-fs": "^4.2.9",
- "micromatch": "^4.0.4",
- "pretty-format": "^28.1.3",
- "slash": "^3.0.0",
- "stack-utils": "^2.0.3"
- }
- },
- "jest-util": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz",
- "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==",
- "dev": true,
- "requires": {
- "@jest/types": "^28.1.3",
- "@types/node": "*",
- "chalk": "^4.0.0",
- "ci-info": "^3.2.0",
- "graceful-fs": "^4.2.9",
- "picomatch": "^2.2.3"
- }
- }
- }
- },
- "jest-util": {
- "version": "27.5.1",
- "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-27.5.1.tgz",
- "integrity": "sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==",
- "dev": true,
- "requires": {
- "@jest/types": "^27.5.1",
- "@types/node": "*",
- "chalk": "^4.0.0",
- "ci-info": "^3.2.0",
- "graceful-fs": "^4.2.9",
- "picomatch": "^2.2.3"
- }
- },
- "jest-validate": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-28.1.3.tgz",
- "integrity": "sha512-SZbOGBWEsaTxBGCOpsRWlXlvNkvTkY0XxRfh7zYmvd8uL5Qzyg0CHAXiXKROflh801quA6+/DsT4ODDthOC/OA==",
- "dev": true,
- "requires": {
- "@jest/types": "^28.1.3",
- "camelcase": "^6.2.0",
- "chalk": "^4.0.0",
- "jest-get-type": "^28.0.2",
- "leven": "^3.1.0",
- "pretty-format": "^28.1.3"
- },
- "dependencies": {
- "@jest/types": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz",
- "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==",
- "dev": true,
- "requires": {
- "@jest/schemas": "^28.1.3",
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^3.0.0",
- "@types/node": "*",
- "@types/yargs": "^17.0.8",
- "chalk": "^4.0.0"
- }
- },
- "@types/yargs": {
- "version": "17.0.10",
- "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.10.tgz",
- "integrity": "sha512-gmEaFwpj/7f/ROdtIlci1R1VYU1J4j95m8T+Tj3iBgiBFKg1foE/PSl93bBd5T9LDXNPo8UlNN6W0qwD8O5OaA==",
- "dev": true,
- "requires": {
- "@types/yargs-parser": "*"
- }
- },
- "camelcase": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
- "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==",
- "dev": true
- }
- }
- },
- "jest-watcher": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-28.1.3.tgz",
- "integrity": "sha512-t4qcqj9hze+jviFPUN3YAtAEeFnr/azITXQEMARf5cMwKY2SMBRnCQTXLixTl20OR6mLh9KLMrgVJgJISym+1g==",
- "dev": true,
- "requires": {
- "@jest/test-result": "^28.1.3",
- "@jest/types": "^28.1.3",
- "@types/node": "*",
- "ansi-escapes": "^4.2.1",
- "chalk": "^4.0.0",
- "emittery": "^0.10.2",
- "jest-util": "^28.1.3",
- "string-length": "^4.0.1"
- },
- "dependencies": {
- "@jest/types": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz",
- "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==",
- "dev": true,
- "requires": {
- "@jest/schemas": "^28.1.3",
- "@types/istanbul-lib-coverage": "^2.0.0",
- "@types/istanbul-reports": "^3.0.0",
- "@types/node": "*",
- "@types/yargs": "^17.0.8",
- "chalk": "^4.0.0"
- }
- },
- "@types/yargs": {
- "version": "17.0.10",
- "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.10.tgz",
- "integrity": "sha512-gmEaFwpj/7f/ROdtIlci1R1VYU1J4j95m8T+Tj3iBgiBFKg1foE/PSl93bBd5T9LDXNPo8UlNN6W0qwD8O5OaA==",
- "dev": true,
- "requires": {
- "@types/yargs-parser": "*"
- }
- },
- "jest-util": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz",
- "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==",
- "dev": true,
- "requires": {
- "@jest/types": "^28.1.3",
- "@types/node": "*",
- "chalk": "^4.0.0",
- "ci-info": "^3.2.0",
- "graceful-fs": "^4.2.9",
- "picomatch": "^2.2.3"
- }
- }
- }
- },
- "jest-worker": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-28.1.3.tgz",
- "integrity": "sha512-CqRA220YV/6jCo8VWvAt1KKx6eek1VIHMPeLEbpcfSfkEeWyBNppynM/o6q+Wmw+sOhos2ml34wZbSX3G13//g==",
- "dev": true,
- "requires": {
- "@types/node": "*",
- "merge-stream": "^2.0.0",
- "supports-color": "^8.0.0"
- },
- "dependencies": {
- "supports-color": {
- "version": "8.1.1",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
- "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
- "dev": true,
- "requires": {
- "has-flag": "^4.0.0"
- }
- }
- }
- },
"joycon": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz",
"integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw=="
},
"jquery": {
- "version": "3.6.0",
- "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.0.tgz",
- "integrity": "sha512-JVzAR/AjBvVt2BmYhxRCSYysDsPcssdmTFnzyLEts9qNwmjmu4JTAMYubEfwVOSwpQ1I1sKKFcxhZCI2buerfw=="
+ "version": "3.6.1",
+ "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.1.tgz",
+ "integrity": "sha512-opJeO4nCucVnsjiXOE+/PcCgYw9Gwpvs/a6B1LL/lQhwWwpbVEVYDZ1FokFr8PRc7ghYlrFPuyHuiiDNTQxmcw=="
},
"jquery.are-you-sure": {
"version": "1.9.0",
@@ -19184,6 +13912,12 @@
"jquery": ">=1.4.2"
}
},
+ "js-sdsl": {
+ "version": "4.1.5",
+ "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.1.5.tgz",
+ "integrity": "sha512-08bOAKweV2NUC1wqTtf3qZlnpOX/R2DU9ikpjOHs0H+ibQv3zpncVQg6um4uYtRtrwIX8M4Nh3ytK4HGlYAq7Q==",
+ "dev": true
+ },
"js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@@ -19199,16 +13933,44 @@
"argparse": "^2.0.1"
}
},
- "jsep": {
- "version": "1.3.6",
- "resolved": "https://registry.npmjs.org/jsep/-/jsep-1.3.6.tgz",
- "integrity": "sha512-o7fP1eZVROIChADx7HKiwGRVI0tUqgUUGhaok6DP7cMxpDeparuooREDBDeNk2G5KIB49MBSkRYsCOu4PmZ+1w==",
- "dev": true
+ "jsdom": {
+ "version": "20.0.1",
+ "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-20.0.1.tgz",
+ "integrity": "sha512-pksjj7Rqoa+wdpkKcLzQRHhJCEE42qQhl/xLMUKHgoSejaKOdaXEAnqs6uDNwMl/fciHTzKeR8Wm8cw7N+g98A==",
+ "dev": true,
+ "requires": {
+ "abab": "^2.0.6",
+ "acorn": "^8.8.0",
+ "acorn-globals": "^7.0.0",
+ "cssom": "^0.5.0",
+ "cssstyle": "^2.3.0",
+ "data-urls": "^3.0.2",
+ "decimal.js": "^10.4.1",
+ "domexception": "^4.0.0",
+ "escodegen": "^2.0.0",
+ "form-data": "^4.0.0",
+ "html-encoding-sniffer": "^3.0.0",
+ "http-proxy-agent": "^5.0.0",
+ "https-proxy-agent": "^5.0.1",
+ "is-potential-custom-element-name": "^1.0.1",
+ "nwsapi": "^2.2.2",
+ "parse5": "^7.1.1",
+ "saxes": "^6.0.0",
+ "symbol-tree": "^3.2.4",
+ "tough-cookie": "^4.1.2",
+ "w3c-xmlserializer": "^3.0.0",
+ "webidl-conversions": "^7.0.0",
+ "whatwg-encoding": "^2.0.0",
+ "whatwg-mimetype": "^3.0.0",
+ "whatwg-url": "^11.0.0",
+ "ws": "^8.9.0",
+ "xml-name-validator": "^4.0.0"
+ }
},
- "jsesc": {
- "version": "2.5.2",
- "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
- "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
+ "jsep": {
+ "version": "1.3.7",
+ "resolved": "https://registry.npmjs.org/jsep/-/jsep-1.3.7.tgz",
+ "integrity": "sha512-NFbZTr1t13fPKw53swmZFKwBkEDWDnno7uLJk+a+Rw9tGDTkGgnGdZJ8A/o3gR1+XaAXmSsbpfIBIBgqRBZWDA==",
"dev": true
},
"json-parse-even-better-errors": {
@@ -19248,9 +14010,9 @@
}
},
"jsonpath-plus": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/jsonpath-plus/-/jsonpath-plus-6.0.1.tgz",
- "integrity": "sha512-EvGovdvau6FyLexFH2OeXfIITlgIbgZoAZe3usiySeaIDm5QS+A10DKNpaPBBqqRSZr2HN6HVNXxtwUAr2apEw==",
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/jsonpath-plus/-/jsonpath-plus-7.1.0.tgz",
+ "integrity": "sha512-gTaNRsPWO/K2KY6MrqaUFClF9kmuM6MFH5Dhg1VYDODgFbByw1yb7xu3hrViE/sz+dGOeMWgCzwUwQtAnCTE9g==",
"dev": true
},
"jsonpointer": {
@@ -19264,6 +14026,14 @@
"resolved": "https://registry.npmjs.org/just-extend/-/just-extend-5.1.1.tgz",
"integrity": "sha512-b+z6yF1d4EOyDgylzQo5IminlUmzSeqR1hs/bzjBNjuGras4FXq/6TrzjxfN0j+TmI0ltJzTNlqXUMCniciwKQ=="
},
+ "katex": {
+ "version": "0.16.2",
+ "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.2.tgz",
+ "integrity": "sha512-70DJdQAyh9EMsthw3AaQlDyFf54X7nWEUIa5W+rq8XOpEk//w5Th7/8SqFqpvi/KZ2t6MHUj4f9wLmztBmAYQA==",
+ "requires": {
+ "commander": "^8.0.0"
+ }
+ },
"khroma": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/khroma/-/khroma-2.0.0.tgz",
@@ -19274,12 +14044,6 @@
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
"integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw=="
},
- "kleur": {
- "version": "3.0.3",
- "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz",
- "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==",
- "dev": true
- },
"klona": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/klona/-/klona-2.0.5.tgz",
@@ -19327,9 +14091,9 @@
}
},
"less-loader": {
- "version": "11.0.0",
- "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-11.0.0.tgz",
- "integrity": "sha512-9+LOWWjuoectIEx3zrfN83NAGxSUB5pWEabbbidVQVgZhN+wN68pOvuyirVlH1IK4VT1f3TmlyvAnCXh8O5KEw==",
+ "version": "11.1.0",
+ "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-11.1.0.tgz",
+ "integrity": "sha512-C+uDBV7kS7W5fJlUjq5mPBeBVhYpTIm5gB09APT9o3n/ILeaXVsiSFTbZpTJCJwQ/Crczfn3DmfQFwxYusWFug==",
"requires": {
"klona": "^2.0.4"
}
@@ -19398,6 +14162,15 @@
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
"dev": true
},
+ "linkify-it": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-4.0.1.tgz",
+ "integrity": "sha512-C7bfi1UZmoj8+PQx22XyeXCuBlokoyWQL5pWSP+EI6nzRylyThouddufc2c1NDIcP9k5agmN9fLpA7VNJfIiqw==",
+ "dev": true,
+ "requires": {
+ "uc.micro": "^1.0.1"
+ }
+ },
"loader-runner": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz",
@@ -19413,14 +14186,19 @@
"json5": "^2.1.2"
}
},
+ "local-pkg": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.4.2.tgz",
+ "integrity": "sha512-mlERgSPrbxU3BP4qBqAvvwlgW4MTg78iwJdGGnv7kibKjWcJksrG3t6LB5lXI93wXRDvG4NpUgJFmTG4T6rdrg==",
+ "dev": true
+ },
"locate-path": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz",
- "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==",
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+ "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
"dev": true,
"requires": {
- "p-locate": "^2.0.0",
- "path-exists": "^3.0.0"
+ "p-locate": "^5.0.0"
}
},
"lodash": {
@@ -19480,6 +14258,15 @@
"integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==",
"dev": true
},
+ "loupe": {
+ "version": "2.3.4",
+ "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.4.tgz",
+ "integrity": "sha512-OvKfgCC2Ndby6aSTREl5aCCPTNIzlDfQZvZxNUrBrihDhL3xcrYegTblhmEiCrg2kKQz4XsFIaemE5BF4ybSaQ==",
+ "dev": true,
+ "requires": {
+ "get-func-name": "^2.0.0"
+ }
+ },
"lru-cache": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
@@ -19493,47 +14280,111 @@
"version": "0.25.9",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz",
"integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==",
- "dev": true,
"requires": {
"sourcemap-codec": "^1.4.8"
}
},
- "make-dir": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
- "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
- "dev": true,
- "requires": {
- "semver": "^6.0.0"
- },
- "dependencies": {
- "semver": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
- "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
- "dev": true
- }
- }
- },
- "makeerror": {
- "version": "1.0.12",
- "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz",
- "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==",
- "dev": true,
- "requires": {
- "tmpl": "1.0.5"
- }
- },
"map-obj": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz",
"integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==",
"dev": true
},
+ "markdown-it": {
+ "version": "13.0.1",
+ "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-13.0.1.tgz",
+ "integrity": "sha512-lTlxriVoy2criHP0JKRhO2VDG9c2ypWCsT237eDiLqi09rmbKoUetyGHq2uOIRoRS//kfoJckS0eUzzkDR+k2Q==",
+ "dev": true,
+ "requires": {
+ "argparse": "^2.0.1",
+ "entities": "~3.0.1",
+ "linkify-it": "^4.0.1",
+ "mdurl": "^1.0.1",
+ "uc.micro": "^1.0.5"
+ }
+ },
+ "markdownlint": {
+ "version": "0.26.2",
+ "resolved": "https://registry.npmjs.org/markdownlint/-/markdownlint-0.26.2.tgz",
+ "integrity": "sha512-2Am42YX2Ex5SQhRq35HxYWDfz1NLEOZWWN25nqd2h3AHRKsGRE+Qg1gt1++exW792eXTrR4jCNHfShfWk9Nz8w==",
+ "dev": true,
+ "requires": {
+ "markdown-it": "13.0.1"
+ }
+ },
+ "markdownlint-cli": {
+ "version": "0.32.2",
+ "resolved": "https://registry.npmjs.org/markdownlint-cli/-/markdownlint-cli-0.32.2.tgz",
+ "integrity": "sha512-xmJT1rGueUgT4yGNwk6D0oqQr90UJ7nMyakXtqjgswAkEhYYqjHew9RY8wDbOmh2R270IWjuKSeZzHDEGPAUkQ==",
+ "dev": true,
+ "requires": {
+ "commander": "~9.4.0",
+ "get-stdin": "~9.0.0",
+ "glob": "~8.0.3",
+ "ignore": "~5.2.0",
+ "js-yaml": "^4.1.0",
+ "jsonc-parser": "~3.1.0",
+ "markdownlint": "~0.26.2",
+ "markdownlint-rule-helpers": "~0.17.2",
+ "minimatch": "~5.1.0",
+ "run-con": "~1.2.11"
+ },
+ "dependencies": {
+ "brace-expansion": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "commander": {
+ "version": "9.4.1",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-9.4.1.tgz",
+ "integrity": "sha512-5EEkTNyHNGFPD2H+c/dXXfQZYa/scCKasxWcXJaWnNJ99pnQN9Vnmqow+p+PlFPE63Q6mThaZws1T+HxfpgtPw==",
+ "dev": true
+ },
+ "glob": {
+ "version": "8.0.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz",
+ "integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^5.0.1",
+ "once": "^1.3.0"
+ }
+ },
+ "jsonc-parser": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.1.0.tgz",
+ "integrity": "sha512-DRf0QjnNeCUds3xTjKlQQ3DpJD51GvDjJfnxUVWg6PZTo2otSm+slzNAxU/35hF8/oJIKoG9slq30JYOsF2azg==",
+ "dev": true
+ },
+ "minimatch": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz",
+ "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^2.0.1"
+ }
+ }
+ }
+ },
+ "markdownlint-rule-helpers": {
+ "version": "0.17.2",
+ "resolved": "https://registry.npmjs.org/markdownlint-rule-helpers/-/markdownlint-rule-helpers-0.17.2.tgz",
+ "integrity": "sha512-XaeoW2NYSlWxMCZM2B3H7YTG6nlaLfkEZWMBhr4hSPlq9MuY2sy83+Xr89jXOqZMZYjvi5nBCGoFh7hHoPKZmA==",
+ "dev": true
+ },
"marked": {
- "version": "4.0.18",
- "resolved": "https://registry.npmjs.org/marked/-/marked-4.0.18.tgz",
- "integrity": "sha512-wbLDJ7Zh0sqA0Vdg6aqlbT+yPxqLblpAZh1mK2+AO2twQkPywvvqQNfEPVwSSRjZ7dZcdeVBIAgiO7MMp3Dszw=="
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/marked/-/marked-4.1.1.tgz",
+ "integrity": "sha512-0cNMnTcUJPxbA6uWmCmjWz4NJRe/0Xfk2NhXCUHjew9qJzFN20krFnsUe7QynwqOwa5m1fZ4UDg0ycKFVC0ccw=="
},
"mathml-tag-names": {
"version": "2.1.3",
@@ -19547,6 +14398,12 @@
"integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==",
"dev": true
},
+ "mdurl": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz",
+ "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==",
+ "dev": true
+ },
"meow": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/meow/-/meow-9.0.0.tgz",
@@ -19611,14 +14468,6 @@
}
}
},
- "merge-source-map": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.1.0.tgz",
- "integrity": "sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==",
- "requires": {
- "source-map": "^0.6.1"
- }
- },
"merge-stream": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
@@ -19630,18 +14479,18 @@
"integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="
},
"mermaid": {
- "version": "9.1.3",
- "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-9.1.3.tgz",
- "integrity": "sha512-jTIYiqKwsUXVCoxHUVkK8t0QN3zSKIdJlb9thT0J5jCnzXyc+gqTbZE2QmjRfavFTPPn5eRy5zaFp7V+6RhxYg==",
+ "version": "9.1.7",
+ "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-9.1.7.tgz",
+ "integrity": "sha512-MRVHXy5FLjnUQUG7YS3UN9jEN6FXCJbFCXVGJQjVIbiR6Vhw0j/6pLIjqsiah9xoHmQU6DEaKOvB3S1g/1nBPA==",
"requires": {
"@braintree/sanitize-url": "^6.0.0",
"d3": "^7.0.0",
"dagre": "^0.8.5",
"dagre-d3": "^0.6.4",
- "dompurify": "2.3.8",
+ "dompurify": "2.4.0",
"graphlib": "^2.1.8",
"khroma": "^2.0.0",
- "moment-mini": "^2.24.0",
+ "moment-mini": "2.24.0",
"stylis": "^4.0.10"
}
},
@@ -19673,12 +14522,6 @@
"mime-db": "1.52.0"
}
},
- "mimic-fn": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
- "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
- "dev": true
- },
"min-indent": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz",
@@ -19704,7 +14547,8 @@
"minimist": {
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
- "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q=="
+ "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==",
+ "dev": true
},
"minimist-options": {
"version": "4.1.0",
@@ -19723,9 +14567,9 @@
"integrity": "sha512-9ARkWHBs+6YJIvrIp0Ik5tyTTtP9PoV0Ssu2Ocq5y9v8+NOOpWiRshAp8c4rZVWTOe+157on/5G+zj5pwIQFEQ=="
},
"monaco-editor": {
- "version": "0.33.0",
- "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.33.0.tgz",
- "integrity": "sha512-VcRWPSLIUEgQJQIE0pVT8FcGBIgFoxz7jtqctE+IiCxWugD0DwgyQBcZBhdSrdMC84eumoqMZsGl2GTreOzwqw=="
+ "version": "0.34.0",
+ "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.34.0.tgz",
+ "integrity": "sha512-VF+S5zG8wxfinLKLrWcl4WUizMx+LeJrG4PM/M78OhcwocpV0jiyhX/pG6Q9jIOhrb/ckYi6nHnaR5OojlOZCQ=="
},
"monaco-editor-webpack-plugin": {
"version": "7.0.1",
@@ -19797,6 +14641,15 @@
"jsep": "^1.2.0",
"jsonpath-plus": "^6.0.1",
"lodash.topath": "^4.5.2"
+ },
+ "dependencies": {
+ "jsonpath-plus": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/jsonpath-plus/-/jsonpath-plus-6.0.1.tgz",
+ "integrity": "sha512-EvGovdvau6FyLexFH2OeXfIITlgIbgZoAZe3usiySeaIDm5QS+A10DKNpaPBBqqRSZr2HN6HVNXxtwUAr2apEw==",
+ "dev": true,
+ "optional": true
+ }
}
},
"node-fetch": {
@@ -19806,14 +14659,32 @@
"dev": true,
"requires": {
"whatwg-url": "^5.0.0"
+ },
+ "dependencies": {
+ "tr46": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
+ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
+ "dev": true
+ },
+ "webidl-conversions": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
+ "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
+ "dev": true
+ },
+ "whatwg-url": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
+ "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
+ "dev": true,
+ "requires": {
+ "tr46": "~0.0.3",
+ "webidl-conversions": "^3.0.0"
+ }
+ }
}
},
- "node-int64": {
- "version": "0.4.0",
- "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz",
- "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==",
- "dev": true
- },
"node-releases": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz",
@@ -19845,15 +14716,6 @@
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
"dev": true
},
- "npm-run-path": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz",
- "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==",
- "dev": true,
- "requires": {
- "path-key": "^3.0.0"
- }
- },
"nth-check": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
@@ -19863,6 +14725,12 @@
"boolbase": "^1.0.0"
}
},
+ "nwsapi": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.2.tgz",
+ "integrity": "sha512-90yv+6538zuvUMnN+zCr8LuV6bPFdq50304114vJYJ8RDyK8D5O9Phpbd6SZWgI7PwzmmfN1upeOJlvybDSgCw==",
+ "dev": true
+ },
"object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
@@ -19881,14 +14749,14 @@
"dev": true
},
"object.assign": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz",
- "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==",
+ "version": "4.1.4",
+ "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz",
+ "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==",
"dev": true,
"requires": {
- "call-bind": "^1.0.0",
- "define-properties": "^1.1.3",
- "has-symbols": "^1.0.1",
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.1.4",
+ "has-symbols": "^1.0.3",
"object-keys": "^1.1.1"
}
},
@@ -19911,15 +14779,6 @@
"wrappy": "1"
}
},
- "onetime": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
- "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
- "dev": true,
- "requires": {
- "mimic-fn": "^2.1.0"
- }
- },
"optionator": {
"version": "0.9.1",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz",
@@ -19944,29 +14803,12 @@
}
},
"p-locate": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz",
- "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==",
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+ "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
"dev": true,
"requires": {
- "p-limit": "^1.1.0"
- },
- "dependencies": {
- "p-limit": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz",
- "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==",
- "dev": true,
- "requires": {
- "p-try": "^1.0.0"
- }
- },
- "p-try": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz",
- "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==",
- "dev": true
- }
+ "p-limit": "^3.0.2"
}
},
"p-try": {
@@ -19989,6 +14831,25 @@
"pac-resolver": "^5.0.0",
"raw-body": "^2.2.0",
"socks-proxy-agent": "5"
+ },
+ "dependencies": {
+ "@tootallnate/once": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz",
+ "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==",
+ "dev": true
+ },
+ "http-proxy-agent": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz",
+ "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==",
+ "dev": true,
+ "requires": {
+ "@tootallnate/once": "1",
+ "agent-base": "6",
+ "debug": "4"
+ }
+ }
}
},
"pac-resolver": {
@@ -20011,12 +14872,6 @@
"callsites": "^3.0.0"
}
},
- "parse-cache-control": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/parse-cache-control/-/parse-cache-control-1.0.1.tgz",
- "integrity": "sha512-60zvsJReQPX5/QP0Kzfd/VrpjScIQ7SHBW6bFCYfEP+fp0Eppr1SHhIO5nd1PjZtvclzSzES9D/p5nFJurwfWg==",
- "dev": true
- },
"parse-json": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
@@ -20039,11 +14894,27 @@
"resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz",
"integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA=="
},
+ "parse5": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.1.tgz",
+ "integrity": "sha512-kwpuwzB+px5WUg9pyK0IcK/shltJN5/OVhQagxhCQNtT9Y9QRZqNY2e1cmbu/paRh5LMnz/oVTVLBpjFmMZhSg==",
+ "dev": true,
+ "requires": {
+ "entities": "^4.4.0"
+ },
+ "dependencies": {
+ "entities": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz",
+ "integrity": "sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA==",
+ "dev": true
+ }
+ }
+ },
"path-exists": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
- "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==",
- "dev": true
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="
},
"path-is-absolute": {
"version": "1.0.1",
@@ -20066,6 +14937,12 @@
"integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
"dev": true
},
+ "pathval": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz",
+ "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==",
+ "dev": true
+ },
"picocolors": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
@@ -20082,12 +14959,6 @@
"integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
"optional": true
},
- "pirates": {
- "version": "4.0.5",
- "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz",
- "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==",
- "dev": true
- },
"pkg-dir": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
@@ -20128,14 +14999,15 @@
"requires": {
"p-limit": "^2.2.0"
}
- },
- "path-exists": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
- "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="
}
}
},
+ "playwright-core": {
+ "version": "1.27.0",
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.27.0.tgz",
+ "integrity": "sha512-VBKaaFUVKDo3akW+o4DwbK1ZyXh46tcSwQKPK3lruh8IJd5feu55XVZx4vOkbb2uqrNdIF51sgsadYT533SdpA==",
+ "dev": true
+ },
"pluralize": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz",
@@ -20148,15 +15020,10 @@
"integrity": "sha512-PxkIc/2ZpLiEzQXu5YRDOUgBlfGYBY8156HY5ZcRAwwonMk5W/MrJP2LLkG/hF7GEQzaHo2aS7ho6ZLCOvf+6g==",
"dev": true
},
- "popper.js": {
- "version": "1.16.1",
- "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz",
- "integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ=="
- },
"postcss": {
- "version": "8.4.14",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz",
- "integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==",
+ "version": "8.4.17",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.17.tgz",
+ "integrity": "sha512-UNxNOLQydcOFi41yHNMcKRZ39NeXlr8AxGuZJsdub8vIb12fHzcq37DTU/QtbI6WLxNg2gF9Z+8qtRwTj1UI1Q==",
"requires": {
"nanoid": "^3.3.4",
"picocolors": "^1.0.0",
@@ -20241,32 +15108,6 @@
"integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
"dev": true
},
- "prettier": {
- "version": "2.7.1",
- "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz",
- "integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==",
- "optional": true
- },
- "pretty-format": {
- "version": "28.1.3",
- "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz",
- "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==",
- "dev": true,
- "requires": {
- "@jest/schemas": "^28.1.3",
- "ansi-regex": "^5.0.1",
- "ansi-styles": "^5.0.0",
- "react-is": "^18.0.0"
- },
- "dependencies": {
- "ansi-styles": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
- "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
- "dev": true
- }
- }
- },
"pretty-ms": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-8.0.0.tgz",
@@ -20281,31 +15122,6 @@
"integrity": "sha512-dKp+C4iXWK4vVYZmYSd0KBH5F/h1HoZRsbJ82AVKRO3PEo8L4lBS/vLwhVtpwwuYcoIsVY+1JYKR268yn480uQ==",
"dev": true
},
- "process-nextick-args": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
- "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
- "dev": true
- },
- "promise": {
- "version": "8.1.0",
- "resolved": "https://registry.npmjs.org/promise/-/promise-8.1.0.tgz",
- "integrity": "sha512-W04AqnILOL/sPRXziNicCjSNRruLAuIHEOVBazepu0545DDNGYHz7ar9ZgZ1fMU8/MA4mVxp5rkBWRi6OXIy3Q==",
- "dev": true,
- "requires": {
- "asap": "~2.0.6"
- }
- },
- "prompts": {
- "version": "2.4.2",
- "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz",
- "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==",
- "dev": true,
- "requires": {
- "kleur": "^3.0.3",
- "sisteransi": "^1.0.5"
- }
- },
"proxy-agent": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-5.0.0.tgz",
@@ -20320,6 +15136,25 @@
"pac-proxy-agent": "^5.0.0",
"proxy-from-env": "^1.0.0",
"socks-proxy-agent": "^5.0.0"
+ },
+ "dependencies": {
+ "@tootallnate/once": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz",
+ "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==",
+ "dev": true
+ },
+ "http-proxy-agent": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz",
+ "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==",
+ "dev": true,
+ "requires": {
+ "@tootallnate/once": "1",
+ "agent-base": "6",
+ "debug": "4"
+ }
+ }
}
},
"proxy-from-env": {
@@ -20334,24 +15169,22 @@
"integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==",
"optional": true
},
- "pseudomap": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
- "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ=="
+ "psl": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz",
+ "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==",
+ "dev": true
},
"punycode": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
},
- "qs": {
- "version": "6.11.0",
- "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
- "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
- "dev": true,
- "requires": {
- "side-channel": "^1.0.4"
- }
+ "querystringify": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
+ "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==",
+ "dev": true
},
"queue-microtask": {
"version": "1.2.3",
@@ -20395,12 +15228,6 @@
}
}
},
- "react-is": {
- "version": "18.2.0",
- "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
- "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==",
- "dev": true
- },
"read-pkg": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz",
@@ -20469,12 +15296,6 @@
"p-limit": "^2.2.0"
}
},
- "path-exists": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
- "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
- "dev": true
- },
"type-fest": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz",
@@ -20513,11 +15334,6 @@
"strip-indent": "^3.0.0"
}
},
- "regenerator-runtime": {
- "version": "0.13.9",
- "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz",
- "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA=="
- },
"regexp-tree": {
"version": "0.1.24",
"resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.24.tgz",
@@ -20552,6 +15368,12 @@
"resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
"integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="
},
+ "requires-port": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
+ "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==",
+ "dev": true
+ },
"reserved": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/reserved/-/reserved-0.1.2.tgz",
@@ -20589,12 +15411,6 @@
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
"dev": true
},
- "resolve.exports": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.0.tgz",
- "integrity": "sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ==",
- "dev": true
- },
"reusify": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
@@ -20615,15 +15431,26 @@
"integrity": "sha512-ndEIpszUHiG4HtDsQLeIuMvRsDnn8c8rYStabochtUeCvfuvNptb5TUbVD68LRAILPX7p9nqQGh4xJgn3EHS/g=="
},
"rollup": {
- "version": "2.77.0",
- "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.77.0.tgz",
- "integrity": "sha512-vL8xjY4yOQEw79DvyXLijhnhh+R/O9zpF/LEgkCebZFtb6ELeN9H3/2T0r8+mp+fFTBHZ5qGpOpW2ela2zRt3g==",
+ "version": "2.79.1",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz",
+ "integrity": "sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==",
"dev": true,
- "peer": true,
"requires": {
"fsevents": "~2.3.2"
}
},
+ "run-con": {
+ "version": "1.2.11",
+ "resolved": "https://registry.npmjs.org/run-con/-/run-con-1.2.11.tgz",
+ "integrity": "sha512-NEMGsUT+cglWkzEr4IFK21P4Jca45HqiAbIIZIBdX5+UZTB24Mb/21iNGgz9xZa8tL6vbW7CXmq7MFN42+VjNQ==",
+ "dev": true,
+ "requires": {
+ "deep-extend": "^0.6.0",
+ "ini": "~3.0.0",
+ "minimist": "^1.2.6",
+ "strip-json-comments": "~3.1.1"
+ }
+ },
"run-parallel": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
@@ -20651,6 +15478,17 @@
"regexp-tree": "~0.1.1"
}
},
+ "safe-regex-test": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz",
+ "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==",
+ "dev": true,
+ "requires": {
+ "call-bind": "^1.0.2",
+ "get-intrinsic": "^1.1.3",
+ "is-regex": "^1.1.4"
+ }
+ },
"safe-stable-stringify": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-1.1.1.tgz",
@@ -20668,6 +15506,15 @@
"integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==",
"optional": true
},
+ "saxes": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz",
+ "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==",
+ "dev": true,
+ "requires": {
+ "xmlchars": "^2.2.0"
+ }
+ },
"schema-utils": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz",
@@ -20680,9 +15527,9 @@
}
},
"semver": {
- "version": "7.3.7",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz",
- "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==",
+ "version": "7.3.8",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz",
+ "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==",
"requires": {
"lru-cache": "^6.0.0"
},
@@ -20763,12 +15610,6 @@
"jsep": "^1.1.2"
}
},
- "sisteransi": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz",
- "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==",
- "dev": true
- },
"slash": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
@@ -20793,9 +15634,9 @@
"dev": true
},
"socks": {
- "version": "2.7.0",
- "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.0.tgz",
- "integrity": "sha512-scnOe9y4VuiNUULJN72GrM26BNOjVsfPXI+j+98PkyEfsIXroa5ofyjT+FzGvn/xHs73U2JtoBYAVx9Hl4quSA==",
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz",
+ "integrity": "sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==",
"dev": true,
"requires": {
"ip": "^2.0.0",
@@ -20841,21 +15682,10 @@
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
"integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw=="
},
- "source-map-support": {
- "version": "0.5.13",
- "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz",
- "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==",
- "dev": true,
- "requires": {
- "buffer-from": "^1.0.0",
- "source-map": "^0.6.0"
- }
- },
"sourcemap-codec": {
"version": "1.4.8",
"resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
- "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==",
- "dev": true
+ "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA=="
},
"spdx-compare": {
"version": "1.0.0",
@@ -20900,9 +15730,9 @@
}
},
"spdx-license-ids": {
- "version": "3.0.11",
- "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz",
- "integrity": "sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g=="
+ "version": "3.0.12",
+ "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.12.tgz",
+ "integrity": "sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA=="
},
"spdx-ranges": {
"version": "2.1.1",
@@ -20919,35 +15749,12 @@
"spdx-ranges": "^2.0.0"
}
},
- "sprintf-js": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
- "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",
- "dev": true
- },
"stable": {
"version": "0.1.8",
"resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz",
"integrity": "sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==",
"dev": true
},
- "stack-utils": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.5.tgz",
- "integrity": "sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA==",
- "dev": true,
- "requires": {
- "escape-string-regexp": "^2.0.0"
- },
- "dependencies": {
- "escape-string-regexp": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz",
- "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==",
- "dev": true
- }
- }
- },
"stacktracey": {
"version": "2.1.8",
"resolved": "https://registry.npmjs.org/stacktracey/-/stacktracey-2.1.8.tgz",
@@ -20970,16 +15777,6 @@
"integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==",
"dev": true
},
- "string-length": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz",
- "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==",
- "dev": true,
- "requires": {
- "char-regex": "^1.0.2",
- "strip-ansi": "^6.0.0"
- }
- },
"string-width": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
@@ -21020,18 +15817,6 @@
"ansi-regex": "^5.0.1"
}
},
- "strip-bom": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz",
- "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==",
- "dev": true
- },
- "strip-final-newline": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz",
- "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==",
- "dev": true
- },
"strip-indent": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz",
@@ -21047,6 +15832,15 @@
"integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
"dev": true
},
+ "strip-literal": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-0.4.2.tgz",
+ "integrity": "sha512-pv48ybn4iE1O9RLgCAN0iU4Xv7RlBTiit6DKmMiErbs9x1wH6vXBs45tWc0H5wUIF6TLTrKweqkmYF/iraQKNw==",
+ "dev": true,
+ "requires": {
+ "acorn": "^8.8.0"
+ }
+ },
"style-search": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/style-search/-/style-search-0.1.0.tgz",
@@ -21054,22 +15848,20 @@
"dev": true
},
"stylelint": {
- "version": "14.9.1",
- "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-14.9.1.tgz",
- "integrity": "sha512-RdAkJdPiLqHawCSnu21nE27MjNXaVd4WcOHA4vK5GtIGjScfhNnaOuWR2wWdfKFAvcWQPOYe311iveiVKSmwsA==",
+ "version": "14.13.0",
+ "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-14.13.0.tgz",
+ "integrity": "sha512-NJSAdloiAB/jgVJKxMR90mWlctvmeBFGFVUvyKngi9+j/qPSJ5ZB+u8jOmGbLTnS7OHrII9NFGehPRyar8U5vg==",
"dev": true,
"requires": {
- "@csstools/selector-specificity": "^2.0.1",
+ "@csstools/selector-specificity": "^2.0.2",
"balanced-match": "^2.0.0",
- "colord": "^2.9.2",
+ "colord": "^2.9.3",
"cosmiconfig": "^7.0.1",
"css-functions-list": "^3.1.0",
"debug": "^4.3.4",
- "execall": "^2.0.0",
- "fast-glob": "^3.2.11",
- "fastest-levenshtein": "^1.0.12",
+ "fast-glob": "^3.2.12",
+ "fastest-levenshtein": "^1.0.16",
"file-entry-cache": "^6.0.1",
- "get-stdin": "^8.0.0",
"global-modules": "^2.0.0",
"globby": "^11.1.0",
"globjoin": "^0.1.4",
@@ -21084,7 +15876,7 @@
"micromatch": "^4.0.5",
"normalize-path": "^3.0.0",
"picocolors": "^1.0.0",
- "postcss": "^8.4.14",
+ "postcss": "^8.4.16",
"postcss-media-query-parser": "^0.2.3",
"postcss-resolve-nested-selector": "^0.1.1",
"postcss-safe-parser": "^6.0.0",
@@ -21094,11 +15886,11 @@
"string-width": "^4.2.3",
"strip-ansi": "^6.0.1",
"style-search": "^0.1.0",
- "supports-hyperlinks": "^2.2.0",
+ "supports-hyperlinks": "^2.3.0",
"svg-tags": "^1.0.0",
"table": "^6.8.0",
"v8-compile-cache": "^2.3.0",
- "write-file-atomic": "^4.0.1"
+ "write-file-atomic": "^4.0.2"
},
"dependencies": {
"balanced-match": {
@@ -21116,25 +15908,25 @@
}
},
"stylelint-config-recommended": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-8.0.0.tgz",
- "integrity": "sha512-IK6dWvE000+xBv9jbnHOnBq01gt6HGVB2ZTsot+QsMpe82doDQ9hvplxfv4YnpEuUwVGGd9y6nbaAnhrjcxhZQ==",
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-9.0.0.tgz",
+ "integrity": "sha512-9YQSrJq4NvvRuTbzDsWX3rrFOzOlYBmZP+o513BJN/yfEmGSr0AxdvrWs0P/ilSpVV/wisamAHu5XSk8Rcf4CQ==",
"dev": true,
"requires": {}
},
"stylelint-config-standard": {
- "version": "26.0.0",
- "resolved": "https://registry.npmjs.org/stylelint-config-standard/-/stylelint-config-standard-26.0.0.tgz",
- "integrity": "sha512-hUuB7LaaqM8abvkOO84wh5oYSkpXgTzHu2Zza6e7mY+aOmpNTjoFBRxSLlzY0uAOMWEFx0OMKzr+reG1BUtcqQ==",
+ "version": "28.0.0",
+ "resolved": "https://registry.npmjs.org/stylelint-config-standard/-/stylelint-config-standard-28.0.0.tgz",
+ "integrity": "sha512-q/StuowDdDmFCravzGHAwgS9pjX0bdOQUEBBDIkIWsQuYGgYz/xsO8CM6eepmIQ1fc5bKdDVimlJZ6MoOUcJ5Q==",
"dev": true,
"requires": {
- "stylelint-config-recommended": "^8.0.0"
+ "stylelint-config-recommended": "^9.0.0"
}
},
"stylis": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.1.1.tgz",
- "integrity": "sha512-lVrM/bNdhVX2OgBFNa2YJ9Lxj7kPzylieHd3TNjuGE0Re9JB7joL5VUKOVH1kdNNJTgGPpT8hmwIAPLaSyEVFQ=="
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.1.2.tgz",
+ "integrity": "sha512-Nn2CCrG2ZaFziDxaZPN43CXqn+j7tcdjPFCkRBkFue8QYXC2HdEwnw5TCBo4yQZ2WxKYeSi0fdoOrtEqgDrXbA=="
},
"superstruct": {
"version": "0.10.13",
@@ -21145,15 +15937,14 @@
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
- "dev": true,
"requires": {
"has-flag": "^4.0.0"
}
},
"supports-hyperlinks": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz",
- "integrity": "sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ==",
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz",
+ "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==",
"dev": true,
"requires": {
"has-flag": "^4.0.0",
@@ -21184,32 +15975,26 @@
"csso": "^4.2.0",
"picocolors": "^1.0.0",
"stable": "^0.1.8"
+ },
+ "dependencies": {
+ "commander": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
+ "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
+ "dev": true
+ }
}
},
"swagger-ui-dist": {
- "version": "4.13.0",
- "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-4.13.0.tgz",
- "integrity": "sha512-5yqhkUU9uV5oT/MTMBeSgDGI0Vx6eCOU43AszQBs88poI8OB1v+FoXEFHv+NaBbEfTkXCMWlAJrH6iWyDzLETQ=="
+ "version": "4.14.2",
+ "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-4.14.2.tgz",
+ "integrity": "sha512-kOIU7Ts3TrXDLb3/c9jRe4qGp8O3bRT19FFJA8wJfrRFkcK/4atPn3krhtBVJ57ZkNNofworXHxuYwmaisXBdg=="
},
- "sync-request": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/sync-request/-/sync-request-6.1.0.tgz",
- "integrity": "sha512-8fjNkrNlNCrVc/av+Jn+xxqfCjYaBoHqCsDz6mt030UMxJGr+GSfCV1dQt2gRtlL63+VPidwDVLr7V2OcTSdRw==",
- "dev": true,
- "requires": {
- "http-response-object": "^3.0.1",
- "sync-rpc": "^1.2.1",
- "then-request": "^6.0.0"
- }
- },
- "sync-rpc": {
- "version": "1.3.6",
- "resolved": "https://registry.npmjs.org/sync-rpc/-/sync-rpc-1.3.6.tgz",
- "integrity": "sha512-J8jTXuZzRlvU7HemDgHi3pGnh/rkoqR/OZSjhTyyZrEkkYQbk7Z33AXp37mkPfPpfdOuj7Ex3H/TJM1z48uPQw==",
- "dev": true,
- "requires": {
- "get-port": "^3.1.0"
- }
+ "symbol-tree": {
+ "version": "3.2.4",
+ "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
+ "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==",
+ "dev": true
},
"table": {
"version": "6.8.0",
@@ -21229,20 +16014,10 @@
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
"integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ=="
},
- "terminal-link": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz",
- "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==",
- "dev": true,
- "requires": {
- "ansi-escapes": "^4.2.1",
- "supports-hyperlinks": "^2.0.0"
- }
- },
"terser": {
- "version": "5.14.2",
- "resolved": "https://registry.npmjs.org/terser/-/terser-5.14.2.tgz",
- "integrity": "sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==",
+ "version": "5.15.1",
+ "resolved": "https://registry.npmjs.org/terser/-/terser-5.15.1.tgz",
+ "integrity": "sha512-K1faMUvpm/FBxjBXud0LWVAGxmvoPbZbfTCYbSgaaYQaIXI3/TdI7a7ZGA73Zrou6Q8Zmz3oeUTsp/dj+ag2Xw==",
"requires": {
"@jridgewell/source-map": "^0.3.2",
"acorn": "^8.5.0",
@@ -21267,15 +16042,15 @@
}
},
"terser-webpack-plugin": {
- "version": "5.3.3",
- "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.3.tgz",
- "integrity": "sha512-Fx60G5HNYknNTNQnzQ1VePRuu89ZVYWfjRAeT5rITuCY/1b08s49e5kSQwHDirKZWuoKOBRFS98EUUoZ9kLEwQ==",
+ "version": "5.3.6",
+ "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.6.tgz",
+ "integrity": "sha512-kfLFk+PoLUQIbLmB1+PZDMRSZS99Mp+/MHqDNmMA6tOItzRt+Npe3E+fsMs5mfcM0wCtrrdU387UnV+vnSffXQ==",
"requires": {
- "@jridgewell/trace-mapping": "^0.3.7",
+ "@jridgewell/trace-mapping": "^0.3.14",
"jest-worker": "^27.4.5",
"schema-utils": "^3.1.1",
"serialize-javascript": "^6.0.0",
- "terser": "^5.7.2"
+ "terser": "^5.14.1"
},
"dependencies": {
"ajv": {
@@ -21330,49 +16105,29 @@
}
}
},
- "test-exclude": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz",
- "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==",
- "dev": true,
- "requires": {
- "@istanbuljs/schema": "^0.1.2",
- "glob": "^7.1.4",
- "minimatch": "^3.0.4"
- }
- },
"text-table": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
"integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
"dev": true
},
- "then-request": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/then-request/-/then-request-6.0.2.tgz",
- "integrity": "sha512-3ZBiG7JvP3wbDzA9iNY5zJQcHL4jn/0BWtXIkagfz7QgOL/LqjCEOBQuJNZfu0XYnv5JhKh+cDxCPM4ILrqruA==",
- "dev": true,
- "requires": {
- "@types/concat-stream": "^1.6.0",
- "@types/form-data": "0.0.33",
- "@types/node": "^8.0.0",
- "@types/qs": "^6.2.31",
- "caseless": "~0.12.0",
- "concat-stream": "^1.6.0",
- "form-data": "^2.2.0",
- "http-basic": "^8.1.1",
- "http-response-object": "^3.0.1",
- "promise": "^8.0.0",
- "qs": "^6.4.0"
- },
- "dependencies": {
- "@types/node": {
- "version": "8.10.66",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.66.tgz",
- "integrity": "sha512-tktOkFUA4kXx2hhhrB8bIFb5TbwzS4uOhKEmwiD+NoiL0qtP2OQ9mFldbgD4dV1djrlBYP6eBuQZiWjuHUpqFw==",
- "dev": true
- }
- }
+ "tinybench": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.3.0.tgz",
+ "integrity": "sha512-zs1gMVBwyyG2QbVchYIbnabRhMOCGvrwZz/q+SV+LIMa9q5YDQZi2kkI6ZRqV2Bz7ba1uvrc7ieUoE4KWnGeKg==",
+ "dev": true
+ },
+ "tinypool": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.3.0.tgz",
+ "integrity": "sha512-NX5KeqHOBZU6Bc0xj9Vr5Szbb1j8tUHIeD18s41aDJaPeC5QTdEhK0SpdpUrZlj2nv5cctNcSjaKNanXlfcVEQ==",
+ "dev": true
+ },
+ "tinyspy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-1.0.2.tgz",
+ "integrity": "sha512-bSGlgwLBYf7PnUsQ6WOc6SJ3pGOcd+d8AA6EUnLDDM0kWEstC1JIlSZA3UNliDXhd9ABoS7hiRBDCu+XP/sf1Q==",
+ "dev": true
},
"tippy.js": {
"version": "6.3.7",
@@ -21382,18 +16137,6 @@
"@popperjs/core": "^2.9.0"
}
},
- "tmpl": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz",
- "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==",
- "dev": true
- },
- "to-fast-properties": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
- "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==",
- "dev": true
- },
"to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
@@ -21408,11 +16151,34 @@
"integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
"dev": true
},
+ "tough-cookie": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.2.tgz",
+ "integrity": "sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ==",
+ "dev": true,
+ "requires": {
+ "psl": "^1.1.33",
+ "punycode": "^2.1.1",
+ "universalify": "^0.2.0",
+ "url-parse": "^1.5.3"
+ },
+ "dependencies": {
+ "universalify": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz",
+ "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==",
+ "dev": true
+ }
+ }
+ },
"tr46": {
- "version": "0.0.3",
- "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
- "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
- "dev": true
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz",
+ "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==",
+ "dev": true,
+ "requires": {
+ "punycode": "^2.1.1"
+ }
},
"tributejs": {
"version": "5.1.3",
@@ -21480,16 +16246,16 @@
"integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
"dev": true
},
- "typedarray": {
- "version": "0.0.6",
- "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
- "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==",
- "dev": true
- },
"typo-js": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/typo-js/-/typo-js-1.2.1.tgz",
- "integrity": "sha512-bTGLjbD3WqZDR3CgEFkyi9Q/SS2oM29ipXrWfDb4M74ea69QwKAECVceYpaBu0GfdnASMg9Qfl67ttB23nePHg=="
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/typo-js/-/typo-js-1.2.2.tgz",
+ "integrity": "sha512-C7pYBQK17EjSg8tVNY91KHdUt5Nf6FMJ+c3js076quPmBML57PmNMzAcIq/2kf/hSYtFABNDIYNYlJRl5BJhGw=="
+ },
+ "uc.micro": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz",
+ "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==",
+ "dev": true
},
"uint8-to-base64": {
"version": "0.2.0",
@@ -21521,18 +16287,18 @@
"dev": true
},
"update-browserslist-db": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.5.tgz",
- "integrity": "sha512-dteFFpCyvuDdr9S/ff1ISkKt/9YZxKjI9WlRR99c180GaztJtRa/fn18FdxGVKVsnPY7/a/FDN68mcvUmP4U7Q==",
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz",
+ "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==",
"requires": {
"escalade": "^3.1.1",
"picocolors": "^1.0.0"
}
},
"updates": {
- "version": "13.1.2",
- "resolved": "https://registry.npmjs.org/updates/-/updates-13.1.2.tgz",
- "integrity": "sha512-wixXdKufbYwxKFMqWmkjnf6vlkZ8Lpx8fWYFrkxawNO9j7xlGQHCtbqW7LHkl/+tl57fFlvgvQ5dAIrseqk3Qw==",
+ "version": "13.1.8",
+ "resolved": "https://registry.npmjs.org/updates/-/updates-13.1.8.tgz",
+ "integrity": "sha512-hcDCkif1i4MWArx1QkJjLHyPCr8taYyzoypQ7ST/vbmNJyH/cpiawQf+5frvH5uekiSSwtuQNWKdFWB3UH4sew==",
"dev": true
},
"uri-js": {
@@ -21549,6 +16315,16 @@
"integrity": "sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ==",
"dev": true
},
+ "url-parse": {
+ "version": "1.5.10",
+ "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
+ "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==",
+ "dev": true,
+ "requires": {
+ "querystringify": "^2.1.1",
+ "requires-port": "^1.0.0"
+ }
+ },
"util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
@@ -21560,34 +16336,12 @@
"integrity": "sha512-O11mqxmi7wMKCo6HKFt5AhO4BwY3VV68YU07tgxfz8zJTIxr4BpsezN49Ffwy9j3ZpwwJp4fkRwjRzq3uWE6Rg==",
"dev": true
},
- "v-tooltip": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/v-tooltip/-/v-tooltip-2.1.3.tgz",
- "integrity": "sha512-xXngyxLQTOx/yUEy50thb8te7Qo4XU6h4LZB6cvEfVd9mnysUxLEoYwGWDdqR+l69liKsy3IPkdYff3J1gAJ5w==",
- "requires": {
- "@babel/runtime": "^7.13.10",
- "lodash": "^4.17.21",
- "popper.js": "^1.16.1",
- "vue-resize": "^1.0.1"
- }
- },
"v8-compile-cache": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz",
"integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==",
"dev": true
},
- "v8-to-istanbul": {
- "version": "9.0.1",
- "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.0.1.tgz",
- "integrity": "sha512-74Y4LqY74kLE6IFyIjPtkSTWzUZmj8tdHT9Ii/26dvQ6K9Dl2NbEfj0XgU2sHCtKgt5VupqhlO/5aWuqS+IY1w==",
- "dev": true,
- "requires": {
- "@jridgewell/trace-mapping": "^0.3.12",
- "@types/istanbul-lib-coverage": "^2.0.1",
- "convert-source-map": "^1.6.0"
- }
- },
"validate-npm-package-license": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
@@ -21607,10 +16361,53 @@
"builtins": "^1.0.3"
}
},
+ "vite": {
+ "version": "3.1.8",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-3.1.8.tgz",
+ "integrity": "sha512-m7jJe3nufUbuOfotkntGFupinL/fmuTNuQmiVE7cH2IZMuf4UbfbGYMUT3jVWgGYuRVLY9j8NnrRqgw5rr5QTg==",
+ "dev": true,
+ "requires": {
+ "esbuild": "^0.15.9",
+ "fsevents": "~2.3.2",
+ "postcss": "^8.4.16",
+ "resolve": "^1.22.1",
+ "rollup": "~2.78.0"
+ },
+ "dependencies": {
+ "rollup": {
+ "version": "2.78.1",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.78.1.tgz",
+ "integrity": "sha512-VeeCgtGi4P+o9hIg+xz4qQpRl6R401LWEXBmxYKOV4zlF82lyhgh2hTZnheFUbANE8l2A41F458iwj2vEYaXJg==",
+ "dev": true,
+ "requires": {
+ "fsevents": "~2.3.2"
+ }
+ }
+ }
+ },
+ "vitest": {
+ "version": "0.24.1",
+ "resolved": "https://registry.npmjs.org/vitest/-/vitest-0.24.1.tgz",
+ "integrity": "sha512-NKkK1xnDIOOr42pKBfGQQl6b6IWdFVBpG6ZS1T+nUlJuqcOiZ7lxjVwHy9wrtTYpJ0BWww9y6bSGYXubD29Nag==",
+ "dev": true,
+ "requires": {
+ "@types/chai": "^4.3.3",
+ "@types/chai-subset": "^1.3.3",
+ "@types/node": "*",
+ "chai": "^4.3.6",
+ "debug": "^4.3.4",
+ "local-pkg": "^0.4.2",
+ "strip-literal": "^0.4.2",
+ "tinybench": "^2.3.0",
+ "tinypool": "^0.3.0",
+ "tinyspy": "^1.0.2",
+ "vite": "^3.0.0"
+ }
+ },
"vm2": {
- "version": "3.9.10",
- "resolved": "https://registry.npmjs.org/vm2/-/vm2-3.9.10.tgz",
- "integrity": "sha512-AuECTSvwu2OHLAZYhG716YzwodKCIJxB6u1zG7PgSQwIgAlEaoXH52bxdcvT8GkGjnYK7r7yWDW0m0sOsPuBjQ==",
+ "version": "3.9.11",
+ "resolved": "https://registry.npmjs.org/vm2/-/vm2-3.9.11.tgz",
+ "integrity": "sha512-PFG8iJRSjvvBdisowQ7iVF580DXb1uCIiGaXgm7tynMR1uTBlv7UJlB1zdv5KJ+Tmq1f0Upnj3fayoEOPpCBKg==",
"dev": true,
"requires": {
"acorn": "^8.7.0",
@@ -21618,31 +16415,30 @@
}
},
"vue": {
- "version": "2.6.14",
- "resolved": "https://registry.npmjs.org/vue/-/vue-2.6.14.tgz",
- "integrity": "sha512-x2284lgYvjOMj3Za7kqzRcUSxBboHqtgRE2zlos1qWaOye5yUmHn42LB1250NJBLRwEcdrB0JRwyPTEPhfQjiQ=="
- },
- "vue-bar-graph": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/vue-bar-graph/-/vue-bar-graph-1.3.1.tgz",
- "integrity": "sha512-C0U594QoEI91PuXIrygfIRDRPDrpICrsJ0iYxuJJzDUENpWqahZGsqZZj8XRJGXsPUI8Ri1rIo91uaNI/ht79w==",
+ "version": "3.2.40",
+ "resolved": "https://registry.npmjs.org/vue/-/vue-3.2.40.tgz",
+ "integrity": "sha512-1mGHulzUbl2Nk3pfvI5aXYYyJUs1nm4kyvuz38u4xlQkLUn1i2R7nDbI4TufECmY8v1qNBHYy62bCaM+3cHP2A==",
"requires": {
- "gsap": "^3.6.1",
- "vue": "^2.6.12"
+ "@vue/compiler-dom": "3.2.40",
+ "@vue/compiler-sfc": "3.2.40",
+ "@vue/runtime-dom": "3.2.40",
+ "@vue/server-renderer": "3.2.40",
+ "@vue/shared": "3.2.40"
}
},
- "vue-calendar-heatmap": {
- "version": "0.8.4",
- "resolved": "https://registry.npmjs.org/vue-calendar-heatmap/-/vue-calendar-heatmap-0.8.4.tgz",
- "integrity": "sha512-Hx7OYBY1ghUIxKmFIIzpLT4XlcrwnI3WpadJEj/sKj5quoxwEuSDKmf94v0zWOHeQ/2CrB1G66geaKR/O56+OQ==",
+ "vue-bar-graph": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/vue-bar-graph/-/vue-bar-graph-2.0.0.tgz",
+ "integrity": "sha512-IoYP+r5Ggjys6QdUNYFPh7qD41wi/uDOJj9nMawvDgvV6niOz3Dw8O2/98ZnUgjTpcgcGFDaaAaK6qa9x1jgpw==",
"requires": {
- "v-tooltip": "^2.0.0-rc.32"
+ "gsap": "^3.10.4",
+ "vue": "^3.2.37"
}
},
"vue-eslint-parser": {
- "version": "9.0.3",
- "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.0.3.tgz",
- "integrity": "sha512-yL+ZDb+9T0ELG4VIFo/2anAOz8SvBdlqEnQnvJ3M7Scq56DvtjY0VY88bByRZB0D4J0u8olBcfrXTVONXsh4og==",
+ "version": "9.1.0",
+ "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.1.0.tgz",
+ "integrity": "sha512-NGn/iQy8/Wb7RrRa4aRkokyCZfOUWk19OP5HP6JEozQFX5AoS/t+Z0ZN7FY4LlmWc4FNI922V7cvX28zctN8dQ==",
"dev": true,
"requires": {
"debug": "^4.3.4",
@@ -21654,101 +16450,31 @@
"semver": "^7.3.6"
}
},
- "vue-hot-reload-api": {
- "version": "2.3.4",
- "resolved": "https://registry.npmjs.org/vue-hot-reload-api/-/vue-hot-reload-api-2.3.4.tgz",
- "integrity": "sha512-BXq3jwIagosjgNVae6tkHzzIk6a8MHFtzAdwhnV5VlvPTFxDCvIttgSiHWjdGoTJvXtmRu5HacExfdarRcFhog=="
- },
"vue-loader": {
- "version": "15.9.8",
- "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-15.9.8.tgz",
- "integrity": "sha512-GwSkxPrihfLR69/dSV3+5CdMQ0D+jXg8Ma1S4nQXKJAznYFX14vHdc/NetQc34Dw+rBbIJyP7JOuVb9Fhprvog==",
+ "version": "17.0.0",
+ "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-17.0.0.tgz",
+ "integrity": "sha512-OWSXjrzIvbF2LtOUmxT3HYgwwubbfFelN8PAP9R9dwpIkj48TVioHhWWSx7W7fk+iF5cgg3CBJRxwTdtLU4Ecg==",
"requires": {
- "@vue/component-compiler-utils": "^3.1.0",
- "hash-sum": "^1.0.2",
- "loader-utils": "^1.1.0",
- "vue-hot-reload-api": "^2.3.0",
- "vue-style-loader": "^4.1.0"
- },
- "dependencies": {
- "json5": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
- "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
- "requires": {
- "minimist": "^1.2.0"
- }
- },
- "loader-utils": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz",
- "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==",
- "requires": {
- "big.js": "^5.2.2",
- "emojis-list": "^3.0.0",
- "json5": "^1.0.1"
- }
- }
+ "chalk": "^4.1.0",
+ "hash-sum": "^2.0.0",
+ "loader-utils": "^2.0.0"
}
},
- "vue-resize": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/vue-resize/-/vue-resize-1.0.1.tgz",
- "integrity": "sha512-z5M7lJs0QluJnaoMFTIeGx6dIkYxOwHThlZDeQnWZBizKblb99GSejPnK37ZbNE/rVwDcYcHY+Io+AxdpY952w==",
+ "vue3-calendar-heatmap": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/vue3-calendar-heatmap/-/vue3-calendar-heatmap-2.0.0.tgz",
+ "integrity": "sha512-BchyC33WiZryYatFINj3LWqgyE6X82Huzf7abA23tsF/IbaRZVwZzie8SmGaYvezEBiPXhJogQ3dtxIuXFjkBw==",
"requires": {
- "@babel/runtime": "^7.13.10"
+ "tippy.js": "^6.3.7"
}
},
- "vue-style-loader": {
- "version": "4.1.3",
- "resolved": "https://registry.npmjs.org/vue-style-loader/-/vue-style-loader-4.1.3.tgz",
- "integrity": "sha512-sFuh0xfbtpRlKfm39ss/ikqs9AbKCoXZBpHeVZ8Tx650o0k0q/YCM7FRvigtxpACezfq6af+a7JeqVTWvncqDg==",
- "requires": {
- "hash-sum": "^1.0.2",
- "loader-utils": "^1.0.2"
- },
- "dependencies": {
- "json5": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
- "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
- "requires": {
- "minimist": "^1.2.0"
- }
- },
- "loader-utils": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz",
- "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==",
- "requires": {
- "big.js": "^5.2.2",
- "emojis-list": "^3.0.0",
- "json5": "^1.0.1"
- }
- }
- }
- },
- "vue-template-compiler": {
- "version": "2.6.14",
- "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.6.14.tgz",
- "integrity": "sha512-ODQS1SyMbjKoO1JBJZojSw6FE4qnh9rIpUZn2EUT86FKizx9uH5z6uXiIrm4/Nb/gwxTi/o17ZDEGWAXHvtC7g==",
- "requires": {
- "de-indent": "^1.0.2",
- "he": "^1.1.0"
- }
- },
- "vue-template-es2015-compiler": {
- "version": "1.9.1",
- "resolved": "https://registry.npmjs.org/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz",
- "integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw=="
- },
- "walker": {
- "version": "1.0.8",
- "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz",
- "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==",
+ "w3c-xmlserializer": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-3.0.0.tgz",
+ "integrity": "sha512-3WFqGEgSXIyGhOmAFtlicJNMjEps8b1MG31NCA0/vOF9+nKMUW1ckhi9cnNHmf88Rzw5V+dwIwsm2C7X8k9aQg==",
"dev": true,
"requires": {
- "makeerror": "1.0.12"
+ "xml-name-validator": "^4.0.0"
}
},
"watchpack": {
@@ -21767,20 +16493,20 @@
"dev": true
},
"webpack": {
- "version": "5.73.0",
- "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.73.0.tgz",
- "integrity": "sha512-svjudQRPPa0YiOYa2lM/Gacw0r6PvxptHj4FuEKQ2kX05ZLkjbVc5MnPs6its5j7IZljnIqSVo/OsY2X0IpHGA==",
+ "version": "5.74.0",
+ "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.74.0.tgz",
+ "integrity": "sha512-A2InDwnhhGN4LYctJj6M1JEaGL7Luj6LOmyBHjcI8529cm5p6VXiTIW2sn6ffvEAKmveLzvu4jrihwXtPojlAA==",
"requires": {
"@types/eslint-scope": "^3.7.3",
"@types/estree": "^0.0.51",
"@webassemblyjs/ast": "1.11.1",
"@webassemblyjs/wasm-edit": "1.11.1",
"@webassemblyjs/wasm-parser": "1.11.1",
- "acorn": "^8.4.1",
+ "acorn": "^8.7.1",
"acorn-import-assertions": "^1.7.6",
"browserslist": "^4.14.5",
"chrome-trace-event": "^1.0.2",
- "enhanced-resolve": "^5.9.3",
+ "enhanced-resolve": "^5.10.0",
"es-module-lexer": "^0.9.0",
"eslint-scope": "5.1.1",
"events": "^3.2.0",
@@ -21793,7 +16519,7 @@
"schema-utils": "^3.1.0",
"tapable": "^2.1.1",
"terser-webpack-plugin": "^5.1.3",
- "watchpack": "^2.3.1",
+ "watchpack": "^2.4.0",
"webpack-sources": "^3.2.3"
},
"dependencies": {
@@ -21872,6 +16598,13 @@
"interpret": "^2.2.0",
"rechoir": "^0.7.0",
"webpack-merge": "^5.7.3"
+ },
+ "dependencies": {
+ "commander": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
+ "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw=="
+ }
}
},
"webpack-merge": {
@@ -21908,21 +16641,13 @@
"dev": true
},
"whatwg-url": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
- "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
+ "version": "11.0.0",
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz",
+ "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==",
"dev": true,
"requires": {
- "tr46": "~0.0.3",
- "webidl-conversions": "^3.0.0"
- },
- "dependencies": {
- "webidl-conversions": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
- "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
- "dev": true
- }
+ "tr46": "^3.0.0",
+ "webidl-conversions": "^7.0.0"
}
},
"which": {
@@ -21951,12 +16676,6 @@
"resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz",
"integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw=="
},
- "wolfy87-eventemitter": {
- "version": "5.2.9",
- "resolved": "https://registry.npmjs.org/wolfy87-eventemitter/-/wolfy87-eventemitter-5.2.9.tgz",
- "integrity": "sha512-P+6vtWyuDw+MB01X7UeF8TaHBvbCovf4HPEMF/SV7BdDc1SMTiBy13SRD71lQh4ExFTG1d/WNzDGDCyOKSMblw==",
- "dev": true
- },
"word-wrap": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz",
@@ -21964,24 +16683,24 @@
"dev": true
},
"workbox-core": {
- "version": "6.5.3",
- "resolved": "https://registry.npmjs.org/workbox-core/-/workbox-core-6.5.3.tgz",
- "integrity": "sha512-Bb9ey5n/M9x+l3fBTlLpHt9ASTzgSGj6vxni7pY72ilB/Pb3XtN+cZ9yueboVhD5+9cNQrC9n/E1fSrqWsUz7Q=="
+ "version": "6.5.4",
+ "resolved": "https://registry.npmjs.org/workbox-core/-/workbox-core-6.5.4.tgz",
+ "integrity": "sha512-OXYb+m9wZm8GrORlV2vBbE5EC1FKu71GGp0H4rjmxmF4/HLbMCoTFws87M3dFwgpmg0v00K++PImpNQ6J5NQ6Q=="
},
"workbox-routing": {
- "version": "6.5.3",
- "resolved": "https://registry.npmjs.org/workbox-routing/-/workbox-routing-6.5.3.tgz",
- "integrity": "sha512-DFjxcuRAJjjt4T34RbMm3MCn+xnd36UT/2RfPRfa8VWJGItGJIn7tG+GwVTdHmvE54i/QmVTJepyAGWtoLPTmg==",
+ "version": "6.5.4",
+ "resolved": "https://registry.npmjs.org/workbox-routing/-/workbox-routing-6.5.4.tgz",
+ "integrity": "sha512-apQswLsbrrOsBUWtr9Lf80F+P1sHnQdYodRo32SjiByYi36IDyL2r7BH1lJtFX8fwNHDa1QOVY74WKLLS6o5Pg==",
"requires": {
- "workbox-core": "6.5.3"
+ "workbox-core": "6.5.4"
}
},
"workbox-strategies": {
- "version": "6.5.3",
- "resolved": "https://registry.npmjs.org/workbox-strategies/-/workbox-strategies-6.5.3.tgz",
- "integrity": "sha512-MgmGRrDVXs7rtSCcetZgkSZyMpRGw8HqL2aguszOc3nUmzGZsT238z/NN9ZouCxSzDu3PQ3ZSKmovAacaIhu1w==",
+ "version": "6.5.4",
+ "resolved": "https://registry.npmjs.org/workbox-strategies/-/workbox-strategies-6.5.4.tgz",
+ "integrity": "sha512-DEtsxhx0LIYWkJBTQolRxG4EI0setTJkqR4m7r4YpBdxtWJH1Mbg01Cj8ZjNOO8etqfA3IZaOPHUxCs8cBsKLw==",
"requires": {
- "workbox-core": "6.5.3"
+ "workbox-core": "6.5.4"
}
},
"worker-loader": {
@@ -22043,9 +16762,9 @@
"integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA=="
},
"ansi-styles": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.1.0.tgz",
- "integrity": "sha512-VbqNsoz55SYGczauuup0MFUyXNQviSpFTj1RQtFzmQLk18qbVSpTFFGMT293rmDaQuKCT6InmbuEyUne4mTuxQ=="
+ "version": "6.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.1.1.tgz",
+ "integrity": "sha512-qDOv24WjnYuL+wbwHdlsYZFy+cgPtrYw0Tn7GLORicQp9BkQLzrgI3Pm4VyR9ERZ41YTn7KlMPuL1n05WdZvmg=="
},
"emoji-regex": {
"version": "9.2.2",
@@ -22078,21 +16797,34 @@
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
},
"write-file-atomic": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.1.tgz",
- "integrity": "sha512-nSKUxgAbyioruk6hU87QzVbY279oYT6uiwgDoujth2ju4mJ+TZau7SQBhtbTmUyuNYTuXnSyRn66FV0+eCgcrQ==",
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz",
+ "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==",
"dev": true,
"requires": {
"imurmurhash": "^0.1.4",
"signal-exit": "^3.0.7"
}
},
+ "ws": {
+ "version": "8.9.0",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.9.0.tgz",
+ "integrity": "sha512-Ja7nszREasGaYUYCI2k4lCKIRTt+y7XuqVoHR44YpI49TtryyqbqvDMn5eqfW7e6HzTukDRIsXqzVHScqRcafg==",
+ "dev": true,
+ "requires": {}
+ },
"xml-name-validator": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz",
"integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==",
"dev": true
},
+ "xmlchars": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
+ "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==",
+ "dev": true
+ },
"xregexp": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/xregexp/-/xregexp-2.0.0.tgz",
@@ -22133,9 +16865,9 @@
},
"dependencies": {
"yargs-parser": {
- "version": "21.0.1",
- "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.1.tgz",
- "integrity": "sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg==",
+ "version": "21.1.1",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
+ "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
"dev": true
}
}
diff --git a/package.json b/package.json
index f4752aeec9..cd83d399a0 100644
--- a/package.json
+++ b/package.json
@@ -8,58 +8,62 @@
},
"dependencies": {
"@claviska/jquery-minicolors": "2.3.6",
- "@primer/octicons": "17.3.0",
+ "@mcaptcha/vanilla-glue": "0.1.0-alpha-3",
+ "@primer/octicons": "17.7.0",
+ "@vue/compiler-sfc": "3.2.40",
"add-asset-webpack-plugin": "2.0.1",
"css-loader": "6.7.1",
"dropzone": "6.0.0-beta.2",
- "easymde": "2.16.1",
- "esbuild-loader": "2.19.0",
+ "easymde": "2.18.0",
+ "esbuild-loader": "2.20.0",
"escape-goat": "4.0.0",
- "fast-glob": "3.2.11",
+ "fast-glob": "3.2.12",
"font-awesome": "4.7.0",
- "jquery": "3.6.0",
+ "jquery": "3.6.1",
"jquery.are-you-sure": "1.9.0",
+ "katex": "0.16.2",
"less": "4.1.3",
- "less-loader": "11.0.0",
+ "less-loader": "11.1.0",
"license-checker-webpack-plugin": "0.2.1",
- "mermaid": "9.1.3",
+ "mermaid": "9.1.7",
"mini-css-extract-plugin": "2.6.1",
- "monaco-editor": "0.33.0",
+ "monaco-editor": "0.34.0",
"monaco-editor-webpack-plugin": "7.0.1",
"pretty-ms": "8.0.0",
"sortablejs": "1.15.0",
- "swagger-ui-dist": "4.13.0",
+ "swagger-ui-dist": "4.14.2",
"tippy.js": "6.3.7",
"tributejs": "5.1.3",
"uint8-to-base64": "0.2.0",
- "vue": "2.6.14",
- "vue-bar-graph": "1.3.1",
- "vue-calendar-heatmap": "0.8.4",
- "vue-loader": "15.9.8",
- "vue-template-compiler": "2.6.14",
- "webpack": "5.73.0",
+ "vue": "3.2.40",
+ "vue-bar-graph": "2.0.0",
+ "vue-loader": "17.0.0",
+ "vue3-calendar-heatmap": "2.0.0",
+ "webpack": "5.74.0",
"webpack-cli": "4.10.0",
- "workbox-routing": "6.5.3",
- "workbox-strategies": "6.5.3",
+ "workbox-routing": "6.5.4",
+ "workbox-strategies": "6.5.4",
"worker-loader": "3.0.8",
"wrap-ansi": "8.0.1"
},
"devDependencies": {
- "@happy-dom/jest-environment": "6.0.4",
- "@stoplight/spectral-cli": "6.4.1",
- "eslint": "8.20.0",
+ "@playwright/test": "1.27.0",
+ "@rollup/pluginutils": "5.0.1",
+ "@stoplight/spectral-cli": "6.5.1",
+ "eslint": "8.25.0",
"eslint-plugin-import": "2.26.0",
"eslint-plugin-jquery": "1.5.1",
- "eslint-plugin-sonarjs": "0.13.0",
- "eslint-plugin-unicorn": "43.0.2",
- "eslint-plugin-vue": "9.2.0",
- "jest": "28.1.3",
- "jest-extended": "3.0.1",
+ "eslint-plugin-sonarjs": "0.15.0",
+ "eslint-plugin-unicorn": "44.0.2",
+ "eslint-plugin-vue": "9.6.0",
+ "jsdom": "20.0.1",
+ "markdownlint-cli": "0.32.2",
"postcss-less": "6.0.0",
- "stylelint": "14.9.1",
- "stylelint-config-standard": "26.0.0",
+ "stylelint": "14.13.0",
+ "stylelint-config-standard": "28.0.0",
"svgo": "2.8.0",
- "updates": "13.1.2"
+ "updates": "13.1.8",
+ "vitest": "0.24.1"
},
"browserslist": [
"defaults",
diff --git a/playwright.config.js b/playwright.config.js
new file mode 100644
index 0000000000..b7badf1cc0
--- /dev/null
+++ b/playwright.config.js
@@ -0,0 +1,101 @@
+// @ts-check
+import {devices} from '@playwright/test';
+
+const BASE_URL = process.env.GITEA_URL?.replace?.(/\/$/g, '') || 'http://localhost:3000';
+
+/**
+ * @see https://playwright.dev/docs/test-configuration
+ * @type {import('@playwright/test').PlaywrightTestConfig}
+ */
+export default {
+ testDir: './tests/e2e/',
+ testMatch: /.*\.test\.e2e\.js/, // Match any .test.e2e.js files
+
+ /* Maximum time one test can run for. */
+ timeout: 30 * 1000,
+
+ expect: {
+
+ /**
+ * Maximum time expect() should wait for the condition to be met.
+ * For example in `await expect(locator).toHaveText();`
+ */
+ timeout: 2000
+ },
+
+ /* Fail the build on CI if you accidentally left test.only in the source code. */
+ forbidOnly: Boolean(process.env.CI),
+
+ /* Retry on CI only */
+ retries: process.env.CI ? 2 : 0,
+
+ /* Reporter to use. See https://playwright.dev/docs/test-reporters */
+ reporter: process.env.CI ? 'list' : [['list'], ['html', {outputFolder: 'tests/e2e/reports/', open: 'never'}]],
+
+ /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
+ use: {
+ headless: true, // set to false to debug
+
+ locale: 'en-US',
+
+ /* Maximum time each action such as `click()` can take. Defaults to 0 (no limit). */
+ actionTimeout: 1000,
+
+ /* Maximum time allowed for navigation, such as `page.goto()`. */
+ navigationTimeout: 5 * 1000,
+
+ /* Base URL to use in actions like `await page.goto('/')`. */
+ baseURL: BASE_URL,
+
+ /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
+ trace: 'on-first-retry',
+
+ screenshot: 'only-on-failure',
+ },
+
+ /* Configure projects for major browsers */
+ projects: [
+ {
+ name: 'chromium',
+
+ /* Project-specific settings. */
+ use: {
+ ...devices['Desktop Chrome'],
+ },
+ },
+
+ // disabled because of https://github.com/go-gitea/gitea/issues/21355
+ // {
+ // name: 'firefox',
+ // use: {
+ // ...devices['Desktop Firefox'],
+ // },
+ // },
+
+ {
+ name: 'webkit',
+ use: {
+ ...devices['Desktop Safari'],
+ },
+ },
+
+ /* Test against mobile viewports. */
+ {
+ name: 'Mobile Chrome',
+ use: {
+ ...devices['Pixel 5'],
+ },
+ },
+ {
+ name: 'Mobile Safari',
+ use: {
+ ...devices['iPhone 12'],
+ },
+ },
+ ],
+
+ /* Folder for test artifacts such as screenshots, videos, traces, etc. */
+ outputDir: 'tests/e2e/test-artifacts/',
+ /* Folder for test artifacts such as screenshots, videos, traces, etc. */
+ snapshotDir: 'tests/e2e/test-snapshots/',
+};
diff --git a/public/img/svg/fontawesome-send.svg b/public/img/svg/fontawesome-send.svg
new file mode 100644
index 0000000000..b1170fd9e7
--- /dev/null
+++ b/public/img/svg/fontawesome-send.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/img/svg/gitea-exclamation.svg b/public/img/svg/gitea-exclamation.svg
new file mode 100644
index 0000000000..d6c86136b3
--- /dev/null
+++ b/public/img/svg/gitea-exclamation.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/img/svg/gitea-join.svg b/public/img/svg/gitea-join.svg
new file mode 100644
index 0000000000..678b9374f7
--- /dev/null
+++ b/public/img/svg/gitea-join.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/img/svg/gitea-pub.svg b/public/img/svg/gitea-pub.svg
new file mode 100644
index 0000000000..4a750c7082
--- /dev/null
+++ b/public/img/svg/gitea-pub.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/img/svg/gitea-split.svg b/public/img/svg/gitea-split.svg
new file mode 100644
index 0000000000..f819255cca
--- /dev/null
+++ b/public/img/svg/gitea-split.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/img/svg/gitea-vagrant.svg b/public/img/svg/gitea-vagrant.svg
new file mode 100644
index 0000000000..4c1b78cab5
--- /dev/null
+++ b/public/img/svg/gitea-vagrant.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/img/svg/gitea-whitespace.svg b/public/img/svg/gitea-whitespace.svg
new file mode 100644
index 0000000000..6b34f33736
--- /dev/null
+++ b/public/img/svg/gitea-whitespace.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/img/svg/octicon-accessibility-inset.svg b/public/img/svg/octicon-accessibility-inset.svg
new file mode 100644
index 0000000000..ec303f9cb2
--- /dev/null
+++ b/public/img/svg/octicon-accessibility-inset.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/img/svg/octicon-alert-fill.svg b/public/img/svg/octicon-alert-fill.svg
new file mode 100644
index 0000000000..34795cfbe4
--- /dev/null
+++ b/public/img/svg/octicon-alert-fill.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/img/svg/octicon-cache.svg b/public/img/svg/octicon-cache.svg
new file mode 100644
index 0000000000..20b14138d9
--- /dev/null
+++ b/public/img/svg/octicon-cache.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/img/svg/octicon-checkbox.svg b/public/img/svg/octicon-checkbox.svg
new file mode 100644
index 0000000000..f0313bc747
--- /dev/null
+++ b/public/img/svg/octicon-checkbox.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/img/svg/octicon-command-palette.svg b/public/img/svg/octicon-command-palette.svg
new file mode 100644
index 0000000000..92fcd63149
--- /dev/null
+++ b/public/img/svg/octicon-command-palette.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/img/svg/octicon-git-merge-queue.svg b/public/img/svg/octicon-git-merge-queue.svg
new file mode 100644
index 0000000000..17d7767b05
--- /dev/null
+++ b/public/img/svg/octicon-git-merge-queue.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/img/svg/octicon-paperclip.svg b/public/img/svg/octicon-paperclip.svg
new file mode 100644
index 0000000000..ddae143818
--- /dev/null
+++ b/public/img/svg/octicon-paperclip.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/img/svg/octicon-shield-slash.svg b/public/img/svg/octicon-shield-slash.svg
new file mode 100644
index 0000000000..ba4db6776b
--- /dev/null
+++ b/public/img/svg/octicon-shield-slash.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/img/svg/octicon-table.svg b/public/img/svg/octicon-table.svg
index 905b2bb9b4..5b80f78357 100644
--- a/public/img/svg/octicon-table.svg
+++ b/public/img/svg/octicon-table.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/public/img/svg/octicon-tasklist.svg b/public/img/svg/octicon-tasklist.svg
index 81e41a87ce..41b7c90f6d 100644
--- a/public/img/svg/octicon-tasklist.svg
+++ b/public/img/svg/octicon-tasklist.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/routers/api/packages/api.go b/routers/api/packages/api.go
index b5fdc739d7..f6ab961f5e 100644
--- a/routers/api/packages/api.go
+++ b/routers/api/packages/api.go
@@ -5,6 +5,7 @@
package packages
import (
+ gocontext "context"
"net/http"
"regexp"
"strings"
@@ -21,8 +22,10 @@ import (
"code.gitea.io/gitea/routers/api/packages/maven"
"code.gitea.io/gitea/routers/api/packages/npm"
"code.gitea.io/gitea/routers/api/packages/nuget"
+ "code.gitea.io/gitea/routers/api/packages/pub"
"code.gitea.io/gitea/routers/api/packages/pypi"
"code.gitea.io/gitea/routers/api/packages/rubygems"
+ "code.gitea.io/gitea/routers/api/packages/vagrant"
"code.gitea.io/gitea/services/auth"
context_service "code.gitea.io/gitea/services/context"
)
@@ -37,14 +40,15 @@ func reqPackageAccess(accessMode perm.AccessMode) func(ctx *context.Context) {
}
}
-func Routes() *web.Route {
+func Routes(ctx gocontext.Context) *web.Route {
r := web.NewRoute()
- r.Use(context.PackageContexter())
+ r.Use(context.PackageContexter(ctx))
authMethods := []auth.Method{
&auth.OAuth2{},
&auth.Basic{},
+ &nuget.Auth{},
&conan.Auth{},
}
if setting.Service.EnableReverseProxyAuth {
@@ -65,7 +69,7 @@ func Routes() *web.Route {
r.Get("/p2/{vendorname}/{projectname}.json", composer.PackageMetadata)
r.Get("/files/{package}/{version}/{filename}", composer.DownloadPackageFile)
r.Put("", reqPackageAccess(perm.AccessModeWrite), composer.UploadPackage)
- })
+ }, reqPackageAccess(perm.AccessModeRead))
r.Group("/conan", func() {
r.Group("/v1", func() {
r.Get("/ping", conan.Ping)
@@ -153,53 +157,80 @@ func Routes() *web.Route {
}, conan.ExtractPathParameters)
})
})
- })
+ }, reqPackageAccess(perm.AccessModeRead))
r.Group("/generic", func() {
- r.Group("/{packagename}/{packageversion}/{filename}", func() {
- r.Get("", generic.DownloadPackageFile)
- r.Group("", func() {
- r.Put("", generic.UploadPackage)
- r.Delete("", generic.DeletePackage)
- }, reqPackageAccess(perm.AccessModeWrite))
+ r.Group("/{packagename}/{packageversion}", func() {
+ r.Delete("", reqPackageAccess(perm.AccessModeWrite), generic.DeletePackage)
+ r.Group("/{filename}", func() {
+ r.Get("", generic.DownloadPackageFile)
+ r.Group("", func() {
+ r.Put("", generic.UploadPackage)
+ r.Delete("", generic.DeletePackageFile)
+ }, reqPackageAccess(perm.AccessModeWrite))
+ })
})
- })
+ }, reqPackageAccess(perm.AccessModeRead))
r.Group("/helm", func() {
r.Get("/index.yaml", helm.Index)
r.Get("/{filename}", helm.DownloadPackageFile)
r.Post("/api/charts", reqPackageAccess(perm.AccessModeWrite), helm.UploadPackage)
- })
+ }, reqPackageAccess(perm.AccessModeRead))
r.Group("/maven", func() {
r.Put("/*", reqPackageAccess(perm.AccessModeWrite), maven.UploadPackageFile)
r.Get("/*", maven.DownloadPackageFile)
- })
+ }, reqPackageAccess(perm.AccessModeRead))
r.Group("/nuget", func() {
- r.Get("/index.json", nuget.ServiceIndex)
- r.Get("/query", nuget.SearchService)
- r.Group("/registration/{id}", func() {
- r.Get("/index.json", nuget.RegistrationIndex)
- r.Get("/{version}", nuget.RegistrationLeaf)
- })
- r.Group("/package/{id}", func() {
- r.Get("/index.json", nuget.EnumeratePackageVersions)
- r.Get("/{version}/{filename}", nuget.DownloadPackageFile)
+ r.Group("", func() { // Needs to be unauthenticated for the NuGet client.
+ r.Get("/", nuget.ServiceIndexV2)
+ r.Get("/index.json", nuget.ServiceIndexV3)
+ r.Get("/$metadata", nuget.FeedCapabilityResource)
})
r.Group("", func() {
- r.Put("/", nuget.UploadPackage)
- r.Put("/symbolpackage", nuget.UploadSymbolPackage)
- r.Delete("/{id}/{version}", nuget.DeletePackage)
- }, reqPackageAccess(perm.AccessModeWrite))
- r.Get("/symbols/{filename}/{guid:[0-9a-f]{32}}FFFFFFFF/{filename2}", nuget.DownloadSymbolFile)
+ r.Get("/query", nuget.SearchServiceV3)
+ r.Group("/registration/{id}", func() {
+ r.Get("/index.json", nuget.RegistrationIndex)
+ r.Get("/{version}", nuget.RegistrationLeafV3)
+ })
+ r.Group("/package/{id}", func() {
+ r.Get("/index.json", nuget.EnumeratePackageVersionsV3)
+ r.Get("/{version}/{filename}", nuget.DownloadPackageFile)
+ })
+ r.Group("", func() {
+ r.Put("/", nuget.UploadPackage)
+ r.Put("/symbolpackage", nuget.UploadSymbolPackage)
+ r.Delete("/{id}/{version}", nuget.DeletePackage)
+ }, reqPackageAccess(perm.AccessModeWrite))
+ r.Get("/symbols/{filename}/{guid:[0-9a-fA-F]{32}[fF]{8}}/{filename2}", nuget.DownloadSymbolFile)
+ r.Get("/Packages(Id='{id:[^']+}',Version='{version:[^']+}')", nuget.RegistrationLeafV2)
+ r.Get("/Packages()", nuget.SearchServiceV2)
+ r.Get("/FindPackagesById()", nuget.EnumeratePackageVersionsV2)
+ r.Get("/Search()", nuget.SearchServiceV2)
+ }, reqPackageAccess(perm.AccessModeRead))
})
r.Group("/npm", func() {
r.Group("/@{scope}/{id}", func() {
r.Get("", npm.PackageMetadata)
r.Put("", reqPackageAccess(perm.AccessModeWrite), npm.UploadPackage)
- r.Get("/-/{version}/{filename}", npm.DownloadPackageFile)
+ r.Group("/-/{version}/{filename}", func() {
+ r.Get("", npm.DownloadPackageFile)
+ r.Delete("/-rev/{revision}", reqPackageAccess(perm.AccessModeWrite), npm.DeletePackageVersion)
+ })
+ r.Group("/-rev/{revision}", func() {
+ r.Delete("", npm.DeletePackage)
+ r.Put("", npm.DeletePreview)
+ }, reqPackageAccess(perm.AccessModeWrite))
})
r.Group("/{id}", func() {
r.Get("", npm.PackageMetadata)
r.Put("", reqPackageAccess(perm.AccessModeWrite), npm.UploadPackage)
- r.Get("/-/{version}/{filename}", npm.DownloadPackageFile)
+ r.Group("/-/{version}/{filename}", func() {
+ r.Get("", npm.DownloadPackageFile)
+ r.Delete("/-rev/{revision}", reqPackageAccess(perm.AccessModeWrite), npm.DeletePackageVersion)
+ })
+ r.Group("/-rev/{revision}", func() {
+ r.Delete("", npm.DeletePackage)
+ r.Put("", npm.DeletePreview)
+ }, reqPackageAccess(perm.AccessModeWrite))
})
r.Group("/-/package/@{scope}/{id}/dist-tags", func() {
r.Get("", npm.ListPackageTags)
@@ -215,12 +246,29 @@ func Routes() *web.Route {
r.Delete("", npm.DeletePackageTag)
}, reqPackageAccess(perm.AccessModeWrite))
})
- })
+ r.Group("/-/v1/search", func() {
+ r.Get("", npm.PackageSearch)
+ })
+ }, reqPackageAccess(perm.AccessModeRead))
+ r.Group("/pub", func() {
+ r.Group("/api/packages", func() {
+ r.Group("/versions/new", func() {
+ r.Get("", pub.RequestUpload)
+ r.Post("/upload", pub.UploadPackageFile)
+ r.Get("/finalize/{id}/{version}", pub.FinalizePackage)
+ }, reqPackageAccess(perm.AccessModeWrite))
+ r.Group("/{id}", func() {
+ r.Get("", pub.EnumeratePackageVersions)
+ r.Get("/files/{version}", pub.DownloadPackageFile)
+ r.Get("/{version}", pub.PackageVersionMetadata)
+ })
+ })
+ }, reqPackageAccess(perm.AccessModeRead))
r.Group("/pypi", func() {
r.Post("/", reqPackageAccess(perm.AccessModeWrite), pypi.UploadPackageFile)
r.Get("/files/{id}/{version}/{filename}", pypi.DownloadPackageFile)
r.Get("/simple/{id}", pypi.PackageMetadata)
- })
+ }, reqPackageAccess(perm.AccessModeRead))
r.Group("/rubygems", func() {
r.Get("/specs.4.8.gz", rubygems.EnumeratePackages)
r.Get("/latest_specs.4.8.gz", rubygems.EnumeratePackagesLatest)
@@ -231,16 +279,29 @@ func Routes() *web.Route {
r.Post("/", rubygems.UploadPackageFile)
r.Delete("/yank", rubygems.DeletePackage)
}, reqPackageAccess(perm.AccessModeWrite))
- })
- }, context_service.UserAssignmentWeb(), context.PackageAssignment(), reqPackageAccess(perm.AccessModeRead))
+ }, reqPackageAccess(perm.AccessModeRead))
+ r.Group("/vagrant", func() {
+ r.Group("/authenticate", func() {
+ r.Get("", vagrant.CheckAuthenticate)
+ })
+ r.Group("/{name}", func() {
+ r.Head("", vagrant.CheckBoxAvailable)
+ r.Get("", vagrant.EnumeratePackageVersions)
+ r.Group("/{version}/{provider}", func() {
+ r.Get("", vagrant.DownloadPackageFile)
+ r.Put("", reqPackageAccess(perm.AccessModeWrite), vagrant.UploadPackageFile)
+ })
+ })
+ }, reqPackageAccess(perm.AccessModeRead))
+ }, context_service.UserAssignmentWeb(), context.PackageAssignment())
return r
}
-func ContainerRoutes() *web.Route {
+func ContainerRoutes(ctx gocontext.Context) *web.Route {
r := web.NewRoute()
- r.Use(context.PackageContexter())
+ r.Use(context.PackageContexter(ctx))
authMethods := []auth.Method{
&auth.Basic{},
@@ -257,13 +318,16 @@ func ContainerRoutes() *web.Route {
r.Get("", container.ReqContainerAccess, container.DetermineSupport)
r.Get("/token", container.Authenticate)
+ r.Get("/_catalog", container.ReqContainerAccess, container.GetRepositoryList)
r.Group("/{username}", func() {
r.Group("/{image}", func() {
r.Group("/blobs/uploads", func() {
r.Post("", container.InitiateUploadBlob)
r.Group("/{uuid}", func() {
+ r.Get("", container.GetUploadBlob)
r.Patch("", container.UploadBlob)
r.Put("", container.EndUploadBlob)
+ r.Delete("", container.CancelUploadBlob)
})
}, reqPackageAccess(perm.AccessModeWrite))
r.Group("/blobs/{digest}", func() {
@@ -323,7 +387,7 @@ func ContainerRoutes() *web.Route {
}
m := blobsUploadsPattern.FindStringSubmatch(path)
- if len(m) == 3 && (isPut || isPatch) {
+ if len(m) == 3 && (isGet || isPut || isPatch || isDelete) {
reqPackageAccess(perm.AccessModeWrite)(ctx)
if ctx.Written() {
return
@@ -337,10 +401,14 @@ func ContainerRoutes() *web.Route {
ctx.SetParams("uuid", m[2])
- if isPatch {
+ if isGet {
+ container.GetUploadBlob(ctx)
+ } else if isPatch {
container.UploadBlob(ctx)
- } else {
+ } else if isPut {
container.EndUploadBlob(ctx)
+ } else {
+ container.CancelUploadBlob(ctx)
}
return
}
diff --git a/routers/api/packages/composer/api.go b/routers/api/packages/composer/api.go
index 5e1cc293da..ed52d16513 100644
--- a/routers/api/packages/composer/api.go
+++ b/routers/api/packages/composer/api.go
@@ -88,7 +88,7 @@ func createPackageMetadataResponse(registryURL string, pds []*packages_model.Pac
for _, pd := range pds {
packageType := ""
- for _, pvp := range pd.Properties {
+ for _, pvp := range pd.VersionProperties {
if pvp.Name == composer_module.TypeProperty {
packageType = pvp.Value
break
@@ -99,7 +99,7 @@ func createPackageMetadataResponse(registryURL string, pds []*packages_model.Pac
Name: pd.Package.Name,
Version: pd.Version.Version,
Type: packageType,
- Created: time.Unix(int64(pd.Version.CreatedUnix), 0),
+ Created: pd.Version.CreatedUnix.AsLocalTime(),
Metadata: pd.Metadata.(*composer_module.Metadata),
Dist: Dist{
Type: "zip",
diff --git a/routers/api/packages/composer/composer.go b/routers/api/packages/composer/composer.go
index 23de28c7f9..86ef7cbd9a 100644
--- a/routers/api/packages/composer/composer.go
+++ b/routers/api/packages/composer/composer.go
@@ -19,6 +19,7 @@ import (
packages_module "code.gitea.io/gitea/modules/packages"
composer_module "code.gitea.io/gitea/modules/packages/composer"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/routers/api/packages/helper"
packages_service "code.gitea.io/gitea/services/packages"
@@ -62,10 +63,11 @@ func SearchPackages(ctx *context.Context) {
}
opts := &packages_model.PackageSearchOptions{
- OwnerID: ctx.Package.Owner.ID,
- Type: packages_model.TypeComposer,
- Name: packages_model.SearchValue{Value: ctx.FormTrim("q")},
- Paginator: &paginator,
+ OwnerID: ctx.Package.Owner.ID,
+ Type: packages_model.TypeComposer,
+ Name: packages_model.SearchValue{Value: ctx.FormTrim("q")},
+ IsInternal: util.OptionalBoolFalse,
+ Paginator: &paginator,
}
if ctx.FormTrim("type") != "" {
opts.Properties = map[string]string{
@@ -182,7 +184,7 @@ func DownloadPackageFile(ctx *context.Context) {
}
defer s.Close()
- ctx.ServeStream(s, pf.Name)
+ ctx.ServeContent(pf.Name, s, pf.CreatedUnix.AsLocalTime())
}
// UploadPackage creates a new package
@@ -225,7 +227,7 @@ func UploadPackage(ctx *context.Context) {
SemverCompatible: true,
Creator: ctx.Doer,
Metadata: cp.Metadata,
- Properties: map[string]string{
+ VersionProperties: map[string]string{
composer_module.TypeProperty: cp.Type,
},
},
diff --git a/routers/api/packages/conan/conan.go b/routers/api/packages/conan/conan.go
index 04b0bb6cdd..dd078d6ad3 100644
--- a/routers/api/packages/conan/conan.go
+++ b/routers/api/packages/conan/conan.go
@@ -14,6 +14,7 @@ import (
"code.gitea.io/gitea/models/db"
packages_model "code.gitea.io/gitea/models/packages"
conan_model "code.gitea.io/gitea/models/packages/conan"
+ "code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/log"
@@ -33,20 +34,18 @@ const (
packageReferenceKey = "PackageReference"
)
-type stringSet map[string]struct{}
-
var (
- recipeFileList = stringSet{
- conanfileFile: struct{}{},
- "conanmanifest.txt": struct{}{},
- "conan_sources.tgz": struct{}{},
- "conan_export.tgz": struct{}{},
- }
- packageFileList = stringSet{
- conaninfoFile: struct{}{},
- "conanmanifest.txt": struct{}{},
- "conan_package.tgz": struct{}{},
- }
+ recipeFileList = container.SetOf(
+ conanfileFile,
+ "conanmanifest.txt",
+ "conan_sources.tgz",
+ "conan_export.tgz",
+ )
+ packageFileList = container.SetOf(
+ conaninfoFile,
+ "conanmanifest.txt",
+ "conan_package.tgz",
+ )
)
func jsonResponse(ctx *context.Context, status int, obj interface{}) {
@@ -268,7 +267,7 @@ func PackageUploadURLs(ctx *context.Context) {
)
}
-func serveUploadURLs(ctx *context.Context, fileFilter stringSet, uploadURL string) {
+func serveUploadURLs(ctx *context.Context, fileFilter container.Set[string], uploadURL string) {
defer ctx.Req.Body.Close()
var files map[string]int64
@@ -279,7 +278,7 @@ func serveUploadURLs(ctx *context.Context, fileFilter stringSet, uploadURL strin
urls := make(map[string]string)
for file := range files {
- if _, ok := fileFilter[file]; ok {
+ if fileFilter.Contains(file) {
urls[file] = fmt.Sprintf("%s/%s", uploadURL, file)
}
}
@@ -301,12 +300,12 @@ func UploadPackageFile(ctx *context.Context) {
uploadFile(ctx, packageFileList, pref.AsKey())
}
-func uploadFile(ctx *context.Context, fileFilter stringSet, fileKey string) {
+func uploadFile(ctx *context.Context, fileFilter container.Set[string], fileKey string) {
rref := ctx.Data[recipeReferenceKey].(*conan_module.RecipeReference)
pref := ctx.Data[packageReferenceKey].(*conan_module.PackageReference)
filename := ctx.Params("filename")
- if _, ok := fileFilter[filename]; !ok {
+ if !fileFilter.Contains(filename) {
apiError(ctx, http.StatusBadRequest, nil)
return
}
@@ -342,8 +341,7 @@ func uploadFile(ctx *context.Context, fileFilter stringSet, fileKey string) {
Name: rref.Name,
Version: rref.Version,
},
- SemverCompatible: true,
- Creator: ctx.Doer,
+ Creator: ctx.Doer,
}
pfci := &packages_service.PackageFileCreationInfo{
PackageFileInfo: packages_service.PackageFileInfo{
@@ -443,11 +441,11 @@ func DownloadPackageFile(ctx *context.Context) {
downloadFile(ctx, packageFileList, pref.AsKey())
}
-func downloadFile(ctx *context.Context, fileFilter stringSet, fileKey string) {
+func downloadFile(ctx *context.Context, fileFilter container.Set[string], fileKey string) {
rref := ctx.Data[recipeReferenceKey].(*conan_module.RecipeReference)
filename := ctx.Params("filename")
- if _, ok := fileFilter[filename]; !ok {
+ if !fileFilter.Contains(filename) {
apiError(ctx, http.StatusBadRequest, nil)
return
}
@@ -475,7 +473,7 @@ func downloadFile(ctx *context.Context, fileFilter stringSet, fileKey string) {
}
defer s.Close()
- ctx.ServeStream(s, pf.Name)
+ ctx.ServeContent(pf.Name, s, pf.CreatedUnix.AsLocalTime())
}
// DeleteRecipeV1 deletes the requested recipe(s)
@@ -723,7 +721,7 @@ func listRevisions(ctx *context.Context, revisions []*conan_model.PropertyValue)
revs := make([]*revisionInfo, 0, len(revisions))
for _, rev := range revisions {
- revs = append(revs, &revisionInfo{Revision: rev.Value, Time: time.Unix(int64(rev.CreatedUnix), 0)})
+ revs = append(revs, &revisionInfo{Revision: rev.Value, Time: rev.CreatedUnix.AsLocalTime()})
}
jsonResponse(ctx, http.StatusOK, &RevisionList{revs})
@@ -743,7 +741,7 @@ func LatestRecipeRevision(ctx *context.Context) {
return
}
- jsonResponse(ctx, http.StatusOK, &revisionInfo{Revision: revision.Value, Time: time.Unix(int64(revision.CreatedUnix), 0)})
+ jsonResponse(ctx, http.StatusOK, &revisionInfo{Revision: revision.Value, Time: revision.CreatedUnix.AsLocalTime()})
}
// LatestPackageRevision gets the latest package revision
@@ -760,7 +758,7 @@ func LatestPackageRevision(ctx *context.Context) {
return
}
- jsonResponse(ctx, http.StatusOK, &revisionInfo{Revision: revision.Value, Time: time.Unix(int64(revision.CreatedUnix), 0)})
+ jsonResponse(ctx, http.StatusOK, &revisionInfo{Revision: revision.Value, Time: revision.CreatedUnix.AsLocalTime()})
}
// ListRecipeRevisionFiles gets a list of all recipe revision files
diff --git a/routers/api/packages/container/blob.go b/routers/api/packages/container/blob.go
index 8f6254f583..8a9cbd4a15 100644
--- a/routers/api/packages/container/blob.go
+++ b/routers/api/packages/container/blob.go
@@ -29,6 +29,7 @@ func saveAsPackageBlob(hsr packages_module.HashedSizeReader, pi *packages_servic
contentStore := packages_module.NewContentStore()
err := db.WithTx(func(ctx context.Context) error {
+ created := true
p := &packages_model.Package{
OwnerID: pi.Owner.ID,
Type: packages_model.TypeContainer,
@@ -37,12 +38,21 @@ func saveAsPackageBlob(hsr packages_module.HashedSizeReader, pi *packages_servic
}
var err error
if p, err = packages_model.TryInsertPackage(ctx, p); err != nil {
- if err != packages_model.ErrDuplicatePackage {
+ if err == packages_model.ErrDuplicatePackage {
+ created = false
+ } else {
log.Error("Error inserting package: %v", err)
return err
}
}
+ if created {
+ if _, err := packages_model.InsertProperty(ctx, packages_model.PropertyTypePackage, p.ID, container_module.PropertyRepository, strings.ToLower(pi.Owner.LowerName+"/"+pi.Name)); err != nil {
+ log.Error("Error setting package property: %v", err)
+ return err
+ }
+ }
+
pv := &packages_model.PackageVersion{
PackageID: p.ID,
CreatorID: pi.Owner.ID,
diff --git a/routers/api/packages/container/container.go b/routers/api/packages/container/container.go
index 2a564b3446..5bc64e1b29 100644
--- a/routers/api/packages/container/container.go
+++ b/routers/api/packages/container/container.go
@@ -112,7 +112,7 @@ func apiErrorDefined(ctx *context.Context, err *namedError) {
// ReqContainerAccess is a middleware which checks the current user valid (real user or ghost for anonymous access)
func ReqContainerAccess(ctx *context.Context) {
if ctx.Doer == nil {
- ctx.Resp.Header().Add("WWW-Authenticate", `Bearer realm="`+setting.AppURL+`v2/token"`)
+ ctx.Resp.Header().Add("WWW-Authenticate", `Bearer realm="`+setting.AppURL+`v2/token",service="container_registry",scope="*"`)
apiErrorDefined(ctx, errUnauthorized)
}
}
@@ -151,6 +151,39 @@ func Authenticate(ctx *context.Context) {
})
}
+// https://docs.docker.com/registry/spec/api/#listing-repositories
+func GetRepositoryList(ctx *context.Context) {
+ n := ctx.FormInt("n")
+ if n <= 0 || n > 100 {
+ n = 100
+ }
+ last := ctx.FormTrim("last")
+
+ repositories, err := container_model.GetRepositories(ctx, ctx.Doer, n, last)
+ if err != nil {
+ apiError(ctx, http.StatusInternalServerError, err)
+ return
+ }
+
+ type RepositoryList struct {
+ Repositories []string `json:"repositories"`
+ }
+
+ if len(repositories) == n {
+ v := url.Values{}
+ if n > 0 {
+ v.Add("n", strconv.Itoa(n))
+ }
+ v.Add("last", repositories[len(repositories)-1])
+
+ ctx.Resp.Header().Set("Link", fmt.Sprintf(`; rel="next"`, v.Encode()))
+ }
+
+ jsonResponse(ctx, http.StatusOK, RepositoryList{
+ Repositories: repositories,
+ })
+}
+
// https://github.com/opencontainers/distribution-spec/blob/main/spec.md#mounting-a-blob-from-another-repository
// https://github.com/opencontainers/distribution-spec/blob/main/spec.md#single-post
// https://github.com/opencontainers/distribution-spec/blob/main/spec.md#pushing-a-blob-in-chunks
@@ -215,6 +248,27 @@ func InitiateUploadBlob(ctx *context.Context) {
})
}
+// https://docs.docker.com/registry/spec/api/#get-blob-upload
+func GetUploadBlob(ctx *context.Context) {
+ uuid := ctx.Params("uuid")
+
+ upload, err := packages_model.GetBlobUploadByID(ctx, uuid)
+ if err != nil {
+ if err == packages_model.ErrPackageBlobUploadNotExist {
+ apiErrorDefined(ctx, errBlobUploadUnknown)
+ } else {
+ apiError(ctx, http.StatusInternalServerError, err)
+ }
+ return
+ }
+
+ setResponseHeaders(ctx.Resp, &containerHeaders{
+ Range: fmt.Sprintf("0-%d", upload.BytesReceived),
+ UploadUUID: upload.ID,
+ Status: http.StatusNoContent,
+ })
+}
+
// https://github.com/opencontainers/distribution-spec/blob/main/spec.md#pushing-a-blob-in-chunks
func UploadBlob(ctx *context.Context) {
image := ctx.Params("image")
@@ -321,6 +375,30 @@ func EndUploadBlob(ctx *context.Context) {
})
}
+// https://docs.docker.com/registry/spec/api/#delete-blob-upload
+func CancelUploadBlob(ctx *context.Context) {
+ uuid := ctx.Params("uuid")
+
+ _, err := packages_model.GetBlobUploadByID(ctx, uuid)
+ if err != nil {
+ if err == packages_model.ErrPackageBlobUploadNotExist {
+ apiErrorDefined(ctx, errBlobUploadUnknown)
+ } else {
+ apiError(ctx, http.StatusInternalServerError, err)
+ }
+ return
+ }
+
+ if err := container_service.RemoveBlobUploadByID(ctx, uuid); err != nil {
+ apiError(ctx, http.StatusInternalServerError, err)
+ return
+ }
+
+ setResponseHeaders(ctx.Resp, &containerHeaders{
+ Status: http.StatusNoContent,
+ })
+}
+
func getBlobFromContext(ctx *context.Context) (*packages_model.PackageFileDescriptor, error) {
digest := ctx.Params("digest")
diff --git a/routers/api/packages/container/manifest.go b/routers/api/packages/container/manifest.go
index d899ac8ee2..8beed3dbb7 100644
--- a/routers/api/packages/container/manifest.go
+++ b/routers/api/packages/container/manifest.go
@@ -267,6 +267,7 @@ func processImageManifestIndex(mci *manifestCreationInfo, buf *packages_module.H
}
func createPackageAndVersion(ctx context.Context, mci *manifestCreationInfo, metadata *container_module.Metadata) (*packages_model.PackageVersion, error) {
+ created := true
p := &packages_model.Package{
OwnerID: mci.Owner.ID,
Type: packages_model.TypeContainer,
@@ -275,12 +276,21 @@ func createPackageAndVersion(ctx context.Context, mci *manifestCreationInfo, met
}
var err error
if p, err = packages_model.TryInsertPackage(ctx, p); err != nil {
- if err != packages_model.ErrDuplicatePackage {
+ if err == packages_model.ErrDuplicatePackage {
+ created = false
+ } else {
log.Error("Error inserting package: %v", err)
return nil, err
}
}
+ if created {
+ if _, err := packages_model.InsertProperty(ctx, packages_model.PropertyTypePackage, p.ID, container_module.PropertyRepository, strings.ToLower(mci.Owner.LowerName+"/"+mci.Image)); err != nil {
+ log.Error("Error setting package property: %v", err)
+ return nil, err
+ }
+ }
+
metadata.IsTagged = mci.IsTagged
metadataJSON, err := json.Marshal(metadata)
@@ -302,6 +312,9 @@ func createPackageAndVersion(ctx context.Context, mci *manifestCreationInfo, met
return nil, err
}
+ // keep download count on overwrite
+ _pv.DownloadCount = pv.DownloadCount
+
if pv, err = packages_model.GetOrInsertVersion(ctx, _pv); err != nil {
log.Error("Error inserting package: %v", err)
return nil, err
diff --git a/routers/api/packages/generic/generic.go b/routers/api/packages/generic/generic.go
index d862f77259..81891bec26 100644
--- a/routers/api/packages/generic/generic.go
+++ b/routers/api/packages/generic/generic.go
@@ -8,6 +8,7 @@ import (
"errors"
"net/http"
"regexp"
+ "strings"
packages_model "code.gitea.io/gitea/models/packages"
"code.gitea.io/gitea/modules/context"
@@ -15,8 +16,6 @@ import (
packages_module "code.gitea.io/gitea/modules/packages"
"code.gitea.io/gitea/routers/api/packages/helper"
packages_service "code.gitea.io/gitea/services/packages"
-
- "github.com/hashicorp/go-version"
)
var (
@@ -32,22 +31,16 @@ func apiError(ctx *context.Context, status int, obj interface{}) {
// DownloadPackageFile serves the specific generic package.
func DownloadPackageFile(ctx *context.Context) {
- packageName, packageVersion, filename, err := sanitizeParameters(ctx)
- if err != nil {
- apiError(ctx, http.StatusBadRequest, err)
- return
- }
-
s, pf, err := packages_service.GetFileStreamByPackageNameAndVersion(
ctx,
&packages_service.PackageInfo{
Owner: ctx.Package.Owner,
PackageType: packages_model.TypeGeneric,
- Name: packageName,
- Version: packageVersion,
+ Name: ctx.Params("packagename"),
+ Version: ctx.Params("packageversion"),
},
&packages_service.PackageFileInfo{
- Filename: filename,
+ Filename: ctx.Params("filename"),
},
)
if err != nil {
@@ -60,15 +53,23 @@ func DownloadPackageFile(ctx *context.Context) {
}
defer s.Close()
- ctx.ServeStream(s, pf.Name)
+ ctx.ServeContent(pf.Name, s, pf.CreatedUnix.AsLocalTime())
}
// UploadPackage uploads the specific generic package.
// Duplicated packages get rejected.
func UploadPackage(ctx *context.Context) {
- packageName, packageVersion, filename, err := sanitizeParameters(ctx)
- if err != nil {
- apiError(ctx, http.StatusBadRequest, err)
+ packageName := ctx.Params("packagename")
+ filename := ctx.Params("filename")
+
+ if !packageNameRegex.MatchString(packageName) || !filenameRegex.MatchString(filename) {
+ apiError(ctx, http.StatusBadRequest, errors.New("Invalid package name or filename"))
+ return
+ }
+
+ packageVersion := ctx.Params("packageversion")
+ if packageVersion != strings.TrimSpace(packageVersion) {
+ apiError(ctx, http.StatusBadRequest, errors.New("Invalid package version"))
return
}
@@ -89,7 +90,7 @@ func UploadPackage(ctx *context.Context) {
}
defer buf.Close()
- _, _, err = packages_service.CreatePackageAndAddFile(
+ _, _, err = packages_service.CreatePackageOrAddFileToExisting(
&packages_service.PackageCreationInfo{
PackageInfo: packages_service.PackageInfo{
Owner: ctx.Package.Owner,
@@ -97,8 +98,7 @@ func UploadPackage(ctx *context.Context) {
Name: packageName,
Version: packageVersion,
},
- SemverCompatible: true,
- Creator: ctx.Doer,
+ Creator: ctx.Doer,
},
&packages_service.PackageFileCreationInfo{
PackageFileInfo: packages_service.PackageFileInfo{
@@ -109,8 +109,8 @@ func UploadPackage(ctx *context.Context) {
},
)
if err != nil {
- if err == packages_model.ErrDuplicatePackageVersion {
- apiError(ctx, http.StatusBadRequest, err)
+ if err == packages_model.ErrDuplicatePackageFile {
+ apiError(ctx, http.StatusConflict, err)
return
}
apiError(ctx, http.StatusInternalServerError, err)
@@ -122,19 +122,13 @@ func UploadPackage(ctx *context.Context) {
// DeletePackage deletes the specific generic package.
func DeletePackage(ctx *context.Context) {
- packageName, packageVersion, _, err := sanitizeParameters(ctx)
- if err != nil {
- apiError(ctx, http.StatusBadRequest, err)
- return
- }
-
- err = packages_service.RemovePackageVersionByNameAndVersion(
+ err := packages_service.RemovePackageVersionByNameAndVersion(
ctx.Doer,
&packages_service.PackageInfo{
Owner: ctx.Package.Owner,
PackageType: packages_model.TypeGeneric,
- Name: packageName,
- Version: packageVersion,
+ Name: ctx.Params("packagename"),
+ Version: ctx.Params("packageversion"),
},
)
if err != nil {
@@ -146,21 +140,50 @@ func DeletePackage(ctx *context.Context) {
return
}
- ctx.Status(http.StatusOK)
+ ctx.Status(http.StatusNoContent)
}
-func sanitizeParameters(ctx *context.Context) (string, string, string, error) {
- packageName := ctx.Params("packagename")
- filename := ctx.Params("filename")
+// DeletePackageFile deletes the specific file of a generic package.
+func DeletePackageFile(ctx *context.Context) {
+ pv, pf, err := func() (*packages_model.PackageVersion, *packages_model.PackageFile, error) {
+ pv, err := packages_model.GetVersionByNameAndVersion(ctx, ctx.Package.Owner.ID, packages_model.TypeGeneric, ctx.Params("packagename"), ctx.Params("packageversion"))
+ if err != nil {
+ return nil, nil, err
+ }
- if !packageNameRegex.MatchString(packageName) || !filenameRegex.MatchString(filename) {
- return "", "", "", errors.New("Invalid package name or filename")
- }
+ pf, err := packages_model.GetFileForVersionByName(ctx, pv.ID, ctx.Params("filename"), packages_model.EmptyFileKey)
+ if err != nil {
+ return nil, nil, err
+ }
- v, err := version.NewSemver(ctx.Params("packageversion"))
+ return pv, pf, nil
+ }()
if err != nil {
- return "", "", "", err
+ if err == packages_model.ErrPackageNotExist || err == packages_model.ErrPackageFileNotExist {
+ apiError(ctx, http.StatusNotFound, err)
+ return
+ }
+ apiError(ctx, http.StatusInternalServerError, err)
+ return
}
- return packageName, v.String(), filename, nil
+ pfs, err := packages_model.GetFilesByVersionID(ctx, pv.ID)
+ if err != nil {
+ apiError(ctx, http.StatusInternalServerError, err)
+ return
+ }
+
+ if len(pfs) == 1 {
+ if err := packages_service.RemovePackageVersion(ctx.Doer, pv); err != nil {
+ apiError(ctx, http.StatusInternalServerError, err)
+ return
+ }
+ } else {
+ if err := packages_service.DeletePackageFile(ctx, pf); err != nil {
+ apiError(ctx, http.StatusInternalServerError, err)
+ return
+ }
+ }
+
+ ctx.Status(http.StatusNoContent)
}
diff --git a/routers/api/packages/helm/helm.go b/routers/api/packages/helm/helm.go
index ae0643a35a..9c85e0874f 100644
--- a/routers/api/packages/helm/helm.go
+++ b/routers/api/packages/helm/helm.go
@@ -19,6 +19,7 @@ import (
packages_module "code.gitea.io/gitea/modules/packages"
helm_module "code.gitea.io/gitea/modules/packages/helm"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/routers/api/packages/helper"
packages_service "code.gitea.io/gitea/services/packages"
@@ -39,8 +40,9 @@ func apiError(ctx *context.Context, status int, obj interface{}) {
// Index generates the Helm charts index
func Index(ctx *context.Context) {
pvs, _, err := packages_model.SearchVersions(ctx, &packages_model.PackageSearchOptions{
- OwnerID: ctx.Package.Owner.ID,
- Type: packages_model.TypeHelm,
+ OwnerID: ctx.Package.Owner.ID,
+ Type: packages_model.TypeHelm,
+ IsInternal: util.OptionalBoolFalse,
})
if err != nil {
apiError(ctx, http.StatusInternalServerError, err)
@@ -108,6 +110,7 @@ func DownloadPackageFile(ctx *context.Context) {
Value: ctx.Params("package"),
},
HasFileWithName: filename,
+ IsInternal: util.OptionalBoolFalse,
})
if err != nil {
apiError(ctx, http.StatusInternalServerError, err)
@@ -135,7 +138,7 @@ func DownloadPackageFile(ctx *context.Context) {
}
defer s.Close()
- ctx.ServeStream(s, pf.Name)
+ ctx.ServeContent(pf.Name, s, pf.CreatedUnix.AsLocalTime())
}
// UploadPackage creates a new package
diff --git a/routers/api/packages/maven/maven.go b/routers/api/packages/maven/maven.go
index bba4babf04..bf00c199f5 100644
--- a/routers/api/packages/maven/maven.go
+++ b/routers/api/packages/maven/maven.go
@@ -177,7 +177,7 @@ func servePackageFile(ctx *context.Context, params parameters) {
}
}
- ctx.ServeStream(s, pf.Name)
+ ctx.ServeContent(pf.Name, s, pf.CreatedUnix.AsLocalTime())
}
// UploadPackageFile adds a file to the package. If the package does not exist, it gets created.
@@ -266,8 +266,9 @@ func UploadPackageFile(ctx *context.Context) {
PackageFileInfo: packages_service.PackageFileInfo{
Filename: params.Filename,
},
- Data: buf,
- IsLead: false,
+ Data: buf,
+ IsLead: false,
+ OverwriteExisting: params.IsMeta,
}
// If it's the package pom file extract the metadata
diff --git a/routers/api/packages/npm/api.go b/routers/api/packages/npm/api.go
index 56c8977043..490387a0e4 100644
--- a/routers/api/packages/npm/api.go
+++ b/routers/api/packages/npm/api.go
@@ -25,7 +25,7 @@ func createPackageMetadataResponse(registryURL string, pds []*packages_model.Pac
for _, pd := range pds {
versions[pd.SemVer.String()] = createPackageMetadataVersion(registryURL, pd)
- for _, pvp := range pd.Properties {
+ for _, pvp := range pd.VersionProperties {
if pvp.Name == npm_module.TagProperty {
distTags[pvp.Value] = pd.Version.Version
}
@@ -55,15 +55,19 @@ func createPackageMetadataVersion(registryURL string, pd *packages_model.Package
metadata := pd.Metadata.(*npm_module.Metadata)
return &npm_module.PackageMetadataVersion{
- ID: fmt.Sprintf("%s@%s", pd.Package.Name, pd.Version.Version),
- Name: pd.Package.Name,
- Version: pd.Version.Version,
- Description: metadata.Description,
- Author: npm_module.User{Name: metadata.Author},
- Homepage: metadata.ProjectURL,
- License: metadata.License,
- Dependencies: metadata.Dependencies,
- Readme: metadata.Readme,
+ ID: fmt.Sprintf("%s@%s", pd.Package.Name, pd.Version.Version),
+ Name: pd.Package.Name,
+ Version: pd.Version.Version,
+ Description: metadata.Description,
+ Author: npm_module.User{Name: metadata.Author},
+ Homepage: metadata.ProjectURL,
+ License: metadata.License,
+ Dependencies: metadata.Dependencies,
+ DevDependencies: metadata.DevelopmentDependencies,
+ PeerDependencies: metadata.PeerDependencies,
+ OptionalDependencies: metadata.OptionalDependencies,
+ Readme: metadata.Readme,
+ Bin: metadata.Bin,
Dist: npm_module.PackageDistribution{
Shasum: pd.Files[0].Blob.HashSHA1,
Integrity: "sha512-" + base64.StdEncoding.EncodeToString(hashBytes),
@@ -71,3 +75,38 @@ func createPackageMetadataVersion(registryURL string, pd *packages_model.Package
},
}
}
+
+func createPackageSearchResponse(pds []*packages_model.PackageDescriptor, total int64) *npm_module.PackageSearch {
+ objects := make([]*npm_module.PackageSearchObject, 0, len(pds))
+ for _, pd := range pds {
+ metadata := pd.Metadata.(*npm_module.Metadata)
+
+ scope := metadata.Scope
+ if scope == "" {
+ scope = "unscoped"
+ }
+
+ objects = append(objects, &npm_module.PackageSearchObject{
+ Package: &npm_module.PackageSearchPackage{
+ Scope: scope,
+ Name: metadata.Name,
+ Version: pd.Version.Version,
+ Date: pd.Version.CreatedUnix.AsLocalTime(),
+ Description: metadata.Description,
+ Author: npm_module.User{Name: metadata.Author},
+ Publisher: npm_module.User{Name: pd.Owner.Name},
+ Maintainers: []npm_module.User{}, // npm cli needs this field
+ Keywords: metadata.Keywords,
+ Links: &npm_module.PackageSearchPackageLinks{
+ Registry: pd.FullWebLink(),
+ Homepage: metadata.ProjectURL,
+ },
+ },
+ })
+ }
+
+ return &npm_module.PackageSearch{
+ Objects: objects,
+ Total: total,
+ }
+}
diff --git a/routers/api/packages/npm/npm.go b/routers/api/packages/npm/npm.go
index d127134d44..2989ce6e7f 100644
--- a/routers/api/packages/npm/npm.go
+++ b/routers/api/packages/npm/npm.go
@@ -18,6 +18,7 @@ import (
packages_module "code.gitea.io/gitea/modules/packages"
npm_module "code.gitea.io/gitea/modules/packages/npm"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/routers/api/packages/helper"
packages_service "code.gitea.io/gitea/services/packages"
@@ -102,7 +103,7 @@ func DownloadPackageFile(ctx *context.Context) {
}
defer s.Close()
- ctx.ServeStream(s, pf.Name)
+ ctx.ServeContent(pf.Name, s, pf.CreatedUnix.AsLocalTime())
}
// UploadPackage creates a new package
@@ -163,6 +164,63 @@ func UploadPackage(ctx *context.Context) {
ctx.Status(http.StatusCreated)
}
+// DeletePreview does nothing
+// The client tells the server what package version it knows about after deleting a version.
+func DeletePreview(ctx *context.Context) {
+ ctx.Status(http.StatusOK)
+}
+
+// DeletePackageVersion deletes the package version
+func DeletePackageVersion(ctx *context.Context) {
+ packageName := packageNameFromParams(ctx)
+ packageVersion := ctx.Params("version")
+
+ err := packages_service.RemovePackageVersionByNameAndVersion(
+ ctx.Doer,
+ &packages_service.PackageInfo{
+ Owner: ctx.Package.Owner,
+ PackageType: packages_model.TypeNpm,
+ Name: packageName,
+ Version: packageVersion,
+ },
+ )
+ if err != nil {
+ if err == packages_model.ErrPackageNotExist {
+ apiError(ctx, http.StatusNotFound, err)
+ return
+ }
+ apiError(ctx, http.StatusInternalServerError, err)
+ return
+ }
+
+ ctx.Status(http.StatusOK)
+}
+
+// DeletePackage deletes the package and all versions
+func DeletePackage(ctx *context.Context) {
+ packageName := packageNameFromParams(ctx)
+
+ pvs, err := packages_model.GetVersionsByPackageName(ctx, ctx.Package.Owner.ID, packages_model.TypeNpm, packageName)
+ if err != nil {
+ apiError(ctx, http.StatusInternalServerError, err)
+ return
+ }
+
+ if len(pvs) == 0 {
+ apiError(ctx, http.StatusNotFound, err)
+ return
+ }
+
+ for _, pv := range pvs {
+ if err := packages_service.RemovePackageVersion(ctx.Doer, pv); err != nil {
+ apiError(ctx, http.StatusInternalServerError, err)
+ return
+ }
+ }
+
+ ctx.Status(http.StatusOK)
+}
+
// ListPackageTags returns all tags for a package
func ListPackageTags(ctx *context.Context) {
packageName := packageNameFromParams(ctx)
@@ -261,6 +319,7 @@ func setPackageTag(tag string, pv *packages_model.PackageVersion, deleteOnly boo
Properties: map[string]string{
npm_module.TagProperty: tag,
},
+ IsInternal: util.OptionalBoolFalse,
})
if err != nil {
return err
@@ -291,3 +350,35 @@ func setPackageTag(tag string, pv *packages_model.PackageVersion, deleteOnly boo
return committer.Commit()
}
+
+func PackageSearch(ctx *context.Context) {
+ pvs, total, err := packages_model.SearchLatestVersions(ctx, &packages_model.PackageSearchOptions{
+ OwnerID: ctx.Package.Owner.ID,
+ Type: packages_model.TypeNpm,
+ Name: packages_model.SearchValue{
+ ExactMatch: false,
+ Value: ctx.FormTrim("text"),
+ },
+ Paginator: db.NewAbsoluteListOptions(
+ ctx.FormInt("from"),
+ ctx.FormInt("size"),
+ ),
+ })
+ if err != nil {
+ apiError(ctx, http.StatusInternalServerError, err)
+ return
+ }
+
+ pds, err := packages_model.GetPackageDescriptors(ctx, pvs)
+ if err != nil {
+ apiError(ctx, http.StatusInternalServerError, err)
+ return
+ }
+
+ resp := createPackageSearchResponse(
+ pds,
+ total,
+ )
+
+ ctx.JSON(http.StatusOK, resp)
+}
diff --git a/routers/api/packages/nuget/api_v2.go b/routers/api/packages/nuget/api_v2.go
new file mode 100644
index 0000000000..60a5d9c0e4
--- /dev/null
+++ b/routers/api/packages/nuget/api_v2.go
@@ -0,0 +1,393 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package nuget
+
+import (
+ "encoding/xml"
+ "strings"
+ "time"
+
+ packages_model "code.gitea.io/gitea/models/packages"
+ nuget_module "code.gitea.io/gitea/modules/packages/nuget"
+)
+
+type AtomTitle struct {
+ Type string `xml:"type,attr"`
+ Text string `xml:",chardata"`
+}
+
+type ServiceCollection struct {
+ Href string `xml:"href,attr"`
+ Title AtomTitle `xml:"atom:title"`
+}
+
+type ServiceWorkspace struct {
+ Title AtomTitle `xml:"atom:title"`
+ Collection ServiceCollection `xml:"collection"`
+}
+
+type ServiceIndexResponseV2 struct {
+ XMLName xml.Name `xml:"service"`
+ Base string `xml:"base,attr"`
+ Xmlns string `xml:"xmlns,attr"`
+ XmlnsAtom string `xml:"xmlns:atom,attr"`
+ Workspace ServiceWorkspace `xml:"workspace"`
+}
+
+type EdmxPropertyRef struct {
+ Name string `xml:"Name,attr"`
+}
+
+type EdmxProperty struct {
+ Name string `xml:"Name,attr"`
+ Type string `xml:"Type,attr"`
+ Nullable bool `xml:"Nullable,attr"`
+}
+
+type EdmxEntityType struct {
+ Name string `xml:"Name,attr"`
+ HasStream bool `xml:"m:HasStream,attr"`
+ Keys []EdmxPropertyRef `xml:"Key>PropertyRef"`
+ Properties []EdmxProperty `xml:"Property"`
+}
+
+type EdmxFunctionParameter struct {
+ Name string `xml:"Name,attr"`
+ Type string `xml:"Type,attr"`
+}
+
+type EdmxFunctionImport struct {
+ Name string `xml:"Name,attr"`
+ ReturnType string `xml:"ReturnType,attr"`
+ EntitySet string `xml:"EntitySet,attr"`
+ Parameter []EdmxFunctionParameter `xml:"Parameter"`
+}
+
+type EdmxEntitySet struct {
+ Name string `xml:"Name,attr"`
+ EntityType string `xml:"EntityType,attr"`
+}
+
+type EdmxEntityContainer struct {
+ Name string `xml:"Name,attr"`
+ IsDefaultEntityContainer bool `xml:"m:IsDefaultEntityContainer,attr"`
+ EntitySet EdmxEntitySet `xml:"EntitySet"`
+ FunctionImports []EdmxFunctionImport `xml:"FunctionImport"`
+}
+
+type EdmxSchema struct {
+ Xmlns string `xml:"xmlns,attr"`
+ Namespace string `xml:"Namespace,attr"`
+ EntityType *EdmxEntityType `xml:"EntityType,omitempty"`
+ EntityContainer *EdmxEntityContainer `xml:"EntityContainer,omitempty"`
+}
+
+type EdmxDataServices struct {
+ XmlnsM string `xml:"xmlns:m,attr"`
+ DataServiceVersion string `xml:"m:DataServiceVersion,attr"`
+ MaxDataServiceVersion string `xml:"m:MaxDataServiceVersion,attr"`
+ Schema []EdmxSchema `xml:"Schema"`
+}
+
+type EdmxMetadata struct {
+ XMLName xml.Name `xml:"edmx:Edmx"`
+ XmlnsEdmx string `xml:"xmlns:edmx,attr"`
+ Version string `xml:"Version,attr"`
+ DataServices EdmxDataServices `xml:"edmx:DataServices"`
+}
+
+var Metadata = &EdmxMetadata{
+ XmlnsEdmx: "http://schemas.microsoft.com/ado/2007/06/edmx",
+ Version: "1.0",
+ DataServices: EdmxDataServices{
+ XmlnsM: "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata",
+ DataServiceVersion: "2.0",
+ MaxDataServiceVersion: "2.0",
+ Schema: []EdmxSchema{
+ {
+ Xmlns: "http://schemas.microsoft.com/ado/2006/04/edm",
+ Namespace: "NuGetGallery.OData",
+ EntityType: &EdmxEntityType{
+ Name: "V2FeedPackage",
+ HasStream: true,
+ Keys: []EdmxPropertyRef{
+ {Name: "Id"},
+ {Name: "Version"},
+ },
+ Properties: []EdmxProperty{
+ {
+ Name: "Id",
+ Type: "Edm.String",
+ },
+ {
+ Name: "Version",
+ Type: "Edm.String",
+ },
+ {
+ Name: "NormalizedVersion",
+ Type: "Edm.String",
+ Nullable: true,
+ },
+ {
+ Name: "Authors",
+ Type: "Edm.String",
+ Nullable: true,
+ },
+ {
+ Name: "Created",
+ Type: "Edm.DateTime",
+ },
+ {
+ Name: "Dependencies",
+ Type: "Edm.String",
+ },
+ {
+ Name: "Description",
+ Type: "Edm.String",
+ },
+ {
+ Name: "DownloadCount",
+ Type: "Edm.Int64",
+ },
+ {
+ Name: "LastUpdated",
+ Type: "Edm.DateTime",
+ },
+ {
+ Name: "Published",
+ Type: "Edm.DateTime",
+ },
+ {
+ Name: "PackageSize",
+ Type: "Edm.Int64",
+ },
+ {
+ Name: "ProjectUrl",
+ Type: "Edm.String",
+ Nullable: true,
+ },
+ {
+ Name: "ReleaseNotes",
+ Type: "Edm.String",
+ Nullable: true,
+ },
+ {
+ Name: "RequireLicenseAcceptance",
+ Type: "Edm.Boolean",
+ Nullable: false,
+ },
+ {
+ Name: "Title",
+ Type: "Edm.String",
+ Nullable: true,
+ },
+ {
+ Name: "VersionDownloadCount",
+ Type: "Edm.Int64",
+ Nullable: false,
+ },
+ },
+ },
+ },
+ {
+ Xmlns: "http://schemas.microsoft.com/ado/2006/04/edm",
+ Namespace: "NuGetGallery",
+ EntityContainer: &EdmxEntityContainer{
+ Name: "V2FeedContext",
+ IsDefaultEntityContainer: true,
+ EntitySet: EdmxEntitySet{
+ Name: "Packages",
+ EntityType: "NuGetGallery.OData.V2FeedPackage",
+ },
+ FunctionImports: []EdmxFunctionImport{
+ {
+ Name: "Search",
+ ReturnType: "Collection(NuGetGallery.OData.V2FeedPackage)",
+ EntitySet: "Packages",
+ Parameter: []EdmxFunctionParameter{
+ {
+ Name: "searchTerm",
+ Type: "Edm.String",
+ },
+ },
+ },
+ {
+ Name: "FindPackagesById",
+ ReturnType: "Collection(NuGetGallery.OData.V2FeedPackage)",
+ EntitySet: "Packages",
+ Parameter: []EdmxFunctionParameter{
+ {
+ Name: "id",
+ Type: "Edm.String",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+}
+
+type FeedEntryCategory struct {
+ Term string `xml:"term,attr"`
+ Scheme string `xml:"scheme,attr"`
+}
+
+type FeedEntryLink struct {
+ Rel string `xml:"rel,attr"`
+ Href string `xml:"href,attr"`
+}
+
+type TypedValue[T any] struct {
+ Type string `xml:"type,attr,omitempty"`
+ Value T `xml:",chardata"`
+}
+
+type FeedEntryProperties struct {
+ Version string `xml:"d:Version"`
+ NormalizedVersion string `xml:"d:NormalizedVersion"`
+ Authors string `xml:"d:Authors"`
+ Dependencies string `xml:"d:Dependencies"`
+ Description string `xml:"d:Description"`
+ VersionDownloadCount TypedValue[int64] `xml:"d:VersionDownloadCount"`
+ DownloadCount TypedValue[int64] `xml:"d:DownloadCount"`
+ PackageSize TypedValue[int64] `xml:"d:PackageSize"`
+ Created TypedValue[time.Time] `xml:"d:Created"`
+ LastUpdated TypedValue[time.Time] `xml:"d:LastUpdated"`
+ Published TypedValue[time.Time] `xml:"d:Published"`
+ ProjectURL string `xml:"d:ProjectUrl,omitempty"`
+ ReleaseNotes string `xml:"d:ReleaseNotes,omitempty"`
+ RequireLicenseAcceptance TypedValue[bool] `xml:"d:RequireLicenseAcceptance"`
+ Title string `xml:"d:Title"`
+}
+
+type FeedEntry struct {
+ XMLName xml.Name `xml:"entry"`
+ Xmlns string `xml:"xmlns,attr,omitempty"`
+ XmlnsD string `xml:"xmlns:d,attr,omitempty"`
+ XmlnsM string `xml:"xmlns:m,attr,omitempty"`
+ Base string `xml:"xml:base,attr,omitempty"`
+ ID string `xml:"id"`
+ Category FeedEntryCategory `xml:"category"`
+ Links []FeedEntryLink `xml:"link"`
+ Title TypedValue[string] `xml:"title"`
+ Updated time.Time `xml:"updated"`
+ Author string `xml:"author>name"`
+ Summary string `xml:"summary"`
+ Properties *FeedEntryProperties `xml:"m:properties"`
+ Content string `xml:",innerxml"`
+}
+
+type FeedResponse struct {
+ XMLName xml.Name `xml:"feed"`
+ Xmlns string `xml:"xmlns,attr,omitempty"`
+ XmlnsD string `xml:"xmlns:d,attr,omitempty"`
+ XmlnsM string `xml:"xmlns:m,attr,omitempty"`
+ Base string `xml:"xml:base,attr,omitempty"`
+ ID string `xml:"id"`
+ Title TypedValue[string] `xml:"title"`
+ Updated time.Time `xml:"updated"`
+ Link FeedEntryLink `xml:"link"`
+ Entries []*FeedEntry `xml:"entry"`
+ Count int64 `xml:"m:count"`
+}
+
+func createFeedResponse(l *linkBuilder, totalEntries int64, pds []*packages_model.PackageDescriptor) *FeedResponse {
+ entries := make([]*FeedEntry, 0, len(pds))
+ for _, pd := range pds {
+ entries = append(entries, createEntry(l, pd, false))
+ }
+
+ return &FeedResponse{
+ Xmlns: "http://www.w3.org/2005/Atom",
+ Base: l.Base,
+ XmlnsD: "http://schemas.microsoft.com/ado/2007/08/dataservices",
+ XmlnsM: "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata",
+ ID: "http://schemas.datacontract.org/2004/07/",
+ Updated: time.Now(),
+ Link: FeedEntryLink{Rel: "self", Href: l.Base},
+ Count: totalEntries,
+ Entries: entries,
+ }
+}
+
+func createEntryResponse(l *linkBuilder, pd *packages_model.PackageDescriptor) *FeedEntry {
+ return createEntry(l, pd, true)
+}
+
+func createEntry(l *linkBuilder, pd *packages_model.PackageDescriptor, withNamespace bool) *FeedEntry {
+ metadata := pd.Metadata.(*nuget_module.Metadata)
+
+ id := l.GetPackageMetadataURL(pd.Package.Name, pd.Version.Version)
+
+ // Workaround to force a self-closing tag to satisfy XmlReader.IsEmptyElement used by the NuGet client.
+ // https://learn.microsoft.com/en-us/dotnet/api/system.xml.xmlreader.isemptyelement
+ content := ``
+
+ createdValue := TypedValue[time.Time]{
+ Type: "Edm.DateTime",
+ Value: pd.Version.CreatedUnix.AsLocalTime(),
+ }
+
+ entry := &FeedEntry{
+ ID: id,
+ Category: FeedEntryCategory{Term: "NuGetGallery.OData.V2FeedPackage", Scheme: "http://schemas.microsoft.com/ado/2007/08/dataservices/scheme"},
+ Links: []FeedEntryLink{
+ {Rel: "self", Href: id},
+ {Rel: "edit", Href: id},
+ },
+ Title: TypedValue[string]{Type: "text", Value: pd.Package.Name},
+ Updated: pd.Version.CreatedUnix.AsLocalTime(),
+ Author: metadata.Authors,
+ Content: content,
+ Properties: &FeedEntryProperties{
+ Version: pd.Version.Version,
+ NormalizedVersion: normalizeVersion(pd.SemVer),
+ Authors: metadata.Authors,
+ Dependencies: buildDependencyString(metadata),
+ Description: metadata.Description,
+ VersionDownloadCount: TypedValue[int64]{Type: "Edm.Int64", Value: pd.Version.DownloadCount},
+ DownloadCount: TypedValue[int64]{Type: "Edm.Int64", Value: pd.Version.DownloadCount},
+ PackageSize: TypedValue[int64]{Type: "Edm.Int64", Value: pd.CalculateBlobSize()},
+ Created: createdValue,
+ LastUpdated: createdValue,
+ Published: createdValue,
+ ProjectURL: metadata.ProjectURL,
+ ReleaseNotes: metadata.ReleaseNotes,
+ RequireLicenseAcceptance: TypedValue[bool]{Type: "Edm.Boolean", Value: metadata.RequireLicenseAcceptance},
+ Title: pd.Package.Name,
+ },
+ }
+
+ if withNamespace {
+ entry.Xmlns = "http://www.w3.org/2005/Atom"
+ entry.Base = l.Base
+ entry.XmlnsD = "http://schemas.microsoft.com/ado/2007/08/dataservices"
+ entry.XmlnsM = "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata"
+ }
+
+ return entry
+}
+
+func buildDependencyString(metadata *nuget_module.Metadata) string {
+ var b strings.Builder
+ first := true
+ for group, deps := range metadata.Dependencies {
+ for _, dep := range deps {
+ if !first {
+ b.WriteByte('|')
+ }
+ first = false
+
+ b.WriteString(dep.ID)
+ b.WriteByte(':')
+ b.WriteString(dep.Version)
+ b.WriteByte(':')
+ b.WriteString(group)
+ }
+ }
+ return b.String()
+}
diff --git a/routers/api/packages/nuget/api.go b/routers/api/packages/nuget/api_v3.go
similarity index 76%
rename from routers/api/packages/nuget/api.go
rename to routers/api/packages/nuget/api_v3.go
index b449cfc5bb..bb3e447bd6 100644
--- a/routers/api/packages/nuget/api.go
+++ b/routers/api/packages/nuget/api_v3.go
@@ -16,36 +16,19 @@ import (
"github.com/hashicorp/go-version"
)
-// ServiceIndexResponse https://docs.microsoft.com/en-us/nuget/api/service-index#resources
-type ServiceIndexResponse struct {
+// https://docs.microsoft.com/en-us/nuget/api/service-index#resources
+type ServiceIndexResponseV3 struct {
Version string `json:"version"`
Resources []ServiceResource `json:"resources"`
}
-// ServiceResource https://docs.microsoft.com/en-us/nuget/api/service-index#resource
+// https://docs.microsoft.com/en-us/nuget/api/service-index#resource
type ServiceResource struct {
ID string `json:"@id"`
Type string `json:"@type"`
}
-func createServiceIndexResponse(root string) *ServiceIndexResponse {
- return &ServiceIndexResponse{
- Version: "3.0.0",
- Resources: []ServiceResource{
- {ID: root + "/query", Type: "SearchQueryService"},
- {ID: root + "/query", Type: "SearchQueryService/3.0.0-beta"},
- {ID: root + "/query", Type: "SearchQueryService/3.0.0-rc"},
- {ID: root + "/registration", Type: "RegistrationsBaseUrl"},
- {ID: root + "/registration", Type: "RegistrationsBaseUrl/3.0.0-beta"},
- {ID: root + "/registration", Type: "RegistrationsBaseUrl/3.0.0-rc"},
- {ID: root + "/package", Type: "PackageBaseAddress/3.0.0"},
- {ID: root, Type: "PackagePublish/2.0.0"},
- {ID: root + "/symbolpackage", Type: "SymbolPackagePublish/4.9.0"},
- },
- }
-}
-
-// RegistrationIndexResponse https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource#response
+// https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource#response
type RegistrationIndexResponse struct {
RegistrationIndexURL string `json:"@id"`
Type []string `json:"@type"`
@@ -53,7 +36,7 @@ type RegistrationIndexResponse struct {
Pages []*RegistrationIndexPage `json:"items"`
}
-// RegistrationIndexPage https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource#registration-page-object
+// https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource#registration-page-object
type RegistrationIndexPage struct {
RegistrationPageURL string `json:"@id"`
Lower string `json:"lower"`
@@ -62,14 +45,14 @@ type RegistrationIndexPage struct {
Items []*RegistrationIndexPageItem `json:"items"`
}
-// RegistrationIndexPageItem https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource#registration-leaf-object-in-a-page
+// https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource#registration-leaf-object-in-a-page
type RegistrationIndexPageItem struct {
RegistrationLeafURL string `json:"@id"`
PackageContentURL string `json:"packageContent"`
CatalogEntry *CatalogEntry `json:"catalogEntry"`
}
-// CatalogEntry https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource#catalog-entry
+// https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource#catalog-entry
type CatalogEntry struct {
CatalogLeafURL string `json:"@id"`
PackageContentURL string `json:"packageContent"`
@@ -83,13 +66,13 @@ type CatalogEntry struct {
DependencyGroups []*PackageDependencyGroup `json:"dependencyGroups"`
}
-// PackageDependencyGroup https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource#package-dependency-group
+// https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource#package-dependency-group
type PackageDependencyGroup struct {
TargetFramework string `json:"targetFramework"`
Dependencies []*PackageDependency `json:"dependencies"`
}
-// PackageDependency https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource#package-dependency
+// https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource#package-dependency
type PackageDependency struct {
ID string `json:"id"`
Range string `json:"range"`
@@ -162,7 +145,7 @@ func createDependencyGroups(pd *packages_model.PackageDescriptor) []*PackageDepe
return dependencyGroups
}
-// RegistrationLeafResponse https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource#registration-leaf
+// https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource#registration-leaf
type RegistrationLeafResponse struct {
RegistrationLeafURL string `json:"@id"`
Type []string `json:"@type"`
@@ -176,14 +159,14 @@ func createRegistrationLeafResponse(l *linkBuilder, pd *packages_model.PackageDe
return &RegistrationLeafResponse{
Type: []string{"Package", "http://schema.nuget.org/catalog#Permalink"},
Listed: true,
- Published: time.Unix(int64(pd.Version.CreatedUnix), 0),
+ Published: pd.Version.CreatedUnix.AsLocalTime(),
RegistrationLeafURL: l.GetRegistrationLeafURL(pd.Package.Name, pd.Version.Version),
PackageContentURL: l.GetPackageDownloadURL(pd.Package.Name, pd.Version.Version),
RegistrationIndexURL: l.GetRegistrationIndexURL(pd.Package.Name),
}
}
-// PackageVersionsResponse https://docs.microsoft.com/en-us/nuget/api/package-base-address-resource#response
+// https://docs.microsoft.com/en-us/nuget/api/package-base-address-resource#response
type PackageVersionsResponse struct {
Versions []string `json:"versions"`
}
@@ -199,13 +182,13 @@ func createPackageVersionsResponse(pds []*packages_model.PackageDescriptor) *Pac
}
}
-// SearchResultResponse https://docs.microsoft.com/en-us/nuget/api/search-query-service-resource#response
+// https://docs.microsoft.com/en-us/nuget/api/search-query-service-resource#response
type SearchResultResponse struct {
TotalHits int64 `json:"totalHits"`
Data []*SearchResult `json:"data"`
}
-// SearchResult https://docs.microsoft.com/en-us/nuget/api/search-query-service-resource#search-result
+// https://docs.microsoft.com/en-us/nuget/api/search-query-service-resource#search-result
type SearchResult struct {
ID string `json:"id"`
Version string `json:"version"`
@@ -216,7 +199,7 @@ type SearchResult struct {
RegistrationIndexURL string `json:"registration"`
}
-// SearchResultVersion https://docs.microsoft.com/en-us/nuget/api/search-query-service-resource#search-result
+// https://docs.microsoft.com/en-us/nuget/api/search-query-service-resource#search-result
type SearchResultVersion struct {
RegistrationLeafURL string `json:"@id"`
Version string `json:"version"`
@@ -224,20 +207,13 @@ type SearchResultVersion struct {
}
func createSearchResultResponse(l *linkBuilder, totalHits int64, pds []*packages_model.PackageDescriptor) *SearchResultResponse {
+ grouped := make(map[string][]*packages_model.PackageDescriptor)
+ for _, pd := range pds {
+ grouped[pd.Package.Name] = append(grouped[pd.Package.Name], pd)
+ }
+
data := make([]*SearchResult, 0, len(pds))
-
- if len(pds) > 0 {
- groupID := pds[0].Package.Name
- group := make([]*packages_model.PackageDescriptor, 0, 10)
-
- for i := 0; i < len(pds); i++ {
- if groupID != pds[i].Package.Name {
- data = append(data, createSearchResult(l, group))
- groupID = pds[i].Package.Name
- group = group[:0]
- }
- group = append(group, pds[i])
- }
+ for _, group := range grouped {
data = append(data, createSearchResult(l, group))
}
diff --git a/routers/api/packages/nuget/auth.go b/routers/api/packages/nuget/auth.go
new file mode 100644
index 0000000000..1dad452648
--- /dev/null
+++ b/routers/api/packages/nuget/auth.go
@@ -0,0 +1,45 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package nuget
+
+import (
+ "net/http"
+
+ auth_model "code.gitea.io/gitea/models/auth"
+ user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/timeutil"
+ "code.gitea.io/gitea/services/auth"
+)
+
+type Auth struct{}
+
+func (a *Auth) Name() string {
+ return "nuget"
+}
+
+// https://docs.microsoft.com/en-us/nuget/api/package-publish-resource#request-parameters
+func (a *Auth) Verify(req *http.Request, w http.ResponseWriter, store auth.DataStore, sess auth.SessionStore) *user_model.User {
+ token, err := auth_model.GetAccessTokenBySHA(req.Header.Get("X-NuGet-ApiKey"))
+ if err != nil {
+ if !(auth_model.IsErrAccessTokenNotExist(err) || auth_model.IsErrAccessTokenEmpty(err)) {
+ log.Error("GetAccessTokenBySHA: %v", err)
+ }
+ return nil
+ }
+
+ u, err := user_model.GetUserByID(token.UID)
+ if err != nil {
+ log.Error("GetUserByID: %v", err)
+ return nil
+ }
+
+ token.UpdatedUnix = timeutil.TimeStampNow()
+ if err := auth_model.UpdateAccessToken(token); err != nil {
+ log.Error("UpdateAccessToken: %v", err)
+ }
+
+ return u
+}
diff --git a/routers/api/packages/nuget/links.go b/routers/api/packages/nuget/links.go
index f782c7f2cb..618b54ae8d 100644
--- a/routers/api/packages/nuget/links.go
+++ b/routers/api/packages/nuget/links.go
@@ -26,3 +26,8 @@ func (l *linkBuilder) GetRegistrationLeafURL(id, version string) string {
func (l *linkBuilder) GetPackageDownloadURL(id, version string) string {
return fmt.Sprintf("%s/package/%s/%s/%s.%s.nupkg", l.Base, id, version, id, version)
}
+
+// GetPackageMetadataURL builds the package metadata url
+func (l *linkBuilder) GetPackageMetadataURL(id, version string) string {
+ return fmt.Sprintf("%s/Packages(Id='%s',Version='%s')", l.Base, id, version)
+}
diff --git a/routers/api/packages/nuget/nuget.go b/routers/api/packages/nuget/nuget.go
index 013c0c1e33..e84aef3160 100644
--- a/routers/api/packages/nuget/nuget.go
+++ b/routers/api/packages/nuget/nuget.go
@@ -5,18 +5,22 @@
package nuget
import (
+ "encoding/xml"
"errors"
"fmt"
"io"
"net/http"
+ "regexp"
"strings"
"code.gitea.io/gitea/models/db"
packages_model "code.gitea.io/gitea/models/packages"
"code.gitea.io/gitea/modules/context"
+ "code.gitea.io/gitea/modules/log"
packages_module "code.gitea.io/gitea/modules/packages"
nuget_module "code.gitea.io/gitea/modules/packages/nuget"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/routers/api/packages/helper"
packages_service "code.gitea.io/gitea/services/packages"
)
@@ -29,19 +33,126 @@ func apiError(ctx *context.Context, status int, obj interface{}) {
})
}
-// ServiceIndex https://docs.microsoft.com/en-us/nuget/api/service-index
-func ServiceIndex(ctx *context.Context) {
- resp := createServiceIndexResponse(setting.AppURL + "api/packages/" + ctx.Package.Owner.Name + "/nuget")
-
- ctx.JSON(http.StatusOK, resp)
+func xmlResponse(ctx *context.Context, status int, obj interface{}) {
+ ctx.Resp.Header().Set("Content-Type", "application/atom+xml; charset=utf-8")
+ ctx.Resp.WriteHeader(status)
+ if _, err := ctx.Resp.Write([]byte(xml.Header)); err != nil {
+ log.Error("Write failed: %v", err)
+ }
+ if err := xml.NewEncoder(ctx.Resp).Encode(obj); err != nil {
+ log.Error("XML encode failed: %v", err)
+ }
}
-// SearchService https://docs.microsoft.com/en-us/nuget/api/search-query-service-resource#search-for-packages
-func SearchService(ctx *context.Context) {
+// https://github.com/NuGet/NuGet.Client/blob/dev/src/NuGet.Core/NuGet.Protocol/LegacyFeed/V2FeedQueryBuilder.cs
+func ServiceIndexV2(ctx *context.Context) {
+ base := setting.AppURL + "api/packages/" + ctx.Package.Owner.Name + "/nuget"
+
+ xmlResponse(ctx, http.StatusOK, &ServiceIndexResponseV2{
+ Base: base,
+ Xmlns: "http://www.w3.org/2007/app",
+ XmlnsAtom: "http://www.w3.org/2005/Atom",
+ Workspace: ServiceWorkspace{
+ Title: AtomTitle{
+ Type: "text",
+ Text: "Default",
+ },
+ Collection: ServiceCollection{
+ Href: "Packages",
+ Title: AtomTitle{
+ Type: "text",
+ Text: "Packages",
+ },
+ },
+ },
+ })
+}
+
+// https://docs.microsoft.com/en-us/nuget/api/service-index
+func ServiceIndexV3(ctx *context.Context) {
+ root := setting.AppURL + "api/packages/" + ctx.Package.Owner.Name + "/nuget"
+
+ ctx.JSON(http.StatusOK, &ServiceIndexResponseV3{
+ Version: "3.0.0",
+ Resources: []ServiceResource{
+ {ID: root + "/query", Type: "SearchQueryService"},
+ {ID: root + "/query", Type: "SearchQueryService/3.0.0-beta"},
+ {ID: root + "/query", Type: "SearchQueryService/3.0.0-rc"},
+ {ID: root + "/registration", Type: "RegistrationsBaseUrl"},
+ {ID: root + "/registration", Type: "RegistrationsBaseUrl/3.0.0-beta"},
+ {ID: root + "/registration", Type: "RegistrationsBaseUrl/3.0.0-rc"},
+ {ID: root + "/package", Type: "PackageBaseAddress/3.0.0"},
+ {ID: root, Type: "PackagePublish/2.0.0"},
+ {ID: root + "/symbolpackage", Type: "SymbolPackagePublish/4.9.0"},
+ },
+ })
+}
+
+// https://github.com/NuGet/NuGet.Client/blob/dev/src/NuGet.Core/NuGet.Protocol/LegacyFeed/LegacyFeedCapabilityResourceV2Feed.cs
+func FeedCapabilityResource(ctx *context.Context) {
+ xmlResponse(ctx, http.StatusOK, Metadata)
+}
+
+var searchTermExtract = regexp.MustCompile(`'([^']+)'`)
+
+// https://github.com/NuGet/NuGet.Client/blob/dev/src/NuGet.Core/NuGet.Protocol/LegacyFeed/V2FeedQueryBuilder.cs
+func SearchServiceV2(ctx *context.Context) {
+ searchTerm := strings.Trim(ctx.FormTrim("searchTerm"), "'")
+ if searchTerm == "" {
+ // $filter contains a query like:
+ // (((Id ne null) and substringof('microsoft',tolower(Id)))
+ // We don't support these queries, just extract the search term.
+ match := searchTermExtract.FindStringSubmatch(ctx.FormTrim("$filter"))
+ if len(match) == 2 {
+ searchTerm = strings.TrimSpace(match[1])
+ }
+ }
+
+ skip, take := ctx.FormInt("skip"), ctx.FormInt("take")
+ if skip == 0 {
+ skip = ctx.FormInt("$skip")
+ }
+ if take == 0 {
+ take = ctx.FormInt("$top")
+ }
+
+ pvs, total, err := packages_model.SearchVersions(ctx, &packages_model.PackageSearchOptions{
+ OwnerID: ctx.Package.Owner.ID,
+ Type: packages_model.TypeNuGet,
+ Name: packages_model.SearchValue{Value: searchTerm},
+ IsInternal: util.OptionalBoolFalse,
+ Paginator: db.NewAbsoluteListOptions(
+ skip,
+ take,
+ ),
+ })
+ if err != nil {
+ apiError(ctx, http.StatusInternalServerError, err)
+ return
+ }
+
+ pds, err := packages_model.GetPackageDescriptors(ctx, pvs)
+ if err != nil {
+ apiError(ctx, http.StatusInternalServerError, err)
+ return
+ }
+
+ resp := createFeedResponse(
+ &linkBuilder{setting.AppURL + "api/packages/" + ctx.Package.Owner.Name + "/nuget"},
+ total,
+ pds,
+ )
+
+ xmlResponse(ctx, http.StatusOK, resp)
+}
+
+// https://docs.microsoft.com/en-us/nuget/api/search-query-service-resource#search-for-packages
+func SearchServiceV3(ctx *context.Context) {
pvs, count, err := packages_model.SearchVersions(ctx, &packages_model.PackageSearchOptions{
- OwnerID: ctx.Package.Owner.ID,
- Type: packages_model.TypeNuGet,
- Name: packages_model.SearchValue{Value: ctx.FormTrim("q")},
+ OwnerID: ctx.Package.Owner.ID,
+ Type: packages_model.TypeNuGet,
+ Name: packages_model.SearchValue{Value: ctx.FormTrim("q")},
+ IsInternal: util.OptionalBoolFalse,
Paginator: db.NewAbsoluteListOptions(
ctx.FormInt("skip"),
ctx.FormInt("take"),
@@ -67,7 +178,7 @@ func SearchService(ctx *context.Context) {
ctx.JSON(http.StatusOK, resp)
}
-// RegistrationIndex https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource#registration-index
+// https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource#registration-index
func RegistrationIndex(ctx *context.Context) {
packageName := ctx.Params("id")
@@ -95,12 +206,41 @@ func RegistrationIndex(ctx *context.Context) {
ctx.JSON(http.StatusOK, resp)
}
-// RegistrationLeaf https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource#registration-leaf
-func RegistrationLeaf(ctx *context.Context) {
+// https://github.com/NuGet/NuGet.Client/blob/dev/src/NuGet.Core/NuGet.Protocol/LegacyFeed/V2FeedQueryBuilder.cs
+func RegistrationLeafV2(ctx *context.Context) {
+ packageName := ctx.Params("id")
+ packageVersion := ctx.Params("version")
+
+ pv, err := packages_model.GetVersionByNameAndVersion(ctx, ctx.Package.Owner.ID, packages_model.TypeNuGet, packageName, packageVersion)
+ if err != nil {
+ if err == packages_model.ErrPackageNotExist {
+ apiError(ctx, http.StatusNotFound, err)
+ return
+ }
+ apiError(ctx, http.StatusInternalServerError, err)
+ return
+ }
+
+ pd, err := packages_model.GetPackageDescriptor(ctx, pv)
+ if err != nil {
+ apiError(ctx, http.StatusInternalServerError, err)
+ return
+ }
+
+ resp := createEntryResponse(
+ &linkBuilder{setting.AppURL + "api/packages/" + ctx.Package.Owner.Name + "/nuget"},
+ pd,
+ )
+
+ xmlResponse(ctx, http.StatusOK, resp)
+}
+
+// https://docs.microsoft.com/en-us/nuget/api/registration-base-url-resource#registration-leaf
+func RegistrationLeafV3(ctx *context.Context) {
packageName := ctx.Params("id")
packageVersion := strings.TrimSuffix(ctx.Params("version"), ".json")
- pv, err := packages_model.GetVersionByNameAndVersion(db.DefaultContext, ctx.Package.Owner.ID, packages_model.TypeNuGet, packageName, packageVersion)
+ pv, err := packages_model.GetVersionByNameAndVersion(ctx, ctx.Package.Owner.ID, packages_model.TypeNuGet, packageName, packageVersion)
if err != nil {
if err == packages_model.ErrPackageNotExist {
apiError(ctx, http.StatusNotFound, err)
@@ -124,8 +264,33 @@ func RegistrationLeaf(ctx *context.Context) {
ctx.JSON(http.StatusOK, resp)
}
-// EnumeratePackageVersions https://docs.microsoft.com/en-us/nuget/api/package-base-address-resource#enumerate-package-versions
-func EnumeratePackageVersions(ctx *context.Context) {
+// https://github.com/NuGet/NuGet.Client/blob/dev/src/NuGet.Core/NuGet.Protocol/LegacyFeed/V2FeedQueryBuilder.cs
+func EnumeratePackageVersionsV2(ctx *context.Context) {
+ packageName := strings.Trim(ctx.FormTrim("id"), "'")
+
+ pvs, err := packages_model.GetVersionsByPackageName(ctx, ctx.Package.Owner.ID, packages_model.TypeNuGet, packageName)
+ if err != nil {
+ apiError(ctx, http.StatusInternalServerError, err)
+ return
+ }
+
+ pds, err := packages_model.GetPackageDescriptors(ctx, pvs)
+ if err != nil {
+ apiError(ctx, http.StatusInternalServerError, err)
+ return
+ }
+
+ resp := createFeedResponse(
+ &linkBuilder{setting.AppURL + "api/packages/" + ctx.Package.Owner.Name + "/nuget"},
+ int64(len(pds)),
+ pds,
+ )
+
+ xmlResponse(ctx, http.StatusOK, resp)
+}
+
+// https://docs.microsoft.com/en-us/nuget/api/package-base-address-resource#enumerate-package-versions
+func EnumeratePackageVersionsV3(ctx *context.Context) {
packageName := ctx.Params("id")
pvs, err := packages_model.GetVersionsByPackageName(ctx, ctx.Package.Owner.ID, packages_model.TypeNuGet, packageName)
@@ -149,7 +314,7 @@ func EnumeratePackageVersions(ctx *context.Context) {
ctx.JSON(http.StatusOK, resp)
}
-// DownloadPackageFile https://docs.microsoft.com/en-us/nuget/api/package-base-address-resource#download-package-content-nupkg
+// https://docs.microsoft.com/en-us/nuget/api/package-base-address-resource#download-package-content-nupkg
func DownloadPackageFile(ctx *context.Context) {
packageName := ctx.Params("id")
packageVersion := ctx.Params("version")
@@ -177,7 +342,7 @@ func DownloadPackageFile(ctx *context.Context) {
}
defer s.Close()
- ctx.ServeStream(s, pf.Name)
+ ctx.ServeContent(pf.Name, s, pf.CreatedUnix.AsLocalTime())
}
// UploadPackage creates a new package with the metadata contained in the uploaded nupgk file
@@ -215,7 +380,7 @@ func UploadPackage(ctx *context.Context) {
)
if err != nil {
if err == packages_model.ErrDuplicatePackageVersion {
- apiError(ctx, http.StatusBadRequest, err)
+ apiError(ctx, http.StatusConflict, err)
return
}
apiError(ctx, http.StatusInternalServerError, err)
@@ -272,7 +437,7 @@ func UploadSymbolPackage(ctx *context.Context) {
case packages_model.ErrPackageNotExist:
apiError(ctx, http.StatusNotFound, err)
case packages_model.ErrDuplicatePackageFile:
- apiError(ctx, http.StatusBadRequest, err)
+ apiError(ctx, http.StatusConflict, err)
default:
apiError(ctx, http.StatusInternalServerError, err)
}
@@ -297,7 +462,7 @@ func UploadSymbolPackage(ctx *context.Context) {
if err != nil {
switch err {
case packages_model.ErrDuplicatePackageFile:
- apiError(ctx, http.StatusBadRequest, err)
+ apiError(ctx, http.StatusConflict, err)
default:
apiError(ctx, http.StatusInternalServerError, err)
}
@@ -348,10 +513,10 @@ func processUploadedFile(ctx *context.Context, expectedType nuget_module.Package
return np, buf, closables
}
-// DownloadSymbolFile https://github.com/dotnet/symstore/blob/main/docs/specs/Simple_Symbol_Query_Protocol.md#request
+// https://github.com/dotnet/symstore/blob/main/docs/specs/Simple_Symbol_Query_Protocol.md#request
func DownloadSymbolFile(ctx *context.Context) {
filename := ctx.Params("filename")
- guid := ctx.Params("guid")
+ guid := ctx.Params("guid")[:32]
filename2 := ctx.Params("filename2")
if filename != filename2 {
@@ -376,7 +541,7 @@ func DownloadSymbolFile(ctx *context.Context) {
return
}
- s, _, err := packages_service.GetPackageFileStream(ctx, pfs[0])
+ s, pf, err := packages_service.GetPackageFileStream(ctx, pfs[0])
if err != nil {
if err == packages_model.ErrPackageNotExist || err == packages_model.ErrPackageFileNotExist {
apiError(ctx, http.StatusNotFound, err)
@@ -387,7 +552,7 @@ func DownloadSymbolFile(ctx *context.Context) {
}
defer s.Close()
- ctx.ServeStream(s, pfs[0].Name)
+ ctx.ServeContent(pf.Name, s, pf.CreatedUnix.AsLocalTime())
}
// DeletePackage hard deletes the package
@@ -412,4 +577,6 @@ func DeletePackage(ctx *context.Context) {
}
apiError(ctx, http.StatusInternalServerError, err)
}
+
+ ctx.Status(http.StatusNoContent)
}
diff --git a/routers/api/packages/pub/pub.go b/routers/api/packages/pub/pub.go
new file mode 100644
index 0000000000..9af0ceeb0e
--- /dev/null
+++ b/routers/api/packages/pub/pub.go
@@ -0,0 +1,275 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package pub
+
+import (
+ "fmt"
+ "io"
+ "net/http"
+ "net/url"
+ "sort"
+ "strings"
+ "time"
+
+ packages_model "code.gitea.io/gitea/models/packages"
+ "code.gitea.io/gitea/modules/context"
+ "code.gitea.io/gitea/modules/json"
+ "code.gitea.io/gitea/modules/log"
+ packages_module "code.gitea.io/gitea/modules/packages"
+ pub_module "code.gitea.io/gitea/modules/packages/pub"
+ "code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/routers/api/packages/helper"
+ packages_service "code.gitea.io/gitea/services/packages"
+)
+
+func jsonResponse(ctx *context.Context, status int, obj interface{}) {
+ resp := ctx.Resp
+ resp.Header().Set("Content-Type", "application/vnd.pub.v2+json")
+ resp.WriteHeader(status)
+ if err := json.NewEncoder(resp).Encode(obj); err != nil {
+ log.Error("JSON encode: %v", err)
+ }
+}
+
+func apiError(ctx *context.Context, status int, obj interface{}) {
+ type Error struct {
+ Code string `json:"code"`
+ Message string `json:"message"`
+ }
+ type ErrorWrapper struct {
+ Error Error `json:"error"`
+ }
+
+ helper.LogAndProcessError(ctx, status, obj, func(message string) {
+ jsonResponse(ctx, status, ErrorWrapper{
+ Error: Error{
+ Code: http.StatusText(status),
+ Message: message,
+ },
+ })
+ })
+}
+
+type packageVersions struct {
+ Name string `json:"name"`
+ Latest *versionMetadata `json:"latest"`
+ Versions []*versionMetadata `json:"versions"`
+}
+
+type versionMetadata struct {
+ Version string `json:"version"`
+ ArchiveURL string `json:"archive_url"`
+ Published time.Time `json:"published"`
+ Pubspec interface{} `json:"pubspec,omitempty"`
+}
+
+func packageDescriptorToMetadata(baseURL string, pd *packages_model.PackageDescriptor) *versionMetadata {
+ return &versionMetadata{
+ Version: pd.Version.Version,
+ ArchiveURL: fmt.Sprintf("%s/files/%s.tar.gz", baseURL, url.PathEscape(pd.Version.Version)),
+ Published: pd.Version.CreatedUnix.AsLocalTime(),
+ Pubspec: pd.Metadata.(*pub_module.Metadata).Pubspec,
+ }
+}
+
+func baseURL(ctx *context.Context) string {
+ return setting.AppURL + "api/packages/" + ctx.Package.Owner.Name + "/pub/api/packages"
+}
+
+// https://github.com/dart-lang/pub/blob/master/doc/repository-spec-v2.md#list-all-versions-of-a-package
+func EnumeratePackageVersions(ctx *context.Context) {
+ packageName := ctx.Params("id")
+
+ pvs, err := packages_model.GetVersionsByPackageName(ctx, ctx.Package.Owner.ID, packages_model.TypePub, packageName)
+ if err != nil {
+ apiError(ctx, http.StatusInternalServerError, err)
+ return
+ }
+ if len(pvs) == 0 {
+ apiError(ctx, http.StatusNotFound, err)
+ return
+ }
+
+ pds, err := packages_model.GetPackageDescriptors(ctx, pvs)
+ if err != nil {
+ apiError(ctx, http.StatusInternalServerError, err)
+ return
+ }
+
+ sort.Slice(pds, func(i, j int) bool {
+ return pds[i].SemVer.LessThan(pds[j].SemVer)
+ })
+
+ baseURL := fmt.Sprintf("%s/%s", baseURL(ctx), url.PathEscape(pds[0].Package.Name))
+
+ versions := make([]*versionMetadata, 0, len(pds))
+ for _, pd := range pds {
+ versions = append(versions, packageDescriptorToMetadata(baseURL, pd))
+ }
+
+ jsonResponse(ctx, http.StatusOK, &packageVersions{
+ Name: pds[0].Package.Name,
+ Latest: packageDescriptorToMetadata(baseURL, pds[0]),
+ Versions: versions,
+ })
+}
+
+// https://github.com/dart-lang/pub/blob/master/doc/repository-spec-v2.md#deprecated-inspect-a-specific-version-of-a-package
+func PackageVersionMetadata(ctx *context.Context) {
+ packageName := ctx.Params("id")
+ packageVersion := ctx.Params("version")
+
+ pv, err := packages_model.GetVersionByNameAndVersion(ctx, ctx.Package.Owner.ID, packages_model.TypePub, packageName, packageVersion)
+ if err != nil {
+ if err == packages_model.ErrPackageNotExist {
+ apiError(ctx, http.StatusNotFound, err)
+ return
+ }
+ apiError(ctx, http.StatusInternalServerError, err)
+ return
+ }
+
+ pd, err := packages_model.GetPackageDescriptor(ctx, pv)
+ if err != nil {
+ apiError(ctx, http.StatusInternalServerError, err)
+ return
+ }
+
+ jsonResponse(ctx, http.StatusOK, packageDescriptorToMetadata(
+ fmt.Sprintf("%s/%s", baseURL(ctx), url.PathEscape(pd.Package.Name)),
+ pd,
+ ))
+}
+
+// https://github.com/dart-lang/pub/blob/master/doc/repository-spec-v2.md#publishing-packages
+func RequestUpload(ctx *context.Context) {
+ type UploadRequest struct {
+ URL string `json:"url"`
+ Fields map[string]string `json:"fields"`
+ }
+
+ jsonResponse(ctx, http.StatusOK, UploadRequest{
+ URL: baseURL(ctx) + "/versions/new/upload",
+ Fields: make(map[string]string),
+ })
+}
+
+// https://github.com/dart-lang/pub/blob/master/doc/repository-spec-v2.md#publishing-packages
+func UploadPackageFile(ctx *context.Context) {
+ file, _, err := ctx.Req.FormFile("file")
+ if err != nil {
+ apiError(ctx, http.StatusBadRequest, err)
+ return
+ }
+ defer file.Close()
+
+ buf, err := packages_module.CreateHashedBufferFromReader(file, 32*1024*1024)
+ if err != nil {
+ apiError(ctx, http.StatusInternalServerError, err)
+ return
+ }
+ defer buf.Close()
+
+ pck, err := pub_module.ParsePackage(buf)
+ if err != nil {
+ apiError(ctx, http.StatusInternalServerError, err)
+ return
+ }
+
+ if _, err := buf.Seek(0, io.SeekStart); err != nil {
+ apiError(ctx, http.StatusInternalServerError, err)
+ return
+ }
+
+ _, _, err = packages_service.CreatePackageAndAddFile(
+ &packages_service.PackageCreationInfo{
+ PackageInfo: packages_service.PackageInfo{
+ Owner: ctx.Package.Owner,
+ PackageType: packages_model.TypePub,
+ Name: pck.Name,
+ Version: pck.Version,
+ },
+ SemverCompatible: true,
+ Creator: ctx.Doer,
+ Metadata: pck.Metadata,
+ },
+ &packages_service.PackageFileCreationInfo{
+ PackageFileInfo: packages_service.PackageFileInfo{
+ Filename: strings.ToLower(pck.Version + ".tar.gz"),
+ },
+ Data: buf,
+ IsLead: true,
+ },
+ )
+ if err != nil {
+ if err == packages_model.ErrDuplicatePackageVersion {
+ apiError(ctx, http.StatusBadRequest, err)
+ return
+ }
+ apiError(ctx, http.StatusInternalServerError, err)
+ return
+ }
+
+ ctx.Resp.Header().Set("Location", fmt.Sprintf("%s/versions/new/finalize/%s/%s", baseURL(ctx), url.PathEscape(pck.Name), url.PathEscape(pck.Version)))
+ ctx.Status(http.StatusNoContent)
+}
+
+// https://github.com/dart-lang/pub/blob/master/doc/repository-spec-v2.md#publishing-packages
+func FinalizePackage(ctx *context.Context) {
+ packageName := ctx.Params("id")
+ packageVersion := ctx.Params("version")
+
+ _, err := packages_model.GetVersionByNameAndVersion(ctx, ctx.Package.Owner.ID, packages_model.TypePub, packageName, packageVersion)
+ if err != nil {
+ if err == packages_model.ErrPackageNotExist {
+ apiError(ctx, http.StatusNotFound, err)
+ return
+ }
+ apiError(ctx, http.StatusInternalServerError, err)
+ return
+ }
+
+ type Success struct {
+ Message string `json:"message"`
+ }
+ type SuccessWrapper struct {
+ Success Success `json:"success"`
+ }
+
+ jsonResponse(ctx, http.StatusOK, SuccessWrapper{Success{}})
+}
+
+// https://github.com/dart-lang/pub/blob/master/doc/repository-spec-v2.md#deprecated-download-a-specific-version-of-a-package
+func DownloadPackageFile(ctx *context.Context) {
+ packageName := ctx.Params("id")
+ packageVersion := strings.TrimSuffix(ctx.Params("version"), ".tar.gz")
+
+ pv, err := packages_model.GetVersionByNameAndVersion(ctx, ctx.Package.Owner.ID, packages_model.TypePub, packageName, packageVersion)
+ if err != nil {
+ if err == packages_model.ErrPackageNotExist {
+ apiError(ctx, http.StatusNotFound, err)
+ return
+ }
+ apiError(ctx, http.StatusInternalServerError, err)
+ return
+ }
+
+ pd, err := packages_model.GetPackageDescriptor(ctx, pv)
+ if err != nil {
+ apiError(ctx, http.StatusInternalServerError, err)
+ return
+ }
+
+ pf := pd.Files[0].File
+
+ s, _, err := packages_service.GetPackageFileStream(ctx, pf)
+ if err != nil {
+ apiError(ctx, http.StatusInternalServerError, err)
+ return
+ }
+ defer s.Close()
+
+ ctx.ServeContent(pf.Name, s, pf.CreatedUnix.AsLocalTime())
+}
diff --git a/routers/api/packages/pypi/pypi.go b/routers/api/packages/pypi/pypi.go
index 9209c4edd5..3a046abe18 100644
--- a/routers/api/packages/pypi/pypi.go
+++ b/routers/api/packages/pypi/pypi.go
@@ -16,7 +16,6 @@ import (
packages_module "code.gitea.io/gitea/modules/packages"
pypi_module "code.gitea.io/gitea/modules/packages/pypi"
"code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/templates"
"code.gitea.io/gitea/modules/validation"
"code.gitea.io/gitea/routers/api/packages/helper"
packages_service "code.gitea.io/gitea/services/packages"
@@ -24,7 +23,7 @@ import (
// https://www.python.org/dev/peps/pep-0503/#normalized-names
var normalizer = strings.NewReplacer(".", "-", "_", "-")
-var nameMatcher = regexp.MustCompile(`\A[a-z0-9\.\-_]+\z`)
+var nameMatcher = regexp.MustCompile(`\A[a-zA-Z0-9\.\-_]+\z`)
// https://www.python.org/dev/peps/pep-0440/#appendix-b-parsing-version-strings-with-regular-expressions
var versionMatcher = regexp.MustCompile(`^([1-9][0-9]*!)?(0|[1-9][0-9]*)(\.(0|[1-9][0-9]*))*((a|b|rc)(0|[1-9][0-9]*))?(\.post(0|[1-9][0-9]*))?(\.dev(0|[1-9][0-9]*))?$`)
@@ -58,7 +57,6 @@ func PackageMetadata(ctx *context.Context) {
ctx.Data["RegistryURL"] = setting.AppURL + "api/packages/" + ctx.Package.Owner.Name + "/pypi"
ctx.Data["PackageDescriptor"] = pds[0]
ctx.Data["PackageDescriptors"] = pds
- ctx.Render = templates.HTMLRenderer()
ctx.HTML(http.StatusOK, "api/packages/pypi/simple")
}
@@ -90,7 +88,7 @@ func DownloadPackageFile(ctx *context.Context) {
}
defer s.Close()
- ctx.ServeStream(s, pf.Name)
+ ctx.ServeContent(pf.Name, s, pf.CreatedUnix.AsLocalTime())
}
// UploadPackageFile adds a file to the package. If the package does not exist, it gets created.
diff --git a/routers/api/packages/rubygems/rubygems.go b/routers/api/packages/rubygems/rubygems.go
index b3815a914e..319c94b91f 100644
--- a/routers/api/packages/rubygems/rubygems.go
+++ b/routers/api/packages/rubygems/rubygems.go
@@ -16,6 +16,7 @@ import (
"code.gitea.io/gitea/modules/context"
packages_module "code.gitea.io/gitea/modules/packages"
rubygems_module "code.gitea.io/gitea/modules/packages/rubygems"
+ "code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/routers/api/packages/helper"
packages_service "code.gitea.io/gitea/services/packages"
)
@@ -40,8 +41,9 @@ func EnumeratePackages(ctx *context.Context) {
// EnumeratePackagesLatest serves the list of the latest version of every package
func EnumeratePackagesLatest(ctx *context.Context) {
pvs, _, err := packages_model.SearchLatestVersions(ctx, &packages_model.PackageSearchOptions{
- OwnerID: ctx.Package.Owner.ID,
- Type: packages_model.TypeRubyGems,
+ OwnerID: ctx.Package.Owner.ID,
+ Type: packages_model.TypeRubyGems,
+ IsInternal: util.OptionalBoolFalse,
})
if err != nil {
apiError(ctx, http.StatusInternalServerError, err)
@@ -186,7 +188,7 @@ func DownloadPackageFile(ctx *context.Context) {
}
defer s.Close()
- ctx.ServeStream(s, pf.Name)
+ ctx.ServeContent(pf.Name, s, pf.CreatedUnix.AsLocalTime())
}
// UploadPackageFile adds a file to the package. If the package does not exist, it gets created.
@@ -289,6 +291,7 @@ func getVersionsByFilename(ctx *context.Context, filename string) ([]*packages_m
OwnerID: ctx.Package.Owner.ID,
Type: packages_model.TypeRubyGems,
HasFileWithName: filename,
+ IsInternal: util.OptionalBoolFalse,
})
return pvs, err
}
diff --git a/routers/api/packages/vagrant/vagrant.go b/routers/api/packages/vagrant/vagrant.go
new file mode 100644
index 0000000000..7750e5dc4b
--- /dev/null
+++ b/routers/api/packages/vagrant/vagrant.go
@@ -0,0 +1,239 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package vagrant
+
+import (
+ "fmt"
+ "io"
+ "net/http"
+ "net/url"
+ "sort"
+ "strings"
+
+ packages_model "code.gitea.io/gitea/models/packages"
+ "code.gitea.io/gitea/modules/context"
+ packages_module "code.gitea.io/gitea/modules/packages"
+ vagrant_module "code.gitea.io/gitea/modules/packages/vagrant"
+ "code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/routers/api/packages/helper"
+ packages_service "code.gitea.io/gitea/services/packages"
+
+ "github.com/hashicorp/go-version"
+)
+
+func apiError(ctx *context.Context, status int, obj interface{}) {
+ helper.LogAndProcessError(ctx, status, obj, func(message string) {
+ ctx.JSON(status, struct {
+ Errors []string `json:"errors"`
+ }{
+ Errors: []string{
+ message,
+ },
+ })
+ })
+}
+
+func CheckAuthenticate(ctx *context.Context) {
+ if ctx.Doer == nil {
+ apiError(ctx, http.StatusUnauthorized, "Invalid access token")
+ return
+ }
+
+ ctx.Status(http.StatusOK)
+}
+
+func CheckBoxAvailable(ctx *context.Context) {
+ pvs, err := packages_model.GetVersionsByPackageName(ctx, ctx.Package.Owner.ID, packages_model.TypeVagrant, ctx.Params("name"))
+ if err != nil {
+ apiError(ctx, http.StatusInternalServerError, err)
+ return
+ }
+ if len(pvs) == 0 {
+ apiError(ctx, http.StatusNotFound, err)
+ return
+ }
+
+ ctx.JSON(http.StatusOK, nil) // needs to be Content-Type: application/json
+}
+
+type packageMetadata struct {
+ Name string `json:"name"`
+ Description string `json:"description,omitempty"`
+ ShortDescription string `json:"short_description,omitempty"`
+ Versions []*versionMetadata `json:"versions"`
+}
+
+type versionMetadata struct {
+ Version string `json:"version"`
+ Status string `json:"status"`
+ DescriptionHTML string `json:"description_html,omitempty"`
+ DescriptionMarkdown string `json:"description_markdown,omitempty"`
+ Providers []*providerData `json:"providers"`
+}
+
+type providerData struct {
+ Name string `json:"name"`
+ URL string `json:"url"`
+ Checksum string `json:"checksum"`
+ ChecksumType string `json:"checksum_type"`
+}
+
+func packageDescriptorToMetadata(baseURL string, pd *packages_model.PackageDescriptor) *versionMetadata {
+ versionURL := baseURL + "/" + url.PathEscape(pd.Version.Version)
+
+ providers := make([]*providerData, 0, len(pd.Files))
+
+ for _, f := range pd.Files {
+ providers = append(providers, &providerData{
+ Name: f.Properties.GetByName(vagrant_module.PropertyProvider),
+ URL: versionURL + "/" + url.PathEscape(f.File.Name),
+ Checksum: f.Blob.HashSHA512,
+ ChecksumType: "sha512",
+ })
+ }
+
+ return &versionMetadata{
+ Status: "active",
+ Version: pd.Version.Version,
+ Providers: providers,
+ }
+}
+
+func EnumeratePackageVersions(ctx *context.Context) {
+ pvs, err := packages_model.GetVersionsByPackageName(ctx, ctx.Package.Owner.ID, packages_model.TypeVagrant, ctx.Params("name"))
+ if err != nil {
+ apiError(ctx, http.StatusInternalServerError, err)
+ return
+ }
+ if len(pvs) == 0 {
+ apiError(ctx, http.StatusNotFound, err)
+ return
+ }
+
+ pds, err := packages_model.GetPackageDescriptors(ctx, pvs)
+ if err != nil {
+ apiError(ctx, http.StatusInternalServerError, err)
+ return
+ }
+
+ sort.Slice(pds, func(i, j int) bool {
+ return pds[i].SemVer.LessThan(pds[j].SemVer)
+ })
+
+ baseURL := fmt.Sprintf("%sapi/packages/%s/vagrant/%s", setting.AppURL, url.PathEscape(ctx.Package.Owner.Name), url.PathEscape(pds[0].Package.Name))
+
+ versions := make([]*versionMetadata, 0, len(pds))
+ for _, pd := range pds {
+ versions = append(versions, packageDescriptorToMetadata(baseURL, pd))
+ }
+
+ ctx.JSON(http.StatusOK, &packageMetadata{
+ Name: pds[0].Package.Name,
+ Description: pds[len(pds)-1].Metadata.(*vagrant_module.Metadata).Description,
+ Versions: versions,
+ })
+}
+
+func UploadPackageFile(ctx *context.Context) {
+ boxName := ctx.Params("name")
+ boxVersion := ctx.Params("version")
+ _, err := version.NewSemver(boxVersion)
+ if err != nil {
+ apiError(ctx, http.StatusBadRequest, err)
+ return
+ }
+ boxProvider := ctx.Params("provider")
+ if !strings.HasSuffix(boxProvider, ".box") {
+ apiError(ctx, http.StatusBadRequest, err)
+ return
+ }
+
+ upload, needsClose, err := ctx.UploadStream()
+ if err != nil {
+ apiError(ctx, http.StatusInternalServerError, err)
+ return
+ }
+ if needsClose {
+ defer upload.Close()
+ }
+
+ buf, err := packages_module.CreateHashedBufferFromReader(upload, 32*1024*1024)
+ if err != nil {
+ apiError(ctx, http.StatusInternalServerError, err)
+ return
+ }
+ defer buf.Close()
+
+ metadata, err := vagrant_module.ParseMetadataFromBox(buf)
+ if err != nil {
+ apiError(ctx, http.StatusInternalServerError, err)
+ return
+ }
+
+ if _, err := buf.Seek(0, io.SeekStart); err != nil {
+ apiError(ctx, http.StatusInternalServerError, err)
+ return
+ }
+
+ _, _, err = packages_service.CreatePackageOrAddFileToExisting(
+ &packages_service.PackageCreationInfo{
+ PackageInfo: packages_service.PackageInfo{
+ Owner: ctx.Package.Owner,
+ PackageType: packages_model.TypeVagrant,
+ Name: boxName,
+ Version: boxVersion,
+ },
+ SemverCompatible: true,
+ Creator: ctx.Doer,
+ Metadata: metadata,
+ },
+ &packages_service.PackageFileCreationInfo{
+ PackageFileInfo: packages_service.PackageFileInfo{
+ Filename: strings.ToLower(boxProvider),
+ },
+ Data: buf,
+ IsLead: true,
+ Properties: map[string]string{
+ vagrant_module.PropertyProvider: strings.TrimSuffix(boxProvider, ".box"),
+ },
+ },
+ )
+ if err != nil {
+ if err == packages_model.ErrDuplicatePackageFile {
+ apiError(ctx, http.StatusConflict, err)
+ return
+ }
+ apiError(ctx, http.StatusInternalServerError, err)
+ return
+ }
+
+ ctx.Status(http.StatusCreated)
+}
+
+func DownloadPackageFile(ctx *context.Context) {
+ s, pf, err := packages_service.GetFileStreamByPackageNameAndVersion(
+ ctx,
+ &packages_service.PackageInfo{
+ Owner: ctx.Package.Owner,
+ PackageType: packages_model.TypeVagrant,
+ Name: ctx.Params("name"),
+ Version: ctx.Params("version"),
+ },
+ &packages_service.PackageFileInfo{
+ Filename: ctx.Params("provider"),
+ },
+ )
+ if err != nil {
+ if err == packages_model.ErrPackageNotExist || err == packages_model.ErrPackageFileNotExist {
+ apiError(ctx, http.StatusNotFound, err)
+ return
+ }
+ apiError(ctx, http.StatusInternalServerError, err)
+ return
+ }
+ defer s.Close()
+
+ ctx.ServeContent(pf.Name, s, pf.CreatedUnix.AsLocalTime())
+}
diff --git a/routers/api/v1/activitypub/person.go b/routers/api/v1/activitypub/person.go
index 7290f1cbd9..542ae7e120 100644
--- a/routers/api/v1/activitypub/person.go
+++ b/routers/api/v1/activitypub/person.go
@@ -98,7 +98,6 @@ func PersonInbox(ctx *context.APIContext) {
// type: string
// required: true
// responses:
- // responses:
// "204":
// "$ref": "#/responses/empty"
diff --git a/routers/api/v1/admin/adopt.go b/routers/api/v1/admin/adopt.go
index 8f11ab67f0..48222f89eb 100644
--- a/routers/api/v1/admin/adopt.go
+++ b/routers/api/v1/admin/adopt.go
@@ -7,10 +7,10 @@ package admin
import (
"net/http"
- "code.gitea.io/gitea/models"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/context"
+ repo_module "code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/routers/api/v1/utils"
repo_service "code.gitea.io/gitea/services/repository"
@@ -110,7 +110,7 @@ func AdoptRepository(ctx *context.APIContext) {
ctx.NotFound()
return
}
- if _, err := repo_service.AdoptRepository(ctx.Doer, ctxUser, models.CreateRepoOptions{
+ if _, err := repo_service.AdoptRepository(ctx.Doer, ctxUser, repo_module.CreateRepoOptions{
Name: repoName,
IsPrivate: true,
}); err != nil {
diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go
index 44e0c290a0..0d11674aa9 100644
--- a/routers/api/v1/api.go
+++ b/routers/api/v1/api.go
@@ -7,64 +7,65 @@
//
// This documentation describes the Gitea API.
//
-// Schemes: http, https
-// BasePath: /api/v1
-// Version: {{AppVer | JSEscape | Safe}}
-// License: MIT http://opensource.org/licenses/MIT
+// Schemes: http, https
+// BasePath: /api/v1
+// Version: {{AppVer | JSEscape | Safe}}
+// License: MIT http://opensource.org/licenses/MIT
//
-// Consumes:
-// - application/json
-// - text/plain
+// Consumes:
+// - application/json
+// - text/plain
//
-// Produces:
-// - application/json
-// - text/html
+// Produces:
+// - application/json
+// - text/html
//
-// Security:
-// - BasicAuth :
-// - Token :
-// - AccessToken :
-// - AuthorizationHeaderToken :
-// - SudoParam :
-// - SudoHeader :
-// - TOTPHeader :
+// Security:
+// - BasicAuth :
+// - Token :
+// - AccessToken :
+// - AuthorizationHeaderToken :
+// - SudoParam :
+// - SudoHeader :
+// - TOTPHeader :
//
-// SecurityDefinitions:
-// BasicAuth:
-// type: basic
-// Token:
-// type: apiKey
-// name: token
-// in: query
-// AccessToken:
-// type: apiKey
-// name: access_token
-// in: query
-// AuthorizationHeaderToken:
-// type: apiKey
-// name: Authorization
-// in: header
-// description: API tokens must be prepended with "token" followed by a space.
-// SudoParam:
-// type: apiKey
-// name: sudo
-// in: query
-// description: Sudo API request as the user provided as the key. Admin privileges are required.
-// SudoHeader:
-// type: apiKey
-// name: Sudo
-// in: header
-// description: Sudo API request as the user provided as the key. Admin privileges are required.
-// TOTPHeader:
-// type: apiKey
-// name: X-GITEA-OTP
-// in: header
-// description: Must be used in combination with BasicAuth if two-factor authentication is enabled.
+// SecurityDefinitions:
+// BasicAuth:
+// type: basic
+// Token:
+// type: apiKey
+// name: token
+// in: query
+// AccessToken:
+// type: apiKey
+// name: access_token
+// in: query
+// AuthorizationHeaderToken:
+// type: apiKey
+// name: Authorization
+// in: header
+// description: API tokens must be prepended with "token" followed by a space.
+// SudoParam:
+// type: apiKey
+// name: sudo
+// in: query
+// description: Sudo API request as the user provided as the key. Admin privileges are required.
+// SudoHeader:
+// type: apiKey
+// name: Sudo
+// in: header
+// description: Sudo API request as the user provided as the key. Admin privileges are required.
+// TOTPHeader:
+// type: apiKey
+// name: X-GITEA-OTP
+// in: header
+// description: Must be used in combination with BasicAuth if two-factor authentication is enabled.
//
// swagger:meta
package v1
import (
+ gocontext "context"
"fmt"
"net/http"
"reflect"
@@ -605,7 +606,7 @@ func buildAuthGroup() *auth.Group {
}
// Routes registers all v1 APIs routes to web application.
-func Routes() *web.Route {
+func Routes(ctx gocontext.Context) *web.Route {
m := web.NewRoute()
m.Use(securityHeaders())
@@ -623,7 +624,7 @@ func Routes() *web.Route {
m.Use(context.APIContexter())
group := buildAuthGroup()
- if err := group.Init(); err != nil {
+ if err := group.Init(ctx); err != nil {
log.Error("Could not initialize '%s' auth method, error: %s", group.Name(), err)
}
@@ -982,6 +983,15 @@ func Routes() *web.Route {
})
}, reqRepoReader(unit.TypeReleases))
m.Post("/mirror-sync", reqToken(), reqRepoWriter(unit.TypeCode), repo.MirrorSync)
+ m.Post("/push_mirrors-sync", reqAdmin(), repo.PushMirrorSync)
+ m.Group("/push_mirrors", func() {
+ m.Combo("").Get(repo.ListPushMirrors).
+ Post(bind(api.CreatePushMirrorOption{}), repo.AddPushMirror)
+ m.Combo("/{name}").
+ Delete(repo.DeletePushMirrorByRemoteName).
+ Get(repo.GetPushMirrorByName)
+ }, reqAdmin())
+
m.Get("/editorconfig/{filename}", context.ReferencesGitRepo(), context.RepoRefForAPI, reqRepoReader(unit.TypeCode), repo.GetEditorconfig)
m.Group("/pulls", func() {
m.Combo("").Get(repo.ListPullRequests).
@@ -992,6 +1002,7 @@ func Routes() *web.Route {
m.Get(".{diffType:diff|patch}", repo.DownloadPullDiffOrPatch)
m.Post("/update", reqToken(), repo.UpdatePullRequest)
m.Get("/commits", repo.GetPullRequestCommits)
+ m.Get("/files", repo.GetPullRequestFiles)
m.Combo("/merge").Get(repo.IsPullRequestMerged).
Post(reqToken(), mustNotBeArchived, bind(forms.MergePullRequestForm{}), repo.MergePullRequest).
Delete(reqToken(), mustNotBeArchived, repo.CancelScheduledAutoMerge)
diff --git a/routers/api/v1/misc/markdown_test.go b/routers/api/v1/misc/markdown_test.go
index 9beb88be16..7809fa5cc7 100644
--- a/routers/api/v1/misc/markdown_test.go
+++ b/routers/api/v1/misc/markdown_test.go
@@ -29,7 +29,7 @@ const (
)
func createContext(req *http.Request) (*context.Context, *httptest.ResponseRecorder) {
- rnd := templates.HTMLRenderer()
+ _, rnd := templates.HTMLRenderer(req.Context())
resp := httptest.NewRecorder()
c := &context.Context{
Req: req,
diff --git a/routers/api/v1/notify/notifications.go b/routers/api/v1/notify/notifications.go
index 44cb4ae769..9948f90a12 100644
--- a/routers/api/v1/notify/notifications.go
+++ b/routers/api/v1/notify/notifications.go
@@ -8,7 +8,7 @@ import (
"net/http"
"strings"
- "code.gitea.io/gitea/models"
+ activities_model "code.gitea.io/gitea/models/activities"
"code.gitea.io/gitea/modules/context"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/routers/api/v1/utils"
@@ -22,16 +22,16 @@ func NewAvailable(ctx *context.APIContext) {
// responses:
// "200":
// "$ref": "#/responses/NotificationCount"
- ctx.JSON(http.StatusOK, api.NotificationCount{New: models.CountUnread(ctx, ctx.Doer.ID)})
+ ctx.JSON(http.StatusOK, api.NotificationCount{New: activities_model.CountUnread(ctx, ctx.Doer.ID)})
}
-func getFindNotificationOptions(ctx *context.APIContext) *models.FindNotificationOptions {
+func getFindNotificationOptions(ctx *context.APIContext) *activities_model.FindNotificationOptions {
before, since, err := context.GetQueryBeforeSince(ctx.Context)
if err != nil {
ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err)
return nil
}
- opts := &models.FindNotificationOptions{
+ opts := &activities_model.FindNotificationOptions{
ListOptions: utils.GetListOptions(ctx),
UserID: ctx.Doer.ID,
UpdatedBeforeUnix: before,
@@ -50,17 +50,17 @@ func getFindNotificationOptions(ctx *context.APIContext) *models.FindNotificatio
return opts
}
-func subjectToSource(value []string) (result []models.NotificationSource) {
+func subjectToSource(value []string) (result []activities_model.NotificationSource) {
for _, v := range value {
switch strings.ToLower(v) {
case "issue":
- result = append(result, models.NotificationSourceIssue)
+ result = append(result, activities_model.NotificationSourceIssue)
case "pull":
- result = append(result, models.NotificationSourcePullRequest)
+ result = append(result, activities_model.NotificationSourcePullRequest)
case "commit":
- result = append(result, models.NotificationSourceCommit)
+ result = append(result, activities_model.NotificationSourceCommit)
case "repository":
- result = append(result, models.NotificationSourceRepository)
+ result = append(result, activities_model.NotificationSourceRepository)
}
}
return result
diff --git a/routers/api/v1/notify/repo.go b/routers/api/v1/notify/repo.go
index 4e9dd806de..f8e1fb0865 100644
--- a/routers/api/v1/notify/repo.go
+++ b/routers/api/v1/notify/repo.go
@@ -9,31 +9,31 @@ import (
"strings"
"time"
- "code.gitea.io/gitea/models"
+ activities_model "code.gitea.io/gitea/models/activities"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/convert"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/structs"
)
-func statusStringToNotificationStatus(status string) models.NotificationStatus {
+func statusStringToNotificationStatus(status string) activities_model.NotificationStatus {
switch strings.ToLower(strings.TrimSpace(status)) {
case "unread":
- return models.NotificationStatusUnread
+ return activities_model.NotificationStatusUnread
case "read":
- return models.NotificationStatusRead
+ return activities_model.NotificationStatusRead
case "pinned":
- return models.NotificationStatusPinned
+ return activities_model.NotificationStatusPinned
default:
return 0
}
}
-func statusStringsToNotificationStatuses(statuses, defaultStatuses []string) []models.NotificationStatus {
+func statusStringsToNotificationStatuses(statuses, defaultStatuses []string) []activities_model.NotificationStatus {
if len(statuses) == 0 {
statuses = defaultStatuses
}
- results := make([]models.NotificationStatus, 0, len(statuses))
+ results := make([]activities_model.NotificationStatus, 0, len(statuses))
for _, status := range statuses {
notificationStatus := statusStringToNotificationStatus(status)
if notificationStatus > 0 {
@@ -109,13 +109,13 @@ func ListRepoNotifications(ctx *context.APIContext) {
}
opts.RepoID = ctx.Repo.Repository.ID
- totalCount, err := models.CountNotifications(opts)
+ totalCount, err := activities_model.CountNotifications(opts)
if err != nil {
ctx.InternalServerError(err)
return
}
- nl, err := models.GetNotifications(ctx, opts)
+ nl, err := activities_model.GetNotifications(ctx, opts)
if err != nil {
ctx.InternalServerError(err)
return
@@ -192,7 +192,7 @@ func ReadRepoNotifications(ctx *context.APIContext) {
}
}
- opts := &models.FindNotificationOptions{
+ opts := &activities_model.FindNotificationOptions{
UserID: ctx.Doer.ID,
RepoID: ctx.Repo.Repository.ID,
UpdatedBeforeUnix: lastRead,
@@ -203,7 +203,7 @@ func ReadRepoNotifications(ctx *context.APIContext) {
opts.Status = statusStringsToNotificationStatuses(statuses, []string{"unread"})
log.Error("%v", opts.Status)
}
- nl, err := models.GetNotifications(ctx, opts)
+ nl, err := activities_model.GetNotifications(ctx, opts)
if err != nil {
ctx.InternalServerError(err)
return
@@ -211,13 +211,13 @@ func ReadRepoNotifications(ctx *context.APIContext) {
targetStatus := statusStringToNotificationStatus(ctx.FormString("to-status"))
if targetStatus == 0 {
- targetStatus = models.NotificationStatusRead
+ targetStatus = activities_model.NotificationStatusRead
}
changed := make([]*structs.NotificationThread, 0, len(nl))
for _, n := range nl {
- notif, err := models.SetNotificationStatus(n.ID, ctx.Doer, targetStatus)
+ notif, err := activities_model.SetNotificationStatus(n.ID, ctx.Doer, targetStatus)
if err != nil {
ctx.InternalServerError(err)
return
diff --git a/routers/api/v1/notify/threads.go b/routers/api/v1/notify/threads.go
index 7d8d34504f..44a1d30a55 100644
--- a/routers/api/v1/notify/threads.go
+++ b/routers/api/v1/notify/threads.go
@@ -8,7 +8,7 @@ import (
"fmt"
"net/http"
- "code.gitea.io/gitea/models"
+ activities_model "code.gitea.io/gitea/models/activities"
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/modules/context"
@@ -86,10 +86,10 @@ func ReadThread(ctx *context.APIContext) {
targetStatus := statusStringToNotificationStatus(ctx.FormString("to-status"))
if targetStatus == 0 {
- targetStatus = models.NotificationStatusRead
+ targetStatus = activities_model.NotificationStatusRead
}
- notif, err := models.SetNotificationStatus(n.ID, ctx.Doer, targetStatus)
+ notif, err := activities_model.SetNotificationStatus(n.ID, ctx.Doer, targetStatus)
if err != nil {
ctx.InternalServerError(err)
return
@@ -101,8 +101,8 @@ func ReadThread(ctx *context.APIContext) {
ctx.JSON(http.StatusResetContent, convert.ToNotificationThread(notif))
}
-func getThread(ctx *context.APIContext) *models.Notification {
- n, err := models.GetNotificationByID(ctx.ParamsInt64(":id"))
+func getThread(ctx *context.APIContext) *activities_model.Notification {
+ n, err := activities_model.GetNotificationByID(ctx.ParamsInt64(":id"))
if err != nil {
if db.IsErrNotExist(err) {
ctx.Error(http.StatusNotFound, "GetNotificationByID", err)
diff --git a/routers/api/v1/notify/user.go b/routers/api/v1/notify/user.go
index b923307783..1b6706e16f 100644
--- a/routers/api/v1/notify/user.go
+++ b/routers/api/v1/notify/user.go
@@ -8,7 +8,7 @@ import (
"net/http"
"time"
- "code.gitea.io/gitea/models"
+ activities_model "code.gitea.io/gitea/models/activities"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/convert"
"code.gitea.io/gitea/modules/structs"
@@ -69,13 +69,13 @@ func ListNotifications(ctx *context.APIContext) {
return
}
- totalCount, err := models.CountNotifications(opts)
+ totalCount, err := activities_model.CountNotifications(opts)
if err != nil {
ctx.InternalServerError(err)
return
}
- nl, err := models.GetNotifications(ctx, opts)
+ nl, err := activities_model.GetNotifications(ctx, opts)
if err != nil {
ctx.InternalServerError(err)
return
@@ -140,7 +140,7 @@ func ReadNotifications(ctx *context.APIContext) {
lastRead = tmpLastRead.Unix()
}
}
- opts := &models.FindNotificationOptions{
+ opts := &activities_model.FindNotificationOptions{
UserID: ctx.Doer.ID,
UpdatedBeforeUnix: lastRead,
}
@@ -148,7 +148,7 @@ func ReadNotifications(ctx *context.APIContext) {
statuses := ctx.FormStrings("status-types")
opts.Status = statusStringsToNotificationStatuses(statuses, []string{"unread"})
}
- nl, err := models.GetNotifications(ctx, opts)
+ nl, err := activities_model.GetNotifications(ctx, opts)
if err != nil {
ctx.InternalServerError(err)
return
@@ -156,13 +156,13 @@ func ReadNotifications(ctx *context.APIContext) {
targetStatus := statusStringToNotificationStatus(ctx.FormString("to-status"))
if targetStatus == 0 {
- targetStatus = models.NotificationStatusRead
+ targetStatus = activities_model.NotificationStatusRead
}
changed := make([]*structs.NotificationThread, 0, len(nl))
for _, n := range nl {
- notif, err := models.SetNotificationStatus(n.ID, ctx.Doer, targetStatus)
+ notif, err := activities_model.SetNotificationStatus(n.ID, ctx.Doer, targetStatus)
if err != nil {
ctx.InternalServerError(err)
return
diff --git a/routers/api/v1/org/team.go b/routers/api/v1/org/team.go
index f8c37303d6..f3e7834a49 100644
--- a/routers/api/v1/org/team.go
+++ b/routers/api/v1/org/team.go
@@ -22,6 +22,7 @@ import (
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers/api/v1/user"
"code.gitea.io/gitea/routers/api/v1/utils"
+ org_service "code.gitea.io/gitea/services/org"
)
// ListTeams list all the teams of an organization
@@ -262,7 +263,7 @@ func EditTeam(ctx *context.APIContext) {
}
if form.CanCreateOrgRepo != nil {
- team.CanCreateOrgRepo = *form.CanCreateOrgRepo
+ team.CanCreateOrgRepo = team.IsOwnerTeam() || *form.CanCreateOrgRepo
}
if len(form.Name) > 0 {
@@ -656,8 +657,8 @@ func AddTeamRepository(ctx *context.APIContext) {
ctx.Error(http.StatusForbidden, "", "Must have admin-level access to the repository")
return
}
- if err := models.AddRepository(ctx.Org.Team, repo); err != nil {
- ctx.Error(http.StatusInternalServerError, "AddRepository", err)
+ if err := org_service.TeamAddRepository(ctx.Org.Team, repo); err != nil {
+ ctx.Error(http.StatusInternalServerError, "TeamAddRepository", err)
return
}
ctx.Status(http.StatusNoContent)
@@ -758,13 +759,17 @@ func SearchTeam(ctx *context.APIContext) {
listOptions := utils.GetListOptions(ctx)
opts := &organization.SearchTeamOptions{
- UserID: ctx.Doer.ID,
Keyword: ctx.FormTrim("q"),
OrgID: ctx.Org.Organization.ID,
IncludeDesc: ctx.FormString("include_desc") == "" || ctx.FormBool("include_desc"),
ListOptions: listOptions,
}
+ // Only admin is allowd to search for all teams
+ if !ctx.Doer.IsAdmin {
+ opts.UserID = ctx.Doer.ID
+ }
+
teams, maxResults, err := organization.SearchTeam(opts)
if err != nil {
log.Error("SearchTeam failed: %v", err)
diff --git a/routers/api/v1/packages/package.go b/routers/api/v1/packages/package.go
index 038924737a..29fa6c85a5 100644
--- a/routers/api/v1/packages/package.go
+++ b/routers/api/v1/packages/package.go
@@ -11,6 +11,7 @@ import (
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/convert"
api "code.gitea.io/gitea/modules/structs"
+ "code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/routers/api/v1/utils"
packages_service "code.gitea.io/gitea/services/packages"
)
@@ -40,7 +41,7 @@ func ListPackages(ctx *context.APIContext) {
// in: query
// description: package type filter
// type: string
- // enum: [composer, conan, container, generic, helm, maven, npm, nuget, pypi, rubygems]
+ // enum: [composer, conan, container, generic, helm, maven, npm, nuget, pub, pypi, rubygems, vagrant]
// - name: q
// in: query
// description: name filter
@@ -55,10 +56,11 @@ func ListPackages(ctx *context.APIContext) {
query := ctx.FormTrim("q")
pvs, count, err := packages.SearchVersions(ctx, &packages.PackageSearchOptions{
- OwnerID: ctx.Package.Owner.ID,
- Type: packages.Type(packageType),
- Name: packages.SearchValue{Value: query},
- Paginator: &listOptions,
+ OwnerID: ctx.Package.Owner.ID,
+ Type: packages.Type(packageType),
+ Name: packages.SearchValue{Value: query},
+ IsInternal: util.OptionalBoolFalse,
+ Paginator: &listOptions,
})
if err != nil {
ctx.Error(http.StatusInternalServerError, "SearchVersions", err)
diff --git a/routers/api/v1/repo/collaborators.go b/routers/api/v1/repo/collaborators.go
index aa425e5828..0e6236216c 100644
--- a/routers/api/v1/repo/collaborators.go
+++ b/routers/api/v1/repo/collaborators.go
@@ -16,6 +16,7 @@ import (
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/convert"
+ repo_module "code.gitea.io/gitea/modules/repository"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers/api/v1/utils"
@@ -174,7 +175,7 @@ func AddCollaborator(ctx *context.APIContext) {
return
}
- if err := models.AddCollaborator(ctx.Repo.Repository, collaborator); err != nil {
+ if err := repo_module.AddCollaborator(ctx.Repo.Repository, collaborator); err != nil {
ctx.Error(http.StatusInternalServerError, "AddCollaborator", err)
return
}
diff --git a/routers/api/v1/repo/commits.go b/routers/api/v1/repo/commits.go
index b196ce9774..4e77c3f2f5 100644
--- a/routers/api/v1/repo/commits.go
+++ b/routers/api/v1/repo/commits.go
@@ -17,7 +17,6 @@ import (
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/validation"
"code.gitea.io/gitea/routers/api/v1/utils"
)
@@ -53,7 +52,7 @@ func GetSingleCommit(ctx *context.APIContext) {
// "$ref": "#/responses/notFound"
sha := ctx.Params(":sha")
- if (validation.GitRefNamePatternInvalid.MatchString(sha) || !validation.CheckGitRefAdditionalRulesValid(sha)) && !git.SHAPattern.MatchString(sha) {
+ if !git.IsValidRefPattern(sha) {
ctx.Error(http.StatusUnprocessableEntity, "no valid ref or sha", fmt.Sprintf("no valid ref or sha: %s", sha))
return
}
@@ -71,7 +70,7 @@ func getCommit(ctx *context.APIContext, identifier string) {
return
}
- json, err := convert.ToCommit(ctx.Repo.Repository, ctx.Repo.GitRepo, commit, nil)
+ json, err := convert.ToCommit(ctx.Repo.Repository, ctx.Repo.GitRepo, commit, nil, true)
if err != nil {
ctx.Error(http.StatusInternalServerError, "toCommit", err)
return
@@ -105,6 +104,10 @@ func GetAllCommits(ctx *context.APIContext) {
// in: query
// description: filepath of a file/dir
// type: string
+ // - name: stat
+ // in: query
+ // description: include diff stats for every commit (disable for speedup, default 'true')
+ // type: boolean
// - name: page
// in: query
// description: page number of results to return (1-based)
@@ -210,9 +213,12 @@ func GetAllCommits(ctx *context.APIContext) {
userCache := make(map[string]*user_model.User)
apiCommits := make([]*api.Commit, len(commits))
+
+ stat := ctx.FormString("stat") == "" || ctx.FormBool("stat")
+
for i, commit := range commits {
// Create json struct
- apiCommits[i], err = convert.ToCommit(ctx.Repo.Repository, ctx.Repo.GitRepo, commit, userCache)
+ apiCommits[i], err = convert.ToCommit(ctx.Repo.Repository, ctx.Repo.GitRepo, commit, userCache, stat)
if err != nil {
ctx.Error(http.StatusInternalServerError, "toCommit", err)
return
diff --git a/routers/api/v1/repo/file.go b/routers/api/v1/repo/file.go
index ba8a938b83..6dead81e6d 100644
--- a/routers/api/v1/repo/file.go
+++ b/routers/api/v1/repo/file.go
@@ -8,6 +8,7 @@ package repo
import (
"bytes"
"encoding/base64"
+ "errors"
"fmt"
"io"
"net/http"
@@ -18,7 +19,6 @@ import (
git_model "code.gitea.io/gitea/models/git"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
- "code.gitea.io/gitea/modules/cache"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/httpcache"
@@ -29,7 +29,7 @@ import (
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers/common"
- "code.gitea.io/gitea/routers/web/repo"
+ archiver_service "code.gitea.io/gitea/services/repository/archiver"
files_service "code.gitea.io/gitea/services/repository/files"
)
@@ -240,12 +240,7 @@ func getBlobForEntry(ctx *context.APIContext) (blob *git.Blob, entry *git.TreeEn
return
}
- var c *git.LastCommitCache
- if setting.CacheService.LastCommit.Enabled && ctx.Repo.CommitsCount >= setting.CacheService.LastCommit.CommitsCount {
- c = git.NewLastCommitCache(ctx.Repo.Repository.FullName(), ctx.Repo.GitRepo, setting.LastCommitCacheTTLSeconds, cache.GetCache())
- }
-
- info, _, err := git.Entries([]*git.TreeEntry{entry}).GetCommitsInfo(ctx, ctx.Repo.Commit, path.Dir("/" + ctx.Repo.TreePath)[1:], c)
+ info, _, err := git.Entries([]*git.TreeEntry{entry}).GetCommitsInfo(ctx, ctx.Repo.Commit, path.Dir("/" + ctx.Repo.TreePath)[1:])
if err != nil {
ctx.Error(http.StatusInternalServerError, "GetCommitsInfo", err)
return
@@ -300,7 +295,53 @@ func GetArchive(ctx *context.APIContext) {
defer gitRepo.Close()
}
- repo.Download(ctx.Context)
+ archiveDownload(ctx)
+}
+
+func archiveDownload(ctx *context.APIContext) {
+ uri := ctx.Params("*")
+ aReq, err := archiver_service.NewRequest(ctx.Repo.Repository.ID, ctx.Repo.GitRepo, uri)
+ if err != nil {
+ if errors.Is(err, archiver_service.ErrUnknownArchiveFormat{}) {
+ ctx.Error(http.StatusBadRequest, "unknown archive format", err)
+ } else if errors.Is(err, archiver_service.RepoRefNotFoundError{}) {
+ ctx.Error(http.StatusNotFound, "unrecognized reference", err)
+ } else {
+ ctx.ServerError("archiver_service.NewRequest", err)
+ }
+ return
+ }
+
+ archiver, err := aReq.Await(ctx)
+ if err != nil {
+ ctx.ServerError("archiver.Await", err)
+ return
+ }
+
+ download(ctx, aReq.GetArchiveName(), archiver)
+}
+
+func download(ctx *context.APIContext, archiveName string, archiver *repo_model.RepoArchiver) {
+ downloadName := ctx.Repo.Repository.Name + "-" + archiveName
+
+ rPath := archiver.RelativePath()
+ if setting.RepoArchive.ServeDirect {
+ // If we have a signed url (S3, object storage), redirect to this directly.
+ u, err := storage.RepoArchives.URL(rPath, downloadName)
+ if u != nil && err == nil {
+ ctx.Redirect(u.String())
+ return
+ }
+ }
+
+ // If we have matched and access to release or issue
+ fr, err := storage.RepoArchives.Open(rPath)
+ if err != nil {
+ ctx.ServerError("Open", err)
+ return
+ }
+ defer fr.Close()
+ ctx.ServeContent(downloadName, fr, archiver.CreatedUnix.AsLocalTime())
}
// GetEditorconfig get editor config of a repository
diff --git a/routers/api/v1/repo/git_ref.go b/routers/api/v1/repo/git_ref.go
index 29b126db9a..47f46df339 100644
--- a/routers/api/v1/repo/git_ref.go
+++ b/routers/api/v1/repo/git_ref.go
@@ -34,7 +34,7 @@ func GetGitAllRefs(ctx *context.APIContext) {
// required: true
// responses:
// "200":
- // "$ref": "#/responses/Reference"
+ // # "$ref": "#/responses/Reference" TODO: swagger doesnt support different output formats by ref
// "$ref": "#/responses/ReferenceList"
// "404":
// "$ref": "#/responses/notFound"
@@ -67,7 +67,7 @@ func GetGitRefs(ctx *context.APIContext) {
// required: true
// responses:
// "200":
- // "$ref": "#/responses/Reference"
+ // # "$ref": "#/responses/Reference" TODO: swagger doesnt support different output formats by ref
// "$ref": "#/responses/ReferenceList"
// "404":
// "$ref": "#/responses/notFound"
diff --git a/routers/api/v1/repo/hook.go b/routers/api/v1/repo/hook.go
index 8a546e581a..86361817cb 100644
--- a/routers/api/v1/repo/hook.go
+++ b/routers/api/v1/repo/hook.go
@@ -13,6 +13,7 @@ import (
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/convert"
"code.gitea.io/gitea/modules/git"
+ "code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers/api/v1/utils"
@@ -140,7 +141,7 @@ func TestHook(ctx *context.APIContext) {
// required: true
// - name: ref
// in: query
- // description: "The name of the commit/branch/tag. Default the repository’s default branch (usually master)"
+ // description: "The name of the commit/branch/tag, indicates which commit will be loaded to the webhook payload."
// type: string
// required: false
// responses:
@@ -153,6 +154,11 @@ func TestHook(ctx *context.APIContext) {
return
}
+ ref := git.BranchPrefix + ctx.Repo.Repository.DefaultBranch
+ if r := ctx.FormTrim("ref"); r != "" {
+ ref = r
+ }
+
hookID := ctx.ParamsInt64(":id")
hook, err := utils.GetRepoHook(ctx, ctx.Repo.Repository.ID, hookID)
if err != nil {
@@ -161,15 +167,18 @@ func TestHook(ctx *context.APIContext) {
commit := convert.ToPayloadCommit(ctx.Repo.Repository, ctx.Repo.Commit)
+ commitID := ctx.Repo.Commit.ID.String()
if err := webhook_service.PrepareWebhook(hook, ctx.Repo.Repository, webhook.HookEventPush, &api.PushPayload{
- Ref: git.BranchPrefix + ctx.Repo.Repository.DefaultBranch,
- Before: ctx.Repo.Commit.ID.String(),
- After: ctx.Repo.Commit.ID.String(),
- Commits: []*api.PayloadCommit{commit},
- HeadCommit: commit,
- Repo: convert.ToRepo(ctx.Repo.Repository, perm.AccessModeNone),
- Pusher: convert.ToUserWithAccessMode(ctx.Doer, perm.AccessModeNone),
- Sender: convert.ToUserWithAccessMode(ctx.Doer, perm.AccessModeNone),
+ Ref: ref,
+ Before: commitID,
+ After: commitID,
+ CompareURL: setting.AppURL + ctx.Repo.Repository.ComposeCompareURL(commitID, commitID),
+ Commits: []*api.PayloadCommit{commit},
+ TotalCommits: 1,
+ HeadCommit: commit,
+ Repo: convert.ToRepo(ctx.Repo.Repository, perm.AccessModeNone),
+ Pusher: convert.ToUserWithAccessMode(ctx.Doer, perm.AccessModeNone),
+ Sender: convert.ToUserWithAccessMode(ctx.Doer, perm.AccessModeNone),
}); err != nil {
ctx.Error(http.StatusInternalServerError, "PrepareWebhook: ", err)
return
diff --git a/routers/api/v1/repo/issue.go b/routers/api/v1/repo/issue.go
index ddad18ef62..08e3e03741 100644
--- a/routers/api/v1/repo/issue.go
+++ b/routers/api/v1/repo/issue.go
@@ -282,7 +282,7 @@ func SearchIssues(ctx *context.APIContext) {
}
}
- ctx.SetLinkHeader(int(filteredCount), setting.UI.IssuePagingNum)
+ ctx.SetLinkHeader(int(filteredCount), limit)
ctx.SetTotalCountHeader(filteredCount)
ctx.JSON(http.StatusOK, convert.ToAPIIssueList(issues))
}
diff --git a/routers/api/v1/repo/issue_tracked_time.go b/routers/api/v1/repo/issue_tracked_time.go
index b9a6c5af64..1e26403ec8 100644
--- a/routers/api/v1/repo/issue_tracked_time.go
+++ b/routers/api/v1/repo/issue_tracked_time.go
@@ -566,6 +566,8 @@ func ListMyTrackedTimes(ctx *context.APIContext) {
// swagger:operation GET /user/times user userCurrentTrackedTimes
// ---
// summary: List the current user's tracked times
+ // produces:
+ // - application/json
// parameters:
// - name: page
// in: query
@@ -575,9 +577,6 @@ func ListMyTrackedTimes(ctx *context.APIContext) {
// in: query
// description: page size of results
// type: integer
- // produces:
- // - application/json
- // parameters:
// - name: since
// in: query
// description: Only show times updated after the given time. This is a timestamp in RFC 3339 format
diff --git a/routers/api/v1/repo/migrate.go b/routers/api/v1/repo/migrate.go
index f868c53951..ed371d787c 100644
--- a/routers/api/v1/repo/migrate.go
+++ b/routers/api/v1/repo/migrate.go
@@ -170,7 +170,7 @@ func Migrate(ctx *context.APIContext) {
opts.Releases = false
}
- repo, err := repo_module.CreateRepository(ctx.Doer, repoOwner, models.CreateRepoOptions{
+ repo, err := repo_module.CreateRepository(ctx.Doer, repoOwner, repo_module.CreateRepoOptions{
Name: opts.RepoName,
Description: opts.Description,
OriginalURL: form.CloneAddr,
diff --git a/routers/api/v1/repo/mirror.go b/routers/api/v1/repo/mirror.go
index 3d29383550..91e5e0c031 100644
--- a/routers/api/v1/repo/mirror.go
+++ b/routers/api/v1/repo/mirror.go
@@ -6,13 +6,25 @@ package repo
import (
"errors"
+ "fmt"
"net/http"
+ "time"
+ "code.gitea.io/gitea/models"
+ "code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
"code.gitea.io/gitea/modules/context"
+ "code.gitea.io/gitea/modules/convert"
mirror_module "code.gitea.io/gitea/modules/mirror"
"code.gitea.io/gitea/modules/setting"
+ api "code.gitea.io/gitea/modules/structs"
+ "code.gitea.io/gitea/modules/util"
+ "code.gitea.io/gitea/modules/web"
+ "code.gitea.io/gitea/routers/api/v1/utils"
+ "code.gitea.io/gitea/services/forms"
+ "code.gitea.io/gitea/services/migrations"
+ mirror_service "code.gitea.io/gitea/services/mirror"
)
// MirrorSync adds a mirrored repository to the sync queue
@@ -63,3 +75,317 @@ func MirrorSync(ctx *context.APIContext) {
ctx.Status(http.StatusOK)
}
+
+// PushMirrorSync adds all push mirrored repositories to the sync queue
+func PushMirrorSync(ctx *context.APIContext) {
+ // swagger:operation POST /repos/{owner}/{repo}/push_mirrors-sync repository repoPushMirrorSync
+ // ---
+ // summary: Sync all push mirrored repository
+ // produces:
+ // - application/json
+ // parameters:
+ // - name: owner
+ // in: path
+ // description: owner of the repo to sync
+ // type: string
+ // required: true
+ // - name: repo
+ // in: path
+ // description: name of the repo to sync
+ // type: string
+ // required: true
+ // responses:
+ // "200":
+ // "$ref": "#/responses/empty"
+ // "400":
+ // "$ref": "#/responses/error"
+ // "403":
+ // "$ref": "#/responses/forbidden"
+
+ if !setting.Mirror.Enabled {
+ ctx.Error(http.StatusBadRequest, "PushMirrorSync", "Mirror feature is disabled")
+ return
+ }
+ // Get All push mirrors of a specific repo
+ pushMirrors, _, err := repo_model.GetPushMirrorsByRepoID(ctx, ctx.Repo.Repository.ID, db.ListOptions{})
+ if err != nil {
+ ctx.Error(http.StatusNotFound, "PushMirrorSync", err)
+ return
+ }
+ for _, mirror := range pushMirrors {
+ ok := mirror_service.SyncPushMirror(ctx, mirror.ID)
+ if !ok {
+ ctx.Error(http.StatusInternalServerError, "PushMirrorSync", "error occurred when syncing push mirror "+mirror.RemoteName)
+ return
+ }
+ }
+
+ ctx.Status(http.StatusOK)
+}
+
+// ListPushMirrors get list of push mirrors of a repository
+func ListPushMirrors(ctx *context.APIContext) {
+ // swagger:operation GET /repos/{owner}/{repo}/push_mirrors repository repoListPushMirrors
+ // ---
+ // summary: Get all push mirrors of the repository
+ // produces:
+ // - application/json
+ // parameters:
+ // - name: owner
+ // in: path
+ // description: owner of the repo
+ // type: string
+ // required: true
+ // - name: repo
+ // in: path
+ // description: name of the repo
+ // type: string
+ // required: true
+ // - name: page
+ // in: query
+ // description: page number of results to return (1-based)
+ // type: integer
+ // - name: limit
+ // in: query
+ // description: page size of results
+ // type: integer
+ // responses:
+ // "200":
+ // "$ref": "#/responses/PushMirrorList"
+ // "400":
+ // "$ref": "#/responses/error"
+ // "403":
+ // "$ref": "#/responses/forbidden"
+
+ if !setting.Mirror.Enabled {
+ ctx.Error(http.StatusBadRequest, "GetPushMirrorsByRepoID", "Mirror feature is disabled")
+ return
+ }
+
+ repo := ctx.Repo.Repository
+ // Get all push mirrors for the specified repository.
+ pushMirrors, count, err := repo_model.GetPushMirrorsByRepoID(ctx, repo.ID, utils.GetListOptions(ctx))
+ if err != nil {
+ ctx.Error(http.StatusNotFound, "GetPushMirrorsByRepoID", err)
+ return
+ }
+
+ responsePushMirrors := make([]*api.PushMirror, 0, len(pushMirrors))
+ for _, mirror := range pushMirrors {
+ m, err := convert.ToPushMirror(mirror)
+ if err == nil {
+ responsePushMirrors = append(responsePushMirrors, m)
+ }
+
+ }
+ ctx.SetLinkHeader(len(responsePushMirrors), utils.GetListOptions(ctx).PageSize)
+ ctx.SetTotalCountHeader(count)
+ ctx.JSON(http.StatusOK, responsePushMirrors)
+}
+
+// GetPushMirrorByName get push mirror of a repository by name
+func GetPushMirrorByName(ctx *context.APIContext) {
+ // swagger:operation GET /repos/{owner}/{repo}/push_mirrors/{name} repository repoGetPushMirrorByRemoteName
+ // ---
+ // summary: Get push mirror of the repository by remoteName
+ // produces:
+ // - application/json
+ // parameters:
+ // - name: owner
+ // in: path
+ // description: owner of the repo
+ // type: string
+ // required: true
+ // - name: repo
+ // in: path
+ // description: name of the repo
+ // type: string
+ // required: true
+ // - name: name
+ // in: path
+ // description: remote name of push mirror
+ // type: string
+ // required: true
+ // responses:
+ // "200":
+ // "$ref": "#/responses/PushMirror"
+ // "400":
+ // "$ref": "#/responses/error"
+ // "403":
+ // "$ref": "#/responses/forbidden"
+
+ if !setting.Mirror.Enabled {
+ ctx.Error(http.StatusBadRequest, "GetPushMirrorByRemoteName", "Mirror feature is disabled")
+ return
+ }
+
+ mirrorName := ctx.Params(":name")
+ // Get push mirror of a specific repo by remoteName
+ pushMirror, err := repo_model.GetPushMirror(ctx, repo_model.PushMirrorOptions{RepoID: ctx.Repo.Repository.ID, RemoteName: mirrorName})
+ if err != nil {
+ ctx.Error(http.StatusNotFound, "GetPushMirrors", err)
+ return
+ }
+ m, err := convert.ToPushMirror(pushMirror)
+ if err != nil {
+ ctx.ServerError("GetPushMirrorByRemoteName", err)
+ return
+ }
+ ctx.JSON(http.StatusOK, m)
+}
+
+// AddPushMirror adds a push mirror to a repository
+func AddPushMirror(ctx *context.APIContext) {
+ // swagger:operation POST /repos/{owner}/{repo}/push_mirrors repository repoAddPushMirror
+ // ---
+ // summary: add a push mirror to the repository
+ // consumes:
+ // - application/json
+ // produces:
+ // - application/json
+ // parameters:
+ // - name: owner
+ // in: path
+ // description: owner of the repo
+ // type: string
+ // required: true
+ // - name: repo
+ // in: path
+ // description: name of the repo
+ // type: string
+ // required: true
+ // - name: body
+ // in: body
+ // schema:
+ // "$ref": "#/definitions/CreatePushMirrorOption"
+ // responses:
+ // "201":
+ // "$ref": "#/responses/PushMirror"
+ // "403":
+ // "$ref": "#/responses/forbidden"
+ // "400":
+ // "$ref": "#/responses/error"
+
+ if !setting.Mirror.Enabled {
+ ctx.Error(http.StatusBadRequest, "AddPushMirror", "Mirror feature is disabled")
+ return
+ }
+
+ pushMirror := web.GetForm(ctx).(*api.CreatePushMirrorOption)
+ CreatePushMirror(ctx, pushMirror)
+}
+
+// DeletePushMirrorByRemoteName deletes a push mirror from a repository by remoteName
+func DeletePushMirrorByRemoteName(ctx *context.APIContext) {
+ // swagger:operation DELETE /repos/{owner}/{repo}/push_mirrors/{name} repository repoDeletePushMirror
+ // ---
+ // summary: deletes a push mirror from a repository by remoteName
+ // produces:
+ // - application/json
+ // parameters:
+ // - name: owner
+ // in: path
+ // description: owner of the repo
+ // type: string
+ // required: true
+ // - name: repo
+ // in: path
+ // description: name of the repo
+ // type: string
+ // required: true
+ // - name: name
+ // in: path
+ // description: remote name of the pushMirror
+ // type: string
+ // required: true
+ // responses:
+ // "204":
+ // "$ref": "#/responses/empty"
+ // "404":
+ // "$ref": "#/responses/notFound"
+ // "400":
+ // "$ref": "#/responses/error"
+
+ if !setting.Mirror.Enabled {
+ ctx.Error(http.StatusBadRequest, "DeletePushMirrorByName", "Mirror feature is disabled")
+ return
+ }
+
+ remoteName := ctx.Params(":name")
+ // Delete push mirror on repo by name.
+ err := repo_model.DeletePushMirrors(ctx, repo_model.PushMirrorOptions{RepoID: ctx.Repo.Repository.ID, RemoteName: remoteName})
+ if err != nil {
+ ctx.Error(http.StatusNotFound, "DeletePushMirrors", err)
+ return
+ }
+ ctx.Status(http.StatusNoContent)
+}
+
+func CreatePushMirror(ctx *context.APIContext, mirrorOption *api.CreatePushMirrorOption) {
+ repo := ctx.Repo.Repository
+
+ interval, err := time.ParseDuration(mirrorOption.Interval)
+ if err != nil || (interval != 0 && interval < setting.Mirror.MinInterval) {
+ ctx.Error(http.StatusBadRequest, "CreatePushMirror", err)
+ return
+ }
+
+ address, err := forms.ParseRemoteAddr(mirrorOption.RemoteAddress, mirrorOption.RemoteUsername, mirrorOption.RemotePassword)
+ if err == nil {
+ err = migrations.IsMigrateURLAllowed(address, ctx.ContextUser)
+ }
+ if err != nil {
+ HandleRemoteAddressError(ctx, err)
+ return
+ }
+
+ remoteSuffix, err := util.CryptoRandomString(10)
+ if err != nil {
+ ctx.ServerError("CryptoRandomString", err)
+ return
+ }
+
+ pushMirror := &repo_model.PushMirror{
+ RepoID: repo.ID,
+ Repo: repo,
+ RemoteName: fmt.Sprintf("remote_mirror_%s", remoteSuffix),
+ Interval: interval,
+ }
+
+ if err = repo_model.InsertPushMirror(ctx, pushMirror); err != nil {
+ ctx.ServerError("InsertPushMirror", err)
+ return
+ }
+
+ // if the registration of the push mirrorOption fails remove it from the database
+ if err = mirror_service.AddPushMirrorRemote(ctx, pushMirror, address); err != nil {
+ if err := repo_model.DeletePushMirrors(ctx, repo_model.PushMirrorOptions{ID: pushMirror.ID, RepoID: pushMirror.RepoID}); err != nil {
+ ctx.ServerError("DeletePushMirrors", err)
+ }
+ ctx.ServerError("AddPushMirrorRemote", err)
+ return
+ }
+ m, err := convert.ToPushMirror(pushMirror)
+ if err != nil {
+ ctx.ServerError("ToPushMirror", err)
+ return
+ }
+ ctx.JSON(http.StatusOK, m)
+}
+
+func HandleRemoteAddressError(ctx *context.APIContext, err error) {
+ if models.IsErrInvalidCloneAddr(err) {
+ addrErr := err.(*models.ErrInvalidCloneAddr)
+ switch {
+ case addrErr.IsProtocolInvalid:
+ ctx.Error(http.StatusBadRequest, "CreatePushMirror", "Invalid mirror protocol")
+ case addrErr.IsURLError:
+ ctx.Error(http.StatusBadRequest, "CreatePushMirror", "Invalid Url ")
+ case addrErr.IsPermissionDenied:
+ ctx.Error(http.StatusUnauthorized, "CreatePushMirror", "Permission denied")
+ default:
+ ctx.Error(http.StatusBadRequest, "CreatePushMirror", "Unknown error")
+ }
+ return
+ }
+}
diff --git a/routers/api/v1/repo/notes.go b/routers/api/v1/repo/notes.go
index bd8e27e40b..ee3133adec 100644
--- a/routers/api/v1/repo/notes.go
+++ b/routers/api/v1/repo/notes.go
@@ -12,7 +12,6 @@ import (
"code.gitea.io/gitea/modules/convert"
"code.gitea.io/gitea/modules/git"
api "code.gitea.io/gitea/modules/structs"
- "code.gitea.io/gitea/modules/validation"
)
// GetNote Get a note corresponding to a single commit from a repository
@@ -47,7 +46,7 @@ func GetNote(ctx *context.APIContext) {
// "$ref": "#/responses/notFound"
sha := ctx.Params(":sha")
- if (validation.GitRefNamePatternInvalid.MatchString(sha) || !validation.CheckGitRefAdditionalRulesValid(sha)) && !git.SHAPattern.MatchString(sha) {
+ if !git.IsValidRefPattern(sha) {
ctx.Error(http.StatusUnprocessableEntity, "no valid ref or sha", fmt.Sprintf("no valid ref or sha: %s", sha))
return
}
@@ -70,7 +69,7 @@ func getNote(ctx *context.APIContext, identifier string) {
return
}
- cmt, err := convert.ToCommit(ctx.Repo.Repository, ctx.Repo.GitRepo, note.Commit, nil)
+ cmt, err := convert.ToCommit(ctx.Repo.Repository, ctx.Repo.GitRepo, note.Commit, nil, true)
if err != nil {
ctx.Error(http.StatusInternalServerError, "ToCommit", err)
return
diff --git a/routers/api/v1/repo/pull.go b/routers/api/v1/repo/pull.go
index 50d2c9484f..f6507dceba 100644
--- a/routers/api/v1/repo/pull.go
+++ b/routers/api/v1/repo/pull.go
@@ -14,6 +14,7 @@ import (
"time"
"code.gitea.io/gitea/models"
+ activities_model "code.gitea.io/gitea/models/activities"
issues_model "code.gitea.io/gitea/models/issues"
access_model "code.gitea.io/gitea/models/perm/access"
pull_model "code.gitea.io/gitea/models/pull"
@@ -25,6 +26,7 @@ import (
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/notification"
+ "code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/web"
@@ -32,6 +34,7 @@ import (
asymkey_service "code.gitea.io/gitea/services/asymkey"
"code.gitea.io/gitea/services/automerge"
"code.gitea.io/gitea/services/forms"
+ "code.gitea.io/gitea/services/gitdiff"
issue_service "code.gitea.io/gitea/services/issue"
pull_service "code.gitea.io/gitea/services/pull"
repo_service "code.gitea.io/gitea/services/repository"
@@ -753,7 +756,7 @@ func MergePullRequest(ctx *context.APIContext) {
if ctx.IsSigned {
// Update issue-user.
- if err = models.SetIssueReadBy(ctx, pr.Issue.ID, ctx.Doer.ID); err != nil {
+ if err = activities_model.SetIssueReadBy(ctx, pr.Issue.ID, ctx.Doer.ID); err != nil {
ctx.Error(http.StatusInternalServerError, "ReadBy", err)
return
}
@@ -1303,7 +1306,7 @@ func GetPullRequestCommits(ctx *context.APIContext) {
apiCommits := make([]*api.Commit, 0, end-start)
for i := start; i < end; i++ {
- apiCommit, err := convert.ToCommit(ctx.Repo.Repository, baseGitRepo, commits[i], userCache)
+ apiCommit, err := convert.ToCommit(ctx.Repo.Repository, baseGitRepo, commits[i], userCache, true)
if err != nil {
ctx.ServerError("toCommit", err)
return
@@ -1322,3 +1325,137 @@ func GetPullRequestCommits(ctx *context.APIContext) {
ctx.JSON(http.StatusOK, &apiCommits)
}
+
+// GetPullRequestFiles gets all changed files associated with a given PR
+func GetPullRequestFiles(ctx *context.APIContext) {
+ // swagger:operation GET /repos/{owner}/{repo}/pulls/{index}/files repository repoGetPullRequestFiles
+ // ---
+ // summary: Get changed files for a pull request
+ // produces:
+ // - application/json
+ // parameters:
+ // - name: owner
+ // in: path
+ // description: owner of the repo
+ // type: string
+ // required: true
+ // - name: repo
+ // in: path
+ // description: name of the repo
+ // type: string
+ // required: true
+ // - name: index
+ // in: path
+ // description: index of the pull request to get
+ // type: integer
+ // format: int64
+ // required: true
+ // - name: skip-to
+ // in: query
+ // description: skip to given file
+ // type: string
+ // - name: whitespace
+ // in: query
+ // description: whitespace behavior
+ // type: string
+ // enum: [ignore-all, ignore-change, ignore-eol, show-all]
+ // - name: page
+ // in: query
+ // description: page number of results to return (1-based)
+ // type: integer
+ // - name: limit
+ // in: query
+ // description: page size of results
+ // type: integer
+ // responses:
+ // "200":
+ // "$ref": "#/responses/ChangedFileList"
+ // "404":
+ // "$ref": "#/responses/notFound"
+
+ pr, err := issues_model.GetPullRequestByIndex(ctx, ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
+ if err != nil {
+ if issues_model.IsErrPullRequestNotExist(err) {
+ ctx.NotFound()
+ } else {
+ ctx.Error(http.StatusInternalServerError, "GetPullRequestByIndex", err)
+ }
+ return
+ }
+
+ if err := pr.LoadBaseRepo(); err != nil {
+ ctx.InternalServerError(err)
+ return
+ }
+
+ if err := pr.LoadHeadRepo(); err != nil {
+ ctx.InternalServerError(err)
+ return
+ }
+
+ baseGitRepo := ctx.Repo.GitRepo
+
+ var prInfo *git.CompareInfo
+ if pr.HasMerged {
+ prInfo, err = baseGitRepo.GetCompareInfo(pr.BaseRepo.RepoPath(), pr.MergeBase, pr.GetGitRefName(), true, false)
+ } else {
+ prInfo, err = baseGitRepo.GetCompareInfo(pr.BaseRepo.RepoPath(), pr.BaseBranch, pr.GetGitRefName(), true, false)
+ }
+ if err != nil {
+ ctx.ServerError("GetCompareInfo", err)
+ return
+ }
+
+ headCommitID, err := baseGitRepo.GetRefCommitID(pr.GetGitRefName())
+ if err != nil {
+ ctx.ServerError("GetRefCommitID", err)
+ return
+ }
+
+ startCommitID := prInfo.MergeBase
+ endCommitID := headCommitID
+
+ maxLines, maxFiles := setting.Git.MaxGitDiffLines, setting.Git.MaxGitDiffFiles
+
+ diff, err := gitdiff.GetDiff(baseGitRepo,
+ &gitdiff.DiffOptions{
+ BeforeCommitID: startCommitID,
+ AfterCommitID: endCommitID,
+ SkipTo: ctx.FormString("skip-to"),
+ MaxLines: maxLines,
+ MaxLineCharacters: setting.Git.MaxGitDiffLineCharacters,
+ MaxFiles: maxFiles,
+ WhitespaceBehavior: gitdiff.GetWhitespaceFlag(ctx.FormString("whitespace")),
+ })
+ if err != nil {
+ ctx.ServerError("GetDiff", err)
+ return
+ }
+
+ listOptions := utils.GetListOptions(ctx)
+
+ totalNumberOfFiles := diff.NumFiles
+ totalNumberOfPages := int(math.Ceil(float64(totalNumberOfFiles) / float64(listOptions.PageSize)))
+
+ start, end := listOptions.GetStartEnd()
+
+ if end > totalNumberOfFiles {
+ end = totalNumberOfFiles
+ }
+
+ apiFiles := make([]*api.ChangedFile, 0, end-start)
+ for i := start; i < end; i++ {
+ apiFiles = append(apiFiles, convert.ToChangedFile(diff.Files[i], pr.HeadRepo, endCommitID))
+ }
+
+ ctx.SetLinkHeader(totalNumberOfFiles, listOptions.PageSize)
+ ctx.SetTotalCountHeader(int64(totalNumberOfFiles))
+
+ ctx.RespHeader().Set("X-Page", strconv.Itoa(listOptions.Page))
+ ctx.RespHeader().Set("X-PerPage", strconv.Itoa(listOptions.PageSize))
+ ctx.RespHeader().Set("X-PageCount", strconv.Itoa(totalNumberOfPages))
+ ctx.RespHeader().Set("X-HasMore", strconv.FormatBool(listOptions.Page < totalNumberOfPages))
+ ctx.AppendAccessControlExposeHeaders("X-Page", "X-PerPage", "X-PageCount", "X-HasMore")
+
+ ctx.JSON(http.StatusOK, &apiFiles)
+}
diff --git a/routers/api/v1/repo/release.go b/routers/api/v1/repo/release.go
index 80009f78e9..acc9696e1b 100644
--- a/routers/api/v1/repo/release.go
+++ b/routers/api/v1/repo/release.go
@@ -9,6 +9,7 @@ import (
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/models/perm"
+ repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/convert"
@@ -49,12 +50,12 @@ func GetRelease(ctx *context.APIContext) {
// "$ref": "#/responses/notFound"
id := ctx.ParamsInt64(":id")
- release, err := models.GetReleaseByID(ctx, id)
- if err != nil && !models.IsErrReleaseNotExist(err) {
+ release, err := repo_model.GetReleaseByID(ctx, id)
+ if err != nil && !repo_model.IsErrReleaseNotExist(err) {
ctx.Error(http.StatusInternalServerError, "GetReleaseByID", err)
return
}
- if err != nil && models.IsErrReleaseNotExist(err) ||
+ if err != nil && repo_model.IsErrReleaseNotExist(err) ||
release.IsTag || release.RepoID != ctx.Repo.Repository.ID {
ctx.NotFound()
return
@@ -114,7 +115,7 @@ func ListReleases(ctx *context.APIContext) {
listOptions.PageSize = ctx.FormInt("per_page")
}
- opts := models.FindReleasesOptions{
+ opts := repo_model.FindReleasesOptions{
ListOptions: listOptions,
IncludeDrafts: ctx.Repo.AccessMode >= perm.AccessModeWrite || ctx.Repo.UnitAccessMode(unit.TypeReleases) >= perm.AccessModeWrite,
IncludeTags: false,
@@ -122,7 +123,7 @@ func ListReleases(ctx *context.APIContext) {
IsPreRelease: ctx.FormOptionalBool("pre-release"),
}
- releases, err := models.GetReleasesByRepoID(ctx.Repo.Repository.ID, opts)
+ releases, err := repo_model.GetReleasesByRepoID(ctx.Repo.Repository.ID, opts)
if err != nil {
ctx.Error(http.StatusInternalServerError, "GetReleasesByRepoID", err)
return
@@ -136,7 +137,7 @@ func ListReleases(ctx *context.APIContext) {
rels[i] = convert.ToRelease(release)
}
- filteredCount, err := models.CountReleasesByRepoID(ctx.Repo.Repository.ID, opts)
+ filteredCount, err := repo_model.CountReleasesByRepoID(ctx.Repo.Repository.ID, opts)
if err != nil {
ctx.InternalServerError(err)
return
@@ -179,9 +180,9 @@ func CreateRelease(ctx *context.APIContext) {
// "409":
// "$ref": "#/responses/error"
form := web.GetForm(ctx).(*api.CreateReleaseOption)
- rel, err := models.GetRelease(ctx.Repo.Repository.ID, form.TagName)
+ rel, err := repo_model.GetRelease(ctx.Repo.Repository.ID, form.TagName)
if err != nil {
- if !models.IsErrReleaseNotExist(err) {
+ if !repo_model.IsErrReleaseNotExist(err) {
ctx.Error(http.StatusInternalServerError, "GetRelease", err)
return
}
@@ -189,7 +190,7 @@ func CreateRelease(ctx *context.APIContext) {
if len(form.Target) == 0 {
form.Target = ctx.Repo.Repository.DefaultBranch
}
- rel = &models.Release{
+ rel = &repo_model.Release{
RepoID: ctx.Repo.Repository.ID,
PublisherID: ctx.Doer.ID,
Publisher: ctx.Doer,
@@ -203,7 +204,7 @@ func CreateRelease(ctx *context.APIContext) {
Repo: ctx.Repo.Repository,
}
if err := release_service.CreateRelease(ctx.Repo.GitRepo, rel, nil, ""); err != nil {
- if models.IsErrReleaseAlreadyExist(err) {
+ if repo_model.IsErrReleaseAlreadyExist(err) {
ctx.Error(http.StatusConflict, "ReleaseAlreadyExist", err)
} else {
ctx.Error(http.StatusInternalServerError, "CreateRelease", err)
@@ -272,12 +273,12 @@ func EditRelease(ctx *context.APIContext) {
form := web.GetForm(ctx).(*api.EditReleaseOption)
id := ctx.ParamsInt64(":id")
- rel, err := models.GetReleaseByID(ctx, id)
- if err != nil && !models.IsErrReleaseNotExist(err) {
+ rel, err := repo_model.GetReleaseByID(ctx, id)
+ if err != nil && !repo_model.IsErrReleaseNotExist(err) {
ctx.Error(http.StatusInternalServerError, "GetReleaseByID", err)
return
}
- if err != nil && models.IsErrReleaseNotExist(err) ||
+ if err != nil && repo_model.IsErrReleaseNotExist(err) ||
rel.IsTag || rel.RepoID != ctx.Repo.Repository.ID {
ctx.NotFound()
return
@@ -307,7 +308,7 @@ func EditRelease(ctx *context.APIContext) {
}
// reload data from database
- rel, err = models.GetReleaseByID(ctx, id)
+ rel, err = repo_model.GetReleaseByID(ctx, id)
if err != nil {
ctx.Error(http.StatusInternalServerError, "GetReleaseByID", err)
return
@@ -350,12 +351,12 @@ func DeleteRelease(ctx *context.APIContext) {
// "$ref": "#/responses/empty"
id := ctx.ParamsInt64(":id")
- rel, err := models.GetReleaseByID(ctx, id)
- if err != nil && !models.IsErrReleaseNotExist(err) {
+ rel, err := repo_model.GetReleaseByID(ctx, id)
+ if err != nil && !repo_model.IsErrReleaseNotExist(err) {
ctx.Error(http.StatusInternalServerError, "GetReleaseByID", err)
return
}
- if err != nil && models.IsErrReleaseNotExist(err) ||
+ if err != nil && repo_model.IsErrReleaseNotExist(err) ||
rel.IsTag || rel.RepoID != ctx.Repo.Repository.ID {
ctx.NotFound()
return
diff --git a/routers/api/v1/repo/release_attachment.go b/routers/api/v1/repo/release_attachment.go
index 8694653c06..a469877c13 100644
--- a/routers/api/v1/repo/release_attachment.go
+++ b/routers/api/v1/repo/release_attachment.go
@@ -7,7 +7,6 @@ package repo
import (
"net/http"
- "code.gitea.io/gitea/models"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/convert"
@@ -57,6 +56,10 @@ func GetReleaseAttachment(ctx *context.APIContext) {
attachID := ctx.ParamsInt64(":asset")
attach, err := repo_model.GetAttachmentByID(ctx, attachID)
if err != nil {
+ if repo_model.IsErrAttachmentNotExist(err) {
+ ctx.NotFound()
+ return
+ }
ctx.Error(http.StatusInternalServerError, "GetAttachmentByID", err)
return
}
@@ -98,8 +101,12 @@ func ListReleaseAttachments(ctx *context.APIContext) {
// "$ref": "#/responses/AttachmentList"
releaseID := ctx.ParamsInt64(":id")
- release, err := models.GetReleaseByID(ctx, releaseID)
+ release, err := repo_model.GetReleaseByID(ctx, releaseID)
if err != nil {
+ if repo_model.IsErrReleaseNotExist(err) {
+ ctx.NotFound()
+ return
+ }
ctx.Error(http.StatusInternalServerError, "GetReleaseByID", err)
return
}
@@ -164,8 +171,12 @@ func CreateReleaseAttachment(ctx *context.APIContext) {
// Check if release exists an load release
releaseID := ctx.ParamsInt64(":id")
- release, err := models.GetReleaseByID(ctx, releaseID)
+ release, err := repo_model.GetReleaseByID(ctx, releaseID)
if err != nil {
+ if repo_model.IsErrReleaseNotExist(err) {
+ ctx.NotFound()
+ return
+ }
ctx.Error(http.StatusInternalServerError, "GetReleaseByID", err)
return
}
@@ -244,6 +255,10 @@ func EditReleaseAttachment(ctx *context.APIContext) {
attachID := ctx.ParamsInt64(":asset")
attach, err := repo_model.GetAttachmentByID(ctx, attachID)
if err != nil {
+ if repo_model.IsErrAttachmentNotExist(err) {
+ ctx.NotFound()
+ return
+ }
ctx.Error(http.StatusInternalServerError, "GetAttachmentByID", err)
return
}
@@ -302,6 +317,10 @@ func DeleteReleaseAttachment(ctx *context.APIContext) {
attachID := ctx.ParamsInt64(":asset")
attach, err := repo_model.GetAttachmentByID(ctx, attachID)
if err != nil {
+ if repo_model.IsErrAttachmentNotExist(err) {
+ ctx.NotFound()
+ return
+ }
ctx.Error(http.StatusInternalServerError, "GetAttachmentByID", err)
return
}
diff --git a/routers/api/v1/repo/release_tags.go b/routers/api/v1/repo/release_tags.go
index 73dee73e1a..2cb97c58a0 100644
--- a/routers/api/v1/repo/release_tags.go
+++ b/routers/api/v1/repo/release_tags.go
@@ -8,6 +8,7 @@ import (
"net/http"
"code.gitea.io/gitea/models"
+ repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/convert"
releaseservice "code.gitea.io/gitea/services/release"
@@ -44,9 +45,9 @@ func GetReleaseByTag(ctx *context.APIContext) {
tag := ctx.Params(":tag")
- release, err := models.GetRelease(ctx.Repo.Repository.ID, tag)
+ release, err := repo_model.GetRelease(ctx.Repo.Repository.ID, tag)
if err != nil {
- if models.IsErrReleaseNotExist(err) {
+ if repo_model.IsErrReleaseNotExist(err) {
ctx.NotFound()
return
}
@@ -97,9 +98,9 @@ func DeleteReleaseByTag(ctx *context.APIContext) {
tag := ctx.Params(":tag")
- release, err := models.GetRelease(ctx.Repo.Repository.ID, tag)
+ release, err := repo_model.GetRelease(ctx.Repo.Repository.ID, tag)
if err != nil {
- if models.IsErrReleaseNotExist(err) {
+ if repo_model.IsErrReleaseNotExist(err) {
ctx.NotFound()
return
}
diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go
index cdd1f7d5c4..de8a4d1864 100644
--- a/routers/api/v1/repo/repo.go
+++ b/routers/api/v1/repo/repo.go
@@ -11,7 +11,6 @@ import (
"strings"
"time"
- "code.gitea.io/gitea/models"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/organization"
"code.gitea.io/gitea/models/perm"
@@ -232,7 +231,7 @@ func CreateUserRepo(ctx *context.APIContext, owner *user_model.User, opt api.Cre
if opt.AutoInit && opt.Readme == "" {
opt.Readme = "Default"
}
- repo, err := repo_service.CreateRepository(ctx.Doer, owner, models.CreateRepoOptions{
+ repo, err := repo_service.CreateRepository(ctx.Doer, owner, repo_module.CreateRepoOptions{
Name: opt.Name,
Description: opt.Description,
IssueLabels: opt.IssueLabels,
@@ -585,7 +584,6 @@ func Edit(ctx *context.APIContext) {
// description: name of the repo to edit
// type: string
// required: true
- // required: true
// - name: body
// in: body
// description: "Properties of a repo that you can edit"
@@ -734,8 +732,13 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error {
var units []repo_model.RepoUnit
var deleteUnitTypes []unit_model.Type
+ currHasIssues := repo.UnitEnabledCtx(ctx, unit_model.TypeIssues)
+ newHasIssues := currHasIssues
if opts.HasIssues != nil {
- if *opts.HasIssues && opts.ExternalTracker != nil && !unit_model.TypeExternalTracker.UnitGlobalDisabled() {
+ newHasIssues = *opts.HasIssues
+ }
+ if currHasIssues || newHasIssues {
+ if newHasIssues && opts.ExternalTracker != nil && !unit_model.TypeExternalTracker.UnitGlobalDisabled() {
// Check that values are valid
if !validation.IsValidExternalURL(opts.ExternalTracker.ExternalTrackerURL) {
err := fmt.Errorf("External tracker URL not valid")
@@ -752,13 +755,14 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error {
RepoID: repo.ID,
Type: unit_model.TypeExternalTracker,
Config: &repo_model.ExternalTrackerConfig{
- ExternalTrackerURL: opts.ExternalTracker.ExternalTrackerURL,
- ExternalTrackerFormat: opts.ExternalTracker.ExternalTrackerFormat,
- ExternalTrackerStyle: opts.ExternalTracker.ExternalTrackerStyle,
+ ExternalTrackerURL: opts.ExternalTracker.ExternalTrackerURL,
+ ExternalTrackerFormat: opts.ExternalTracker.ExternalTrackerFormat,
+ ExternalTrackerStyle: opts.ExternalTracker.ExternalTrackerStyle,
+ ExternalTrackerRegexpPattern: opts.ExternalTracker.ExternalTrackerRegexpPattern,
},
})
deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeIssues)
- } else if *opts.HasIssues && opts.ExternalTracker == nil && !unit_model.TypeIssues.UnitGlobalDisabled() {
+ } else if newHasIssues && opts.ExternalTracker == nil && !unit_model.TypeIssues.UnitGlobalDisabled() {
// Default to built-in tracker
var config *repo_model.IssuesConfig
@@ -785,7 +789,7 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error {
Config: config,
})
deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeExternalTracker)
- } else if !*opts.HasIssues {
+ } else if !newHasIssues {
if !unit_model.TypeExternalTracker.UnitGlobalDisabled() {
deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeExternalTracker)
}
@@ -795,8 +799,13 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error {
}
}
+ currHasWiki := repo.UnitEnabledCtx(ctx, unit_model.TypeWiki)
+ newHasWiki := currHasWiki
if opts.HasWiki != nil {
- if *opts.HasWiki && opts.ExternalWiki != nil && !unit_model.TypeExternalWiki.UnitGlobalDisabled() {
+ newHasWiki = *opts.HasWiki
+ }
+ if currHasWiki || newHasWiki {
+ if newHasWiki && opts.ExternalWiki != nil && !unit_model.TypeExternalWiki.UnitGlobalDisabled() {
// Check that values are valid
if !validation.IsValidExternalURL(opts.ExternalWiki.ExternalWikiURL) {
err := fmt.Errorf("External wiki URL not valid")
@@ -812,7 +821,7 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error {
},
})
deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeWiki)
- } else if *opts.HasWiki && opts.ExternalWiki == nil && !unit_model.TypeWiki.UnitGlobalDisabled() {
+ } else if newHasWiki && opts.ExternalWiki == nil && !unit_model.TypeWiki.UnitGlobalDisabled() {
config := &repo_model.UnitConfig{}
units = append(units, repo_model.RepoUnit{
RepoID: repo.ID,
@@ -820,7 +829,7 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error {
Config: config,
})
deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeExternalWiki)
- } else if !*opts.HasWiki {
+ } else if !newHasWiki {
if !unit_model.TypeExternalWiki.UnitGlobalDisabled() {
deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeExternalWiki)
}
@@ -830,8 +839,13 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error {
}
}
+ currHasPullRequests := repo.UnitEnabledCtx(ctx, unit_model.TypePullRequests)
+ newHasPullRequests := currHasPullRequests
if opts.HasPullRequests != nil {
- if *opts.HasPullRequests && !unit_model.TypePullRequests.UnitGlobalDisabled() {
+ newHasPullRequests = *opts.HasPullRequests
+ }
+ if currHasPullRequests || newHasPullRequests {
+ if newHasPullRequests && !unit_model.TypePullRequests.UnitGlobalDisabled() {
// We do allow setting individual PR settings through the API, so
// we get the config settings and then set them
// if those settings were provided in the opts.
@@ -891,7 +905,7 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error {
Type: unit_model.TypePullRequests,
Config: config,
})
- } else if !*opts.HasPullRequests && !unit_model.TypePullRequests.UnitGlobalDisabled() {
+ } else if !newHasPullRequests && !unit_model.TypePullRequests.UnitGlobalDisabled() {
deleteUnitTypes = append(deleteUnitTypes, unit_model.TypePullRequests)
}
}
diff --git a/routers/api/v1/repo/tag.go b/routers/api/v1/repo/tag.go
index 433d823c7e..f0f8503996 100644
--- a/routers/api/v1/repo/tag.go
+++ b/routers/api/v1/repo/tag.go
@@ -10,6 +10,7 @@ import (
"net/http"
"code.gitea.io/gitea/models"
+ repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/convert"
api "code.gitea.io/gitea/modules/structs"
@@ -249,9 +250,9 @@ func DeleteTag(ctx *context.APIContext) {
// "$ref": "#/responses/conflict"
tagName := ctx.Params("*")
- tag, err := models.GetRelease(ctx.Repo.Repository.ID, tagName)
+ tag, err := repo_model.GetRelease(ctx.Repo.Repository.ID, tagName)
if err != nil {
- if models.IsErrReleaseNotExist(err) {
+ if repo_model.IsErrReleaseNotExist(err) {
ctx.NotFound()
return
}
diff --git a/routers/api/v1/repo/teams.go b/routers/api/v1/repo/teams.go
index 47c69d722b..f485f2086e 100644
--- a/routers/api/v1/repo/teams.go
+++ b/routers/api/v1/repo/teams.go
@@ -12,6 +12,7 @@ import (
"code.gitea.io/gitea/models/organization"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/convert"
+ org_service "code.gitea.io/gitea/services/org"
)
// ListTeams list a repository's teams
@@ -199,7 +200,7 @@ func changeRepoTeam(ctx *context.APIContext, add bool) {
ctx.Error(http.StatusUnprocessableEntity, "alreadyAdded", fmt.Errorf("team '%s' is already added to repo", team.Name))
return
}
- err = models.AddRepository(team, ctx.Repo.Repository)
+ err = org_service.TeamAddRepository(team, ctx.Repo.Repository)
} else {
if !repoHasTeam {
ctx.Error(http.StatusUnprocessableEntity, "notAdded", fmt.Errorf("team '%s' was not added to repo", team.Name))
diff --git a/routers/api/v1/repo/wiki.go b/routers/api/v1/repo/wiki.go
index a3a5904925..6d95648542 100644
--- a/routers/api/v1/repo/wiki.go
+++ b/routers/api/v1/repo/wiki.go
@@ -10,10 +10,11 @@ import (
"net/http"
"net/url"
- "code.gitea.io/gitea/models"
+ repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/convert"
"code.gitea.io/gitea/modules/git"
+ "code.gitea.io/gitea/modules/notification"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
@@ -72,9 +73,9 @@ func NewWikiPage(ctx *context.APIContext) {
form.ContentBase64 = string(content)
if err := wiki_service.AddWikiPage(ctx, ctx.Doer, ctx.Repo.Repository, wikiName, form.ContentBase64, form.Message); err != nil {
- if models.IsErrWikiReservedName(err) {
+ if repo_model.IsErrWikiReservedName(err) {
ctx.Error(http.StatusBadRequest, "IsErrWikiReservedName", err)
- } else if models.IsErrWikiAlreadyExist(err) {
+ } else if repo_model.IsErrWikiAlreadyExist(err) {
ctx.Error(http.StatusBadRequest, "IsErrWikiAlreadyExists", err)
} else {
ctx.Error(http.StatusInternalServerError, "AddWikiPage", err)
@@ -85,6 +86,7 @@ func NewWikiPage(ctx *context.APIContext) {
wikiPage := getWikiPage(ctx, wikiName)
if !ctx.Written() {
+ notification.NotifyNewWikiPage(ctx.Doer, ctx.Repo.Repository, wikiName, form.Message)
ctx.JSON(http.StatusCreated, wikiPage)
}
}
@@ -152,6 +154,7 @@ func EditWikiPage(ctx *context.APIContext) {
wikiPage := getWikiPage(ctx, newWikiName)
if !ctx.Written() {
+ notification.NotifyEditWikiPage(ctx.Doer, ctx.Repo.Repository, newWikiName, form.Message)
ctx.JSON(http.StatusOK, wikiPage)
}
}
@@ -242,6 +245,8 @@ func DeleteWikiPage(ctx *context.APIContext) {
return
}
+ notification.NotifyDeleteWikiPage(ctx.Doer, ctx.Repo.Repository, wikiName)
+
ctx.Status(http.StatusNoContent)
}
@@ -314,7 +319,7 @@ func ListWikiPages(ctx *context.APIContext) {
}
wikiName, err := wiki_service.FilenameToName(entry.Name())
if err != nil {
- if models.IsErrWikiInvalidFileName(err) {
+ if repo_model.IsErrWikiInvalidFileName(err) {
continue
}
ctx.Error(http.StatusInternalServerError, "WikiFilenameToName", err)
@@ -427,9 +432,9 @@ func ListPageRevisions(ctx *context.APIContext) {
}
// get Commit Count
- commitsHistory, err := wikiRepo.CommitsByFileAndRangeNoFollow("master", pageFilename, page)
+ commitsHistory, err := wikiRepo.CommitsByFileAndRange("master", pageFilename, page)
if err != nil {
- ctx.Error(http.StatusInternalServerError, "CommitsByFileAndRangeNoFollow", err)
+ ctx.Error(http.StatusInternalServerError, "CommitsByFileAndRange", err)
return
}
diff --git a/routers/api/v1/swagger/options.go b/routers/api/v1/swagger/options.go
index df3d011246..e8cfc0706f 100644
--- a/routers/api/v1/swagger/options.go
+++ b/routers/api/v1/swagger/options.go
@@ -169,4 +169,7 @@ type swaggerParameterBodies struct {
// in:body
CreateWikiPageOptions api.CreateWikiPageOptions
+
+ // in:body
+ CreatePushMirrorOption api.CreatePushMirrorOption
}
diff --git a/routers/api/v1/swagger/repo.go b/routers/api/v1/swagger/repo.go
index ab802db781..642b1b7b91 100644
--- a/routers/api/v1/swagger/repo.go
+++ b/routers/api/v1/swagger/repo.go
@@ -254,6 +254,28 @@ type swaggerCommitList struct {
Body []api.Commit `json:"body"`
}
+// ChangedFileList
+// swagger:response ChangedFileList
+type swaggerChangedFileList struct {
+ // The current page
+ Page int `json:"X-Page"`
+
+ // Commits per page
+ PerPage int `json:"X-PerPage"`
+
+ // Total commit count
+ Total int `json:"X-Total"`
+
+ // Total number of pages
+ PageCount int `json:"X-PageCount"`
+
+ // True if there is another page
+ HasMore bool `json:"X-HasMore"`
+
+ // in: body
+ Body []api.ChangedFile `json:"body"`
+}
+
// Note
// swagger:response Note
type swaggerNote struct {
@@ -345,6 +367,20 @@ type swaggerWikiCommitList struct {
Body api.WikiCommitList `json:"body"`
}
+// PushMirror
+// swagger:response PushMirror
+type swaggerPushMirror struct {
+ // in:body
+ Body api.PushMirror `json:"body"`
+}
+
+// PushMirrorList
+// swagger:response PushMirrorList
+type swaggerPushMirrorList struct {
+ // in:body
+ Body []api.PushMirror `json:"body"`
+}
+
// RepoCollaboratorPermission
// swagger:response RepoCollaboratorPermission
type swaggerRepoCollaboratorPermission struct {
diff --git a/routers/api/v1/swagger/user.go b/routers/api/v1/swagger/user.go
index a4d5201236..857bdc2a14 100644
--- a/routers/api/v1/swagger/user.go
+++ b/routers/api/v1/swagger/user.go
@@ -5,7 +5,7 @@
package swagger
import (
- "code.gitea.io/gitea/models"
+ activities_model "code.gitea.io/gitea/models/activities"
api "code.gitea.io/gitea/modules/structs"
)
@@ -40,7 +40,7 @@ type swaggerModelEditUserOption struct {
// swagger:response UserHeatmapData
type swaggerResponseUserHeatmapData struct {
// in:body
- Body []models.UserHeatmapData `json:"body"`
+ Body []activities_model.UserHeatmapData `json:"body"`
}
// UserSettings
diff --git a/routers/api/v1/user/app.go b/routers/api/v1/user/app.go
index 0d2e8401cc..a94db79239 100644
--- a/routers/api/v1/user/app.go
+++ b/routers/api/v1/user/app.go
@@ -11,8 +11,7 @@ import (
"net/http"
"strconv"
- "code.gitea.io/gitea/models"
- "code.gitea.io/gitea/models/auth"
+ auth_model "code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/convert"
api "code.gitea.io/gitea/modules/structs"
@@ -45,14 +44,14 @@ func ListAccessTokens(ctx *context.APIContext) {
// "200":
// "$ref": "#/responses/AccessTokenList"
- opts := models.ListAccessTokensOptions{UserID: ctx.Doer.ID, ListOptions: utils.GetListOptions(ctx)}
+ opts := auth_model.ListAccessTokensOptions{UserID: ctx.Doer.ID, ListOptions: utils.GetListOptions(ctx)}
- count, err := models.CountAccessTokens(opts)
+ count, err := auth_model.CountAccessTokens(opts)
if err != nil {
ctx.InternalServerError(err)
return
}
- tokens, err := models.ListAccessTokens(opts)
+ tokens, err := auth_model.ListAccessTokens(opts)
if err != nil {
ctx.InternalServerError(err)
return
@@ -98,12 +97,12 @@ func CreateAccessToken(ctx *context.APIContext) {
form := web.GetForm(ctx).(*api.CreateAccessTokenOption)
- t := &models.AccessToken{
+ t := &auth_model.AccessToken{
UID: ctx.Doer.ID,
Name: form.Name,
}
- exist, err := models.AccessTokenByNameExists(t)
+ exist, err := auth_model.AccessTokenByNameExists(t)
if err != nil {
ctx.InternalServerError(err)
return
@@ -113,7 +112,7 @@ func CreateAccessToken(ctx *context.APIContext) {
return
}
- if err := models.NewAccessToken(t); err != nil {
+ if err := auth_model.NewAccessToken(t); err != nil {
ctx.Error(http.StatusInternalServerError, "NewAccessToken", err)
return
}
@@ -155,7 +154,7 @@ func DeleteAccessToken(ctx *context.APIContext) {
tokenID, _ := strconv.ParseInt(token, 0, 64)
if tokenID == 0 {
- tokens, err := models.ListAccessTokens(models.ListAccessTokensOptions{
+ tokens, err := auth_model.ListAccessTokens(auth_model.ListAccessTokensOptions{
Name: token,
UserID: ctx.Doer.ID,
})
@@ -180,8 +179,8 @@ func DeleteAccessToken(ctx *context.APIContext) {
return
}
- if err := models.DeleteAccessTokenByID(tokenID, ctx.Doer.ID); err != nil {
- if models.IsErrAccessTokenNotExist(err) {
+ if err := auth_model.DeleteAccessTokenByID(tokenID, ctx.Doer.ID); err != nil {
+ if auth_model.IsErrAccessTokenNotExist(err) {
ctx.NotFound()
} else {
ctx.Error(http.StatusInternalServerError, "DeleteAccessTokenByID", err)
@@ -213,7 +212,7 @@ func CreateOauth2Application(ctx *context.APIContext) {
data := web.GetForm(ctx).(*api.CreateOAuth2ApplicationOptions)
- app, err := auth.CreateOAuth2Application(ctx, auth.CreateOAuth2ApplicationOptions{
+ app, err := auth_model.CreateOAuth2Application(ctx, auth_model.CreateOAuth2ApplicationOptions{
Name: data.Name,
UserID: ctx.Doer.ID,
RedirectURIs: data.RedirectURIs,
@@ -252,7 +251,7 @@ func ListOauth2Applications(ctx *context.APIContext) {
// "200":
// "$ref": "#/responses/OAuth2ApplicationList"
- apps, total, err := auth.ListOAuth2Applications(ctx.Doer.ID, utils.GetListOptions(ctx))
+ apps, total, err := auth_model.ListOAuth2Applications(ctx.Doer.ID, utils.GetListOptions(ctx))
if err != nil {
ctx.Error(http.StatusInternalServerError, "ListOAuth2Applications", err)
return
@@ -288,8 +287,8 @@ func DeleteOauth2Application(ctx *context.APIContext) {
// "404":
// "$ref": "#/responses/notFound"
appID := ctx.ParamsInt64(":id")
- if err := auth.DeleteOAuth2Application(appID, ctx.Doer.ID); err != nil {
- if auth.IsErrOAuthApplicationNotFound(err) {
+ if err := auth_model.DeleteOAuth2Application(appID, ctx.Doer.ID); err != nil {
+ if auth_model.IsErrOAuthApplicationNotFound(err) {
ctx.NotFound()
} else {
ctx.Error(http.StatusInternalServerError, "DeleteOauth2ApplicationByID", err)
@@ -320,9 +319,9 @@ func GetOauth2Application(ctx *context.APIContext) {
// "404":
// "$ref": "#/responses/notFound"
appID := ctx.ParamsInt64(":id")
- app, err := auth.GetOAuth2ApplicationByID(ctx, appID)
+ app, err := auth_model.GetOAuth2ApplicationByID(ctx, appID)
if err != nil {
- if auth.IsErrOauthClientIDInvalid(err) || auth.IsErrOAuthApplicationNotFound(err) {
+ if auth_model.IsErrOauthClientIDInvalid(err) || auth_model.IsErrOAuthApplicationNotFound(err) {
ctx.NotFound()
} else {
ctx.Error(http.StatusInternalServerError, "GetOauth2ApplicationByID", err)
@@ -363,14 +362,14 @@ func UpdateOauth2Application(ctx *context.APIContext) {
data := web.GetForm(ctx).(*api.CreateOAuth2ApplicationOptions)
- app, err := auth.UpdateOAuth2Application(auth.UpdateOAuth2ApplicationOptions{
+ app, err := auth_model.UpdateOAuth2Application(auth_model.UpdateOAuth2ApplicationOptions{
Name: data.Name,
UserID: ctx.Doer.ID,
ID: appID,
RedirectURIs: data.RedirectURIs,
})
if err != nil {
- if auth.IsErrOauthClientIDInvalid(err) || auth.IsErrOAuthApplicationNotFound(err) {
+ if auth_model.IsErrOauthClientIDInvalid(err) || auth_model.IsErrOAuthApplicationNotFound(err) {
ctx.NotFound()
} else {
ctx.Error(http.StatusInternalServerError, "UpdateOauth2ApplicationByID", err)
diff --git a/routers/api/v1/user/email.go b/routers/api/v1/user/email.go
index 170ffb7736..31c13172fc 100644
--- a/routers/api/v1/user/email.go
+++ b/routers/api/v1/user/email.go
@@ -48,11 +48,6 @@ func AddEmail(ctx *context.APIContext) {
// produces:
// - application/json
// parameters:
- // - name: options
- // in: body
- // schema:
- // "$ref": "#/definitions/CreateEmailOption"
- // parameters:
// - name: body
// in: body
// schema:
diff --git a/routers/api/v1/user/gpg_key.go b/routers/api/v1/user/gpg_key.go
index b211a24a0e..b87cf0041e 100644
--- a/routers/api/v1/user/gpg_key.go
+++ b/routers/api/v1/user/gpg_key.go
@@ -7,6 +7,7 @@ package user
import (
"fmt"
"net/http"
+ "strings"
asymkey_model "code.gitea.io/gitea/models/asymkey"
"code.gitea.io/gitea/models/db"
@@ -177,6 +178,12 @@ func VerifyUserGPGKey(ctx *context.APIContext) {
token := asymkey_model.VerificationToken(ctx.Doer, 1)
lastToken := asymkey_model.VerificationToken(ctx.Doer, 0)
+ form.KeyID = strings.TrimLeft(form.KeyID, "0")
+ if form.KeyID == "" {
+ ctx.NotFound()
+ return
+ }
+
_, err := asymkey_model.VerifyGPGKey(ctx.Doer.ID, form.KeyID, token, form.Signature)
if err != nil && asymkey_model.IsErrGPGInvalidTokenSignature(err) {
_, err = asymkey_model.VerifyGPGKey(ctx.Doer.ID, form.KeyID, lastToken, form.Signature)
diff --git a/routers/api/v1/user/user.go b/routers/api/v1/user/user.go
index 2a3cb15c0f..69197aef23 100644
--- a/routers/api/v1/user/user.go
+++ b/routers/api/v1/user/user.go
@@ -8,7 +8,7 @@ package user
import (
"net/http"
- "code.gitea.io/gitea/models"
+ activities_model "code.gitea.io/gitea/models/activities"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/convert"
@@ -139,7 +139,7 @@ func GetUserHeatmapData(ctx *context.APIContext) {
// "404":
// "$ref": "#/responses/notFound"
- heatmap, err := models.GetUserHeatmapDataByUser(ctx.ContextUser, ctx.Doer)
+ heatmap, err := activities_model.GetUserHeatmapDataByUser(ctx.ContextUser, ctx.Doer)
if err != nil {
ctx.Error(http.StatusInternalServerError, "GetUserHeatmapDataByUser", err)
return
diff --git a/routers/api/v1/utils/git.go b/routers/api/v1/utils/git.go
index ac64d5b87b..816f8b3595 100644
--- a/routers/api/v1/utils/git.go
+++ b/routers/api/v1/utils/git.go
@@ -10,6 +10,7 @@ import (
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/git"
+ "code.gitea.io/gitea/modules/log"
)
// ResolveRefOrSha resolve ref to sha if exist
@@ -19,6 +20,7 @@ func ResolveRefOrSha(ctx *context.APIContext, ref string) string {
return ""
}
+ sha := ref
// Search branches and tags
for _, refType := range []string{"heads", "tags"} {
refSHA, lastMethodName, err := searchRefCommitByType(ctx, refType, ref)
@@ -27,10 +29,19 @@ func ResolveRefOrSha(ctx *context.APIContext, ref string) string {
return ""
}
if refSHA != "" {
- return refSHA
+ sha = refSHA
+ break
}
}
- return ref
+
+ if ctx.Repo.GitRepo != nil {
+ err := ctx.Repo.GitRepo.AddLastCommitCache(ctx.Repo.Repository.GetCommitsCountCacheKey(ref, ref != sha), ctx.Repo.Repository.FullName(), sha)
+ if err != nil {
+ log.Error("Unable to get commits count for %s in %s. Error: %v", sha, ctx.Repo.Repository.FullName(), err)
+ }
+ }
+
+ return sha
}
// GetGitRefs return git references based on filter
diff --git a/routers/api/v1/utils/hook.go b/routers/api/v1/utils/hook.go
index f0dc595ad5..7e4dfca9ad 100644
--- a/routers/api/v1/utils/hook.go
+++ b/routers/api/v1/utils/hook.go
@@ -15,7 +15,6 @@ import (
"code.gitea.io/gitea/modules/json"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
- "code.gitea.io/gitea/routers/utils"
webhook_service "code.gitea.io/gitea/services/webhook"
)
@@ -127,6 +126,7 @@ func addHook(ctx *context.APIContext, form *api.CreateHookOption, orgID, repoID
PullRequestComment: pullHook(form.Events, string(webhook.HookEventPullRequestComment)),
PullRequestReview: pullHook(form.Events, "pull_request_review"),
PullRequestSync: pullHook(form.Events, string(webhook.HookEventPullRequestSync)),
+ Wiki: util.IsStringInSlice(string(webhook.HookEventWiki), form.Events, true),
Repository: util.IsStringInSlice(string(webhook.HookEventRepository), form.Events, true),
Release: util.IsStringInSlice(string(webhook.HookEventRelease), form.Events, true),
},
@@ -141,14 +141,15 @@ func addHook(ctx *context.APIContext, form *api.CreateHookOption, orgID, repoID
ctx.Error(http.StatusUnprocessableEntity, "", "Missing config option: channel")
return nil, false
}
+ channel = strings.TrimSpace(channel)
- if !utils.IsValidSlackChannel(channel) {
+ if !webhook_service.IsValidSlackChannel(channel) {
ctx.Error(http.StatusBadRequest, "", "Invalid slack channel name")
return nil, false
}
meta, err := json.Marshal(&webhook_service.SlackMeta{
- Channel: strings.TrimSpace(channel),
+ Channel: channel,
Username: form.Config["username"],
IconURL: form.Config["icon_url"],
Color: form.Config["color"],
@@ -249,6 +250,7 @@ func editHook(ctx *context.APIContext, form *api.EditHookOption, w *webhook.Webh
w.Delete = util.IsStringInSlice(string(webhook.HookEventDelete), form.Events, true)
w.Fork = util.IsStringInSlice(string(webhook.HookEventFork), form.Events, true)
w.Repository = util.IsStringInSlice(string(webhook.HookEventRepository), form.Events, true)
+ w.Wiki = util.IsStringInSlice(string(webhook.HookEventWiki), form.Events, true)
w.Release = util.IsStringInSlice(string(webhook.HookEventRelease), form.Events, true)
w.BranchFilter = form.BranchFilter
diff --git a/routers/common/repo.go b/routers/common/repo.go
index b3cd749115..a9e80fad48 100644
--- a/routers/common/repo.go
+++ b/routers/common/repo.go
@@ -7,12 +7,13 @@ package common
import (
"fmt"
"io"
+ "net/url"
"path"
"path/filepath"
"strings"
"time"
- "code.gitea.io/gitea/modules/charset"
+ charsetModule "code.gitea.io/gitea/modules/charset"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/httpcache"
@@ -42,7 +43,7 @@ func ServeBlob(ctx *context.Context, blob *git.Blob, lastModified time.Time) err
}
// ServeData download file from io.Reader
-func ServeData(ctx *context.Context, name string, size int64, reader io.Reader) error {
+func ServeData(ctx *context.Context, filePath string, size int64, reader io.Reader) error {
buf := make([]byte, 1024)
n, err := util.ReadAtMost(reader, buf)
if err != nil {
@@ -52,56 +53,73 @@ func ServeData(ctx *context.Context, name string, size int64, reader io.Reader)
buf = buf[:n]
}
- ctx.Resp.Header().Set("Cache-Control", "public,max-age=86400")
+ httpcache.AddCacheControlToHeader(ctx.Resp.Header(), 5*time.Minute)
if size >= 0 {
ctx.Resp.Header().Set("Content-Length", fmt.Sprintf("%d", size))
} else {
- log.Error("ServeData called to serve data: %s with size < 0: %d", name, size)
+ log.Error("ServeData called to serve data: %s with size < 0: %d", filePath, size)
}
- name = path.Base(name)
- // Google Chrome dislike commas in filenames, so let's change it to a space
- name = strings.ReplaceAll(name, ",", " ")
+ fileName := path.Base(filePath)
+ sniffedType := typesniffer.DetectContentType(buf)
+ isPlain := sniffedType.IsText() || ctx.FormBool("render")
+ mimeType := ""
+ charset := ""
- st := typesniffer.DetectContentType(buf)
-
- mappedMimeType := ""
if setting.MimeTypeMap.Enabled {
- fileExtension := strings.ToLower(filepath.Ext(name))
- mappedMimeType = setting.MimeTypeMap.Map[fileExtension]
+ fileExtension := strings.ToLower(filepath.Ext(fileName))
+ mimeType = setting.MimeTypeMap.Map[fileExtension]
}
- if st.IsText() || ctx.FormBool("render") {
- cs, err := charset.DetectEncoding(buf)
- if err != nil {
- log.Error("Detect raw file %s charset failed: %v, using by default utf-8", name, err)
- cs = "utf-8"
- }
- if mappedMimeType == "" {
- mappedMimeType = "text/plain"
- }
- ctx.Resp.Header().Set("Content-Type", mappedMimeType+"; charset="+strings.ToLower(cs))
- } else {
- ctx.Resp.Header().Set("Access-Control-Expose-Headers", "Content-Disposition")
- if mappedMimeType != "" {
- ctx.Resp.Header().Set("Content-Type", mappedMimeType)
- }
- if (st.IsImage() || st.IsPDF()) && (setting.UI.SVG.Enabled || !st.IsSvgImage()) {
- ctx.Resp.Header().Set("Content-Disposition", fmt.Sprintf(`inline; filename="%s"`, name))
- if st.IsSvgImage() || st.IsPDF() {
- ctx.Resp.Header().Set("Content-Security-Policy", "default-src 'none'; style-src 'unsafe-inline'; sandbox")
- ctx.Resp.Header().Set("X-Content-Type-Options", "nosniff")
- if st.IsSvgImage() {
- ctx.Resp.Header().Set("Content-Type", typesniffer.SvgMimeType)
- } else {
- ctx.Resp.Header().Set("Content-Type", typesniffer.ApplicationOctetStream)
- }
- }
+
+ if mimeType == "" {
+ if sniffedType.IsBrowsableBinaryType() {
+ mimeType = sniffedType.GetMimeType()
+ } else if isPlain {
+ mimeType = "text/plain"
} else {
- ctx.Resp.Header().Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, name))
+ mimeType = typesniffer.ApplicationOctetStream
}
}
+ if isPlain {
+ charset, err = charsetModule.DetectEncoding(buf)
+ if err != nil {
+ log.Error("Detect raw file %s charset failed: %v, using by default utf-8", filePath, err)
+ charset = "utf-8"
+ }
+ }
+
+ if charset != "" {
+ ctx.Resp.Header().Set("Content-Type", mimeType+"; charset="+strings.ToLower(charset))
+ } else {
+ ctx.Resp.Header().Set("Content-Type", mimeType)
+ }
+ ctx.Resp.Header().Set("X-Content-Type-Options", "nosniff")
+
+ isSVG := sniffedType.IsSvgImage()
+
+ // serve types that can present a security risk with CSP
+ if isSVG {
+ ctx.Resp.Header().Set("Content-Security-Policy", "default-src 'none'; style-src 'unsafe-inline'; sandbox")
+ } else if sniffedType.IsPDF() {
+ // no sandbox attribute for pdf as it breaks rendering in at least safari. this
+ // should generally be safe as scripts inside PDF can not escape the PDF document
+ // see https://bugs.chromium.org/p/chromium/issues/detail?id=413851 for more discussion
+ ctx.Resp.Header().Set("Content-Security-Policy", "default-src 'none'; style-src 'unsafe-inline'")
+ }
+
+ disposition := "inline"
+ if isSVG && !setting.UI.SVG.Enabled {
+ disposition = "attachment"
+ }
+
+ // encode filename per https://datatracker.ietf.org/doc/html/rfc5987
+ encodedFileName := `filename*=UTF-8''` + url.PathEscape(fileName)
+
+ ctx.Resp.Header().Set("Content-Disposition", disposition+"; "+encodedFileName)
+ ctx.Resp.Header().Set("Access-Control-Expose-Headers", "Content-Disposition")
+
_, err = ctx.Resp.Write(buf)
if err != nil {
return err
diff --git a/routers/init.go b/routers/init.go
index e640ca4845..0f2e993413 100644
--- a/routers/init.go
+++ b/routers/init.go
@@ -11,7 +11,6 @@ import (
"code.gitea.io/gitea/models"
asymkey_model "code.gitea.io/gitea/models/asymkey"
- "code.gitea.io/gitea/modules/appstate"
"code.gitea.io/gitea/modules/cache"
"code.gitea.io/gitea/modules/eventsource"
"code.gitea.io/gitea/modules/git"
@@ -27,6 +26,8 @@ import (
"code.gitea.io/gitea/modules/ssh"
"code.gitea.io/gitea/modules/storage"
"code.gitea.io/gitea/modules/svg"
+ "code.gitea.io/gitea/modules/system"
+ "code.gitea.io/gitea/modules/templates"
"code.gitea.io/gitea/modules/translation"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
@@ -75,8 +76,8 @@ func InitGitServices() {
}
func syncAppPathForGit(ctx context.Context) error {
- runtimeState := new(appstate.RuntimeState)
- if err := appstate.AppState.Get(runtimeState); err != nil {
+ runtimeState := new(system.RuntimeState)
+ if err := system.AppState.Get(runtimeState); err != nil {
return err
}
if runtimeState.LastAppPath != setting.AppPath {
@@ -89,7 +90,7 @@ func syncAppPathForGit(ctx context.Context) error {
mustInit(asymkey_model.RewriteAllPublicKeys)
runtimeState.LastAppPath = setting.AppPath
- return appstate.AppState.Set(runtimeState)
+ return system.AppState.Set(runtimeState)
}
return nil
}
@@ -100,10 +101,8 @@ func GlobalInitInstalled(ctx context.Context) {
log.Fatal("Gitea is not installed")
}
- mustInitCtx(ctx, git.InitOnceWithSync)
+ mustInitCtx(ctx, git.InitFull)
log.Info("Git Version: %s (home: %s)", git.VersionInfo(), git.HomeDir())
-
- git.CheckLFSVersion()
log.Info("AppPath: %s", setting.AppPath)
log.Info("AppWorkPath: %s", setting.AppWorkPath)
log.Info("Custom path: %s", setting.CustomPath)
@@ -112,12 +111,12 @@ func GlobalInitInstalled(ctx context.Context) {
log.Info("Run Mode: %s", util.ToTitleCase(setting.RunMode))
// Setup i18n
- translation.InitLocales()
+ translation.InitLocales(ctx)
setting.NewServices()
mustInit(storage.Init)
- mailer.NewContext()
+ mailer.NewContext(ctx)
mustInit(cache.NewContext)
notification.NewContext()
mustInit(archiver.Init)
@@ -134,10 +133,10 @@ func GlobalInitInstalled(ctx context.Context) {
mustInitCtx(ctx, common.InitDBEngine)
log.Info("ORM engine initialization successful!")
- mustInit(appstate.Init)
+ mustInit(system.Init)
mustInit(oauth2.Init)
- models.NewRepoContext()
+ mustInit(models.Init)
mustInit(repo_service.Init)
// Booting long running goroutines.
@@ -165,18 +164,19 @@ func GlobalInitInstalled(ctx context.Context) {
}
// NormalRoutes represents non install routes
-func NormalRoutes() *web.Route {
+func NormalRoutes(ctx context.Context) *web.Route {
+ ctx, _ = templates.HTMLRenderer(ctx)
r := web.NewRoute()
for _, middle := range common.Middlewares() {
r.Use(middle)
}
- r.Mount("/", web_routers.Routes())
- r.Mount("/api/v1", apiv1.Routes())
+ r.Mount("/", web_routers.Routes(ctx))
+ r.Mount("/api/v1", apiv1.Routes(ctx))
r.Mount("/api/internal", private.Routes())
if setting.Packages.Enabled {
- r.Mount("/api/packages", packages_router.Routes())
- r.Mount("/v2", packages_router.ContainerRoutes())
+ r.Mount("/api/packages", packages_router.Routes(ctx))
+ r.Mount("/v2", packages_router.ContainerRoutes(ctx))
}
return r
}
diff --git a/routers/install/install.go b/routers/install/install.go
index 27c3509fde..962dee8c86 100644
--- a/routers/install/install.go
+++ b/routers/install/install.go
@@ -6,17 +6,20 @@
package install
import (
+ goctx "context"
"fmt"
"net/http"
"os"
"os/exec"
"path/filepath"
+ "strconv"
"strings"
"time"
"code.gitea.io/gitea/models/db"
db_install "code.gitea.io/gitea/models/db/install"
"code.gitea.io/gitea/models/migrations"
+ system_model "code.gitea.io/gitea/models/system"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/context"
@@ -51,39 +54,41 @@ func getSupportedDbTypeNames() (dbTypeNames []map[string]string) {
}
// Init prepare for rendering installation page
-func Init(next http.Handler) http.Handler {
- rnd := templates.HTMLRenderer()
+func Init(ctx goctx.Context) func(next http.Handler) http.Handler {
+ _, rnd := templates.HTMLRenderer(ctx)
dbTypeNames := getSupportedDbTypeNames()
- return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
- if setting.InstallLock {
- resp.Header().Add("Refresh", "1; url="+setting.AppURL+"user/login")
- _ = rnd.HTML(resp, http.StatusOK, string(tplPostInstall), nil)
- return
- }
- locale := middleware.Locale(resp, req)
- startTime := time.Now()
- ctx := context.Context{
- Resp: context.NewResponse(resp),
- Flash: &middleware.Flash{},
- Locale: locale,
- Render: rnd,
- Session: session.GetSession(req),
- Data: map[string]interface{}{
- "locale": locale,
- "Title": locale.Tr("install.install"),
- "PageIsInstall": true,
- "DbTypeNames": dbTypeNames,
- "AllLangs": translation.AllLangs(),
- "PageStartTime": startTime,
+ return func(next http.Handler) http.Handler {
+ return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
+ if setting.InstallLock {
+ resp.Header().Add("Refresh", "1; url="+setting.AppURL+"user/login")
+ _ = rnd.HTML(resp, http.StatusOK, string(tplPostInstall), nil)
+ return
+ }
+ locale := middleware.Locale(resp, req)
+ startTime := time.Now()
+ ctx := context.Context{
+ Resp: context.NewResponse(resp),
+ Flash: &middleware.Flash{},
+ Locale: locale,
+ Render: rnd,
+ Session: session.GetSession(req),
+ Data: map[string]interface{}{
+ "locale": locale,
+ "Title": locale.Tr("install.install"),
+ "PageIsInstall": true,
+ "DbTypeNames": dbTypeNames,
+ "AllLangs": translation.AllLangs(),
+ "PageStartTime": startTime,
- "PasswordHashAlgorithms": user_model.AvailableHashAlgorithms,
- },
- }
- defer ctx.Close()
+ "PasswordHashAlgorithms": user_model.AvailableHashAlgorithms,
+ },
+ }
+ defer ctx.Close()
- ctx.Req = context.WithContext(req, &ctx)
- next.ServeHTTP(resp, ctx.Req)
- })
+ ctx.Req = context.WithContext(req, &ctx)
+ next.ServeHTTP(resp, ctx.Req)
+ })
+ }
}
// Install render installation page
@@ -133,7 +138,8 @@ func Install(ctx *context.Context) {
// E-mail service settings
if setting.MailService != nil {
- form.SMTPHost = setting.MailService.Host
+ form.SMTPAddr = setting.MailService.SMTPAddr
+ form.SMTPPort = setting.MailService.SMTPPort
form.SMTPFrom = setting.MailService.From
form.SMTPUser = setting.MailService.User
form.SMTPPasswd = setting.MailService.Passwd
@@ -143,8 +149,9 @@ func Install(ctx *context.Context) {
// Server and other services settings
form.OfflineMode = setting.OfflineMode
- form.DisableGravatar = setting.DisableGravatar
- form.EnableFederatedAvatar = setting.EnableFederatedAvatar
+ form.DisableGravatar = false // when installing, there is no database connection so that given a default value
+ form.EnableFederatedAvatar = false // when installing, there is no database connection so that given a default value
+
form.EnableOpenIDSignIn = setting.Service.EnableOpenIDSignIn
form.EnableOpenIDSignUp = setting.Service.EnableOpenIDSignUp
form.DisableRegistration = setting.Service.DisableRegistration
@@ -368,7 +375,6 @@ func SubmitInstall(ctx *context.Context) {
ctx.RenderWithErr(ctx.Tr("install.invalid_db_setting", err), tplInstall, &form)
return
}
- db.UnsetDefaultEngine()
// Save settings.
cfg := ini.Empty()
@@ -421,9 +427,10 @@ func SubmitInstall(ctx *context.Context) {
cfg.Section("server").Key("LFS_START_SERVER").SetValue("false")
}
- if len(strings.TrimSpace(form.SMTPHost)) > 0 {
+ if len(strings.TrimSpace(form.SMTPAddr)) > 0 {
cfg.Section("mailer").Key("ENABLED").SetValue("true")
- cfg.Section("mailer").Key("HOST").SetValue(form.SMTPHost)
+ cfg.Section("mailer").Key("SMTP_ADDR").SetValue(form.SMTPAddr)
+ cfg.Section("mailer").Key("SMTP_PORT").SetValue(form.SMTPPort)
cfg.Section("mailer").Key("FROM").SetValue(form.SMTPFrom)
cfg.Section("mailer").Key("USER").SetValue(form.SMTPUser)
cfg.Section("mailer").Key("PASSWD").SetValue(form.SMTPPasswd)
@@ -434,7 +441,11 @@ func SubmitInstall(ctx *context.Context) {
cfg.Section("service").Key("ENABLE_NOTIFY_MAIL").SetValue(fmt.Sprint(form.MailNotify))
cfg.Section("server").Key("OFFLINE_MODE").SetValue(fmt.Sprint(form.OfflineMode))
- cfg.Section("picture").Key("DISABLE_GRAVATAR").SetValue(fmt.Sprint(form.DisableGravatar))
+ // if you are reinstalling, this maybe not right because of missing version
+ if err := system_model.SetSettingNoVersion(system_model.KeyPictureDisableGravatar, strconv.FormatBool(form.DisableGravatar)); err != nil {
+ ctx.RenderWithErr(ctx.Tr("install.secret_key_failed", err), tplInstall, &form)
+ return
+ }
cfg.Section("picture").Key("ENABLE_FEDERATED_AVATAR").SetValue(fmt.Sprint(form.EnableFederatedAvatar))
cfg.Section("openid").Key("ENABLE_OPENID_SIGNIN").SetValue(fmt.Sprint(form.EnableOpenIDSignIn))
cfg.Section("openid").Key("ENABLE_OPENID_SIGNUP").SetValue(fmt.Sprint(form.EnableOpenIDSignUp))
@@ -496,6 +507,9 @@ func SubmitInstall(ctx *context.Context) {
return
}
+ // unset default engine before reload database setting
+ db.UnsetDefaultEngine()
+
// ---- All checks are passed
// Reload settings (and re-initialize database connection)
diff --git a/routers/install/routes.go b/routers/install/routes.go
index fdabcb9dc2..7617477827 100644
--- a/routers/install/routes.go
+++ b/routers/install/routes.go
@@ -5,6 +5,7 @@
package install
import (
+ goctx "context"
"fmt"
"net/http"
"path"
@@ -29,8 +30,8 @@ func (d *dataStore) GetData() map[string]interface{} {
return *d
}
-func installRecovery() func(next http.Handler) http.Handler {
- rnd := templates.HTMLRenderer()
+func installRecovery(ctx goctx.Context) func(next http.Handler) http.Handler {
+ _, rnd := templates.HTMLRenderer(ctx)
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
defer func() {
@@ -82,7 +83,7 @@ func installRecovery() func(next http.Handler) http.Handler {
}
// Routes registers the install routes
-func Routes() *web.Route {
+func Routes(ctx goctx.Context) *web.Route {
r := web.NewRoute()
for _, middle := range common.Middlewares() {
r.Use(middle)
@@ -105,8 +106,8 @@ func Routes() *web.Route {
Domain: setting.SessionConfig.Domain,
}))
- r.Use(installRecovery())
- r.Use(Init)
+ r.Use(installRecovery(ctx))
+ r.Use(Init(ctx))
r.Get("/", Install)
r.Post("/", web.Bind(forms.InstallForm{}), SubmitInstall)
r.Get("/api/healthz", healthcheck.Check)
diff --git a/routers/install/routes_test.go b/routers/install/routes_test.go
index 29003c3841..e69d2d15df 100644
--- a/routers/install/routes_test.go
+++ b/routers/install/routes_test.go
@@ -5,13 +5,16 @@
package install
import (
+ "context"
"testing"
"github.com/stretchr/testify/assert"
)
func TestRoutes(t *testing.T) {
- routes := Routes()
+ ctx, cancel := context.WithCancel(context.Background())
+ defer cancel()
+ routes := Routes(ctx)
assert.NotNil(t, routes)
assert.EqualValues(t, "/", routes.R.Routes()[0].Pattern)
assert.Nil(t, routes.R.Routes()[0].SubRoutes)
diff --git a/routers/install/setting.go b/routers/install/setting.go
index cf0a01ce31..c4912f1124 100644
--- a/routers/install/setting.go
+++ b/routers/install/setting.go
@@ -24,7 +24,7 @@ func PreloadSettings(ctx context.Context) bool {
log.Info("Log path: %s", setting.LogRootPath)
log.Info("Configuration file: %s", setting.CustomConf)
log.Info("Prepare to run install page")
- translation.InitLocales()
+ translation.InitLocales(ctx)
if setting.EnableSQLite3 {
log.Info("SQLite3 is supported")
}
diff --git a/routers/private/internal.go b/routers/private/internal.go
index 061c7f3c82..f8e451e80f 100644
--- a/routers/private/internal.go
+++ b/routers/private/internal.go
@@ -17,6 +17,7 @@ import (
"code.gitea.io/gitea/modules/web"
"gitea.com/go-chi/binding"
+ chi_middleware "github.com/go-chi/chi/v5/middleware"
)
// CheckInternalToken check internal token is set
@@ -24,6 +25,11 @@ func CheckInternalToken(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
tokens := req.Header.Get("Authorization")
fields := strings.SplitN(tokens, " ", 2)
+ if setting.InternalToken == "" {
+ log.Warn(`The INTERNAL_TOKEN setting is missing from the configuration file: %q, internal API can't work.`, setting.CustomConf)
+ http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden)
+ return
+ }
if len(fields) != 2 || fields[0] != "Bearer" || fields[1] != setting.InternalToken {
log.Debug("Forbidden attempt to access internal url: Authorization header: %s", tokens)
http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden)
@@ -52,6 +58,9 @@ func Routes() *web.Route {
r := web.NewRoute()
r.Use(context.PrivateContexter())
r.Use(CheckInternalToken)
+ // Log the real ip address of the request from SSH is really helpful for diagnosing sometimes.
+ // Since internal API will be sent only from Gitea sub commands and it's under control (checked by InternalToken), we can trust the headers.
+ r.Use(chi_middleware.RealIP)
r.Post("/ssh/authorized_keys", AuthorizedPublicKeyByContent)
r.Post("/ssh/{id}/update/{repoid}", UpdatePublicKeyInRepo)
diff --git a/routers/private/mail.go b/routers/private/mail.go
index 966a838168..e858992aee 100644
--- a/routers/private/mail.go
+++ b/routers/private/mail.go
@@ -9,6 +9,7 @@ import (
"net/http"
"strconv"
+ "code.gitea.io/gitea/models/db"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/json"
@@ -59,7 +60,7 @@ func SendEmail(ctx *context.PrivateContext) {
}
}
} else {
- err := user_model.IterateUser(func(user *user_model.User) error {
+ err := db.IterateObjects(ctx, func(user *user_model.User) error {
if len(user.Email) > 0 && user.IsActive {
emails = append(emails, user.Email)
}
diff --git a/routers/utils/utils.go b/routers/utils/utils.go
index f15bc1e62e..66eaa1d9ce 100644
--- a/routers/utils/utils.go
+++ b/routers/utils/utils.go
@@ -20,25 +20,6 @@ func RemoveUsernameParameterSuffix(name string) string {
return name
}
-// IsValidSlackChannel validates a channel name conforms to what slack expects.
-// It makes sure a channel name cannot be empty and invalid ( only an # )
-func IsValidSlackChannel(channelName string) bool {
- switch len(strings.TrimSpace(channelName)) {
- case 0:
- return false
- case 1:
- // Keep default behaviour where a channel name is still
- // valid without an #
- // But if it contains only an #, it should be regarded as
- // invalid
- if channelName[0] == '#' {
- return false
- }
- }
-
- return true
-}
-
// SanitizeFlashErrorString will sanitize a flash error string
func SanitizeFlashErrorString(x string) string {
return strings.ReplaceAll(html.EscapeString(x), "\n", "
")
diff --git a/routers/utils/utils_test.go b/routers/utils/utils_test.go
index f49ed77b6f..42cf948e30 100644
--- a/routers/utils/utils_test.go
+++ b/routers/utils/utils_test.go
@@ -18,23 +18,6 @@ func TestRemoveUsernameParameterSuffix(t *testing.T) {
assert.Equal(t, "", RemoveUsernameParameterSuffix(""))
}
-func TestIsValidSlackChannel(t *testing.T) {
- tt := []struct {
- channelName string
- expected bool
- }{
- {"gitea", true},
- {" ", false},
- {"#", false},
- {"gitea ", true},
- {" gitea", true},
- }
-
- for _, v := range tt {
- assert.Equal(t, v.expected, IsValidSlackChannel(v.channelName))
- }
-}
-
func TestIsExternalURL(t *testing.T) {
setting.AppURL = "https://try.gitea.io/"
type test struct {
diff --git a/routers/web/admin/admin.go b/routers/web/admin/admin.go
index ebe5066d2c..d0664eb780 100644
--- a/routers/web/admin/admin.go
+++ b/routers/web/admin/admin.go
@@ -8,18 +8,13 @@ package admin
import (
"fmt"
"net/http"
- "net/url"
- "os"
"runtime"
"strconv"
- "strings"
"time"
- "code.gitea.io/gitea/models"
+ activities_model "code.gitea.io/gitea/models/activities"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/context"
- "code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/process"
"code.gitea.io/gitea/modules/queue"
@@ -27,18 +22,13 @@ import (
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/translation"
"code.gitea.io/gitea/modules/updatechecker"
- "code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/services/cron"
"code.gitea.io/gitea/services/forms"
- "code.gitea.io/gitea/services/mailer"
-
- "gitea.com/go-chi/session"
)
const (
tplDashboard base.TplName = "admin/dashboard"
- tplConfig base.TplName = "admin/config"
tplMonitor base.TplName = "admin/monitor"
tplStacktrace base.TplName = "admin/stacktrace"
tplQueue base.TplName = "admin/queue"
@@ -128,7 +118,7 @@ func Dashboard(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("admin.dashboard")
ctx.Data["PageIsAdmin"] = true
ctx.Data["PageIsAdminDashboard"] = true
- ctx.Data["Stats"] = models.GetStatistic()
+ ctx.Data["Stats"] = activities_model.GetStatistic()
ctx.Data["NeedUpdate"] = updatechecker.GetNeedUpdate()
ctx.Data["RemoteVersion"] = updatechecker.GetRemoteVersion()
// FIXME: update periodically
@@ -144,7 +134,7 @@ func DashboardPost(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("admin.dashboard")
ctx.Data["PageIsAdmin"] = true
ctx.Data["PageIsAdminDashboard"] = true
- ctx.Data["Stats"] = models.GetStatistic()
+ ctx.Data["Stats"] = activities_model.GetStatistic()
updateSystemStatus()
ctx.Data["SysStatus"] = sysStatus
@@ -165,164 +155,6 @@ func DashboardPost(ctx *context.Context) {
}
}
-// SendTestMail send test mail to confirm mail service is OK
-func SendTestMail(ctx *context.Context) {
- email := ctx.FormString("email")
- // Send a test email to the user's email address and redirect back to Config
- if err := mailer.SendTestMail(email); err != nil {
- ctx.Flash.Error(ctx.Tr("admin.config.test_mail_failed", email, err))
- } else {
- ctx.Flash.Info(ctx.Tr("admin.config.test_mail_sent", email))
- }
-
- ctx.Redirect(setting.AppSubURL + "/admin/config")
-}
-
-func shadowPasswordKV(cfgItem, splitter string) string {
- fields := strings.Split(cfgItem, splitter)
- for i := 0; i < len(fields); i++ {
- if strings.HasPrefix(fields[i], "password=") {
- fields[i] = "password=******"
- break
- }
- }
- return strings.Join(fields, splitter)
-}
-
-func shadowURL(provider, cfgItem string) string {
- u, err := url.Parse(cfgItem)
- if err != nil {
- log.Error("Shadowing Password for %v failed: %v", provider, err)
- return cfgItem
- }
- if u.User != nil {
- atIdx := strings.Index(cfgItem, "@")
- if atIdx > 0 {
- colonIdx := strings.LastIndex(cfgItem[:atIdx], ":")
- if colonIdx > 0 {
- return cfgItem[:colonIdx+1] + "******" + cfgItem[atIdx:]
- }
- }
- }
- return cfgItem
-}
-
-func shadowPassword(provider, cfgItem string) string {
- switch provider {
- case "redis":
- return shadowPasswordKV(cfgItem, ",")
- case "mysql":
- // root:@tcp(localhost:3306)/macaron?charset=utf8
- atIdx := strings.Index(cfgItem, "@")
- if atIdx > 0 {
- colonIdx := strings.Index(cfgItem[:atIdx], ":")
- if colonIdx > 0 {
- return cfgItem[:colonIdx+1] + "******" + cfgItem[atIdx:]
- }
- }
- return cfgItem
- case "postgres":
- // user=jiahuachen dbname=macaron port=5432 sslmode=disable
- if !strings.HasPrefix(cfgItem, "postgres://") {
- return shadowPasswordKV(cfgItem, " ")
- }
- fallthrough
- case "couchbase":
- return shadowURL(provider, cfgItem)
- // postgres://pqgotest:password@localhost/pqgotest?sslmode=verify-full
- // Notice: use shadowURL
- }
- return cfgItem
-}
-
-// Config show admin config page
-func Config(ctx *context.Context) {
- ctx.Data["Title"] = ctx.Tr("admin.config")
- ctx.Data["PageIsAdmin"] = true
- ctx.Data["PageIsAdminConfig"] = true
-
- ctx.Data["CustomConf"] = setting.CustomConf
- ctx.Data["AppUrl"] = setting.AppURL
- ctx.Data["Domain"] = setting.Domain
- ctx.Data["OfflineMode"] = setting.OfflineMode
- ctx.Data["DisableRouterLog"] = setting.DisableRouterLog
- ctx.Data["RunUser"] = setting.RunUser
- ctx.Data["RunMode"] = util.ToTitleCase(setting.RunMode)
- ctx.Data["GitVersion"] = git.VersionInfo()
-
- ctx.Data["RepoRootPath"] = setting.RepoRootPath
- ctx.Data["CustomRootPath"] = setting.CustomPath
- ctx.Data["StaticRootPath"] = setting.StaticRootPath
- ctx.Data["LogRootPath"] = setting.LogRootPath
- ctx.Data["ScriptType"] = setting.ScriptType
- ctx.Data["ReverseProxyAuthUser"] = setting.ReverseProxyAuthUser
- ctx.Data["ReverseProxyAuthEmail"] = setting.ReverseProxyAuthEmail
-
- ctx.Data["SSH"] = setting.SSH
- ctx.Data["LFS"] = setting.LFS
-
- ctx.Data["Service"] = setting.Service
- ctx.Data["DbCfg"] = setting.Database
- ctx.Data["Webhook"] = setting.Webhook
-
- ctx.Data["MailerEnabled"] = false
- if setting.MailService != nil {
- ctx.Data["MailerEnabled"] = true
- ctx.Data["Mailer"] = setting.MailService
- }
-
- ctx.Data["CacheAdapter"] = setting.CacheService.Adapter
- ctx.Data["CacheInterval"] = setting.CacheService.Interval
-
- ctx.Data["CacheConn"] = shadowPassword(setting.CacheService.Adapter, setting.CacheService.Conn)
- ctx.Data["CacheItemTTL"] = setting.CacheService.TTL
-
- sessionCfg := setting.SessionConfig
- if sessionCfg.Provider == "VirtualSession" {
- var realSession session.Options
- if err := json.Unmarshal([]byte(sessionCfg.ProviderConfig), &realSession); err != nil {
- log.Error("Unable to unmarshall session config for virtualed provider config: %s\nError: %v", sessionCfg.ProviderConfig, err)
- }
- sessionCfg.Provider = realSession.Provider
- sessionCfg.ProviderConfig = realSession.ProviderConfig
- sessionCfg.CookieName = realSession.CookieName
- sessionCfg.CookiePath = realSession.CookiePath
- sessionCfg.Gclifetime = realSession.Gclifetime
- sessionCfg.Maxlifetime = realSession.Maxlifetime
- sessionCfg.Secure = realSession.Secure
- sessionCfg.Domain = realSession.Domain
- }
- sessionCfg.ProviderConfig = shadowPassword(sessionCfg.Provider, sessionCfg.ProviderConfig)
- ctx.Data["SessionConfig"] = sessionCfg
-
- ctx.Data["DisableGravatar"] = setting.DisableGravatar
- ctx.Data["EnableFederatedAvatar"] = setting.EnableFederatedAvatar
-
- ctx.Data["Git"] = setting.Git
-
- type envVar struct {
- Name, Value string
- }
-
- envVars := map[string]*envVar{}
- if len(os.Getenv("GITEA_WORK_DIR")) > 0 {
- envVars["GITEA_WORK_DIR"] = &envVar{"GITEA_WORK_DIR", os.Getenv("GITEA_WORK_DIR")}
- }
- if len(os.Getenv("GITEA_CUSTOM")) > 0 {
- envVars["GITEA_CUSTOM"] = &envVar{"GITEA_CUSTOM", os.Getenv("GITEA_CUSTOM")}
- }
-
- ctx.Data["EnvVars"] = envVars
- ctx.Data["Loggers"] = setting.GetLogDescriptions()
- ctx.Data["EnableAccessLog"] = setting.EnableAccessLog
- ctx.Data["AccessLogTemplate"] = setting.AccessLogTemplate
- ctx.Data["DisableRouterLog"] = setting.DisableRouterLog
- ctx.Data["EnableXORMLog"] = setting.EnableXORMLog
- ctx.Data["LogSQL"] = setting.Database.LogSQL
-
- ctx.HTML(http.StatusOK, tplConfig)
-}
-
// Monitor show admin monitor page
func Monitor(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("admin.monitor")
diff --git a/routers/web/admin/applications.go b/routers/web/admin/applications.go
new file mode 100644
index 0000000000..c7a9c3100f
--- /dev/null
+++ b/routers/web/admin/applications.go
@@ -0,0 +1,93 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package admin
+
+import (
+ "fmt"
+ "net/http"
+
+ "code.gitea.io/gitea/models/auth"
+ "code.gitea.io/gitea/modules/base"
+ "code.gitea.io/gitea/modules/context"
+ "code.gitea.io/gitea/modules/setting"
+ user_setting "code.gitea.io/gitea/routers/web/user/setting"
+)
+
+var (
+ tplSettingsApplications base.TplName = "admin/applications/list"
+ tplSettingsOauth2ApplicationEdit base.TplName = "admin/applications/oauth2_edit"
+)
+
+func newOAuth2CommonHandlers() *user_setting.OAuth2CommonHandlers {
+ return &user_setting.OAuth2CommonHandlers{
+ OwnerID: 0,
+ BasePathList: fmt.Sprintf("%s/admin/applications", setting.AppSubURL),
+ BasePathEditPrefix: fmt.Sprintf("%s/admin/applications/oauth2", setting.AppSubURL),
+ TplAppEdit: tplSettingsOauth2ApplicationEdit,
+ }
+}
+
+// Applications render org applications page (for org, at the moment, there are only OAuth2 applications)
+func Applications(ctx *context.Context) {
+ ctx.Data["Title"] = ctx.Tr("settings.applications")
+ ctx.Data["PageIsAdmin"] = true
+ ctx.Data["PageIsAdminApplications"] = true
+
+ apps, err := auth.GetOAuth2ApplicationsByUserID(ctx, 0)
+ if err != nil {
+ ctx.ServerError("GetOAuth2ApplicationsByUserID", err)
+ return
+ }
+ ctx.Data["Applications"] = apps
+
+ ctx.HTML(http.StatusOK, tplSettingsApplications)
+}
+
+// ApplicationsPost response for adding an oauth2 application
+func ApplicationsPost(ctx *context.Context) {
+ ctx.Data["Title"] = ctx.Tr("settings.applications")
+ ctx.Data["PageIsAdmin"] = true
+ ctx.Data["PageIsAdminApplications"] = true
+
+ oa := newOAuth2CommonHandlers()
+ oa.AddApp(ctx)
+}
+
+// EditApplication displays the given application
+func EditApplication(ctx *context.Context) {
+ ctx.Data["PageIsAdmin"] = true
+ ctx.Data["PageIsAdminApplications"] = true
+
+ oa := newOAuth2CommonHandlers()
+ oa.EditShow(ctx)
+}
+
+// EditApplicationPost response for editing oauth2 application
+func EditApplicationPost(ctx *context.Context) {
+ ctx.Data["Title"] = ctx.Tr("settings.applications")
+ ctx.Data["PageIsAdmin"] = true
+ ctx.Data["PageIsAdminApplications"] = true
+
+ oa := newOAuth2CommonHandlers()
+ oa.EditSave(ctx)
+}
+
+// ApplicationsRegenerateSecret handles the post request for regenerating the secret
+func ApplicationsRegenerateSecret(ctx *context.Context) {
+ ctx.Data["Title"] = ctx.Tr("settings")
+ ctx.Data["PageIsAdmin"] = true
+ ctx.Data["PageIsAdminApplications"] = true
+
+ oa := newOAuth2CommonHandlers()
+ oa.RegenerateSecret(ctx)
+}
+
+// DeleteApplication deletes the given oauth2 application
+func DeleteApplication(ctx *context.Context) {
+ oa := newOAuth2CommonHandlers()
+ oa.DeleteApp(ctx)
+}
+
+// TODO: revokes the grant with the given id
diff --git a/routers/web/admin/auths.go b/routers/web/admin/auths.go
index 7ea8a52809..b79b317555 100644
--- a/routers/web/admin/auths.go
+++ b/routers/web/admin/auths.go
@@ -159,7 +159,7 @@ func parseLDAPConfig(form forms.AuthenticationForm) *ldap.Source {
func parseSMTPConfig(form forms.AuthenticationForm) *smtp.Source {
return &smtp.Source{
Auth: form.SMTPAuth,
- Host: form.SMTPHost,
+ Addr: form.SMTPAddr,
Port: form.SMTPPort,
AllowedDomains: form.AllowedDomains,
ForceSMTPS: form.ForceSMTPS,
diff --git a/routers/web/admin/config.go b/routers/web/admin/config.go
new file mode 100644
index 0000000000..614d3d4f66
--- /dev/null
+++ b/routers/web/admin/config.go
@@ -0,0 +1,217 @@
+// Copyright 2014 The Gogs Authors. All rights reserved.
+// Copyright 2019 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package admin
+
+import (
+ "net/http"
+ "net/url"
+ "os"
+ "strings"
+
+ system_model "code.gitea.io/gitea/models/system"
+ "code.gitea.io/gitea/modules/base"
+ "code.gitea.io/gitea/modules/context"
+ "code.gitea.io/gitea/modules/git"
+ "code.gitea.io/gitea/modules/json"
+ "code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/setting"
+ system_module "code.gitea.io/gitea/modules/system"
+ "code.gitea.io/gitea/modules/util"
+ "code.gitea.io/gitea/services/mailer"
+
+ "gitea.com/go-chi/session"
+)
+
+const tplConfig base.TplName = "admin/config"
+
+// SendTestMail send test mail to confirm mail service is OK
+func SendTestMail(ctx *context.Context) {
+ email := ctx.FormString("email")
+ // Send a test email to the user's email address and redirect back to Config
+ if err := mailer.SendTestMail(email); err != nil {
+ ctx.Flash.Error(ctx.Tr("admin.config.test_mail_failed", email, err))
+ } else {
+ ctx.Flash.Info(ctx.Tr("admin.config.test_mail_sent", email))
+ }
+
+ ctx.Redirect(setting.AppSubURL + "/admin/config")
+}
+
+func shadowPasswordKV(cfgItem, splitter string) string {
+ fields := strings.Split(cfgItem, splitter)
+ for i := 0; i < len(fields); i++ {
+ if strings.HasPrefix(fields[i], "password=") {
+ fields[i] = "password=******"
+ break
+ }
+ }
+ return strings.Join(fields, splitter)
+}
+
+func shadowURL(provider, cfgItem string) string {
+ u, err := url.Parse(cfgItem)
+ if err != nil {
+ log.Error("Shadowing Password for %v failed: %v", provider, err)
+ return cfgItem
+ }
+ if u.User != nil {
+ atIdx := strings.Index(cfgItem, "@")
+ if atIdx > 0 {
+ colonIdx := strings.LastIndex(cfgItem[:atIdx], ":")
+ if colonIdx > 0 {
+ return cfgItem[:colonIdx+1] + "******" + cfgItem[atIdx:]
+ }
+ }
+ }
+ return cfgItem
+}
+
+func shadowPassword(provider, cfgItem string) string {
+ switch provider {
+ case "redis":
+ return shadowPasswordKV(cfgItem, ",")
+ case "mysql":
+ // root:@tcp(localhost:3306)/macaron?charset=utf8
+ atIdx := strings.Index(cfgItem, "@")
+ if atIdx > 0 {
+ colonIdx := strings.Index(cfgItem[:atIdx], ":")
+ if colonIdx > 0 {
+ return cfgItem[:colonIdx+1] + "******" + cfgItem[atIdx:]
+ }
+ }
+ return cfgItem
+ case "postgres":
+ // user=jiahuachen dbname=macaron port=5432 sslmode=disable
+ if !strings.HasPrefix(cfgItem, "postgres://") {
+ return shadowPasswordKV(cfgItem, " ")
+ }
+ fallthrough
+ case "couchbase":
+ return shadowURL(provider, cfgItem)
+ // postgres://pqgotest:password@localhost/pqgotest?sslmode=verify-full
+ // Notice: use shadowURL
+ }
+ return cfgItem
+}
+
+// Config show admin config page
+func Config(ctx *context.Context) {
+ ctx.Data["Title"] = ctx.Tr("admin.config")
+ ctx.Data["PageIsAdmin"] = true
+ ctx.Data["PageIsAdminConfig"] = true
+
+ systemSettings, err := system_model.GetAllSettings()
+ if err != nil {
+ ctx.ServerError("system_model.GetAllSettings", err)
+ return
+ }
+
+ // All editable settings from UI
+ ctx.Data["SystemSettings"] = systemSettings
+ ctx.PageData["adminConfigPage"] = true
+
+ ctx.Data["CustomConf"] = setting.CustomConf
+ ctx.Data["AppUrl"] = setting.AppURL
+ ctx.Data["Domain"] = setting.Domain
+ ctx.Data["OfflineMode"] = setting.OfflineMode
+ ctx.Data["DisableRouterLog"] = setting.DisableRouterLog
+ ctx.Data["RunUser"] = setting.RunUser
+ ctx.Data["RunMode"] = util.ToTitleCase(setting.RunMode)
+ ctx.Data["GitVersion"] = git.VersionInfo()
+
+ ctx.Data["RepoRootPath"] = setting.RepoRootPath
+ ctx.Data["CustomRootPath"] = setting.CustomPath
+ ctx.Data["StaticRootPath"] = setting.StaticRootPath
+ ctx.Data["LogRootPath"] = setting.LogRootPath
+ ctx.Data["ScriptType"] = setting.ScriptType
+ ctx.Data["ReverseProxyAuthUser"] = setting.ReverseProxyAuthUser
+ ctx.Data["ReverseProxyAuthEmail"] = setting.ReverseProxyAuthEmail
+
+ ctx.Data["SSH"] = setting.SSH
+ ctx.Data["LFS"] = setting.LFS
+
+ ctx.Data["Service"] = setting.Service
+ ctx.Data["DbCfg"] = setting.Database
+ ctx.Data["Webhook"] = setting.Webhook
+
+ ctx.Data["MailerEnabled"] = false
+ if setting.MailService != nil {
+ ctx.Data["MailerEnabled"] = true
+ ctx.Data["Mailer"] = setting.MailService
+ }
+
+ ctx.Data["CacheAdapter"] = setting.CacheService.Adapter
+ ctx.Data["CacheInterval"] = setting.CacheService.Interval
+
+ ctx.Data["CacheConn"] = shadowPassword(setting.CacheService.Adapter, setting.CacheService.Conn)
+ ctx.Data["CacheItemTTL"] = setting.CacheService.TTL
+
+ sessionCfg := setting.SessionConfig
+ if sessionCfg.Provider == "VirtualSession" {
+ var realSession session.Options
+ if err := json.Unmarshal([]byte(sessionCfg.ProviderConfig), &realSession); err != nil {
+ log.Error("Unable to unmarshall session config for virtual provider config: %s\nError: %v", sessionCfg.ProviderConfig, err)
+ }
+ sessionCfg.Provider = realSession.Provider
+ sessionCfg.ProviderConfig = realSession.ProviderConfig
+ sessionCfg.CookieName = realSession.CookieName
+ sessionCfg.CookiePath = realSession.CookiePath
+ sessionCfg.Gclifetime = realSession.Gclifetime
+ sessionCfg.Maxlifetime = realSession.Maxlifetime
+ sessionCfg.Secure = realSession.Secure
+ sessionCfg.Domain = realSession.Domain
+ }
+ sessionCfg.ProviderConfig = shadowPassword(sessionCfg.Provider, sessionCfg.ProviderConfig)
+ ctx.Data["SessionConfig"] = sessionCfg
+
+ ctx.Data["Git"] = setting.Git
+
+ type envVar struct {
+ Name, Value string
+ }
+
+ envVars := map[string]*envVar{}
+ if len(os.Getenv("GITEA_WORK_DIR")) > 0 {
+ envVars["GITEA_WORK_DIR"] = &envVar{"GITEA_WORK_DIR", os.Getenv("GITEA_WORK_DIR")}
+ }
+ if len(os.Getenv("GITEA_CUSTOM")) > 0 {
+ envVars["GITEA_CUSTOM"] = &envVar{"GITEA_CUSTOM", os.Getenv("GITEA_CUSTOM")}
+ }
+
+ ctx.Data["EnvVars"] = envVars
+ ctx.Data["Loggers"] = setting.GetLogDescriptions()
+ ctx.Data["EnableAccessLog"] = setting.EnableAccessLog
+ ctx.Data["AccessLogTemplate"] = setting.AccessLogTemplate
+ ctx.Data["DisableRouterLog"] = setting.DisableRouterLog
+ ctx.Data["EnableXORMLog"] = setting.EnableXORMLog
+ ctx.Data["LogSQL"] = setting.Database.LogSQL
+
+ ctx.HTML(http.StatusOK, tplConfig)
+}
+
+func ChangeConfig(ctx *context.Context) {
+ key := strings.TrimSpace(ctx.FormString("key"))
+ if key == "" {
+ ctx.JSON(http.StatusOK, map[string]string{
+ "redirect": ctx.Req.URL.String(),
+ })
+ return
+ }
+ value := ctx.FormString("value")
+ version := ctx.FormInt("version")
+
+ if err := system_module.SetSetting(key, value, version); err != nil {
+ log.Error("set setting failed: %v", err)
+ ctx.JSON(http.StatusOK, map[string]string{
+ "err": ctx.Tr("admin.config.set_setting_failed", key),
+ })
+ return
+ }
+
+ ctx.JSON(http.StatusOK, map[string]interface{}{
+ "version": version + 1,
+ })
+}
diff --git a/routers/web/admin/notice.go b/routers/web/admin/notice.go
index b50549b804..f5ec294cc3 100644
--- a/routers/web/admin/notice.go
+++ b/routers/web/admin/notice.go
@@ -9,7 +9,7 @@ import (
"net/http"
"strconv"
- admin_model "code.gitea.io/gitea/models/admin"
+ system_model "code.gitea.io/gitea/models/system"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
@@ -26,13 +26,13 @@ func Notices(ctx *context.Context) {
ctx.Data["PageIsAdmin"] = true
ctx.Data["PageIsAdminNotices"] = true
- total := admin_model.CountNotices()
+ total := system_model.CountNotices()
page := ctx.FormInt("page")
if page <= 1 {
page = 1
}
- notices, err := admin_model.Notices(page, setting.UI.Admin.NoticePagingNum)
+ notices, err := system_model.Notices(page, setting.UI.Admin.NoticePagingNum)
if err != nil {
ctx.ServerError("Notices", err)
return
@@ -57,7 +57,7 @@ func DeleteNotices(ctx *context.Context) {
}
}
- if err := admin_model.DeleteNoticesByIDs(ids); err != nil {
+ if err := system_model.DeleteNoticesByIDs(ids); err != nil {
ctx.Flash.Error("DeleteNoticesByIDs: " + err.Error())
ctx.Status(http.StatusInternalServerError)
} else {
@@ -68,7 +68,7 @@ func DeleteNotices(ctx *context.Context) {
// EmptyNotices delete all the notices
func EmptyNotices(ctx *context.Context) {
- if err := admin_model.DeleteNotices(0, 0); err != nil {
+ if err := system_model.DeleteNotices(0, 0); err != nil {
ctx.ServerError("DeleteNotices", err)
return
}
diff --git a/routers/web/admin/packages.go b/routers/web/admin/packages.go
index 79bf025dd2..5de8922e6f 100644
--- a/routers/web/admin/packages.go
+++ b/routers/web/admin/packages.go
@@ -13,6 +13,7 @@ import (
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/util"
packages_service "code.gitea.io/gitea/services/packages"
)
@@ -31,9 +32,10 @@ func Packages(ctx *context.Context) {
sort := ctx.FormTrim("sort")
pvs, total, err := packages_model.SearchVersions(ctx, &packages_model.PackageSearchOptions{
- Type: packages_model.Type(packageType),
- Name: packages_model.SearchValue{Value: query},
- Sort: sort,
+ Type: packages_model.Type(packageType),
+ Name: packages_model.SearchValue{Value: query},
+ Sort: sort,
+ IsInternal: util.OptionalBoolFalse,
Paginator: &db.ListOptions{
PageSize: setting.UI.PackagesPagingNum,
Page: page,
diff --git a/routers/web/admin/repos.go b/routers/web/admin/repos.go
index 8ce981ec20..17b00975ec 100644
--- a/routers/web/admin/repos.go
+++ b/routers/web/admin/repos.go
@@ -9,13 +9,13 @@ import (
"net/url"
"strings"
- "code.gitea.io/gitea/models"
"code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
+ repo_module "code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/routers/web/explore"
@@ -148,7 +148,7 @@ func AdoptOrDeleteRepository(ctx *context.Context) {
if has || !isDir {
// Fallthrough to failure mode
} else if action == "adopt" {
- if _, err := repo_service.AdoptRepository(ctx.Doer, ctxUser, models.CreateRepoOptions{
+ if _, err := repo_service.AdoptRepository(ctx.Doer, ctxUser, repo_module.CreateRepoOptions{
Name: dirSplit[1],
IsPrivate: true,
}); err != nil {
diff --git a/routers/web/admin/users.go b/routers/web/admin/users.go
index aab633ec84..5cdfb8142e 100644
--- a/routers/web/admin/users.go
+++ b/routers/web/admin/users.go
@@ -209,7 +209,11 @@ func NewUserPost(ctx *context.Context) {
func prepareUserInfo(ctx *context.Context) *user_model.User {
u, err := user_model.GetUserByID(ctx.ParamsInt64(":userid"))
if err != nil {
- ctx.ServerError("GetUserByID", err)
+ if user_model.IsErrUserNotExist(err) {
+ ctx.Redirect(setting.AppSubURL + "/admin/users")
+ } else {
+ ctx.ServerError("GetUserByID", err)
+ }
return nil
}
ctx.Data["User"] = u
diff --git a/routers/web/admin/users_test.go b/routers/web/admin/users_test.go
index e63367ccf2..da67cd5cb4 100644
--- a/routers/web/admin/users_test.go
+++ b/routers/web/admin/users_test.go
@@ -25,7 +25,7 @@ func TestNewUserPost_MustChangePassword(t *testing.T) {
u := unittest.AssertExistsAndLoadBean(t, &user_model.User{
IsAdmin: true,
ID: 2,
- }).(*user_model.User)
+ })
ctx.Doer = u
@@ -62,7 +62,7 @@ func TestNewUserPost_MustChangePasswordFalse(t *testing.T) {
u := unittest.AssertExistsAndLoadBean(t, &user_model.User{
IsAdmin: true,
ID: 2,
- }).(*user_model.User)
+ })
ctx.Doer = u
@@ -99,7 +99,7 @@ func TestNewUserPost_InvalidEmail(t *testing.T) {
u := unittest.AssertExistsAndLoadBean(t, &user_model.User{
IsAdmin: true,
ID: 2,
- }).(*user_model.User)
+ })
ctx.Doer = u
@@ -129,7 +129,7 @@ func TestNewUserPost_VisibilityDefaultPublic(t *testing.T) {
u := unittest.AssertExistsAndLoadBean(t, &user_model.User{
IsAdmin: true,
ID: 2,
- }).(*user_model.User)
+ })
ctx.Doer = u
@@ -167,7 +167,7 @@ func TestNewUserPost_VisibilityPrivate(t *testing.T) {
u := unittest.AssertExistsAndLoadBean(t, &user_model.User{
IsAdmin: true,
ID: 2,
- }).(*user_model.User)
+ })
ctx.Doer = u
diff --git a/routers/web/auth/auth.go b/routers/web/auth/auth.go
index 610e4d2904..8a4c12d57b 100644
--- a/routers/web/auth/auth.go
+++ b/routers/web/auth/auth.go
@@ -18,6 +18,7 @@ import (
"code.gitea.io/gitea/modules/eventsource"
"code.gitea.io/gitea/modules/hcaptcha"
"code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/mcaptcha"
"code.gitea.io/gitea/modules/password"
"code.gitea.io/gitea/modules/recaptcha"
"code.gitea.io/gitea/modules/session"
@@ -414,6 +415,8 @@ func SignUp(ctx *context.Context) {
ctx.Data["CaptchaType"] = setting.Service.CaptchaType
ctx.Data["RecaptchaSitekey"] = setting.Service.RecaptchaSitekey
ctx.Data["HcaptchaSitekey"] = setting.Service.HcaptchaSitekey
+ ctx.Data["McaptchaSitekey"] = setting.Service.McaptchaSitekey
+ ctx.Data["McaptchaURL"] = setting.Service.McaptchaURL
ctx.Data["PageIsSignUp"] = true
// Show Disabled Registration message if DisableRegistration or AllowOnlyExternalRegistration options are true
@@ -435,6 +438,8 @@ func SignUpPost(ctx *context.Context) {
ctx.Data["CaptchaType"] = setting.Service.CaptchaType
ctx.Data["RecaptchaSitekey"] = setting.Service.RecaptchaSitekey
ctx.Data["HcaptchaSitekey"] = setting.Service.HcaptchaSitekey
+ ctx.Data["McaptchaSitekey"] = setting.Service.McaptchaSitekey
+ ctx.Data["McaptchaURL"] = setting.Service.McaptchaURL
ctx.Data["PageIsSignUp"] = true
// Permission denied if DisableRegistration or AllowOnlyExternalRegistration options are true
@@ -458,6 +463,8 @@ func SignUpPost(ctx *context.Context) {
valid, err = recaptcha.Verify(ctx, form.GRecaptchaResponse)
case setting.HCaptcha:
valid, err = hcaptcha.Verify(ctx, form.HcaptchaResponse)
+ case setting.MCaptcha:
+ valid, err = mcaptcha.Verify(ctx, form.McaptchaResponse)
default:
ctx.ServerError("Unknown Captcha Type", fmt.Errorf("Unknown Captcha Type: %s", setting.Service.CaptchaType))
return
diff --git a/routers/web/auth/linkaccount.go b/routers/web/auth/linkaccount.go
index a2d76e9c5a..4f3f2062b6 100644
--- a/routers/web/auth/linkaccount.go
+++ b/routers/web/auth/linkaccount.go
@@ -16,6 +16,7 @@ import (
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/hcaptcha"
"code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/mcaptcha"
"code.gitea.io/gitea/modules/recaptcha"
"code.gitea.io/gitea/modules/session"
"code.gitea.io/gitea/modules/setting"
@@ -40,6 +41,8 @@ func LinkAccount(ctx *context.Context) {
ctx.Data["RecaptchaURL"] = setting.Service.RecaptchaURL
ctx.Data["RecaptchaSitekey"] = setting.Service.RecaptchaSitekey
ctx.Data["HcaptchaSitekey"] = setting.Service.HcaptchaSitekey
+ ctx.Data["McaptchaSitekey"] = setting.Service.McaptchaSitekey
+ ctx.Data["McaptchaURL"] = setting.Service.McaptchaURL
ctx.Data["DisableRegistration"] = setting.Service.DisableRegistration
ctx.Data["AllowOnlyInternalRegistration"] = setting.Service.AllowOnlyInternalRegistration
ctx.Data["ShowRegistrationButton"] = false
@@ -96,6 +99,8 @@ func LinkAccountPostSignIn(ctx *context.Context) {
ctx.Data["CaptchaType"] = setting.Service.CaptchaType
ctx.Data["RecaptchaSitekey"] = setting.Service.RecaptchaSitekey
ctx.Data["HcaptchaSitekey"] = setting.Service.HcaptchaSitekey
+ ctx.Data["McaptchaSitekey"] = setting.Service.McaptchaSitekey
+ ctx.Data["McaptchaURL"] = setting.Service.McaptchaURL
ctx.Data["DisableRegistration"] = setting.Service.DisableRegistration
ctx.Data["ShowRegistrationButton"] = false
@@ -195,6 +200,8 @@ func LinkAccountPostRegister(ctx *context.Context) {
ctx.Data["CaptchaType"] = setting.Service.CaptchaType
ctx.Data["RecaptchaSitekey"] = setting.Service.RecaptchaSitekey
ctx.Data["HcaptchaSitekey"] = setting.Service.HcaptchaSitekey
+ ctx.Data["McaptchaSitekey"] = setting.Service.McaptchaSitekey
+ ctx.Data["McaptchaURL"] = setting.Service.McaptchaURL
ctx.Data["DisableRegistration"] = setting.Service.DisableRegistration
ctx.Data["ShowRegistrationButton"] = false
@@ -233,6 +240,8 @@ func LinkAccountPostRegister(ctx *context.Context) {
valid, err = recaptcha.Verify(ctx, form.GRecaptchaResponse)
case setting.HCaptcha:
valid, err = hcaptcha.Verify(ctx, form.HcaptchaResponse)
+ case setting.MCaptcha:
+ valid, err = mcaptcha.Verify(ctx, form.McaptchaResponse)
default:
ctx.ServerError("Unknown Captcha Type", fmt.Errorf("Unknown Captcha Type: %s", setting.Service.CaptchaType))
return
diff --git a/routers/web/auth/oauth.go b/routers/web/auth/oauth.go
index 714ae96fa4..c172215b90 100644
--- a/routers/web/auth/oauth.go
+++ b/routers/web/auth/oauth.go
@@ -15,8 +15,8 @@ import (
"net/url"
"strings"
- "code.gitea.io/gitea/models"
"code.gitea.io/gitea/models/auth"
+ org_model "code.gitea.io/gitea/models/organization"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/context"
@@ -296,7 +296,7 @@ func InfoOAuth(ctx *context.Context) {
// returns a list of "org" and "org:team" strings,
// that the given user is a part of.
func getOAuthGroupsForUser(user *user_model.User) ([]string, error) {
- orgs, err := models.GetUserOrgsList(user)
+ orgs, err := org_model.GetUserOrgsList(user)
if err != nil {
return nil, fmt.Errorf("GetUserOrgList: %v", err)
}
@@ -380,10 +380,13 @@ func AuthorizeOAuth(ctx *context.Context) {
return
}
- user, err := user_model.GetUserByID(app.UID)
- if err != nil {
- ctx.ServerError("GetUserByID", err)
- return
+ var user *user_model.User
+ if app.UID != 0 {
+ user, err = user_model.GetUserByID(app.UID)
+ if err != nil {
+ ctx.ServerError("GetUserByID", err)
+ return
+ }
}
if !app.ContainsRedirectURI(form.RedirectURI) {
@@ -475,7 +478,11 @@ func AuthorizeOAuth(ctx *context.Context) {
ctx.Data["State"] = form.State
ctx.Data["Scope"] = form.Scope
ctx.Data["Nonce"] = form.Nonce
- ctx.Data["ApplicationUserLinkHTML"] = "@" + html.EscapeString(user.Name) + ""
+ if user != nil {
+ ctx.Data["ApplicationCreatorLinkHTML"] = fmt.Sprintf(`@%s`, html.EscapeString(user.HomeLink()), html.EscapeString(user.Name))
+ } else {
+ ctx.Data["ApplicationCreatorLinkHTML"] = fmt.Sprintf(`%s`, html.EscapeString(setting.AppSubURL+"/"), html.EscapeString(setting.AppName))
+ }
ctx.Data["ApplicationRedirectDomainHTML"] = "" + html.EscapeString(form.RedirectURI) + ""
// TODO document SESSION <=> FORM
err = ctx.Session.Set("client_id", app.ClientID)
@@ -588,7 +595,8 @@ func OIDCKeys(ctx *context.Context) {
// AccessTokenOAuth manages all access token requests by the client
func AccessTokenOAuth(ctx *context.Context) {
form := *web.GetForm(ctx).(*forms.AccessTokenForm)
- if form.ClientID == "" {
+ // if there is no ClientID or ClientSecret in the request body, fill these fields by the Authorization header and ensure the provided field matches the Authorization header
+ if form.ClientID == "" || form.ClientSecret == "" {
authHeader := ctx.Req.Header.Get("Authorization")
authContent := strings.SplitN(authHeader, " ", 2)
if len(authContent) == 2 && authContent[0] == "Basic" {
@@ -608,7 +616,21 @@ func AccessTokenOAuth(ctx *context.Context) {
})
return
}
+ if form.ClientID != "" && form.ClientID != pair[0] {
+ handleAccessTokenError(ctx, AccessTokenError{
+ ErrorCode: AccessTokenErrorCodeInvalidRequest,
+ ErrorDescription: "client_id in request body inconsistent with Authorization header",
+ })
+ return
+ }
form.ClientID = pair[0]
+ if form.ClientSecret != "" && form.ClientSecret != pair[1] {
+ handleAccessTokenError(ctx, AccessTokenError{
+ ErrorCode: AccessTokenErrorCodeInvalidRequest,
+ ErrorDescription: "client_secret in request body inconsistent with Authorization header",
+ })
+ return
+ }
form.ClientSecret = pair[1]
}
}
@@ -645,7 +667,7 @@ func handleRefreshToken(ctx *context.Context, form forms.AccessTokenForm, server
if err != nil {
handleAccessTokenError(ctx, AccessTokenError{
ErrorCode: AccessTokenErrorCodeUnauthorizedClient,
- ErrorDescription: "client is not authorized",
+ ErrorDescription: "unable to parse refresh token",
})
return
}
@@ -686,16 +708,20 @@ func handleAuthorizationCode(ctx *context.Context, form forms.AccessTokenForm, s
return
}
if !app.ValidateClientSecret([]byte(form.ClientSecret)) {
+ errorDescription := "invalid client secret"
+ if form.ClientSecret == "" {
+ errorDescription = "invalid empty client secret"
+ }
handleAccessTokenError(ctx, AccessTokenError{
ErrorCode: AccessTokenErrorCodeUnauthorizedClient,
- ErrorDescription: "client is not authorized",
+ ErrorDescription: errorDescription,
})
return
}
if form.RedirectURI != "" && !app.ContainsRedirectURI(form.RedirectURI) {
handleAccessTokenError(ctx, AccessTokenError{
ErrorCode: AccessTokenErrorCodeUnauthorizedClient,
- ErrorDescription: "client is not authorized",
+ ErrorDescription: "unexpected redirect URI",
})
return
}
@@ -711,7 +737,7 @@ func handleAuthorizationCode(ctx *context.Context, form forms.AccessTokenForm, s
if !authorizationCode.ValidateCodeChallenge(form.CodeVerifier) {
handleAccessTokenError(ctx, AccessTokenError{
ErrorCode: AccessTokenErrorCodeUnauthorizedClient,
- ErrorDescription: "client is not authorized",
+ ErrorDescription: "failed PKCE code challenge",
})
return
}
diff --git a/routers/web/auth/oauth_test.go b/routers/web/auth/oauth_test.go
index 57f2477dba..48400846d2 100644
--- a/routers/web/auth/oauth_test.go
+++ b/routers/web/auth/oauth_test.go
@@ -60,7 +60,7 @@ func TestNewAccessTokenResponse_OIDCToken(t *testing.T) {
assert.Empty(t, oidcToken.Email)
assert.False(t, oidcToken.EmailVerified)
- user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5}).(*user_model.User)
+ user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5})
grants, err = auth.GetOAuth2GrantsByUserID(db.DefaultContext, user.ID)
assert.NoError(t, err)
assert.Len(t, grants, 1)
diff --git a/routers/web/auth/openid.go b/routers/web/auth/openid.go
index 32ae91da47..3b1065189d 100644
--- a/routers/web/auth/openid.go
+++ b/routers/web/auth/openid.go
@@ -15,6 +15,7 @@ import (
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/hcaptcha"
"code.gitea.io/gitea/modules/log"
+ "code.gitea.io/gitea/modules/mcaptcha"
"code.gitea.io/gitea/modules/recaptcha"
"code.gitea.io/gitea/modules/session"
"code.gitea.io/gitea/modules/setting"
@@ -341,6 +342,8 @@ func RegisterOpenID(ctx *context.Context) {
ctx.Data["RecaptchaSitekey"] = setting.Service.RecaptchaSitekey
ctx.Data["HcaptchaSitekey"] = setting.Service.HcaptchaSitekey
ctx.Data["RecaptchaURL"] = setting.Service.RecaptchaURL
+ ctx.Data["McaptchaSitekey"] = setting.Service.McaptchaSitekey
+ ctx.Data["McaptchaURL"] = setting.Service.McaptchaURL
ctx.Data["OpenID"] = oid
userName, _ := ctx.Session.Get("openid_determined_username").(string)
if userName != "" {
@@ -372,6 +375,8 @@ func RegisterOpenIDPost(ctx *context.Context) {
ctx.Data["CaptchaType"] = setting.Service.CaptchaType
ctx.Data["RecaptchaSitekey"] = setting.Service.RecaptchaSitekey
ctx.Data["HcaptchaSitekey"] = setting.Service.HcaptchaSitekey
+ ctx.Data["McaptchaSitekey"] = setting.Service.McaptchaSitekey
+ ctx.Data["McaptchaURL"] = setting.Service.McaptchaURL
ctx.Data["OpenID"] = oid
if setting.Service.AllowOnlyInternalRegistration {
@@ -397,6 +402,12 @@ func RegisterOpenIDPost(ctx *context.Context) {
return
}
valid, err = hcaptcha.Verify(ctx, form.HcaptchaResponse)
+ case setting.MCaptcha:
+ if err := ctx.Req.ParseForm(); err != nil {
+ ctx.ServerError("", err)
+ return
+ }
+ valid, err = mcaptcha.Verify(ctx, form.McaptchaResponse)
default:
ctx.ServerError("Unknown Captcha Type", fmt.Errorf("Unknown Captcha Type: %s", setting.Service.CaptchaType))
return
diff --git a/routers/web/auth/webauthn.go b/routers/web/auth/webauthn.go
index 4778c9a9a3..917cbdd57b 100644
--- a/routers/web/auth/webauthn.go
+++ b/routers/web/auth/webauthn.go
@@ -5,7 +5,6 @@
package auth
import (
- "encoding/base32"
"errors"
"net/http"
@@ -129,7 +128,7 @@ func WebAuthnLoginAssertionPost(ctx *context.Context) {
}
// Success! Get the credential and update the sign count with the new value we received.
- dbCred, err := auth.GetWebAuthnCredentialByCredID(user.ID, base32.HexEncoding.EncodeToString(cred.ID))
+ dbCred, err := auth.GetWebAuthnCredentialByCredID(user.ID, cred.ID)
if err != nil {
ctx.ServerError("GetWebAuthnCredentialByCredID", err)
return
diff --git a/routers/web/base.go b/routers/web/base.go
index 30a24a1275..2441d6d517 100644
--- a/routers/web/base.go
+++ b/routers/web/base.go
@@ -5,6 +5,7 @@
package web
import (
+ goctx "context"
"errors"
"fmt"
"io"
@@ -123,8 +124,8 @@ func (d *dataStore) GetData() map[string]interface{} {
// Recovery returns a middleware that recovers from any panics and writes a 500 and a log if so.
// This error will be created with the gitea 500 page.
-func Recovery() func(next http.Handler) http.Handler {
- rnd := templates.HTMLRenderer()
+func Recovery(ctx goctx.Context) func(next http.Handler) http.Handler {
+ _, rnd := templates.HTMLRenderer(ctx)
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
defer func() {
diff --git a/routers/web/explore/code.go b/routers/web/explore/code.go
index 3afb2110d9..2357b34fd0 100644
--- a/routers/web/explore/code.go
+++ b/routers/web/explore/code.go
@@ -34,86 +34,91 @@ func Code(ctx *context.Context) {
language := ctx.FormTrim("l")
keyword := ctx.FormTrim("q")
+
+ queryType := ctx.FormTrim("t")
+ isMatch := queryType == "match"
+
+ ctx.Data["Keyword"] = keyword
+ ctx.Data["Language"] = language
+ ctx.Data["queryType"] = queryType
+ ctx.Data["PageIsViewCode"] = true
+
+ if keyword == "" {
+ ctx.HTML(http.StatusOK, tplExploreCode)
+ return
+ }
+
page := ctx.FormInt("page")
if page <= 0 {
page = 1
}
- queryType := ctx.FormTrim("t")
- isMatch := queryType == "match"
-
- if keyword != "" {
- var (
- repoIDs []int64
- err error
- isAdmin bool
- )
- if ctx.Doer != nil {
- isAdmin = ctx.Doer.IsAdmin
- }
-
- // guest user or non-admin user
- if ctx.Doer == nil || !isAdmin {
- repoIDs, err = repo_model.FindUserCodeAccessibleRepoIDs(ctx.Doer)
- if err != nil {
- ctx.ServerError("SearchResults", err)
- return
- }
- }
-
- var (
- total int
- searchResults []*code_indexer.Result
- searchResultLanguages []*code_indexer.SearchResultLanguages
- )
-
- if (len(repoIDs) > 0) || isAdmin {
- total, searchResults, searchResultLanguages, err = code_indexer.PerformSearch(ctx, repoIDs, language, keyword, page, setting.UI.RepoSearchPagingNum, isMatch)
- if err != nil {
- if code_indexer.IsAvailable() {
- ctx.ServerError("SearchResults", err)
- return
- }
- ctx.Data["CodeIndexerUnavailable"] = true
- } else {
- ctx.Data["CodeIndexerUnavailable"] = !code_indexer.IsAvailable()
- }
-
- loadRepoIDs := make([]int64, 0, len(searchResults))
- for _, result := range searchResults {
- var find bool
- for _, id := range loadRepoIDs {
- if id == result.RepoID {
- find = true
- break
- }
- }
- if !find {
- loadRepoIDs = append(loadRepoIDs, result.RepoID)
- }
- }
-
- repoMaps, err := repo_model.GetRepositoriesMapByIDs(loadRepoIDs)
- if err != nil {
- ctx.ServerError("SearchResults", err)
- return
- }
-
- ctx.Data["RepoMaps"] = repoMaps
- }
-
- ctx.Data["Keyword"] = keyword
- ctx.Data["Language"] = language
- ctx.Data["queryType"] = queryType
- ctx.Data["SearchResults"] = searchResults
- ctx.Data["SearchResultLanguages"] = searchResultLanguages
- ctx.Data["PageIsViewCode"] = true
-
- pager := context.NewPagination(total, setting.UI.RepoSearchPagingNum, page, 5)
- pager.SetDefaultParams(ctx)
- pager.AddParam(ctx, "l", "Language")
- ctx.Data["Page"] = pager
+ var (
+ repoIDs []int64
+ err error
+ isAdmin bool
+ )
+ if ctx.Doer != nil {
+ isAdmin = ctx.Doer.IsAdmin
}
+ // guest user or non-admin user
+ if ctx.Doer == nil || !isAdmin {
+ repoIDs, err = repo_model.FindUserCodeAccessibleRepoIDs(ctx, ctx.Doer)
+ if err != nil {
+ ctx.ServerError("FindUserCodeAccessibleRepoIDs", err)
+ return
+ }
+ }
+
+ var (
+ total int
+ searchResults []*code_indexer.Result
+ searchResultLanguages []*code_indexer.SearchResultLanguages
+ )
+
+ if (len(repoIDs) > 0) || isAdmin {
+ total, searchResults, searchResultLanguages, err = code_indexer.PerformSearch(ctx, repoIDs, language, keyword, page, setting.UI.RepoSearchPagingNum, isMatch)
+ if err != nil {
+ if code_indexer.IsAvailable() {
+ ctx.ServerError("SearchResults", err)
+ return
+ }
+ ctx.Data["CodeIndexerUnavailable"] = true
+ } else {
+ ctx.Data["CodeIndexerUnavailable"] = !code_indexer.IsAvailable()
+ }
+
+ loadRepoIDs := make([]int64, 0, len(searchResults))
+ for _, result := range searchResults {
+ var find bool
+ for _, id := range loadRepoIDs {
+ if id == result.RepoID {
+ find = true
+ break
+ }
+ }
+ if !find {
+ loadRepoIDs = append(loadRepoIDs, result.RepoID)
+ }
+ }
+
+ repoMaps, err := repo_model.GetRepositoriesMapByIDs(loadRepoIDs)
+ if err != nil {
+ ctx.ServerError("GetRepositoriesMapByIDs", err)
+ return
+ }
+
+ ctx.Data["RepoMaps"] = repoMaps
+ }
+
+ ctx.Data["SearchResults"] = searchResults
+ ctx.Data["SearchResultLanguages"] = searchResultLanguages
+
+ pager := context.NewPagination(total, setting.UI.RepoSearchPagingNum, page, 5)
+ pager.SetDefaultParams(ctx)
+ pager.AddParam(ctx, "l", "Language")
+ ctx.Data["Page"] = pager
+
ctx.HTML(http.StatusOK, tplExploreCode)
}
diff --git a/routers/web/explore/repo.go b/routers/web/explore/repo.go
index b5485f5832..8cb615b7bc 100644
--- a/routers/web/explore/repo.go
+++ b/routers/web/explore/repo.go
@@ -48,10 +48,11 @@ func RenderRepoSearch(ctx *context.Context, opts *RepoSearchOptions) {
}
var (
- repos []*repo_model.Repository
- count int64
- err error
- orderBy db.SearchOrderBy
+ repos []*repo_model.Repository
+ count int64
+ err error
+ orderBy db.SearchOrderBy
+ onlyShowRelevant bool
)
ctx.Data["SortType"] = ctx.FormString("sort")
@@ -60,8 +61,6 @@ func RenderRepoSearch(ctx *context.Context, opts *RepoSearchOptions) {
orderBy = db.SearchOrderByNewest
case "oldest":
orderBy = db.SearchOrderByOldest
- case "recentupdate":
- orderBy = db.SearchOrderByRecentUpdated
case "leastupdate":
orderBy = db.SearchOrderByLeastUpdated
case "reversealphabetically":
@@ -83,9 +82,16 @@ func RenderRepoSearch(ctx *context.Context, opts *RepoSearchOptions) {
default:
ctx.Data["SortType"] = "recentupdate"
orderBy = db.SearchOrderByRecentUpdated
+ onlyShowRelevant = setting.UI.OnlyShowRelevantRepos && !ctx.FormBool("no_filter")
}
keyword := ctx.FormTrim("q")
+ if keyword != "" {
+ onlyShowRelevant = false
+ }
+
+ ctx.Data["OnlyShowRelevant"] = onlyShowRelevant
+
topicOnly := ctx.FormBool("topic")
ctx.Data["TopicOnly"] = topicOnly
@@ -107,6 +113,7 @@ func RenderRepoSearch(ctx *context.Context, opts *RepoSearchOptions) {
TopicOnly: topicOnly,
Language: language,
IncludeDescription: setting.UI.SearchRepoDescription,
+ OnlyShowRelevant: onlyShowRelevant,
})
if err != nil {
ctx.ServerError("SearchRepository", err)
@@ -133,6 +140,7 @@ func RenderRepoSearch(ctx *context.Context, opts *RepoSearchOptions) {
pager.SetDefaultParams(ctx)
pager.AddParam(ctx, "topic", "TopicOnly")
pager.AddParam(ctx, "language", "Language")
+ pager.AddParamString("no_filter", ctx.FormString("no_filter"))
ctx.Data["Page"] = pager
ctx.HTML(http.StatusOK, opts.TplName)
diff --git a/routers/web/feed/convert.go b/routers/web/feed/convert.go
index 978469d125..645d9370d5 100644
--- a/routers/web/feed/convert.go
+++ b/routers/web/feed/convert.go
@@ -12,7 +12,7 @@ import (
"strconv"
"strings"
- "code.gitea.io/gitea/models"
+ activities_model "code.gitea.io/gitea/models/activities"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/markup"
"code.gitea.io/gitea/modules/markup/markdown"
@@ -23,33 +23,33 @@ import (
"github.com/gorilla/feeds"
)
-func toBranchLink(act *models.Action) string {
- return act.GetRepoLink() + "/src/branch/" + util.PathEscapeSegments(act.GetBranch())
+func toBranchLink(act *activities_model.Action) string {
+ return act.GetRepoAbsoluteLink() + "/src/branch/" + util.PathEscapeSegments(act.GetBranch())
}
-func toTagLink(act *models.Action) string {
- return act.GetRepoLink() + "/src/tag/" + util.PathEscapeSegments(act.GetTag())
+func toTagLink(act *activities_model.Action) string {
+ return act.GetRepoAbsoluteLink() + "/src/tag/" + util.PathEscapeSegments(act.GetTag())
}
-func toIssueLink(act *models.Action) string {
- return act.GetRepoLink() + "/issues/" + url.PathEscape(act.GetIssueInfos()[0])
+func toIssueLink(act *activities_model.Action) string {
+ return act.GetRepoAbsoluteLink() + "/issues/" + url.PathEscape(act.GetIssueInfos()[0])
}
-func toPullLink(act *models.Action) string {
- return act.GetRepoLink() + "/pulls/" + url.PathEscape(act.GetIssueInfos()[0])
+func toPullLink(act *activities_model.Action) string {
+ return act.GetRepoAbsoluteLink() + "/pulls/" + url.PathEscape(act.GetIssueInfos()[0])
}
-func toSrcLink(act *models.Action) string {
- return act.GetRepoLink() + "/src/" + util.PathEscapeSegments(act.GetBranch())
+func toSrcLink(act *activities_model.Action) string {
+ return act.GetRepoAbsoluteLink() + "/src/" + util.PathEscapeSegments(act.GetBranch())
}
-func toReleaseLink(act *models.Action) string {
- return act.GetRepoLink() + "/releases/tag/" + util.PathEscapeSegments(act.GetBranch())
+func toReleaseLink(act *activities_model.Action) string {
+ return act.GetRepoAbsoluteLink() + "/releases/tag/" + util.PathEscapeSegments(act.GetBranch())
}
// renderMarkdown creates a minimal markdown render context from an action.
// If rendering fails, the original markdown text is returned
-func renderMarkdown(ctx *context.Context, act *models.Action, content string) string {
+func renderMarkdown(ctx *context.Context, act *activities_model.Action, content string) string {
markdownCtx := &markup.RenderContext{
Ctx: ctx,
URLPrefix: act.GetRepoLink(),
@@ -67,7 +67,7 @@ func renderMarkdown(ctx *context.Context, act *models.Action, content string) st
}
// feedActionsToFeedItems convert gitea's Action feed to feeds Item
-func feedActionsToFeedItems(ctx *context.Context, actions models.ActionList) (items []*feeds.Item, err error) {
+func feedActionsToFeedItems(ctx *context.Context, actions activities_model.ActionList) (items []*feeds.Item, err error) {
for _, act := range actions {
act.LoadActUser()
@@ -78,112 +78,112 @@ func feedActionsToFeedItems(ctx *context.Context, actions models.ActionList) (it
// title
title = act.ActUser.DisplayName() + " "
switch act.OpType {
- case models.ActionCreateRepo:
- title += ctx.TrHTMLEscapeArgs("action.create_repo", act.GetRepoLink(), act.ShortRepoPath())
- link.Href = act.GetRepoLink()
- case models.ActionRenameRepo:
- title += ctx.TrHTMLEscapeArgs("action.rename_repo", act.GetContent(), act.GetRepoLink(), act.ShortRepoPath())
- link.Href = act.GetRepoLink()
- case models.ActionCommitRepo:
+ case activities_model.ActionCreateRepo:
+ title += ctx.TrHTMLEscapeArgs("action.create_repo", act.GetRepoAbsoluteLink(), act.ShortRepoPath())
+ link.Href = act.GetRepoAbsoluteLink()
+ case activities_model.ActionRenameRepo:
+ title += ctx.TrHTMLEscapeArgs("action.rename_repo", act.GetContent(), act.GetRepoAbsoluteLink(), act.ShortRepoPath())
+ link.Href = act.GetRepoAbsoluteLink()
+ case activities_model.ActionCommitRepo:
link.Href = toBranchLink(act)
if len(act.Content) != 0 {
- title += ctx.TrHTMLEscapeArgs("action.commit_repo", act.GetRepoLink(), link.Href, act.GetBranch(), act.ShortRepoPath())
+ title += ctx.TrHTMLEscapeArgs("action.commit_repo", act.GetRepoAbsoluteLink(), link.Href, act.GetBranch(), act.ShortRepoPath())
} else {
- title += ctx.TrHTMLEscapeArgs("action.create_branch", act.GetRepoLink(), link.Href, act.GetBranch(), act.ShortRepoPath())
+ title += ctx.TrHTMLEscapeArgs("action.create_branch", act.GetRepoAbsoluteLink(), link.Href, act.GetBranch(), act.ShortRepoPath())
}
- case models.ActionCreateIssue:
+ case activities_model.ActionCreateIssue:
link.Href = toIssueLink(act)
title += ctx.TrHTMLEscapeArgs("action.create_issue", link.Href, act.GetIssueInfos()[0], act.ShortRepoPath())
- case models.ActionCreatePullRequest:
+ case activities_model.ActionCreatePullRequest:
link.Href = toPullLink(act)
title += ctx.TrHTMLEscapeArgs("action.create_pull_request", link.Href, act.GetIssueInfos()[0], act.ShortRepoPath())
- case models.ActionTransferRepo:
- link.Href = act.GetRepoLink()
- title += ctx.TrHTMLEscapeArgs("action.transfer_repo", act.GetContent(), act.GetRepoLink(), act.ShortRepoPath())
- case models.ActionPushTag:
+ case activities_model.ActionTransferRepo:
+ link.Href = act.GetRepoAbsoluteLink()
+ title += ctx.TrHTMLEscapeArgs("action.transfer_repo", act.GetContent(), act.GetRepoAbsoluteLink(), act.ShortRepoPath())
+ case activities_model.ActionPushTag:
link.Href = toTagLink(act)
- title += ctx.TrHTMLEscapeArgs("action.push_tag", act.GetRepoLink(), link.Href, act.GetTag(), act.ShortRepoPath())
- case models.ActionCommentIssue:
+ title += ctx.TrHTMLEscapeArgs("action.push_tag", act.GetRepoAbsoluteLink(), link.Href, act.GetTag(), act.ShortRepoPath())
+ case activities_model.ActionCommentIssue:
issueLink := toIssueLink(act)
if link.Href == "#" {
link.Href = issueLink
}
title += ctx.TrHTMLEscapeArgs("action.comment_issue", issueLink, act.GetIssueInfos()[0], act.ShortRepoPath())
- case models.ActionMergePullRequest:
+ case activities_model.ActionMergePullRequest:
pullLink := toPullLink(act)
if link.Href == "#" {
link.Href = pullLink
}
title += ctx.TrHTMLEscapeArgs("action.merge_pull_request", pullLink, act.GetIssueInfos()[0], act.ShortRepoPath())
- case models.ActionCloseIssue:
+ case activities_model.ActionCloseIssue:
issueLink := toIssueLink(act)
if link.Href == "#" {
link.Href = issueLink
}
title += ctx.TrHTMLEscapeArgs("action.close_issue", issueLink, act.GetIssueInfos()[0], act.ShortRepoPath())
- case models.ActionReopenIssue:
+ case activities_model.ActionReopenIssue:
issueLink := toIssueLink(act)
if link.Href == "#" {
link.Href = issueLink
}
title += ctx.TrHTMLEscapeArgs("action.reopen_issue", issueLink, act.GetIssueInfos()[0], act.ShortRepoPath())
- case models.ActionClosePullRequest:
+ case activities_model.ActionClosePullRequest:
pullLink := toPullLink(act)
if link.Href == "#" {
link.Href = pullLink
}
title += ctx.TrHTMLEscapeArgs("action.close_pull_request", pullLink, act.GetIssueInfos()[0], act.ShortRepoPath())
- case models.ActionReopenPullRequest:
+ case activities_model.ActionReopenPullRequest:
pullLink := toPullLink(act)
if link.Href == "#" {
link.Href = pullLink
}
title += ctx.TrHTMLEscapeArgs("action.reopen_pull_request", pullLink, act.GetIssueInfos()[0], act.ShortRepoPath())
- case models.ActionDeleteTag:
- link.Href = act.GetRepoLink()
- title += ctx.TrHTMLEscapeArgs("action.delete_tag", act.GetRepoLink(), act.GetTag(), act.ShortRepoPath())
- case models.ActionDeleteBranch:
- link.Href = act.GetRepoLink()
- title += ctx.TrHTMLEscapeArgs("action.delete_branch", act.GetRepoLink(), html.EscapeString(act.GetBranch()), act.ShortRepoPath())
- case models.ActionMirrorSyncPush:
+ case activities_model.ActionDeleteTag:
+ link.Href = act.GetRepoAbsoluteLink()
+ title += ctx.TrHTMLEscapeArgs("action.delete_tag", act.GetRepoAbsoluteLink(), act.GetTag(), act.ShortRepoPath())
+ case activities_model.ActionDeleteBranch:
+ link.Href = act.GetRepoAbsoluteLink()
+ title += ctx.TrHTMLEscapeArgs("action.delete_branch", act.GetRepoAbsoluteLink(), html.EscapeString(act.GetBranch()), act.ShortRepoPath())
+ case activities_model.ActionMirrorSyncPush:
srcLink := toSrcLink(act)
if link.Href == "#" {
link.Href = srcLink
}
- title += ctx.TrHTMLEscapeArgs("action.mirror_sync_push", act.GetRepoLink(), srcLink, act.GetBranch(), act.ShortRepoPath())
- case models.ActionMirrorSyncCreate:
+ title += ctx.TrHTMLEscapeArgs("action.mirror_sync_push", act.GetRepoAbsoluteLink(), srcLink, act.GetBranch(), act.ShortRepoPath())
+ case activities_model.ActionMirrorSyncCreate:
srcLink := toSrcLink(act)
if link.Href == "#" {
link.Href = srcLink
}
- title += ctx.TrHTMLEscapeArgs("action.mirror_sync_create", act.GetRepoLink(), srcLink, act.GetBranch(), act.ShortRepoPath())
- case models.ActionMirrorSyncDelete:
- link.Href = act.GetRepoLink()
- title += ctx.TrHTMLEscapeArgs("action.mirror_sync_delete", act.GetRepoLink(), act.GetBranch(), act.ShortRepoPath())
- case models.ActionApprovePullRequest:
+ title += ctx.TrHTMLEscapeArgs("action.mirror_sync_create", act.GetRepoAbsoluteLink(), srcLink, act.GetBranch(), act.ShortRepoPath())
+ case activities_model.ActionMirrorSyncDelete:
+ link.Href = act.GetRepoAbsoluteLink()
+ title += ctx.TrHTMLEscapeArgs("action.mirror_sync_delete", act.GetRepoAbsoluteLink(), act.GetBranch(), act.ShortRepoPath())
+ case activities_model.ActionApprovePullRequest:
pullLink := toPullLink(act)
title += ctx.TrHTMLEscapeArgs("action.approve_pull_request", pullLink, act.GetIssueInfos()[0], act.ShortRepoPath())
- case models.ActionRejectPullRequest:
+ case activities_model.ActionRejectPullRequest:
pullLink := toPullLink(act)
title += ctx.TrHTMLEscapeArgs("action.reject_pull_request", pullLink, act.GetIssueInfos()[0], act.ShortRepoPath())
- case models.ActionCommentPull:
+ case activities_model.ActionCommentPull:
pullLink := toPullLink(act)
title += ctx.TrHTMLEscapeArgs("action.comment_pull", pullLink, act.GetIssueInfos()[0], act.ShortRepoPath())
- case models.ActionPublishRelease:
+ case activities_model.ActionPublishRelease:
releaseLink := toReleaseLink(act)
if link.Href == "#" {
link.Href = releaseLink
}
- title += ctx.TrHTMLEscapeArgs("action.publish_release", act.GetRepoLink(), releaseLink, act.ShortRepoPath(), act.Content)
- case models.ActionPullReviewDismissed:
+ title += ctx.TrHTMLEscapeArgs("action.publish_release", act.GetRepoAbsoluteLink(), releaseLink, act.ShortRepoPath(), act.Content)
+ case activities_model.ActionPullReviewDismissed:
pullLink := toPullLink(act)
title += ctx.TrHTMLEscapeArgs("action.review_dismissed", pullLink, act.GetIssueInfos()[0], act.ShortRepoPath(), act.GetIssueInfos()[1])
- case models.ActionStarRepo:
- link.Href = act.GetRepoLink()
- title += ctx.TrHTMLEscapeArgs("action.starred_repo", act.GetRepoLink(), act.GetRepoPath())
- case models.ActionWatchRepo:
- link.Href = act.GetRepoLink()
- title += ctx.TrHTMLEscapeArgs("action.watched_repo", act.GetRepoLink(), act.GetRepoPath())
+ case activities_model.ActionStarRepo:
+ link.Href = act.GetRepoAbsoluteLink()
+ title += ctx.TrHTMLEscapeArgs("action.starred_repo", act.GetRepoAbsoluteLink(), act.GetRepoPath())
+ case activities_model.ActionWatchRepo:
+ link.Href = act.GetRepoAbsoluteLink()
+ title += ctx.TrHTMLEscapeArgs("action.watched_repo", act.GetRepoAbsoluteLink(), act.GetRepoPath())
default:
return nil, fmt.Errorf("unknown action type: %v", act.OpType)
}
@@ -191,16 +191,16 @@ func feedActionsToFeedItems(ctx *context.Context, actions models.ActionList) (it
// description & content
{
switch act.OpType {
- case models.ActionCommitRepo, models.ActionMirrorSyncPush:
+ case activities_model.ActionCommitRepo, activities_model.ActionMirrorSyncPush:
push := templates.ActionContent2Commits(act)
- repoLink := act.GetRepoLink()
+ repoLink := act.GetRepoAbsoluteLink()
for _, commit := range push.Commits {
if len(desc) != 0 {
desc += "\n\n"
}
desc += fmt.Sprintf("%s\n%s",
- html.EscapeString(fmt.Sprintf("%s/commit/%s", act.GetRepoLink(), commit.Sha1)),
+ html.EscapeString(fmt.Sprintf("%s/commit/%s", act.GetRepoAbsoluteLink(), commit.Sha1)),
commit.Sha1,
templates.RenderCommitMessage(ctx, commit.Message, repoLink, nil),
)
@@ -209,23 +209,23 @@ func feedActionsToFeedItems(ctx *context.Context, actions models.ActionList) (it
if push.Len > 1 {
link = &feeds.Link{Href: fmt.Sprintf("%s/%s", setting.AppSubURL, push.CompareURL)}
} else if push.Len == 1 {
- link = &feeds.Link{Href: fmt.Sprintf("%s/commit/%s", act.GetRepoLink(), push.Commits[0].Sha1)}
+ link = &feeds.Link{Href: fmt.Sprintf("%s/commit/%s", act.GetRepoAbsoluteLink(), push.Commits[0].Sha1)}
}
- case models.ActionCreateIssue, models.ActionCreatePullRequest:
+ case activities_model.ActionCreateIssue, activities_model.ActionCreatePullRequest:
desc = strings.Join(act.GetIssueInfos(), "#")
content = renderMarkdown(ctx, act, act.GetIssueContent())
- case models.ActionCommentIssue, models.ActionApprovePullRequest, models.ActionRejectPullRequest, models.ActionCommentPull:
+ case activities_model.ActionCommentIssue, activities_model.ActionApprovePullRequest, activities_model.ActionRejectPullRequest, activities_model.ActionCommentPull:
desc = act.GetIssueTitle()
comment := act.GetIssueInfos()[1]
if len(comment) != 0 {
desc += "\n\n" + renderMarkdown(ctx, act, comment)
}
- case models.ActionMergePullRequest:
+ case activities_model.ActionMergePullRequest:
desc = act.GetIssueInfos()[1]
- case models.ActionCloseIssue, models.ActionReopenIssue, models.ActionClosePullRequest, models.ActionReopenPullRequest:
+ case activities_model.ActionCloseIssue, activities_model.ActionReopenIssue, activities_model.ActionClosePullRequest, activities_model.ActionReopenPullRequest:
desc = act.GetIssueTitle()
- case models.ActionPullReviewDismissed:
+ case activities_model.ActionPullReviewDismissed:
desc = ctx.Tr("action.review_dismissed_reason") + "\n\n" + act.GetIssueInfos()[2]
}
}
diff --git a/routers/web/feed/profile.go b/routers/web/feed/profile.go
index 61a39755f5..0e11f210ce 100644
--- a/routers/web/feed/profile.go
+++ b/routers/web/feed/profile.go
@@ -8,7 +8,7 @@ import (
"net/http"
"time"
- "code.gitea.io/gitea/models"
+ activities_model "code.gitea.io/gitea/models/activities"
"code.gitea.io/gitea/modules/context"
"github.com/gorilla/feeds"
@@ -26,10 +26,12 @@ func ShowUserFeedAtom(ctx *context.Context) {
// showUserFeed show user activity as RSS / Atom feed
func showUserFeed(ctx *context.Context, formatType string) {
- actions, err := models.GetFeeds(ctx, models.GetFeedsOptions{
+ includePrivate := ctx.IsSigned && (ctx.Doer.IsAdmin || ctx.Doer.ID == ctx.ContextUser.ID)
+
+ actions, err := activities_model.GetFeeds(ctx, activities_model.GetFeedsOptions{
RequestedUser: ctx.ContextUser,
Actor: ctx.Doer,
- IncludePrivate: false,
+ IncludePrivate: includePrivate,
OnlyPerformedBy: !ctx.ContextUser.IsOrganization(),
IncludeDeleted: false,
Date: ctx.FormString("date"),
diff --git a/routers/web/feed/repo.go b/routers/web/feed/repo.go
index ac856195b9..027f90872f 100644
--- a/routers/web/feed/repo.go
+++ b/routers/web/feed/repo.go
@@ -7,7 +7,7 @@ package feed
import (
"time"
- "code.gitea.io/gitea/models"
+ activities_model "code.gitea.io/gitea/models/activities"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/context"
@@ -16,7 +16,7 @@ import (
// ShowRepoFeed shows user activity on the repo as RSS / Atom feed
func ShowRepoFeed(ctx *context.Context, repo *repo_model.Repository, formatType string) {
- actions, err := models.GetFeeds(ctx, models.GetFeedsOptions{
+ actions, err := activities_model.GetFeeds(ctx, activities_model.GetFeedsOptions{
RequestedRepo: repo,
Actor: ctx.Doer,
IncludePrivate: true,
diff --git a/routers/web/org/setting.go b/routers/web/org/setting.go
index c22a124e74..3f7bc59856 100644
--- a/routers/web/org/setting.go
+++ b/routers/web/org/setting.go
@@ -24,6 +24,7 @@ import (
user_setting "code.gitea.io/gitea/routers/web/user/setting"
"code.gitea.io/gitea/services/forms"
"code.gitea.io/gitea/services/org"
+ container_service "code.gitea.io/gitea/services/packages/container"
repo_service "code.gitea.io/gitea/services/repository"
user_service "code.gitea.io/gitea/services/user"
)
@@ -88,6 +89,12 @@ func SettingsPost(ctx *context.Context) {
}
return
}
+
+ if err := container_service.UpdateRepositoryNames(ctx, org.AsUser(), form.Name); err != nil {
+ ctx.ServerError("UpdateRepositoryNames", err)
+ return
+ }
+
// reset ctx.org.OrgLink with new name
ctx.Org.OrgLink = setting.AppSubURL + "/org/" + url.PathEscape(form.Name)
log.Trace("Organization name changed: %s -> %s", org.Name, form.Name)
diff --git a/routers/web/org/setting_oauth2.go b/routers/web/org/setting_oauth2.go
new file mode 100644
index 0000000000..47d1141f34
--- /dev/null
+++ b/routers/web/org/setting_oauth2.go
@@ -0,0 +1,93 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package org
+
+import (
+ "fmt"
+ "net/http"
+
+ "code.gitea.io/gitea/models/auth"
+ "code.gitea.io/gitea/modules/base"
+ "code.gitea.io/gitea/modules/context"
+ "code.gitea.io/gitea/modules/setting"
+ user_setting "code.gitea.io/gitea/routers/web/user/setting"
+)
+
+const (
+ tplSettingsApplications base.TplName = "org/settings/applications"
+ tplSettingsOAuthApplicationEdit base.TplName = "org/settings/applications_oauth2_edit"
+)
+
+func newOAuth2CommonHandlers(org *context.Organization) *user_setting.OAuth2CommonHandlers {
+ return &user_setting.OAuth2CommonHandlers{
+ OwnerID: org.Organization.ID,
+ BasePathList: fmt.Sprintf("%s/org/%s/settings/applications", setting.AppSubURL, org.Organization.Name),
+ BasePathEditPrefix: fmt.Sprintf("%s/org/%s/settings/applications/oauth2", setting.AppSubURL, org.Organization.Name),
+ TplAppEdit: tplSettingsOAuthApplicationEdit,
+ }
+}
+
+// Applications render org applications page (for org, at the moment, there are only OAuth2 applications)
+func Applications(ctx *context.Context) {
+ ctx.Data["Title"] = ctx.Tr("settings.applications")
+ ctx.Data["PageIsOrgSettings"] = true
+ ctx.Data["PageIsSettingsApplications"] = true
+
+ apps, err := auth.GetOAuth2ApplicationsByUserID(ctx, ctx.Org.Organization.ID)
+ if err != nil {
+ ctx.ServerError("GetOAuth2ApplicationsByUserID", err)
+ return
+ }
+ ctx.Data["Applications"] = apps
+
+ ctx.HTML(http.StatusOK, tplSettingsApplications)
+}
+
+// OAuthApplicationsPost response for adding an oauth2 application
+func OAuthApplicationsPost(ctx *context.Context) {
+ ctx.Data["Title"] = ctx.Tr("settings.applications")
+ ctx.Data["PageIsOrgSettings"] = true
+ ctx.Data["PageIsSettingsApplications"] = true
+
+ oa := newOAuth2CommonHandlers(ctx.Org)
+ oa.AddApp(ctx)
+}
+
+// OAuth2ApplicationShow displays the given application
+func OAuth2ApplicationShow(ctx *context.Context) {
+ ctx.Data["PageIsOrgSettings"] = true
+ ctx.Data["PageIsSettingsApplications"] = true
+
+ oa := newOAuth2CommonHandlers(ctx.Org)
+ oa.EditShow(ctx)
+}
+
+// OAuth2ApplicationEdit response for editing oauth2 application
+func OAuth2ApplicationEdit(ctx *context.Context) {
+ ctx.Data["Title"] = ctx.Tr("settings.applications")
+ ctx.Data["PageIsOrgSettings"] = true
+ ctx.Data["PageIsSettingsApplications"] = true
+
+ oa := newOAuth2CommonHandlers(ctx.Org)
+ oa.EditSave(ctx)
+}
+
+// OAuthApplicationsRegenerateSecret handles the post request for regenerating the secret
+func OAuthApplicationsRegenerateSecret(ctx *context.Context) {
+ ctx.Data["Title"] = ctx.Tr("settings")
+ ctx.Data["PageIsOrgSettings"] = true
+ ctx.Data["PageIsSettingsApplications"] = true
+
+ oa := newOAuth2CommonHandlers(ctx.Org)
+ oa.RegenerateSecret(ctx)
+}
+
+// DeleteOAuth2Application deletes the given oauth2 application
+func DeleteOAuth2Application(ctx *context.Context) {
+ oa := newOAuth2CommonHandlers(ctx.Org)
+ oa.DeleteApp(ctx)
+}
+
+// TODO: revokes the grant with the given id
diff --git a/routers/web/org/teams.go b/routers/web/org/teams.go
index 284fb096f3..13c88565c4 100644
--- a/routers/web/org/teams.go
+++ b/routers/web/org/teams.go
@@ -26,6 +26,7 @@ import (
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/routers/utils"
"code.gitea.io/gitea/services/forms"
+ org_service "code.gitea.io/gitea/services/org"
)
const (
@@ -194,7 +195,7 @@ func TeamsRepoAction(ctx *context.Context) {
ctx.ServerError("GetRepositoryByName", err)
return
}
- err = models.AddRepository(ctx.Org.Team, repo)
+ err = org_service.TeamAddRepository(ctx.Org.Team, repo)
case "remove":
err = models.RemoveRepository(ctx.Org.Team, ctx.FormInt64("repoid"))
case "addall":
@@ -339,7 +340,7 @@ func SearchTeam(ctx *context.Context) {
}
opts := &organization.SearchTeamOptions{
- UserID: ctx.Doer.ID,
+ // UserID is not set because the router already requires the doer to be an org admin. Thus, we don't need to restrict to teams that the user belongs in
Keyword: ctx.FormTrim("q"),
OrgID: ctx.Org.Organization.ID,
IncludeDesc: ctx.FormString("include_desc") == "" || ctx.FormBool("include_desc"),
@@ -416,7 +417,11 @@ func EditTeamPost(ctx *context.Context) {
isIncludeAllChanged = true
t.IncludesAllRepositories = includesAllRepositories
}
+ t.CanCreateOrgRepo = form.CanCreateOrgRepo
+ } else {
+ t.CanCreateOrgRepo = true
}
+
t.Description = form.Description
if t.AccessMode < perm.AccessModeAdmin {
units := make([]organization.TeamUnit, 0, len(unitPerms))
@@ -433,7 +438,6 @@ func EditTeamPost(ctx *context.Context) {
return
}
}
- t.CanCreateOrgRepo = form.CanCreateOrgRepo
if ctx.HasError() {
ctx.HTML(http.StatusOK, tplTeamNew)
diff --git a/routers/web/repo/activity.go b/routers/web/repo/activity.go
index b2f25ebe72..316cbcd95f 100644
--- a/routers/web/repo/activity.go
+++ b/routers/web/repo/activity.go
@@ -8,7 +8,7 @@ import (
"net/http"
"time"
- "code.gitea.io/gitea/models"
+ activities_model "code.gitea.io/gitea/models/activities"
"code.gitea.io/gitea/models/unit"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/context"
@@ -47,12 +47,12 @@ func Activity(ctx *context.Context) {
ctx.Data["Period"] = "weekly"
timeFrom = timeUntil.Add(-time.Hour * 168)
}
- ctx.Data["DateFrom"] = timeFrom.Format("January 2, 2006")
- ctx.Data["DateUntil"] = timeUntil.Format("January 2, 2006")
+ ctx.Data["DateFrom"] = timeFrom.UTC().Format(time.RFC3339)
+ ctx.Data["DateUntil"] = timeUntil.UTC().Format(time.RFC3339)
ctx.Data["PeriodText"] = ctx.Tr("repo.activity.period." + ctx.Data["Period"].(string))
var err error
- if ctx.Data["Activity"], err = models.GetActivityStats(ctx, ctx.Repo.Repository, timeFrom,
+ if ctx.Data["Activity"], err = activities_model.GetActivityStats(ctx, ctx.Repo.Repository, timeFrom,
ctx.Repo.CanRead(unit.TypeReleases),
ctx.Repo.CanRead(unit.TypeIssues),
ctx.Repo.CanRead(unit.TypePullRequests),
@@ -61,7 +61,7 @@ func Activity(ctx *context.Context) {
return
}
- if ctx.PageData["repoActivityTopAuthors"], err = models.GetActivityStatsTopAuthors(ctx, ctx.Repo.Repository, timeFrom, 10); err != nil {
+ if ctx.PageData["repoActivityTopAuthors"], err = activities_model.GetActivityStatsTopAuthors(ctx, ctx.Repo.Repository, timeFrom, 10); err != nil {
ctx.ServerError("GetActivityStatsTopAuthors", err)
return
}
@@ -94,7 +94,7 @@ func ActivityAuthors(ctx *context.Context) {
}
var err error
- authors, err := models.GetActivityStatsTopAuthors(ctx, ctx.Repo.Repository, timeFrom, 10)
+ authors, err := activities_model.GetActivityStatsTopAuthors(ctx, ctx.Repo.Repository, timeFrom, 10)
if err != nil {
ctx.ServerError("GetActivityStatsTopAuthors", err)
return
diff --git a/routers/web/repo/blame.go b/routers/web/repo/blame.go
index 06c43aec19..c53a53b471 100644
--- a/routers/web/repo/blame.go
+++ b/routers/web/repo/blame.go
@@ -40,7 +40,7 @@ type blameRow struct {
CommitMessage string
CommitSince gotemplate.HTML
Code gotemplate.HTML
- EscapeStatus charset.EscapeStatus
+ EscapeStatus *charset.EscapeStatus
}
// RefBlame render blame page
@@ -235,7 +235,7 @@ func renderBlame(ctx *context.Context, blameParts []git.BlamePart, commitNames m
}
lines := make([]string, 0)
rows := make([]*blameRow, 0)
- escapeStatus := charset.EscapeStatus{}
+ escapeStatus := &charset.EscapeStatus{}
i := 0
commitCnt := 0
@@ -280,7 +280,7 @@ func renderBlame(ctx *context.Context, blameParts []git.BlamePart, commitNames m
fileName := fmt.Sprintf("%v", ctx.Data["FileName"])
line = highlight.Code(fileName, language, line)
- br.EscapeStatus, line = charset.EscapeControlString(line)
+ br.EscapeStatus, line = charset.EscapeControlHTML(line, ctx.Locale)
br.Code = gotemplate.HTML(line)
rows = append(rows, br)
escapeStatus = escapeStatus.Or(br.EscapeStatus)
diff --git a/routers/web/repo/branch.go b/routers/web/repo/branch.go
index d14ba6cbe9..d1f1255db4 100644
--- a/routers/web/repo/branch.go
+++ b/routers/web/repo/branch.go
@@ -427,5 +427,5 @@ func CreateBranch(ctx *context.Context) {
}
ctx.Flash.Success(ctx.Tr("repo.branch.create_success", form.NewBranchName))
- ctx.Redirect(ctx.Repo.RepoLink + "/src/branch/" + util.PathEscapeSegments(form.NewBranchName))
+ ctx.Redirect(ctx.Repo.RepoLink + "/src/branch/" + util.PathEscapeSegments(form.NewBranchName) + "/" + util.PathEscapeSegments(form.CurrentPath))
}
diff --git a/routers/web/repo/commit.go b/routers/web/repo/commit.go
index 164dc181f0..a6553256b0 100644
--- a/routers/web/repo/commit.go
+++ b/routers/web/repo/commit.go
@@ -83,7 +83,7 @@ func Commits(ctx *context.Context) {
ctx.Data["CommitCount"] = commitsCount
ctx.Data["RefName"] = ctx.Repo.RefName
- pager := context.NewPagination(int(commitsCount), setting.Git.CommitsRangeSize, page, 5)
+ pager := context.NewPagination(int(commitsCount), pageSize, page, 5)
pager.SetDefaultParams(ctx)
ctx.Data["Page"] = pager
diff --git a/routers/web/repo/compare.go b/routers/web/repo/compare.go
index 5c46882f3d..e7e68d3c5e 100644
--- a/routers/web/repo/compare.go
+++ b/routers/web/repo/compare.go
@@ -17,7 +17,6 @@ import (
"path/filepath"
"strings"
- "code.gitea.io/gitea/models"
git_model "code.gitea.io/gitea/models/git"
issues_model "code.gitea.io/gitea/models/issues"
access_model "code.gitea.io/gitea/models/perm/access"
@@ -113,17 +112,17 @@ func setCsvCompareContext(ctx *context.Context) {
Error string
}
- ctx.Data["CreateCsvDiff"] = func(diffFile *gitdiff.DiffFile, baseCommit, headCommit *git.Commit) CsvDiffResult {
- if diffFile == nil || baseCommit == nil || headCommit == nil {
+ ctx.Data["CreateCsvDiff"] = func(diffFile *gitdiff.DiffFile, baseBlob, headBlob *git.Blob) CsvDiffResult {
+ if diffFile == nil {
return CsvDiffResult{nil, ""}
}
errTooLarge := errors.New(ctx.Locale.Tr("repo.error.csv.too_large"))
- csvReaderFromCommit := func(ctx *markup.RenderContext, c *git.Commit) (*csv.Reader, io.Closer, error) {
- blob, err := c.GetBlobByPath(diffFile.Name)
- if err != nil {
- return nil, nil, err
+ csvReaderFromCommit := func(ctx *markup.RenderContext, blob *git.Blob) (*csv.Reader, io.Closer, error) {
+ if blob == nil {
+ // It's ok for blob to be nil (file added or deleted)
+ return nil, nil, nil
}
if setting.UI.CSV.MaxFileSize != 0 && setting.UI.CSV.MaxFileSize < blob.Size() {
@@ -139,28 +138,28 @@ func setCsvCompareContext(ctx *context.Context) {
return csvReader, reader, err
}
- baseReader, baseBlobCloser, err := csvReaderFromCommit(&markup.RenderContext{Ctx: ctx, RelativePath: diffFile.OldName}, baseCommit)
+ baseReader, baseBlobCloser, err := csvReaderFromCommit(&markup.RenderContext{Ctx: ctx, RelativePath: diffFile.OldName}, baseBlob)
if baseBlobCloser != nil {
defer baseBlobCloser.Close()
}
- if err == errTooLarge {
- return CsvDiffResult{nil, err.Error()}
- }
if err != nil {
- log.Error("CreateCsvDiff error whilst creating baseReader from file %s in commit %s in %s: %v", diffFile.Name, baseCommit.ID.String(), ctx.Repo.Repository.Name, err)
- return CsvDiffResult{nil, "unable to load file from base commit"}
+ if err == errTooLarge {
+ return CsvDiffResult{nil, err.Error()}
+ }
+ log.Error("error whilst creating csv.Reader from file %s in base commit %s in %s: %v", diffFile.Name, baseBlob.ID.String(), ctx.Repo.Repository.Name, err)
+ return CsvDiffResult{nil, "unable to load file"}
}
- headReader, headBlobCloser, err := csvReaderFromCommit(&markup.RenderContext{Ctx: ctx, RelativePath: diffFile.Name}, headCommit)
+ headReader, headBlobCloser, err := csvReaderFromCommit(&markup.RenderContext{Ctx: ctx, RelativePath: diffFile.Name}, headBlob)
if headBlobCloser != nil {
defer headBlobCloser.Close()
}
- if err == errTooLarge {
- return CsvDiffResult{nil, err.Error()}
- }
if err != nil {
- log.Error("CreateCsvDiff error whilst creating headReader from file %s in commit %s in %s: %v", diffFile.Name, headCommit.ID.String(), ctx.Repo.Repository.Name, err)
- return CsvDiffResult{nil, "unable to load file from head commit"}
+ if err == errTooLarge {
+ return CsvDiffResult{nil, err.Error()}
+ }
+ log.Error("error whilst creating csv.Reader from file %s in head commit %s in %s: %v", diffFile.Name, headBlob.ID.String(), ctx.Repo.Repository.Name, err)
+ return CsvDiffResult{nil, "unable to load file"}
}
sections, err := gitdiff.CreateCsvDiff(diffFile, baseReader, headReader)
@@ -459,7 +458,7 @@ func ParseCompareInfo(ctx *context.Context) *CompareInfo {
if rootRepo != nil &&
rootRepo.ID != ci.HeadRepo.ID &&
rootRepo.ID != baseRepo.ID {
- canRead := models.CheckRepoUnitUser(ctx, rootRepo, ctx.Doer, unit.TypeCode)
+ canRead := access_model.CheckRepoUnitUser(ctx, rootRepo, ctx.Doer, unit.TypeCode)
if canRead {
ctx.Data["RootRepo"] = rootRepo
if !fileOnly {
@@ -484,7 +483,7 @@ func ParseCompareInfo(ctx *context.Context) *CompareInfo {
ownForkRepo.ID != ci.HeadRepo.ID &&
ownForkRepo.ID != baseRepo.ID &&
(rootRepo == nil || ownForkRepo.ID != rootRepo.ID) {
- canRead := models.CheckRepoUnitUser(ctx, ownForkRepo, ctx.Doer, unit.TypeCode)
+ canRead := access_model.CheckRepoUnitUser(ctx, ownForkRepo, ctx.Doer, unit.TypeCode)
if canRead {
ctx.Data["OwnForkRepo"] = ownForkRepo
if !fileOnly {
@@ -785,7 +784,24 @@ func CompareDiff(ctx *context.Context) {
ctx.Data["IsRepoToolbarCommits"] = true
ctx.Data["IsDiffCompare"] = true
ctx.Data["RequireTribute"] = true
- setTemplateIfExists(ctx, pullRequestTemplateKey, nil, pullRequestTemplateCandidates)
+ templateErrs := setTemplateIfExists(ctx, pullRequestTemplateKey, pullRequestTemplateCandidates)
+
+ if len(templateErrs) > 0 {
+ ctx.Flash.Warning(renderErrorOfTemplates(ctx, templateErrs), true)
+ }
+
+ // If a template content is set, prepend the "content". In this case that's only
+ // applicable if you have one commit to compare and that commit has a message.
+ // In that case the commit message will be prepend to the template body.
+ if templateContent, ok := ctx.Data[pullRequestTemplateKey].(string); ok && templateContent != "" {
+ if content, ok := ctx.Data["content"].(string); ok && content != "" {
+ // Re-use the same key as that's priortized over the "content" key.
+ // Add two new lines between the content to ensure there's always at least
+ // one empty line between them.
+ ctx.Data[pullRequestTemplateKey] = content + "\n\n" + templateContent
+ }
+ }
+
ctx.Data["IsAttachmentEnabled"] = setting.Attachment.Enabled
upload.AddUploadContext(ctx, "comment")
diff --git a/routers/web/repo/download.go b/routers/web/repo/download.go
index 6755cda874..cd2d305de6 100644
--- a/routers/web/repo/download.go
+++ b/routers/web/repo/download.go
@@ -10,7 +10,6 @@ import (
"time"
git_model "code.gitea.io/gitea/models/git"
- "code.gitea.io/gitea/modules/cache"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/httpcache"
@@ -99,12 +98,7 @@ func getBlobForEntry(ctx *context.Context) (blob *git.Blob, lastModified time.Ti
return
}
- var c *git.LastCommitCache
- if setting.CacheService.LastCommit.Enabled && ctx.Repo.CommitsCount >= setting.CacheService.LastCommit.CommitsCount {
- c = git.NewLastCommitCache(ctx.Repo.Repository.FullName(), ctx.Repo.GitRepo, setting.LastCommitCacheTTLSeconds, cache.GetCache())
- }
-
- info, _, err := git.Entries([]*git.TreeEntry{entry}).GetCommitsInfo(ctx, ctx.Repo.Commit, path.Dir("/" + ctx.Repo.TreePath)[1:], c)
+ info, _, err := git.Entries([]*git.TreeEntry{entry}).GetCommitsInfo(ctx, ctx.Repo.Commit, path.Dir("/" + ctx.Repo.TreePath)[1:])
if err != nil {
ctx.ServerError("GetCommitsInfo", err)
return
diff --git a/routers/web/repo/editor.go b/routers/web/repo/editor.go
index b510fd504d..e8fc020450 100644
--- a/routers/web/repo/editor.go
+++ b/routers/web/repo/editor.go
@@ -13,6 +13,7 @@ import (
"code.gitea.io/gitea/models"
git_model "code.gitea.io/gitea/models/git"
+ repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/charset"
@@ -763,7 +764,7 @@ func UploadFileToServer(ctx *context.Context) {
return
}
- upload, err := models.NewUpload(name, buf, file)
+ upload, err := repo_model.NewUpload(name, buf, file)
if err != nil {
ctx.Error(http.StatusInternalServerError, fmt.Sprintf("NewUpload: %v", err))
return
@@ -783,7 +784,7 @@ func RemoveUploadFileFromServer(ctx *context.Context) {
return
}
- if err := models.DeleteUploadByUUID(form.File); err != nil {
+ if err := repo_model.DeleteUploadByUUID(form.File); err != nil {
ctx.Error(http.StatusInternalServerError, fmt.Sprintf("DeleteUploadByUUID: %v", err))
return
}
diff --git a/routers/web/repo/http.go b/routers/web/repo/http.go
index 6a85bca16b..5aa2bcd134 100644
--- a/routers/web/repo/http.go
+++ b/routers/web/repo/http.go
@@ -474,11 +474,12 @@ func serviceRPC(ctx gocontext.Context, h serviceHandler, service string) {
cmd := git.NewCommand(h.r.Context(), service, "--stateless-rpc", h.dir)
cmd.SetDescription(fmt.Sprintf("%s %s %s [repo_path: %s]", git.GitExecutable, service, "--stateless-rpc", h.dir))
if err := cmd.Run(&git.RunOpts{
- Dir: h.dir,
- Env: append(os.Environ(), h.environ...),
- Stdout: h.w,
- Stdin: reqBody,
- Stderr: &stderr,
+ Dir: h.dir,
+ Env: append(os.Environ(), h.environ...),
+ Stdout: h.w,
+ Stdin: reqBody,
+ Stderr: &stderr,
+ UseContextTimeout: true,
}); err != nil {
if err.Error() != "signal: killed" {
log.Error("Fail to serve RPC(%s) in %s: %v - %s", service, h.dir, err, stderr.String())
diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go
index 6856c1e0be..2af3d9c430 100644
--- a/routers/web/repo/issue.go
+++ b/routers/web/repo/issue.go
@@ -10,16 +10,15 @@ import (
stdCtx "context"
"errors"
"fmt"
- "io"
"math/big"
"net/http"
"net/url"
- "path"
+ "sort"
"strconv"
"strings"
"time"
- "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"
issues_model "code.gitea.io/gitea/models/issues"
@@ -31,10 +30,12 @@ import (
"code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/base"
+ "code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/convert"
"code.gitea.io/gitea/modules/git"
issue_indexer "code.gitea.io/gitea/modules/indexer/issues"
+ issue_template "code.gitea.io/gitea/modules/issue/template"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/markup"
"code.gitea.io/gitea/modules/markup/markdown"
@@ -45,6 +46,7 @@ import (
"code.gitea.io/gitea/modules/upload"
"code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
+ "code.gitea.io/gitea/routers/utils"
asymkey_service "code.gitea.io/gitea/services/asymkey"
comment_service "code.gitea.io/gitea/services/comments"
"code.gitea.io/gitea/services/forms"
@@ -70,11 +72,23 @@ const (
// IssueTemplateCandidates issue templates
var IssueTemplateCandidates = []string{
"ISSUE_TEMPLATE.md",
+ "ISSUE_TEMPLATE.yaml",
+ "ISSUE_TEMPLATE.yml",
"issue_template.md",
+ "issue_template.yaml",
+ "issue_template.yml",
".gitea/ISSUE_TEMPLATE.md",
+ ".gitea/ISSUE_TEMPLATE.yaml",
+ ".gitea/ISSUE_TEMPLATE.yml",
".gitea/issue_template.md",
+ ".gitea/issue_template.yaml",
+ ".gitea/issue_template.yml",
".github/ISSUE_TEMPLATE.md",
+ ".github/ISSUE_TEMPLATE.yaml",
+ ".github/ISSUE_TEMPLATE.yml",
".github/issue_template.md",
+ ".github/issue_template.yaml",
+ ".github/issue_template.yml",
}
// MustAllowUserComment checks to make sure if an issue is locked.
@@ -133,7 +147,7 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption uti
var (
assigneeID = ctx.FormInt64("assignee")
- posterID int64
+ posterID = ctx.FormInt64("poster")
mentionedID int64
reviewRequestedID int64
forceEmpty bool
@@ -291,6 +305,12 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption uti
return
}
+ ctx.Data["Posters"], err = repo_model.GetIssuePosters(ctx, repo, isPullOption.IsTrue())
+ if err != nil {
+ ctx.ServerError("GetIssuePosters", err)
+ return
+ }
+
handleTeamMentions(ctx)
if ctx.Written() {
return
@@ -364,6 +384,7 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption uti
ctx.Data["SortType"] = sortType
ctx.Data["MilestoneID"] = milestoneID
ctx.Data["AssigneeID"] = assigneeID
+ ctx.Data["PosterID"] = posterID
ctx.Data["IsShowClosed"] = isShowClosed
ctx.Data["Keyword"] = keyword
if isShowClosed {
@@ -379,6 +400,7 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption uti
pager.AddParam(ctx, "labels", "SelectLabels")
pager.AddParam(ctx, "milestone", "MilestoneID")
pager.AddParam(ctx, "assignee", "AssigneeID")
+ pager.AddParam(ctx, "poster", "PosterID")
ctx.Data["Page"] = pager
}
@@ -714,85 +736,66 @@ func RetrieveRepoMetas(ctx *context.Context, repo *repo_model.Repository, isPull
return labels
}
-func getFileContentFromDefaultBranch(ctx *context.Context, filename string) (string, bool) {
- if ctx.Repo.Commit == nil {
- var err error
- ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(ctx.Repo.Repository.DefaultBranch)
- if err != nil {
- return "", false
- }
+func setTemplateIfExists(ctx *context.Context, ctxDataKey string, possibleFiles []string) map[string]error {
+ commit, err := ctx.Repo.GitRepo.GetBranchCommit(ctx.Repo.Repository.DefaultBranch)
+ if err != nil {
+ return nil
}
- entry, err := ctx.Repo.Commit.GetTreeEntryByPath(filename)
- if err != nil {
- return "", false
- }
- if entry.Blob().Size() >= setting.UI.MaxDisplayFileSize {
- return "", false
- }
- r, err := entry.Blob().DataAsync()
- if err != nil {
- return "", false
- }
- defer r.Close()
- bytes, err := io.ReadAll(r)
- if err != nil {
- return "", false
- }
- return string(bytes), true
-}
-
-func setTemplateIfExists(ctx *context.Context, ctxDataKey string, possibleDirs, possibleFiles []string) {
- templateCandidates := make([]string, 0, len(possibleFiles))
- if ctx.FormString("template") != "" {
- for _, dirName := range possibleDirs {
- templateCandidates = append(templateCandidates, path.Join(dirName, ctx.FormString("template")))
- }
+ templateCandidates := make([]string, 0, 1+len(possibleFiles))
+ if t := ctx.FormString("template"); t != "" {
+ templateCandidates = append(templateCandidates, t)
}
templateCandidates = append(templateCandidates, possibleFiles...) // Append files to the end because they should be fallback
+
+ templateErrs := map[string]error{}
for _, filename := range templateCandidates {
- templateContent, found := getFileContentFromDefaultBranch(ctx, filename)
- if found {
- var meta api.IssueTemplate
- templateBody, err := markdown.ExtractMetadata(templateContent, &meta)
- if err != nil {
- log.Debug("could not extract metadata from %s [%s]: %v", filename, ctx.Repo.Repository.FullName(), err)
- ctx.Data[ctxDataKey] = templateContent
- return
- }
- ctx.Data[issueTemplateTitleKey] = meta.Title
- ctx.Data[ctxDataKey] = templateBody
- labelIDs := make([]string, 0, len(meta.Labels))
- if repoLabels, err := issues_model.GetLabelsByRepoID(ctx, ctx.Repo.Repository.ID, "", db.ListOptions{}); err == nil {
- ctx.Data["Labels"] = repoLabels
- if ctx.Repo.Owner.IsOrganization() {
- if orgLabels, err := issues_model.GetLabelsByOrgID(ctx, ctx.Repo.Owner.ID, ctx.FormString("sort"), db.ListOptions{}); err == nil {
- ctx.Data["OrgLabels"] = orgLabels
- repoLabels = append(repoLabels, orgLabels...)
- }
- }
-
- for _, metaLabel := range meta.Labels {
- for _, repoLabel := range repoLabels {
- if strings.EqualFold(repoLabel.Name, metaLabel) {
- repoLabel.IsChecked = true
- labelIDs = append(labelIDs, strconv.FormatInt(repoLabel.ID, 10))
- break
- }
- }
- }
- }
- if !strings.HasPrefix(meta.Ref, "refs/") { // Assume that the ref intended is always a branch - for tags users should use refs/tags/[
- meta.Ref = "refs/heads/" + meta.Ref
- }
-
- ctx.Data["HasSelectedLabel"] = len(labelIDs) > 0
- ctx.Data["label_ids"] = strings.Join(labelIDs, ",")
- ctx.Data["Reference"] = meta.Ref
- ctx.Data["RefEndName"] = git.RefEndName(meta.Ref)
- return
+ if ok, _ := commit.HasFile(filename); !ok {
+ continue
}
+ template, err := issue_template.UnmarshalFromCommit(commit, filename)
+ if err != nil {
+ templateErrs[filename] = err
+ continue
+ }
+ ctx.Data[issueTemplateTitleKey] = template.Title
+ ctx.Data[ctxDataKey] = template.Content
+
+ if template.Type() == api.IssueTemplateTypeYaml {
+ ctx.Data["Fields"] = template.Fields
+ ctx.Data["TemplateFile"] = template.FileName
+ }
+ labelIDs := make([]string, 0, len(template.Labels))
+ if repoLabels, err := issues_model.GetLabelsByRepoID(ctx, ctx.Repo.Repository.ID, "", db.ListOptions{}); err == nil {
+ ctx.Data["Labels"] = repoLabels
+ if ctx.Repo.Owner.IsOrganization() {
+ if orgLabels, err := issues_model.GetLabelsByOrgID(ctx, ctx.Repo.Owner.ID, ctx.FormString("sort"), db.ListOptions{}); err == nil {
+ ctx.Data["OrgLabels"] = orgLabels
+ repoLabels = append(repoLabels, orgLabels...)
+ }
+ }
+
+ for _, metaLabel := range template.Labels {
+ for _, repoLabel := range repoLabels {
+ if strings.EqualFold(repoLabel.Name, metaLabel) {
+ repoLabel.IsChecked = true
+ labelIDs = append(labelIDs, strconv.FormatInt(repoLabel.ID, 10))
+ break
+ }
+ }
+ }
+
+ }
+ if !strings.HasPrefix(template.Ref, "refs/") { // Assume that the ref intended is always a branch - for tags users should use refs/tags/][
+ template.Ref = git.BranchPrefix + template.Ref
+ }
+ ctx.Data["HasSelectedLabel"] = len(labelIDs) > 0
+ ctx.Data["label_ids"] = strings.Join(labelIDs, ",")
+ ctx.Data["Reference"] = template.Ref
+ ctx.Data["RefEndName"] = git.RefEndName(template.Ref)
+ return templateErrs
}
+ return templateErrs
}
// NewIssue render creating issue page
@@ -841,24 +844,62 @@ func NewIssue(ctx *context.Context) {
}
RetrieveRepoMetas(ctx, ctx.Repo.Repository, false)
- setTemplateIfExists(ctx, issueTemplateKey, context.IssueTemplateDirCandidates, IssueTemplateCandidates)
+
+ _, templateErrs := ctx.IssueTemplatesErrorsFromDefaultBranch()
+ if errs := setTemplateIfExists(ctx, issueTemplateKey, IssueTemplateCandidates); len(errs) > 0 {
+ for k, v := range errs {
+ templateErrs[k] = v
+ }
+ }
if ctx.Written() {
return
}
+ if len(templateErrs) > 0 {
+ ctx.Flash.Warning(renderErrorOfTemplates(ctx, templateErrs), true)
+ }
+
ctx.Data["HasIssuesOrPullsWritePermission"] = ctx.Repo.CanWrite(unit.TypeIssues)
ctx.HTML(http.StatusOK, tplIssueNew)
}
+func renderErrorOfTemplates(ctx *context.Context, errs map[string]error) string {
+ var files []string
+ for k := range errs {
+ files = append(files, k)
+ }
+ sort.Strings(files) // keep the output stable
+
+ var lines []string
+ for _, file := range files {
+ lines = append(lines, fmt.Sprintf("%s: %v", file, errs[file]))
+ }
+
+ flashError, err := ctx.RenderToString(tplAlertDetails, map[string]interface{}{
+ "Message": ctx.Tr("repo.issues.choose.ignore_invalid_templates"),
+ "Summary": ctx.Tr("repo.issues.choose.invalid_templates", len(errs)),
+ "Details": utils.SanitizeFlashErrorString(strings.Join(lines, "\n")),
+ })
+ if err != nil {
+ log.Debug("render flash error: %v", err)
+ flashError = ctx.Tr("repo.issues.choose.ignore_invalid_templates")
+ }
+ return flashError
+}
+
// NewIssueChooseTemplate render creating issue from template page
func NewIssueChooseTemplate(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("repo.issues.new")
ctx.Data["PageIsIssueList"] = true
- issueTemplates := ctx.IssueTemplatesFromDefaultBranch()
+ issueTemplates, errs := ctx.IssueTemplatesErrorsFromDefaultBranch()
ctx.Data["IssueTemplates"] = issueTemplates
+ if len(errs) > 0 {
+ ctx.Flash.Warning(renderErrorOfTemplates(ctx, errs), true)
+ }
+
if len(issueTemplates) == 0 {
// The "issues/new" and "issues/new/choose" share the same query parameters "project" and "milestone", if no template here, just redirect to the "issues/new" page with these parameters.
ctx.Redirect(fmt.Sprintf("%s/issues/new?%s", ctx.Repo.Repository.HTMLURL(), ctx.Req.URL.RawQuery), http.StatusSeeOther)
@@ -911,10 +952,11 @@ func ValidateRepoMetas(ctx *context.Context, form forms.CreateIssueForm, isPull
if err != nil {
return nil, nil, 0, 0
}
- labelIDMark := base.Int64sToMap(labelIDs)
+ labelIDMark := make(container.Set[int64])
+ labelIDMark.AddMultiple(labelIDs...)
for i := range labels {
- if labelIDMark[labels[i].ID] {
+ if labelIDMark.Contains(labels[i].ID) {
labels[i].IsChecked = true
hasSelected = true
}
@@ -1027,6 +1069,13 @@ func NewIssuePost(ctx *context.Context) {
return
}
+ content := form.Content
+ if filename := ctx.Req.Form.Get("template-file"); filename != "" {
+ if template, err := issue_template.UnmarshalFromRepo(ctx.Repo.GitRepo, ctx.Repo.Repository.DefaultBranch, filename); err == nil {
+ content = issue_template.RenderToMarkdown(template, ctx.Req.Form)
+ }
+ }
+
issue := &issues_model.Issue{
RepoID: repo.ID,
Repo: repo,
@@ -1034,7 +1083,7 @@ func NewIssuePost(ctx *context.Context) {
PosterID: ctx.Doer.ID,
Poster: ctx.Doer,
MilestoneID: milestoneID,
- Content: form.Content,
+ Content: content,
Ref: form.Ref,
}
@@ -1250,9 +1299,9 @@ func ViewIssue(ctx *context.Context) {
// Metas.
// Check labels.
- labelIDMark := make(map[int64]bool)
- for i := range issue.Labels {
- labelIDMark[issue.Labels[i].ID] = true
+ labelIDMark := make(container.Set[int64])
+ for _, label := range issue.Labels {
+ labelIDMark.Add(label.ID)
}
labels, err := issues_model.GetLabelsByRepoID(ctx, repo.ID, "", db.ListOptions{})
if err != nil {
@@ -1274,7 +1323,7 @@ func ViewIssue(ctx *context.Context) {
hasSelected := false
for i := range labels {
- if labelIDMark[labels[i].ID] {
+ if labelIDMark.Contains(labels[i].ID) {
labels[i].IsChecked = true
hasSelected = true
}
@@ -1293,11 +1342,16 @@ func ViewIssue(ctx *context.Context) {
if issue.IsPull {
canChooseReviewer := ctx.Repo.CanWrite(unit.TypePullRequests)
- if !canChooseReviewer && ctx.Doer != nil && ctx.IsSigned {
- canChooseReviewer, err = issues_model.IsOfficialReviewer(ctx, issue, ctx.Doer)
- if err != nil {
- ctx.ServerError("IsOfficialReviewer", err)
- return
+ if ctx.Doer != nil && ctx.IsSigned {
+ if !canChooseReviewer {
+ canChooseReviewer = ctx.Doer.ID == issue.PosterID
+ }
+ if !canChooseReviewer {
+ canChooseReviewer, err = issues_model.IsOfficialReviewer(ctx, issue, ctx.Doer)
+ if err != nil {
+ ctx.ServerError("IsOfficialReviewer", err)
+ return
+ }
}
}
@@ -1309,7 +1363,7 @@ func ViewIssue(ctx *context.Context) {
if ctx.IsSigned {
// Update issue-user.
- if err = models.SetIssueReadBy(ctx, issue.ID, ctx.Doer.ID); err != nil {
+ if err = activities_model.SetIssueReadBy(ctx, issue.ID, ctx.Doer.ID); err != nil {
ctx.ServerError("ReadBy", err)
return
}
diff --git a/routers/web/repo/lfs.go b/routers/web/repo/lfs.go
index 0e446f2de0..633b8ab1a5 100644
--- a/routers/web/repo/lfs.go
+++ b/routers/web/repo/lfs.go
@@ -18,6 +18,7 @@ import (
git_model "code.gitea.io/gitea/models/git"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/charset"
+ "code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/git/pipeline"
@@ -176,14 +177,12 @@ func LFSLocks(ctx *context.Context) {
return
}
- filemap := make(map[string]bool, len(filelist))
- for _, name := range filelist {
- filemap[name] = true
- }
+ fileset := make(container.Set[string], len(filelist))
+ fileset.AddMultiple(filelist...)
linkable := make([]bool, len(lfsLocks))
for i, lock := range lfsLocks {
- linkable[i] = filemap[lock.Path]
+ linkable[i] = fileset.Contains(lock.Path)
}
ctx.Data["Linkable"] = linkable
@@ -309,7 +308,7 @@ func LFSFileGet(ctx *context.Context) {
// Building code view blocks with line number on server side.
escapedContent := &bytes.Buffer{}
- ctx.Data["EscapeStatus"], _ = charset.EscapeControlReader(rd, escapedContent)
+ ctx.Data["EscapeStatus"], _ = charset.EscapeControlReader(rd, escapedContent, ctx.Locale)
var output bytes.Buffer
lines := strings.Split(escapedContent.String(), "\n")
diff --git a/routers/web/repo/middlewares.go b/routers/web/repo/middlewares.go
index ae4177cf1e..c9e8eb4a89 100644
--- a/routers/web/repo/middlewares.go
+++ b/routers/web/repo/middlewares.go
@@ -7,7 +7,7 @@ package repo
import (
"fmt"
- admin_model "code.gitea.io/gitea/models/admin"
+ system_model "code.gitea.io/gitea/models/system"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/git"
@@ -24,7 +24,7 @@ func SetEditorconfigIfExists(ctx *context.Context) {
if err != nil && !git.IsErrNotExist(err) {
description := fmt.Sprintf("Error while getting .editorconfig file: %v", err)
- if err := admin_model.CreateRepositoryNotice(description); err != nil {
+ if err := system_model.CreateRepositoryNotice(description); err != nil {
ctx.ServerError("ErrCreatingReporitoryNotice", err)
}
return
diff --git a/routers/web/repo/packages.go b/routers/web/repo/packages.go
index 03ea4fc5f4..57db19aa32 100644
--- a/routers/web/repo/packages.go
+++ b/routers/web/repo/packages.go
@@ -9,9 +9,11 @@ import (
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/packages"
+ "code.gitea.io/gitea/models/unit"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/util"
)
const (
@@ -32,10 +34,11 @@ func Packages(ctx *context.Context) {
PageSize: setting.UI.PackagesPagingNum,
Page: page,
},
- OwnerID: ctx.ContextUser.ID,
- RepoID: ctx.Repo.Repository.ID,
- Type: packages.Type(packageType),
- Name: packages.SearchValue{Value: query},
+ OwnerID: ctx.ContextUser.ID,
+ RepoID: ctx.Repo.Repository.ID,
+ Type: packages.Type(packageType),
+ Name: packages.SearchValue{Value: query},
+ IsInternal: util.OptionalBoolFalse,
})
if err != nil {
ctx.ServerError("SearchLatestVersions", err)
@@ -60,6 +63,9 @@ func Packages(ctx *context.Context) {
ctx.Data["Query"] = query
ctx.Data["PackageType"] = packageType
ctx.Data["HasPackages"] = hasPackages
+ if ctx.Repo != nil {
+ ctx.Data["CanWritePackages"] = ctx.IsUserRepoWriter([]unit.Type{unit.TypePackages}) || ctx.IsUserSiteAdmin()
+ }
ctx.Data["PackageDescriptors"] = pds
ctx.Data["Total"] = total
ctx.Data["RepositoryAccessMap"] = map[int64]bool{ctx.Repo.Repository.ID: true} // There is only the current repository
diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go
index 2a961c3cbc..aa2c4cdb53 100644
--- a/routers/web/repo/pull.go
+++ b/routers/web/repo/pull.go
@@ -17,6 +17,7 @@ import (
"time"
"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"
issues_model "code.gitea.io/gitea/models/issues"
@@ -29,6 +30,7 @@ import (
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/git"
+ issue_template "code.gitea.io/gitea/modules/issue/template"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/notification"
"code.gitea.io/gitea/modules/setting"
@@ -57,11 +59,23 @@ const (
var pullRequestTemplateCandidates = []string{
"PULL_REQUEST_TEMPLATE.md",
+ "PULL_REQUEST_TEMPLATE.yaml",
+ "PULL_REQUEST_TEMPLATE.yml",
"pull_request_template.md",
+ "pull_request_template.yaml",
+ "pull_request_template.yml",
".gitea/PULL_REQUEST_TEMPLATE.md",
+ ".gitea/PULL_REQUEST_TEMPLATE.yaml",
+ ".gitea/PULL_REQUEST_TEMPLATE.yml",
".gitea/pull_request_template.md",
+ ".gitea/pull_request_template.yaml",
+ ".gitea/pull_request_template.yml",
".github/PULL_REQUEST_TEMPLATE.md",
+ ".github/PULL_REQUEST_TEMPLATE.yaml",
+ ".github/PULL_REQUEST_TEMPLATE.yml",
".github/pull_request_template.md",
+ ".github/pull_request_template.yaml",
+ ".github/pull_request_template.yml",
}
func getRepository(ctx *context.Context, repoID int64) *repo_model.Repository {
@@ -295,7 +309,7 @@ func checkPullInfo(ctx *context.Context) *issues_model.Issue {
if ctx.IsSigned {
// Update issue-user.
- if err = models.SetIssueReadBy(ctx, issue.ID, ctx.Doer.ID); err != nil {
+ if err = activities_model.SetIssueReadBy(ctx, issue.ID, ctx.Doer.ID); err != nil {
ctx.ServerError("ReadBy", err)
return nil
}
@@ -510,6 +524,8 @@ func PrepareViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *git.C
return nil
}
ctx.Data["GetCommitMessages"] = pull_service.GetSquashMergeCommitMessages(ctx, pull)
+ } else {
+ ctx.Data["GetCommitMessages"] = ""
}
sha, err := baseGitRepo.GetRefCommitID(pull.GetGitRefName())
@@ -1191,6 +1207,13 @@ func CompareAndPullRequestPost(ctx *context.Context) {
return
}
+ content := form.Content
+ if filename := ctx.Req.Form.Get("template-file"); filename != "" {
+ if template, err := issue_template.UnmarshalFromRepo(ctx.Repo.GitRepo, ctx.Repo.Repository.DefaultBranch, filename); err == nil {
+ content = issue_template.RenderToMarkdown(template, ctx.Req.Form)
+ }
+ }
+
pullIssue := &issues_model.Issue{
RepoID: repo.ID,
Repo: repo,
@@ -1199,7 +1222,7 @@ func CompareAndPullRequestPost(ctx *context.Context) {
Poster: ctx.Doer,
MilestoneID: milestoneID,
IsPull: true,
- Content: form.Content,
+ Content: content,
}
pullRequest := &issues_model.PullRequest{
HeadRepoID: ci.HeadRepo.ID,
diff --git a/routers/web/repo/release.go b/routers/web/repo/release.go
index ab87c3e238..1e5710fa98 100644
--- a/routers/web/repo/release.go
+++ b/routers/web/repo/release.go
@@ -12,6 +12,7 @@ import (
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/models/db"
+ 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/modules/base"
@@ -33,7 +34,7 @@ const (
)
// calReleaseNumCommitsBehind calculates given release has how many commits behind release target.
-func calReleaseNumCommitsBehind(repoCtx *context.Repository, release *models.Release, countCache map[string]int64) error {
+func calReleaseNumCommitsBehind(repoCtx *context.Repository, release *repo_model.Release, countCache map[string]int64) error {
// Fast return if release target is same as default branch.
if repoCtx.BranchName == release.Target {
release.NumCommitsBehind = repoCtx.CommitsCount - release.NumCommits
@@ -115,25 +116,33 @@ func releasesOrTags(ctx *context.Context, isTagList bool) {
writeAccess := ctx.Repo.CanWrite(unit.TypeReleases)
ctx.Data["CanCreateRelease"] = writeAccess && !ctx.Repo.Repository.IsArchived
- opts := models.FindReleasesOptions{
- ListOptions: listOptions,
- IncludeDrafts: writeAccess && !isTagList,
- IncludeTags: isTagList,
+ opts := repo_model.FindReleasesOptions{
+ ListOptions: listOptions,
+ }
+ if isTagList {
+ // for the tags list page, show all releases with real tags (having real commit-id),
+ // the drafts should also be included because a real tag might be used as a draft.
+ opts.IncludeDrafts = true
+ opts.IncludeTags = true
+ opts.HasSha1 = util.OptionalBoolTrue
+ } else {
+ // only show draft releases for users who can write, read-only users shouldn't see draft releases.
+ opts.IncludeDrafts = writeAccess
}
- releases, err := models.GetReleasesByRepoID(ctx.Repo.Repository.ID, opts)
+ releases, err := repo_model.GetReleasesByRepoID(ctx.Repo.Repository.ID, opts)
if err != nil {
ctx.ServerError("GetReleasesByRepoID", err)
return
}
- count, err := models.GetReleaseCountByRepoID(ctx.Repo.Repository.ID, opts)
+ count, err := repo_model.GetReleaseCountByRepoID(ctx.Repo.Repository.ID, opts)
if err != nil {
ctx.ServerError("GetReleaseCountByRepoID", err)
return
}
- if err = models.GetReleaseAttachments(ctx, releases...); err != nil {
+ if err = repo_model.GetReleaseAttachments(ctx, releases...); err != nil {
ctx.ServerError("GetReleaseAttachments", err)
return
}
@@ -199,9 +208,9 @@ func SingleRelease(ctx *context.Context) {
writeAccess := ctx.Repo.CanWrite(unit.TypeReleases)
ctx.Data["CanCreateRelease"] = writeAccess && !ctx.Repo.Repository.IsArchived
- release, err := models.GetRelease(ctx.Repo.Repository.ID, ctx.Params("*"))
+ release, err := repo_model.GetRelease(ctx.Repo.Repository.ID, ctx.Params("*"))
if err != nil {
- if models.IsErrReleaseNotExist(err) {
+ if repo_model.IsErrReleaseNotExist(err) {
ctx.NotFound("GetRelease", err)
return
}
@@ -209,7 +218,7 @@ func SingleRelease(ctx *context.Context) {
return
}
- err = models.GetReleaseAttachments(ctx, release)
+ err = repo_model.GetReleaseAttachments(ctx, release)
if err != nil {
ctx.ServerError("GetReleaseAttachments", err)
return
@@ -241,15 +250,15 @@ func SingleRelease(ctx *context.Context) {
return
}
- ctx.Data["Releases"] = []*models.Release{release}
+ ctx.Data["Releases"] = []*repo_model.Release{release}
ctx.HTML(http.StatusOK, tplReleases)
}
// LatestRelease redirects to the latest release
func LatestRelease(ctx *context.Context) {
- release, err := models.GetLatestReleaseByRepoID(ctx.Repo.Repository.ID)
+ release, err := repo_model.GetLatestReleaseByRepoID(ctx.Repo.Repository.ID)
if err != nil {
- if models.IsErrReleaseNotExist(err) {
+ if repo_model.IsErrReleaseNotExist(err) {
ctx.NotFound("LatestRelease", err)
return
}
@@ -272,8 +281,8 @@ func NewRelease(ctx *context.Context) {
ctx.Data["RequireTribute"] = true
ctx.Data["tag_target"] = ctx.Repo.Repository.DefaultBranch
if tagName := ctx.FormString("tag"); len(tagName) > 0 {
- rel, err := models.GetRelease(ctx.Repo.Repository.ID, tagName)
- if err != nil && !models.IsErrReleaseNotExist(err) {
+ rel, err := repo_model.GetRelease(ctx.Repo.Repository.ID, tagName)
+ if err != nil && !repo_model.IsErrReleaseNotExist(err) {
ctx.ServerError("GetRelease", err)
return
}
@@ -321,9 +330,9 @@ func NewReleasePost(ctx *context.Context) {
attachmentUUIDs = form.Files
}
- rel, err := models.GetRelease(ctx.Repo.Repository.ID, form.TagName)
+ rel, err := repo_model.GetRelease(ctx.Repo.Repository.ID, form.TagName)
if err != nil {
- if !models.IsErrReleaseNotExist(err) {
+ if !repo_model.IsErrReleaseNotExist(err) {
ctx.ServerError("GetRelease", err)
return
}
@@ -363,7 +372,7 @@ func NewReleasePost(ctx *context.Context) {
return
}
- rel = &models.Release{
+ rel = &repo_model.Release{
RepoID: ctx.Repo.Repository.ID,
Repo: ctx.Repo.Repository,
PublisherID: ctx.Doer.ID,
@@ -380,7 +389,7 @@ func NewReleasePost(ctx *context.Context) {
if err = releaseservice.CreateRelease(ctx.Repo.GitRepo, rel, attachmentUUIDs, msg); err != nil {
ctx.Data["Err_TagName"] = true
switch {
- case models.IsErrReleaseAlreadyExist(err):
+ case repo_model.IsErrReleaseAlreadyExist(err):
ctx.RenderWithErr(ctx.Tr("repo.release.tag_name_already_exist"), tplReleaseNew, &form)
case models.IsErrInvalidTagName(err):
ctx.RenderWithErr(ctx.Tr("repo.release.tag_name_invalid"), tplReleaseNew, &form)
@@ -427,9 +436,9 @@ func EditRelease(ctx *context.Context) {
upload.AddUploadContext(ctx, "release")
tagName := ctx.Params("*")
- rel, err := models.GetRelease(ctx.Repo.Repository.ID, tagName)
+ rel, err := repo_model.GetRelease(ctx.Repo.Repository.ID, tagName)
if err != nil {
- if models.IsErrReleaseNotExist(err) {
+ if repo_model.IsErrReleaseNotExist(err) {
ctx.NotFound("GetRelease", err)
} else {
ctx.ServerError("GetRelease", err)
@@ -463,9 +472,9 @@ func EditReleasePost(ctx *context.Context) {
ctx.Data["RequireTribute"] = true
tagName := ctx.Params("*")
- rel, err := models.GetRelease(ctx.Repo.Repository.ID, tagName)
+ rel, err := repo_model.GetRelease(ctx.Repo.Repository.ID, tagName)
if err != nil {
- if models.IsErrReleaseNotExist(err) {
+ if repo_model.IsErrReleaseNotExist(err) {
ctx.NotFound("GetRelease", err)
} else {
ctx.ServerError("GetRelease", err)
diff --git a/routers/web/repo/release_test.go b/routers/web/repo/release_test.go
index 33cf54cdc9..16371fc860 100644
--- a/routers/web/repo/release_test.go
+++ b/routers/web/repo/release_test.go
@@ -7,7 +7,7 @@ package repo
import (
"testing"
- "code.gitea.io/gitea/models"
+ repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
"code.gitea.io/gitea/modules/test"
"code.gitea.io/gitea/modules/web"
@@ -52,7 +52,7 @@ func TestNewReleasePost(t *testing.T) {
test.LoadGitRepo(t, ctx)
web.SetForm(ctx, &testCase.Form)
NewReleasePost(ctx)
- unittest.AssertExistsAndLoadBean(t, &models.Release{
+ unittest.AssertExistsAndLoadBean(t, &repo_model.Release{
RepoID: 1,
PublisherID: 2,
TagName: testCase.Form.TagName,
diff --git a/routers/web/repo/repo.go b/routers/web/repo/repo.go
index c2c79e4a0d..974f03f951 100644
--- a/routers/web/repo/repo.go
+++ b/routers/web/repo/repo.go
@@ -10,18 +10,17 @@ import (
"fmt"
"net/http"
"strings"
- "time"
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/organization"
+ 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/modules/base"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/convert"
- "code.gitea.io/gitea/modules/graceful"
"code.gitea.io/gitea/modules/log"
repo_module "code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/setting"
@@ -152,7 +151,7 @@ func Create(ctx *context.Context) {
templateID := ctx.FormInt64("template_id")
if templateID > 0 {
templateRepo, err := repo_model.GetRepositoryByID(templateID)
- if err == nil && models.CheckRepoUnitUser(ctx, templateRepo, ctxUser, unit.TypeCode) {
+ if err == nil && access_model.CheckRepoUnitUser(ctx, templateRepo, ctxUser, unit.TypeCode) {
ctx.Data["repo_template"] = templateID
ctx.Data["repo_template_name"] = templateRepo.Name
}
@@ -257,7 +256,7 @@ func CreatePost(ctx *context.Context) {
return
}
} else {
- repo, err = repo_service.CreateRepository(ctx.Doer, ctxUser, models.CreateRepoOptions{
+ repo, err = repo_service.CreateRepository(ctx.Doer, ctxUser, repo_module.CreateRepoOptions{
Name: form.RepoName,
Description: form.Description,
Gitignores: form.Gitignores,
@@ -358,7 +357,7 @@ func RedirectDownload(ctx *context.Context) {
)
tagNames := []string{vTag}
curRepo := ctx.Repo.Repository
- releases, err := models.GetReleasesByRepoIDAndNames(ctx, curRepo.ID, tagNames)
+ releases, err := repo_model.GetReleasesByRepoIDAndNames(ctx, curRepo.ID, tagNames)
if err != nil {
if repo_model.IsErrAttachmentNotExist(err) {
ctx.Error(http.StatusNotFound)
@@ -389,68 +388,27 @@ func Download(ctx *context.Context) {
if err != nil {
if errors.Is(err, archiver_service.ErrUnknownArchiveFormat{}) {
ctx.Error(http.StatusBadRequest, err.Error())
+ } else if errors.Is(err, archiver_service.RepoRefNotFoundError{}) {
+ ctx.Error(http.StatusNotFound, err.Error())
} else {
ctx.ServerError("archiver_service.NewRequest", err)
}
return
}
- if aReq == nil {
- ctx.Error(http.StatusNotFound)
- return
- }
- archiver, err := repo_model.GetRepoArchiver(ctx, aReq.RepoID, aReq.Type, aReq.CommitID)
+ archiver, err := aReq.Await(ctx)
if err != nil {
- ctx.ServerError("models.GetRepoArchiver", err)
- return
- }
- if archiver != nil && archiver.Status == repo_model.ArchiverReady {
- download(ctx, aReq.GetArchiveName(), archiver)
+ ctx.ServerError("archiver.Await", err)
return
}
- if err := archiver_service.StartArchive(aReq); err != nil {
- ctx.ServerError("archiver_service.StartArchive", err)
- return
- }
-
- var times int
- t := time.NewTicker(time.Second * 1)
- defer t.Stop()
-
- for {
- select {
- case <-graceful.GetManager().HammerContext().Done():
- log.Warn("exit archive download because system stop")
- return
- case <-t.C:
- if times > 20 {
- ctx.ServerError("wait download timeout", nil)
- return
- }
- times++
- archiver, err = repo_model.GetRepoArchiver(ctx, aReq.RepoID, aReq.Type, aReq.CommitID)
- if err != nil {
- ctx.ServerError("archiver_service.StartArchive", err)
- return
- }
- if archiver != nil && archiver.Status == repo_model.ArchiverReady {
- download(ctx, aReq.GetArchiveName(), archiver)
- return
- }
- }
- }
+ download(ctx, aReq.GetArchiveName(), archiver)
}
func download(ctx *context.Context, archiveName string, archiver *repo_model.RepoArchiver) {
downloadName := ctx.Repo.Repository.Name + "-" + archiveName
- rPath, err := archiver.RelativePath()
- if err != nil {
- ctx.ServerError("archiver.RelativePath", err)
- return
- }
-
+ rPath := archiver.RelativePath()
if setting.RepoArchive.ServeDirect {
// If we have a signed url (S3, object storage), redirect to this directly.
u, err := storage.RepoArchives.URL(rPath, downloadName)
@@ -467,7 +425,8 @@ func download(ctx *context.Context, archiveName string, archiver *repo_model.Rep
return
}
defer fr.Close()
- ctx.ServeStream(fr, downloadName)
+
+ ctx.ServeContent(downloadName, fr, archiver.CreatedUnix.AsLocalTime())
}
// InitiateDownload will enqueue an archival request, as needed. It may submit
diff --git a/routers/web/repo/search.go b/routers/web/repo/search.go
index 8f141cb149..3d1835c7c3 100644
--- a/routers/web/repo/search.go
+++ b/routers/web/repo/search.go
@@ -21,14 +21,27 @@ func Search(ctx *context.Context) {
ctx.Redirect(ctx.Repo.RepoLink)
return
}
+
language := ctx.FormTrim("l")
keyword := ctx.FormTrim("q")
+
+ queryType := ctx.FormTrim("t")
+ isMatch := queryType == "match"
+
+ ctx.Data["Keyword"] = keyword
+ ctx.Data["Language"] = language
+ ctx.Data["queryType"] = queryType
+ ctx.Data["PageIsViewCode"] = true
+
+ if keyword == "" {
+ ctx.HTML(http.StatusOK, tplSearch)
+ return
+ }
+
page := ctx.FormInt("page")
if page <= 0 {
page = 1
}
- queryType := ctx.FormTrim("t")
- isMatch := queryType == "match"
total, searchResults, searchResultLanguages, err := code_indexer.PerformSearch(ctx, []int64{ctx.Repo.Repository.ID},
language, keyword, page, setting.UI.RepoSearchPagingNum, isMatch)
@@ -41,13 +54,10 @@ func Search(ctx *context.Context) {
} else {
ctx.Data["CodeIndexerUnavailable"] = !code_indexer.IsAvailable()
}
- ctx.Data["Keyword"] = keyword
- ctx.Data["Language"] = language
- ctx.Data["queryType"] = queryType
+
ctx.Data["SourcePath"] = ctx.Repo.Repository.HTMLURL()
ctx.Data["SearchResults"] = searchResults
ctx.Data["SearchResultLanguages"] = searchResultLanguages
- ctx.Data["PageIsViewCode"] = true
pager := context.NewPagination(total, setting.UI.RepoSearchPagingNum, page, 5)
pager.SetDefaultParams(ctx)
diff --git a/routers/web/repo/setting.go b/routers/web/repo/setting.go
index 5ded0561c6..e7abec0d3e 100644
--- a/routers/web/repo/setting.go
+++ b/routers/web/repo/setting.go
@@ -30,7 +30,7 @@ import (
"code.gitea.io/gitea/modules/lfs"
"code.gitea.io/gitea/modules/log"
mirror_module "code.gitea.io/gitea/modules/mirror"
- "code.gitea.io/gitea/modules/repository"
+ repo_module "code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/typesniffer"
@@ -43,6 +43,7 @@ import (
"code.gitea.io/gitea/services/mailer"
"code.gitea.io/gitea/services/migrations"
mirror_service "code.gitea.io/gitea/services/mirror"
+ org_service "code.gitea.io/gitea/services/org"
repo_service "code.gitea.io/gitea/services/repository"
wiki_service "code.gitea.io/gitea/services/wiki"
)
@@ -90,7 +91,7 @@ func SettingsCtxData(ctx *context.Context) {
}
ctx.Data["StatsIndexerStatus"] = status
}
- pushMirrors, err := repo_model.GetPushMirrorsByRepoID(ctx.Repo.Repository.ID)
+ pushMirrors, _, err := repo_model.GetPushMirrorsByRepoID(ctx, ctx.Repo.Repository.ID, db.ListOptions{})
if err != nil {
ctx.ServerError("GetPushMirrorsByRepoID", err)
return
@@ -228,14 +229,17 @@ func SettingsPost(ctx *context.Context) {
form.MirrorPassword, _ = u.User.Password()
}
- err = migrations.IsMigrateURLAllowed(u.String(), ctx.Doer)
+ address, err := forms.ParseRemoteAddr(form.MirrorAddress, form.MirrorUsername, form.MirrorPassword)
+ if err == nil {
+ err = migrations.IsMigrateURLAllowed(address, ctx.Doer)
+ }
if err != nil {
ctx.Data["Err_MirrorAddress"] = true
handleSettingRemoteAddrError(ctx, err, form)
return
}
- if err := mirror_service.UpdateAddress(ctx, ctx.Repo.Mirror, u.String()); err != nil {
+ if err := mirror_service.UpdateAddress(ctx, ctx.Repo.Mirror, address); err != nil {
ctx.ServerError("UpdateAddress", err)
return
}
@@ -284,7 +288,7 @@ func SettingsPost(ctx *context.Context) {
return
}
- m, err := selectPushMirrorByForm(form, repo)
+ m, err := selectPushMirrorByForm(ctx, form, repo)
if err != nil {
ctx.NotFound("", nil)
return
@@ -305,7 +309,7 @@ func SettingsPost(ctx *context.Context) {
// as an error on the UI for this action
ctx.Data["Err_RepoName"] = nil
- m, err := selectPushMirrorByForm(form, repo)
+ m, err := selectPushMirrorByForm(ctx, form, repo)
if err != nil {
ctx.NotFound("", nil)
return
@@ -316,7 +320,7 @@ func SettingsPost(ctx *context.Context) {
return
}
- if err = repo_model.DeletePushMirrorByID(m.ID); err != nil {
+ if err = repo_model.DeletePushMirrors(ctx, repo_model.PushMirrorOptions{ID: m.ID, RepoID: m.RepoID}); err != nil {
ctx.ServerError("DeletePushMirrorByID", err)
return
}
@@ -364,14 +368,14 @@ func SettingsPost(ctx *context.Context) {
SyncOnCommit: form.PushMirrorSyncOnCommit,
Interval: interval,
}
- if err := repo_model.InsertPushMirror(m); err != nil {
+ if err := repo_model.InsertPushMirror(ctx, m); err != nil {
ctx.ServerError("InsertPushMirror", err)
return
}
if err := mirror_service.AddPushMirrorRemote(ctx, m, address); err != nil {
- if err := repo_model.DeletePushMirrorByID(m.ID); err != nil {
- log.Error("DeletePushMirrorByID %v", err)
+ if err := repo_model.DeletePushMirrors(ctx, repo_model.PushMirrorOptions{ID: m.ID, RepoID: m.RepoID}); err != nil {
+ log.Error("DeletePushMirrors %v", err)
}
ctx.ServerError("AddPushMirrorRemote", err)
return
@@ -476,7 +480,7 @@ func SettingsPost(ctx *context.Context) {
deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeProjects)
}
- if form.EnablePackages && !unit_model.TypeProjects.UnitGlobalDisabled() {
+ if form.EnablePackages && !unit_model.TypePackages.UnitGlobalDisabled() {
units = append(units, repo_model.RepoUnit{
RepoID: repo.ID,
Type: unit_model.TypePackages,
@@ -604,7 +608,7 @@ func SettingsPost(ctx *context.Context) {
}
repo.IsMirror = false
- if _, err := repository.CleanUpMigrateInfo(ctx, repo); err != nil {
+ if _, err := repo_module.CleanUpMigrateInfo(ctx, repo); err != nil {
ctx.ServerError("CleanUpMigrateInfo", err)
return
} else if err = repo_model.DeleteMirrorByRepoID(ctx.Repo.Repository.ID); err != nil {
@@ -913,7 +917,20 @@ func CollaborationPost(ctx *context.Context) {
return
}
- if err = models.AddCollaborator(ctx.Repo.Repository, u); err != nil {
+ // find the owner team of the organization the repo belongs too and
+ // check if the user we're trying to add is an owner.
+ if ctx.Repo.Repository.Owner.IsOrganization() {
+ if isOwner, err := organization.IsOrganizationOwner(ctx, ctx.Repo.Repository.Owner.ID, u.ID); err != nil {
+ ctx.ServerError("IsOrganizationOwner", err)
+ return
+ } else if isOwner {
+ ctx.Flash.Error(ctx.Tr("repo.settings.add_collaborator_owner"))
+ ctx.Redirect(setting.AppSubURL + ctx.Req.URL.EscapedPath())
+ return
+ }
+ }
+
+ if err = repo_module.AddCollaborator(ctx.Repo.Repository, u); err != nil {
ctx.ServerError("AddCollaborator", err)
return
}
@@ -986,8 +1003,8 @@ func AddTeamPost(ctx *context.Context) {
return
}
- if err = models.AddRepository(team, ctx.Repo.Repository); err != nil {
- ctx.ServerError("team.AddRepository", err)
+ if err = org_service.TeamAddRepository(team, ctx.Repo.Repository); err != nil {
+ ctx.ServerError("TeamAddRepository", err)
return
}
@@ -1222,13 +1239,13 @@ func SettingsDeleteAvatar(ctx *context.Context) {
ctx.Redirect(ctx.Repo.RepoLink + "/settings")
}
-func selectPushMirrorByForm(form *forms.RepoSettingForm, repo *repo_model.Repository) (*repo_model.PushMirror, error) {
+func selectPushMirrorByForm(ctx *context.Context, form *forms.RepoSettingForm, repo *repo_model.Repository) (*repo_model.PushMirror, error) {
id, err := strconv.ParseInt(form.PushMirrorID, 10, 64)
if err != nil {
return nil, err
}
- pushMirrors, err := repo_model.GetPushMirrorsByRepoID(repo.ID)
+ pushMirrors, _, err := repo_model.GetPushMirrorsByRepoID(ctx, repo.ID, db.ListOptions{})
if err != nil {
return nil, err
}
diff --git a/routers/web/repo/settings_test.go b/routers/web/repo/settings_test.go
index 946220b4fc..4acd7df5b8 100644
--- a/routers/web/repo/settings_test.go
+++ b/routers/web/repo/settings_test.go
@@ -6,7 +6,6 @@ package repo
import (
"net/http"
- "os"
"testing"
"code.gitea.io/gitea/models"
@@ -19,7 +18,6 @@ import (
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/test"
- "code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/services/forms"
@@ -27,18 +25,13 @@ import (
)
func createSSHAuthorizedKeysTmpPath(t *testing.T) func() {
- tmpDir, err := os.MkdirTemp("", "tmp-ssh")
- if err != nil {
- assert.Fail(t, "Unable to create temporary directory: %v", err)
- return nil
- }
+ tmpDir := t.TempDir()
oldPath := setting.SSH.RootPath
setting.SSH.RootPath = tmpDir
return func() {
setting.SSH.RootPath = oldPath
- util.RemoveAll(tmpDir)
}
}
diff --git a/routers/web/repo/treelist.go b/routers/web/repo/treelist.go
index 35ac0d507f..80f43a0c40 100644
--- a/routers/web/repo/treelist.go
+++ b/routers/web/repo/treelist.go
@@ -22,9 +22,9 @@ func TreeList(ctx *context.Context) {
return
}
- entries, err := tree.ListEntriesRecursive()
+ entries, err := tree.ListEntriesRecursiveFast()
if err != nil {
- ctx.ServerError("ListEntriesRecursive", err)
+ ctx.ServerError("ListEntriesRecursiveFast", err)
return
}
entries.CustomSort(base.NaturalSortLess)
diff --git a/routers/web/repo/view.go b/routers/web/repo/view.go
index 6b6660f774..3e869376ee 100644
--- a/routers/web/repo/view.go
+++ b/routers/web/repo/view.go
@@ -15,11 +15,11 @@ import (
"net/http"
"net/url"
"path"
- "strconv"
"strings"
"time"
- "code.gitea.io/gitea/models"
+ activities_model "code.gitea.io/gitea/models/activities"
+ admin_model "code.gitea.io/gitea/models/admin"
asymkey_model "code.gitea.io/gitea/models/asymkey"
"code.gitea.io/gitea/models/db"
git_model "code.gitea.io/gitea/models/git"
@@ -27,8 +27,8 @@ import (
unit_model "code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/base"
- "code.gitea.io/gitea/modules/cache"
"code.gitea.io/gitea/modules/charset"
+ "code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/highlight"
@@ -58,17 +58,8 @@ type namedBlob struct {
blob *git.Blob
}
-func linesBytesCount(s []byte) int {
- nl := []byte{'\n'}
- n := bytes.Count(s, nl)
- if len(s) > 0 && !bytes.HasSuffix(s, nl) {
- n++
- }
- return n
-}
-
// FIXME: There has to be a more efficient way of doing this
-func getReadmeFileFromPath(commit *git.Commit, treePath string) (*namedBlob, error) {
+func getReadmeFileFromPath(ctx *context.Context, commit *git.Commit, treePath string) (*namedBlob, error) {
tree, err := commit.SubTree(treePath)
if err != nil {
return nil, err
@@ -79,50 +70,33 @@ func getReadmeFileFromPath(commit *git.Commit, treePath string) (*namedBlob, err
return nil, err
}
- var readmeFiles [4]*namedBlob
- exts := []string{".md", ".txt", ""} // sorted by priority
+ // Create a list of extensions in priority order
+ // 1. Markdown files - with and without localisation - e.g. README.en-us.md or README.md
+ // 2. Txt files - e.g. README.txt
+ // 3. No extension - e.g. README
+ exts := append(localizedExtensions(".md", ctx.Language()), ".txt", "") // sorted by priority
+ extCount := len(exts)
+ readmeFiles := make([]*namedBlob, extCount+1)
for _, entry := range entries {
if entry.IsDir() {
continue
}
- for i, ext := range exts {
- if markup.IsReadmeFile(entry.Name(), ext) {
- if readmeFiles[i] == nil || base.NaturalSortLess(readmeFiles[i].name, entry.Blob().Name()) {
- name := entry.Name()
- isSymlink := entry.IsLink()
- target := entry
- if isSymlink {
- target, err = entry.FollowLinks()
- if err != nil && !git.IsErrBadLink(err) {
- return nil, err
- }
- }
- if target != nil && (target.IsExecutable() || target.IsRegular()) {
- readmeFiles[i] = &namedBlob{
- name,
- isSymlink,
- target.Blob(),
- }
- }
- }
- }
- }
-
- if markup.IsReadmeFile(entry.Name()) {
- if readmeFiles[3] == nil || base.NaturalSortLess(readmeFiles[3].name, entry.Blob().Name()) {
+ if i, ok := markup.IsReadmeFileExtension(entry.Name(), exts...); ok {
+ if readmeFiles[i] == nil || base.NaturalSortLess(readmeFiles[i].name, entry.Blob().Name()) {
name := entry.Name()
isSymlink := entry.IsLink()
+ target := entry
if isSymlink {
- entry, err = entry.FollowLinks()
+ target, err = entry.FollowLinks()
if err != nil && !git.IsErrBadLink(err) {
return nil, err
}
}
- if entry != nil && (entry.IsExecutable() || entry.IsRegular()) {
- readmeFiles[3] = &namedBlob{
+ if target != nil && (target.IsExecutable() || target.IsRegular()) {
+ readmeFiles[i] = &namedBlob{
name,
isSymlink,
- entry.Blob(),
+ target.Blob(),
}
}
}
@@ -145,6 +119,7 @@ func renderDirectory(ctx *context.Context, treeLink string) {
}
if ctx.Repo.TreePath != "" {
+ ctx.Data["HideRepoInfo"] = true
ctx.Data["Title"] = ctx.Tr("repo.file.title", ctx.Repo.Repository.Name+"/"+path.Base(ctx.Repo.TreePath), ctx.Repo.RefName)
}
@@ -162,13 +137,38 @@ func renderDirectory(ctx *context.Context, treeLink string) {
renderReadmeFile(ctx, readmeFile, readmeTreelink)
}
+// localizedExtensions prepends the provided language code with and without a
+// regional identifier to the provided extension.
+// Note: the language code will always be lower-cased, if a region is present it must be separated with a `-`
+// Note: ext should be prefixed with a `.`
+func localizedExtensions(ext, languageCode string) (localizedExts []string) {
+ if len(languageCode) < 1 {
+ return []string{ext}
+ }
+
+ lowerLangCode := "." + strings.ToLower(languageCode)
+
+ if strings.Contains(lowerLangCode, "-") {
+ underscoreLangCode := strings.ReplaceAll(lowerLangCode, "-", "_")
+ indexOfDash := strings.Index(lowerLangCode, "-")
+ // e.g. [.zh-cn.md, .zh_cn.md, .zh.md, .md]
+ return []string{lowerLangCode + ext, underscoreLangCode + ext, lowerLangCode[:indexOfDash] + ext, ext}
+ }
+
+ // e.g. [.en.md, .md]
+ return []string{lowerLangCode + ext, ext}
+}
+
func findReadmeFile(ctx *context.Context, entries git.Entries, treeLink string) (*namedBlob, string) {
- // 3 for the extensions in exts[] in order
- // the last one is for a readme that doesn't
- // strictly match an extension
- var readmeFiles [4]*namedBlob
- var docsEntries [3]*git.TreeEntry
- exts := []string{".md", ".txt", ""} // sorted by priority
+ // Create a list of extensions in priority order
+ // 1. Markdown files - with and without localisation - e.g. README.en-us.md or README.md
+ // 2. Txt files - e.g. README.txt
+ // 3. No extension - e.g. README
+ exts := append(localizedExtensions(".md", ctx.Language()), ".txt", "") // sorted by priority
+ extCount := len(exts)
+ readmeFiles := make([]*namedBlob, extCount+1)
+
+ docsEntries := make([]*git.TreeEntry, 3) // (one of docs/, .gitea/ or .github/)
for _, entry := range entries {
if entry.IsDir() {
lowerName := strings.ToLower(entry.Name())
@@ -189,47 +189,24 @@ func findReadmeFile(ctx *context.Context, entries git.Entries, treeLink string)
continue
}
- for i, ext := range exts {
- if markup.IsReadmeFile(entry.Name(), ext) {
- log.Debug("%s", entry.Name())
- name := entry.Name()
- isSymlink := entry.IsLink()
- target := entry
- if isSymlink {
- var err error
- target, err = entry.FollowLinks()
- if err != nil && !git.IsErrBadLink(err) {
- ctx.ServerError("FollowLinks", err)
- return nil, ""
- }
- }
- log.Debug("%t", target == nil)
- if target != nil && (target.IsExecutable() || target.IsRegular()) {
- readmeFiles[i] = &namedBlob{
- name,
- isSymlink,
- target.Blob(),
- }
- }
- }
- }
-
- if markup.IsReadmeFile(entry.Name()) {
+ if i, ok := markup.IsReadmeFileExtension(entry.Name(), exts...); ok {
+ log.Debug("Potential readme file: %s", entry.Name())
name := entry.Name()
isSymlink := entry.IsLink()
+ target := entry
if isSymlink {
var err error
- entry, err = entry.FollowLinks()
+ target, err = entry.FollowLinks()
if err != nil && !git.IsErrBadLink(err) {
ctx.ServerError("FollowLinks", err)
return nil, ""
}
}
- if entry != nil && (entry.IsExecutable() || entry.IsRegular()) {
- readmeFiles[3] = &namedBlob{
+ if target != nil && (target.IsExecutable() || target.IsRegular()) {
+ readmeFiles[i] = &namedBlob{
name,
isSymlink,
- entry.Blob(),
+ target.Blob(),
}
}
}
@@ -250,7 +227,7 @@ func findReadmeFile(ctx *context.Context, entries git.Entries, treeLink string)
continue
}
var err error
- readmeFile, err = getReadmeFileFromPath(ctx.Repo.Commit, entry.GetSubJumpablePathName())
+ readmeFile, err = getReadmeFileFromPath(ctx, ctx.Repo.Commit, entry.GetSubJumpablePathName())
if err != nil {
ctx.ServerError("getReadmeFileFromPath", err)
return nil, ""
@@ -354,40 +331,37 @@ func renderReadmeFile(ctx *context.Context, readmeFile *namedBlob, readmeTreelin
if markupType := markup.Type(readmeFile.name); markupType != "" {
ctx.Data["IsMarkup"] = true
ctx.Data["MarkupType"] = markupType
- var result strings.Builder
- err := markup.Render(&markup.RenderContext{
+
+ ctx.Data["EscapeStatus"], ctx.Data["FileContent"], err = markupRender(ctx, &markup.RenderContext{
Ctx: ctx,
RelativePath: path.Join(ctx.Repo.TreePath, readmeFile.name), // ctx.Repo.TreePath is the directory not the Readme so we must append the Readme filename (and path).
URLPrefix: readmeTreelink,
Metas: ctx.Repo.Repository.ComposeDocumentMetas(),
GitRepo: ctx.Repo.GitRepo,
- }, rd, &result)
+ }, rd)
if err != nil {
- log.Error("Render failed: %v then fallback", err)
+ log.Error("Render failed for %s in %-v: %v Falling back to rendering source", readmeFile.name, ctx.Repo.Repository, err)
buf := &bytes.Buffer{}
- ctx.Data["EscapeStatus"], _ = charset.EscapeControlReader(rd, buf)
+ ctx.Data["EscapeStatus"], _ = charset.EscapeControlReader(rd, buf, ctx.Locale)
ctx.Data["FileContent"] = strings.ReplaceAll(
gotemplate.HTMLEscapeString(buf.String()), "\n", `]
`,
)
- } else {
- ctx.Data["EscapeStatus"], ctx.Data["FileContent"] = charset.EscapeControlString(result.String())
}
} else {
ctx.Data["IsRenderedHTML"] = true
buf := &bytes.Buffer{}
- ctx.Data["EscapeStatus"], err = charset.EscapeControlReader(rd, buf)
+ ctx.Data["EscapeStatus"], err = charset.EscapeControlReader(rd, &charset.BreakWriter{Writer: buf}, ctx.Locale, charset.RuneNBSP)
if err != nil {
log.Error("Read failed: %v", err)
}
- ctx.Data["FileContent"] = strings.ReplaceAll(
- gotemplate.HTMLEscapeString(buf.String()), "\n", `
`,
- )
+ ctx.Data["FileContent"] = buf.String()
}
}
func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink string) {
ctx.Data["IsViewFile"] = true
+ ctx.Data["HideRepoInfo"] = true
blob := entry.Blob()
dataRc, err := blob.DataAsync()
if err != nil {
@@ -403,6 +377,11 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st
ctx.Data["FileName"] = blob.Name()
ctx.Data["RawFileLink"] = rawLink + "/" + util.PathEscapeSegments(ctx.Repo.TreePath)
+ if ctx.Repo.TreePath == ".editorconfig" {
+ _, editorconfigErr := ctx.Repo.GetEditorconfig(ctx.Repo.Commit)
+ ctx.Data["FileError"] = editorconfigErr
+ }
+
buf := make([]byte, 1024)
n, _ := util.ReadAtMost(dataRc, buf)
buf = buf[:n]
@@ -479,7 +458,7 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st
ctx.ServerError("GetTreePathLock", err)
return
}
- ctx.Data["LFSLockOwner"] = u.DisplayName()
+ ctx.Data["LFSLockOwner"] = u.Name
ctx.Data["LFSLockOwnerHomeLink"] = u.HomeLink()
ctx.Data["LFSLockHint"] = ctx.Tr("repo.editor.this_file_locked")
}
@@ -524,40 +503,44 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st
if markupType != "" && !shouldRenderSource {
ctx.Data["IsMarkup"] = true
ctx.Data["MarkupType"] = markupType
- var result strings.Builder
if !detected {
markupType = ""
}
metas := ctx.Repo.Repository.ComposeDocumentMetas()
metas["BranchNameSubURL"] = ctx.Repo.BranchNameSubURL()
- err := markup.Render(&markup.RenderContext{
+ ctx.Data["EscapeStatus"], ctx.Data["FileContent"], err = markupRender(ctx, &markup.RenderContext{
Ctx: ctx,
Type: markupType,
RelativePath: ctx.Repo.TreePath,
URLPrefix: path.Dir(treeLink),
Metas: metas,
GitRepo: ctx.Repo.GitRepo,
- }, rd, &result)
+ }, rd)
if err != nil {
ctx.ServerError("Render", err)
return
}
// to prevent iframe load third-party url
ctx.Resp.Header().Add("Content-Security-Policy", "frame-src 'self'")
- ctx.Data["EscapeStatus"], ctx.Data["FileContent"] = charset.EscapeControlString(result.String())
} else if readmeExist && !shouldRenderSource {
buf := &bytes.Buffer{}
ctx.Data["IsRenderedHTML"] = true
- ctx.Data["EscapeStatus"], _ = charset.EscapeControlReader(rd, buf)
+ ctx.Data["EscapeStatus"], _ = charset.EscapeControlReader(rd, buf, ctx.Locale)
ctx.Data["FileContent"] = strings.ReplaceAll(
gotemplate.HTMLEscapeString(buf.String()), "\n", `
`,
)
} else {
buf, _ := io.ReadAll(rd)
- lineNums := linesBytesCount(buf)
- ctx.Data["NumLines"] = strconv.Itoa(lineNums)
+
+ // empty: 0 lines; "a": one line; "a\n": two lines; "a\nb": two lines;
+ // the NumLines is only used for the display on the UI: "xxx lines"
+ if len(buf) == 0 {
+ ctx.Data["NumLines"] = 0
+ } else {
+ ctx.Data["NumLines"] = bytes.Count(buf, []byte{'\n'}) + 1
+ }
ctx.Data["NumLinesSet"] = true
language := ""
@@ -585,13 +568,18 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st
language = ""
}
}
- fileContent := highlight.File(lineNums, blob.Name(), language, buf)
- status, _ := charset.EscapeControlReader(bytes.NewReader(buf), io.Discard)
- ctx.Data["EscapeStatus"] = status
- statuses := make([]charset.EscapeStatus, len(fileContent))
- for i, line := range fileContent {
- statuses[i], fileContent[i] = charset.EscapeControlString(line)
+ fileContent, err := highlight.File(blob.Name(), language, buf)
+ if err != nil {
+ log.Error("highlight.File failed, fallback to plain text: %v", err)
+ fileContent = highlight.PlainText(buf)
}
+ status := &charset.EscapeStatus{}
+ statuses := make([]*charset.EscapeStatus, len(fileContent))
+ for i, line := range fileContent {
+ statuses[i], fileContent[i] = charset.EscapeControlHTML(line, ctx.Locale)
+ status = status.Or(statuses[i])
+ }
+ ctx.Data["EscapeStatus"] = status
ctx.Data["FileContent"] = fileContent
ctx.Data["LineEscapeStatus"] = statuses
}
@@ -629,20 +617,17 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st
rd := io.MultiReader(bytes.NewReader(buf), dataRc)
ctx.Data["IsMarkup"] = true
ctx.Data["MarkupType"] = markupType
- var result strings.Builder
- err := markup.Render(&markup.RenderContext{
+ ctx.Data["EscapeStatus"], ctx.Data["FileContent"], err = markupRender(ctx, &markup.RenderContext{
Ctx: ctx,
RelativePath: ctx.Repo.TreePath,
URLPrefix: path.Dir(treeLink),
Metas: ctx.Repo.Repository.ComposeDocumentMetas(),
GitRepo: ctx.Repo.GitRepo,
- }, rd, &result)
+ }, rd)
if err != nil {
ctx.ServerError("Render", err)
return
}
-
- ctx.Data["EscapeStatus"], ctx.Data["FileContent"] = charset.EscapeControlString(result.String())
}
}
@@ -661,6 +646,23 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st
}
}
+func markupRender(ctx *context.Context, renderCtx *markup.RenderContext, input io.Reader) (escaped *charset.EscapeStatus, output string, err error) {
+ markupRd, markupWr := io.Pipe()
+ defer markupWr.Close()
+ done := make(chan struct{})
+ go func() {
+ sb := &strings.Builder{}
+ // We allow NBSP here this is rendered
+ escaped, _ = charset.EscapeControlReader(markupRd, sb, ctx.Locale, charset.RuneNBSP)
+ output = sb.String()
+ close(done)
+ }()
+ err = markup.Render(renderCtx, input, markupWr)
+ _ = markupWr.CloseWithError(err)
+ <-done
+ return escaped, output, err
+}
+
func safeURL(address string) string {
u, err := url.Parse(address)
if err != nil {
@@ -673,9 +675,9 @@ func safeURL(address string) string {
func checkHomeCodeViewable(ctx *context.Context) {
if len(ctx.Repo.Units) > 0 {
if ctx.Repo.Repository.IsBeingCreated() {
- task, err := models.GetMigratingTask(ctx.Repo.Repository.ID)
+ task, err := admin_model.GetMigratingTask(ctx.Repo.Repository.ID)
if err != nil {
- if models.IsErrTaskDoesNotExist(err) {
+ if admin_model.IsErrTaskDoesNotExist(err) {
ctx.Data["Repo"] = ctx.Repo
ctx.Data["CloneAddr"] = ""
ctx.Data["Failed"] = true
@@ -701,7 +703,7 @@ func checkHomeCodeViewable(ctx *context.Context) {
if ctx.IsSigned {
// Set repo notification-status read if unread
- if err := models.SetRepoReadBy(ctx, ctx.Repo.Repository.ID, ctx.Doer.ID); err != nil {
+ if err := activities_model.SetRepoReadBy(ctx, ctx.Repo.Repository.ID, ctx.Doer.ID); err != nil {
ctx.ServerError("ReadBy", err)
return
}
@@ -812,28 +814,21 @@ func renderDirectoryFiles(ctx *context.Context, timeout time.Duration) git.Entri
defer cancel()
}
- var c *git.LastCommitCache
- if setting.CacheService.LastCommit.Enabled && ctx.Repo.CommitsCount >= setting.CacheService.LastCommit.CommitsCount {
- c = git.NewLastCommitCache(ctx.Repo.Repository.FullName(), ctx.Repo.GitRepo, setting.LastCommitCacheTTLSeconds, cache.GetCache())
- }
-
- selected := map[string]bool{}
- for _, pth := range ctx.FormStrings("f[]") {
- selected[pth] = true
- }
+ selected := make(container.Set[string])
+ selected.AddMultiple(ctx.FormStrings("f[]")...)
entries := allEntries
if len(selected) > 0 {
entries = make(git.Entries, 0, len(selected))
for _, entry := range allEntries {
- if selected[entry.Name()] {
+ if selected.Contains(entry.Name()) {
entries = append(entries, entry)
}
}
}
var latestCommit *git.Commit
- ctx.Data["Files"], latestCommit, err = entries.GetCommitsInfo(commitInfoCtx, ctx.Repo.Commit, ctx.Repo.TreePath, c)
+ ctx.Data["Files"], latestCommit, err = entries.GetCommitsInfo(commitInfoCtx, ctx.Repo.Commit, ctx.Repo.TreePath)
if err != nil {
ctx.ServerError("GetCommitsInfo", err)
return nil
@@ -902,10 +897,14 @@ func renderCode(ctx *context.Context) {
ctx.Data["PageIsViewCode"] = true
if ctx.Repo.Repository.IsEmpty {
- reallyEmpty, err := ctx.Repo.GitRepo.IsEmpty()
- if err != nil {
- ctx.ServerError("GitRepo.IsEmpty", err)
- return
+ reallyEmpty := true
+ var err error
+ if ctx.Repo.GitRepo != nil {
+ reallyEmpty, err = ctx.Repo.GitRepo.IsEmpty()
+ if err != nil {
+ ctx.ServerError("GitRepo.IsEmpty", err)
+ return
+ }
}
if reallyEmpty {
ctx.HTML(http.StatusOK, tplRepoEMPTY)
diff --git a/routers/web/repo/view_test.go b/routers/web/repo/view_test.go
new file mode 100644
index 0000000000..9d5a88fca4
--- /dev/null
+++ b/routers/web/repo/view_test.go
@@ -0,0 +1,63 @@
+// Copyright 2017 The Gitea Authors. All rights reserved.
+// Copyright 2014 The Gogs Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package repo
+
+import (
+ "reflect"
+ "testing"
+)
+
+func Test_localizedExtensions(t *testing.T) {
+ tests := []struct {
+ name string
+ ext string
+ languageCode string
+ wantLocalizedExts []string
+ }{
+ {
+ name: "empty language",
+ ext: ".md",
+ wantLocalizedExts: []string{".md"},
+ },
+ {
+ name: "No region - lowercase",
+ languageCode: "en",
+ ext: ".csv",
+ wantLocalizedExts: []string{".en.csv", ".csv"},
+ },
+ {
+ name: "No region - uppercase",
+ languageCode: "FR",
+ ext: ".txt",
+ wantLocalizedExts: []string{".fr.txt", ".txt"},
+ },
+ {
+ name: "With region - lowercase",
+ languageCode: "en-us",
+ ext: ".md",
+ wantLocalizedExts: []string{".en-us.md", ".en_us.md", ".en.md", ".md"},
+ },
+ {
+ name: "With region - uppercase",
+ languageCode: "en-CA",
+ ext: ".MD",
+ wantLocalizedExts: []string{".en-ca.MD", ".en_ca.MD", ".en.MD", ".MD"},
+ },
+ {
+ name: "With region - all uppercase",
+ languageCode: "ZH-TW",
+ ext: ".md",
+ wantLocalizedExts: []string{".zh-tw.md", ".zh_tw.md", ".zh.md", ".md"},
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ if gotLocalizedExts := localizedExtensions(tt.ext, tt.languageCode); !reflect.DeepEqual(gotLocalizedExts, tt.wantLocalizedExts) {
+ t.Errorf("localizedExtensions() = %v, want %v", gotLocalizedExts, tt.wantLocalizedExts)
+ }
+ })
+ }
+}
diff --git a/routers/web/repo/webhook.go b/routers/web/repo/webhook.go
index a9b14ee21f..425198ce24 100644
--- a/routers/web/repo/webhook.go
+++ b/routers/web/repo/webhook.go
@@ -178,6 +178,7 @@ func ParseHookEvent(form forms.WebhookForm) *webhook.HookEvent {
PullRequestComment: form.PullRequestComment,
PullRequestReview: form.PullRequestReview,
PullRequestSync: form.PullRequestSync,
+ Wiki: form.Wiki,
Repository: form.Repository,
Package: form.Package,
},
@@ -185,553 +186,385 @@ func ParseHookEvent(form forms.WebhookForm) *webhook.HookEvent {
}
}
+type webhookParams struct {
+ // Type should be imported from webhook package (webhook.XXX)
+ Type string
+
+ URL string
+ ContentType webhook.HookContentType
+ Secret string
+ HTTPMethod string
+ WebhookForm forms.WebhookForm
+ Meta interface{}
+}
+
+func createWebhook(ctx *context.Context, params webhookParams) {
+ ctx.Data["Title"] = ctx.Tr("repo.settings.add_webhook")
+ ctx.Data["PageIsSettingsHooks"] = true
+ ctx.Data["PageIsSettingsHooksNew"] = true
+ ctx.Data["Webhook"] = webhook.Webhook{HookEvent: &webhook.HookEvent{}}
+ ctx.Data["HookType"] = params.Type
+
+ orCtx, err := getOrgRepoCtx(ctx)
+ if err != nil {
+ ctx.ServerError("getOrgRepoCtx", err)
+ return
+ }
+ ctx.Data["BaseLink"] = orCtx.LinkNew
+
+ if ctx.HasError() {
+ ctx.HTML(http.StatusOK, orCtx.NewTemplate)
+ return
+ }
+
+ var meta []byte
+ if params.Meta != nil {
+ meta, err = json.Marshal(params.Meta)
+ if err != nil {
+ ctx.ServerError("Marshal", err)
+ return
+ }
+ }
+
+ w := &webhook.Webhook{
+ RepoID: orCtx.RepoID,
+ URL: params.URL,
+ HTTPMethod: params.HTTPMethod,
+ ContentType: params.ContentType,
+ Secret: params.Secret,
+ HookEvent: ParseHookEvent(params.WebhookForm),
+ IsActive: params.WebhookForm.Active,
+ Type: params.Type,
+ Meta: string(meta),
+ OrgID: orCtx.OrgID,
+ IsSystemWebhook: orCtx.IsSystemWebhook,
+ }
+ if err := w.UpdateEvent(); err != nil {
+ ctx.ServerError("UpdateEvent", err)
+ return
+ } else if err := webhook.CreateWebhook(ctx, w); err != nil {
+ ctx.ServerError("CreateWebhook", err)
+ return
+ }
+
+ ctx.Flash.Success(ctx.Tr("repo.settings.add_hook_success"))
+ ctx.Redirect(orCtx.Link)
+}
+
+func editWebhook(ctx *context.Context, params webhookParams) {
+ ctx.Data["Title"] = ctx.Tr("repo.settings.update_webhook")
+ ctx.Data["PageIsSettingsHooks"] = true
+ ctx.Data["PageIsSettingsHooksEdit"] = true
+
+ orCtx, w := checkWebhook(ctx)
+ if ctx.Written() {
+ return
+ }
+ ctx.Data["Webhook"] = w
+
+ if ctx.HasError() {
+ ctx.HTML(http.StatusOK, orCtx.NewTemplate)
+ return
+ }
+
+ var meta []byte
+ var err error
+ if params.Meta != nil {
+ meta, err = json.Marshal(params.Meta)
+ if err != nil {
+ ctx.ServerError("Marshal", err)
+ return
+ }
+ }
+
+ w.URL = params.URL
+ w.ContentType = params.ContentType
+ w.Secret = params.Secret
+ w.HookEvent = ParseHookEvent(params.WebhookForm)
+ w.IsActive = params.WebhookForm.Active
+ w.HTTPMethod = params.HTTPMethod
+ w.Meta = string(meta)
+
+ if err := w.UpdateEvent(); err != nil {
+ ctx.ServerError("UpdateEvent", err)
+ return
+ } else if err := webhook.UpdateWebhook(w); err != nil {
+ ctx.ServerError("UpdateWebhook", err)
+ return
+ }
+
+ ctx.Flash.Success(ctx.Tr("repo.settings.update_hook_success"))
+ ctx.Redirect(fmt.Sprintf("%s/%d", orCtx.Link, w.ID))
+}
+
// GiteaHooksNewPost response for creating Gitea webhook
func GiteaHooksNewPost(ctx *context.Context) {
+ createWebhook(ctx, giteaHookParams(ctx))
+}
+
+// GiteaHooksEditPost response for editing Gitea webhook
+func GiteaHooksEditPost(ctx *context.Context) {
+ editWebhook(ctx, giteaHookParams(ctx))
+}
+
+func giteaHookParams(ctx *context.Context) webhookParams {
form := web.GetForm(ctx).(*forms.NewWebhookForm)
- ctx.Data["Title"] = ctx.Tr("repo.settings.add_webhook")
- ctx.Data["PageIsSettingsHooks"] = true
- ctx.Data["PageIsSettingsHooksNew"] = true
- ctx.Data["Webhook"] = webhook.Webhook{HookEvent: &webhook.HookEvent{}}
- ctx.Data["HookType"] = webhook.GITEA
-
- orCtx, err := getOrgRepoCtx(ctx)
- if err != nil {
- ctx.ServerError("getOrgRepoCtx", err)
- return
- }
- ctx.Data["BaseLink"] = orCtx.LinkNew
-
- if ctx.HasError() {
- ctx.HTML(http.StatusOK, orCtx.NewTemplate)
- return
- }
contentType := webhook.ContentTypeJSON
if webhook.HookContentType(form.ContentType) == webhook.ContentTypeForm {
contentType = webhook.ContentTypeForm
}
- w := &webhook.Webhook{
- RepoID: orCtx.RepoID,
- URL: form.PayloadURL,
- HTTPMethod: form.HTTPMethod,
- ContentType: contentType,
- Secret: form.Secret,
- HookEvent: ParseHookEvent(form.WebhookForm),
- IsActive: form.Active,
- Type: webhook.GITEA,
- OrgID: orCtx.OrgID,
- IsSystemWebhook: orCtx.IsSystemWebhook,
+ return webhookParams{
+ Type: webhook.GITEA,
+ URL: form.PayloadURL,
+ ContentType: contentType,
+ Secret: form.Secret,
+ HTTPMethod: form.HTTPMethod,
+ WebhookForm: form.WebhookForm,
}
- if err := w.UpdateEvent(); err != nil {
- ctx.ServerError("UpdateEvent", err)
- return
- } else if err := webhook.CreateWebhook(ctx, w); err != nil {
- ctx.ServerError("CreateWebhook", err)
- return
- }
-
- ctx.Flash.Success(ctx.Tr("repo.settings.add_hook_success"))
- ctx.Redirect(orCtx.Link)
}
-// GogsHooksNewPost response for creating webhook
+// GogsHooksNewPost response for creating Gogs webhook
func GogsHooksNewPost(ctx *context.Context) {
- form := web.GetForm(ctx).(*forms.NewGogshookForm)
- newGogsWebhookPost(ctx, *form, webhook.GOGS)
+ createWebhook(ctx, gogsHookParams(ctx))
}
-// newGogsWebhookPost response for creating gogs hook
-func newGogsWebhookPost(ctx *context.Context, form forms.NewGogshookForm, kind webhook.HookType) {
- ctx.Data["Title"] = ctx.Tr("repo.settings.add_webhook")
- ctx.Data["PageIsSettingsHooks"] = true
- ctx.Data["PageIsSettingsHooksNew"] = true
- ctx.Data["Webhook"] = webhook.Webhook{HookEvent: &webhook.HookEvent{}}
- ctx.Data["HookType"] = webhook.GOGS
+// GogsHooksEditPost response for editing Gogs webhook
+func GogsHooksEditPost(ctx *context.Context) {
+ editWebhook(ctx, gogsHookParams(ctx))
+}
- orCtx, err := getOrgRepoCtx(ctx)
- if err != nil {
- ctx.ServerError("getOrgRepoCtx", err)
- return
- }
- ctx.Data["BaseLink"] = orCtx.LinkNew
-
- if ctx.HasError() {
- ctx.HTML(http.StatusOK, orCtx.NewTemplate)
- return
- }
+func gogsHookParams(ctx *context.Context) webhookParams {
+ form := web.GetForm(ctx).(*forms.NewGogshookForm)
contentType := webhook.ContentTypeJSON
if webhook.HookContentType(form.ContentType) == webhook.ContentTypeForm {
contentType = webhook.ContentTypeForm
}
- w := &webhook.Webhook{
- RepoID: orCtx.RepoID,
- URL: form.PayloadURL,
- ContentType: contentType,
- Secret: form.Secret,
- HookEvent: ParseHookEvent(form.WebhookForm),
- IsActive: form.Active,
- Type: kind,
- OrgID: orCtx.OrgID,
- IsSystemWebhook: orCtx.IsSystemWebhook,
+ return webhookParams{
+ Type: webhook.GOGS,
+ URL: form.PayloadURL,
+ ContentType: contentType,
+ Secret: form.Secret,
+ WebhookForm: form.WebhookForm,
}
- if err := w.UpdateEvent(); err != nil {
- ctx.ServerError("UpdateEvent", err)
- return
- } else if err := webhook.CreateWebhook(ctx, w); err != nil {
- ctx.ServerError("CreateWebhook", err)
- return
- }
-
- ctx.Flash.Success(ctx.Tr("repo.settings.add_hook_success"))
- ctx.Redirect(orCtx.Link)
}
-// DiscordHooksNewPost response for creating discord hook
+// DiscordHooksNewPost response for creating Discord webhook
func DiscordHooksNewPost(ctx *context.Context) {
+ createWebhook(ctx, discordHookParams(ctx))
+}
+
+// DiscordHooksEditPost response for editing Discord webhook
+func DiscordHooksEditPost(ctx *context.Context) {
+ editWebhook(ctx, discordHookParams(ctx))
+}
+
+func discordHookParams(ctx *context.Context) webhookParams {
form := web.GetForm(ctx).(*forms.NewDiscordHookForm)
- ctx.Data["Title"] = ctx.Tr("repo.settings")
- ctx.Data["PageIsSettingsHooks"] = true
- ctx.Data["PageIsSettingsHooksNew"] = true
- ctx.Data["Webhook"] = webhook.Webhook{HookEvent: &webhook.HookEvent{}}
- ctx.Data["HookType"] = webhook.DISCORD
- orCtx, err := getOrgRepoCtx(ctx)
- if err != nil {
- ctx.ServerError("getOrgRepoCtx", err)
- return
+ return webhookParams{
+ Type: webhook.DISCORD,
+ URL: form.PayloadURL,
+ ContentType: webhook.ContentTypeJSON,
+ WebhookForm: form.WebhookForm,
+ Meta: &webhook_service.DiscordMeta{
+ Username: form.Username,
+ IconURL: form.IconURL,
+ },
}
-
- if ctx.HasError() {
- ctx.HTML(http.StatusOK, orCtx.NewTemplate)
- return
- }
-
- meta, err := json.Marshal(&webhook_service.DiscordMeta{
- Username: form.Username,
- IconURL: form.IconURL,
- })
- if err != nil {
- ctx.ServerError("Marshal", err)
- return
- }
-
- w := &webhook.Webhook{
- RepoID: orCtx.RepoID,
- URL: form.PayloadURL,
- ContentType: webhook.ContentTypeJSON,
- HookEvent: ParseHookEvent(form.WebhookForm),
- IsActive: form.Active,
- Type: webhook.DISCORD,
- Meta: string(meta),
- OrgID: orCtx.OrgID,
- IsSystemWebhook: orCtx.IsSystemWebhook,
- }
- if err := w.UpdateEvent(); err != nil {
- ctx.ServerError("UpdateEvent", err)
- return
- } else if err := webhook.CreateWebhook(ctx, w); err != nil {
- ctx.ServerError("CreateWebhook", err)
- return
- }
-
- ctx.Flash.Success(ctx.Tr("repo.settings.add_hook_success"))
- ctx.Redirect(orCtx.Link)
}
-// DingtalkHooksNewPost response for creating dingtalk hook
+// DingtalkHooksNewPost response for creating Dingtalk webhook
func DingtalkHooksNewPost(ctx *context.Context) {
+ createWebhook(ctx, dingtalkHookParams(ctx))
+}
+
+// DingtalkHooksEditPost response for editing Dingtalk webhook
+func DingtalkHooksEditPost(ctx *context.Context) {
+ editWebhook(ctx, dingtalkHookParams(ctx))
+}
+
+func dingtalkHookParams(ctx *context.Context) webhookParams {
form := web.GetForm(ctx).(*forms.NewDingtalkHookForm)
- ctx.Data["Title"] = ctx.Tr("repo.settings")
- ctx.Data["PageIsSettingsHooks"] = true
- ctx.Data["PageIsSettingsHooksNew"] = true
- ctx.Data["Webhook"] = webhook.Webhook{HookEvent: &webhook.HookEvent{}}
- ctx.Data["HookType"] = webhook.DINGTALK
- orCtx, err := getOrgRepoCtx(ctx)
- if err != nil {
- ctx.ServerError("getOrgRepoCtx", err)
- return
+ return webhookParams{
+ Type: webhook.DINGTALK,
+ URL: form.PayloadURL,
+ ContentType: webhook.ContentTypeJSON,
+ WebhookForm: form.WebhookForm,
}
-
- if ctx.HasError() {
- ctx.HTML(http.StatusOK, orCtx.NewTemplate)
- return
- }
-
- w := &webhook.Webhook{
- RepoID: orCtx.RepoID,
- URL: form.PayloadURL,
- ContentType: webhook.ContentTypeJSON,
- HookEvent: ParseHookEvent(form.WebhookForm),
- IsActive: form.Active,
- Type: webhook.DINGTALK,
- Meta: "",
- OrgID: orCtx.OrgID,
- IsSystemWebhook: orCtx.IsSystemWebhook,
- }
- if err := w.UpdateEvent(); err != nil {
- ctx.ServerError("UpdateEvent", err)
- return
- } else if err := webhook.CreateWebhook(ctx, w); err != nil {
- ctx.ServerError("CreateWebhook", err)
- return
- }
-
- ctx.Flash.Success(ctx.Tr("repo.settings.add_hook_success"))
- ctx.Redirect(orCtx.Link)
}
-// TelegramHooksNewPost response for creating telegram hook
+// TelegramHooksNewPost response for creating Telegram webhook
func TelegramHooksNewPost(ctx *context.Context) {
+ createWebhook(ctx, telegramHookParams(ctx))
+}
+
+// TelegramHooksEditPost response for editing Telegram webhook
+func TelegramHooksEditPost(ctx *context.Context) {
+ editWebhook(ctx, telegramHookParams(ctx))
+}
+
+func telegramHookParams(ctx *context.Context) webhookParams {
form := web.GetForm(ctx).(*forms.NewTelegramHookForm)
- ctx.Data["Title"] = ctx.Tr("repo.settings")
- ctx.Data["PageIsSettingsHooks"] = true
- ctx.Data["PageIsSettingsHooksNew"] = true
- ctx.Data["Webhook"] = webhook.Webhook{HookEvent: &webhook.HookEvent{}}
- ctx.Data["HookType"] = webhook.TELEGRAM
- orCtx, err := getOrgRepoCtx(ctx)
- if err != nil {
- ctx.ServerError("getOrgRepoCtx", err)
- return
+ return webhookParams{
+ Type: webhook.TELEGRAM,
+ URL: fmt.Sprintf("https://api.telegram.org/bot%s/sendMessage?chat_id=%s", url.PathEscape(form.BotToken), url.QueryEscape(form.ChatID)),
+ ContentType: webhook.ContentTypeJSON,
+ WebhookForm: form.WebhookForm,
+ Meta: &webhook_service.TelegramMeta{
+ BotToken: form.BotToken,
+ ChatID: form.ChatID,
+ },
}
-
- if ctx.HasError() {
- ctx.HTML(http.StatusOK, orCtx.NewTemplate)
- return
- }
-
- meta, err := json.Marshal(&webhook_service.TelegramMeta{
- BotToken: form.BotToken,
- ChatID: form.ChatID,
- })
- if err != nil {
- ctx.ServerError("Marshal", err)
- return
- }
-
- w := &webhook.Webhook{
- RepoID: orCtx.RepoID,
- URL: fmt.Sprintf("https://api.telegram.org/bot%s/sendMessage?chat_id=%s", url.PathEscape(form.BotToken), url.QueryEscape(form.ChatID)),
- ContentType: webhook.ContentTypeJSON,
- HookEvent: ParseHookEvent(form.WebhookForm),
- IsActive: form.Active,
- Type: webhook.TELEGRAM,
- Meta: string(meta),
- OrgID: orCtx.OrgID,
- IsSystemWebhook: orCtx.IsSystemWebhook,
- }
- if err := w.UpdateEvent(); err != nil {
- ctx.ServerError("UpdateEvent", err)
- return
- } else if err := webhook.CreateWebhook(ctx, w); err != nil {
- ctx.ServerError("CreateWebhook", err)
- return
- }
-
- ctx.Flash.Success(ctx.Tr("repo.settings.add_hook_success"))
- ctx.Redirect(orCtx.Link)
}
-// MatrixHooksNewPost response for creating a Matrix hook
+// MatrixHooksNewPost response for creating Matrix webhook
func MatrixHooksNewPost(ctx *context.Context) {
+ createWebhook(ctx, matrixHookParams(ctx))
+}
+
+// MatrixHooksEditPost response for editing Matrix webhook
+func MatrixHooksEditPost(ctx *context.Context) {
+ editWebhook(ctx, matrixHookParams(ctx))
+}
+
+func matrixHookParams(ctx *context.Context) webhookParams {
form := web.GetForm(ctx).(*forms.NewMatrixHookForm)
- ctx.Data["Title"] = ctx.Tr("repo.settings")
- ctx.Data["PageIsSettingsHooks"] = true
- ctx.Data["PageIsSettingsHooksNew"] = true
- ctx.Data["Webhook"] = webhook.Webhook{HookEvent: &webhook.HookEvent{}}
- ctx.Data["HookType"] = webhook.MATRIX
- orCtx, err := getOrgRepoCtx(ctx)
- if err != nil {
- ctx.ServerError("getOrgRepoCtx", err)
- return
+ return webhookParams{
+ Type: webhook.MATRIX,
+ URL: fmt.Sprintf("%s/_matrix/client/r0/rooms/%s/send/m.room.message", form.HomeserverURL, url.PathEscape(form.RoomID)),
+ ContentType: webhook.ContentTypeJSON,
+ HTTPMethod: http.MethodPut,
+ WebhookForm: form.WebhookForm,
+ Meta: &webhook_service.MatrixMeta{
+ HomeserverURL: form.HomeserverURL,
+ Room: form.RoomID,
+ AccessToken: form.AccessToken,
+ MessageType: form.MessageType,
+ },
}
-
- if ctx.HasError() {
- ctx.HTML(http.StatusOK, orCtx.NewTemplate)
- return
- }
-
- meta, err := json.Marshal(&webhook_service.MatrixMeta{
- HomeserverURL: form.HomeserverURL,
- Room: form.RoomID,
- AccessToken: form.AccessToken,
- MessageType: form.MessageType,
- })
- if err != nil {
- ctx.ServerError("Marshal", err)
- return
- }
-
- w := &webhook.Webhook{
- RepoID: orCtx.RepoID,
- URL: fmt.Sprintf("%s/_matrix/client/r0/rooms/%s/send/m.room.message", form.HomeserverURL, url.PathEscape(form.RoomID)),
- ContentType: webhook.ContentTypeJSON,
- HTTPMethod: "PUT",
- HookEvent: ParseHookEvent(form.WebhookForm),
- IsActive: form.Active,
- Type: webhook.MATRIX,
- Meta: string(meta),
- OrgID: orCtx.OrgID,
- IsSystemWebhook: orCtx.IsSystemWebhook,
- }
- if err := w.UpdateEvent(); err != nil {
- ctx.ServerError("UpdateEvent", err)
- return
- } else if err := webhook.CreateWebhook(ctx, w); err != nil {
- ctx.ServerError("CreateWebhook", err)
- return
- }
-
- ctx.Flash.Success(ctx.Tr("repo.settings.add_hook_success"))
- ctx.Redirect(orCtx.Link)
}
-// MSTeamsHooksNewPost response for creating MS Teams hook
+// MSTeamsHooksNewPost response for creating MSTeams webhook
func MSTeamsHooksNewPost(ctx *context.Context) {
+ createWebhook(ctx, mSTeamsHookParams(ctx))
+}
+
+// MSTeamsHooksEditPost response for editing MSTeams webhook
+func MSTeamsHooksEditPost(ctx *context.Context) {
+ editWebhook(ctx, mSTeamsHookParams(ctx))
+}
+
+func mSTeamsHookParams(ctx *context.Context) webhookParams {
form := web.GetForm(ctx).(*forms.NewMSTeamsHookForm)
- ctx.Data["Title"] = ctx.Tr("repo.settings")
- ctx.Data["PageIsSettingsHooks"] = true
- ctx.Data["PageIsSettingsHooksNew"] = true
- ctx.Data["Webhook"] = webhook.Webhook{HookEvent: &webhook.HookEvent{}}
- ctx.Data["HookType"] = webhook.MSTEAMS
- orCtx, err := getOrgRepoCtx(ctx)
- if err != nil {
- ctx.ServerError("getOrgRepoCtx", err)
- return
+ return webhookParams{
+ Type: webhook.MSTEAMS,
+ URL: form.PayloadURL,
+ ContentType: webhook.ContentTypeJSON,
+ WebhookForm: form.WebhookForm,
}
-
- if ctx.HasError() {
- ctx.HTML(http.StatusOK, orCtx.NewTemplate)
- return
- }
-
- w := &webhook.Webhook{
- RepoID: orCtx.RepoID,
- URL: form.PayloadURL,
- ContentType: webhook.ContentTypeJSON,
- HookEvent: ParseHookEvent(form.WebhookForm),
- IsActive: form.Active,
- Type: webhook.MSTEAMS,
- Meta: "",
- OrgID: orCtx.OrgID,
- IsSystemWebhook: orCtx.IsSystemWebhook,
- }
- if err := w.UpdateEvent(); err != nil {
- ctx.ServerError("UpdateEvent", err)
- return
- } else if err := webhook.CreateWebhook(ctx, w); err != nil {
- ctx.ServerError("CreateWebhook", err)
- return
- }
-
- ctx.Flash.Success(ctx.Tr("repo.settings.add_hook_success"))
- ctx.Redirect(orCtx.Link)
}
-// SlackHooksNewPost response for creating slack hook
+// SlackHooksNewPost response for creating Slack webhook
func SlackHooksNewPost(ctx *context.Context) {
+ createWebhook(ctx, slackHookParams(ctx))
+}
+
+// SlackHooksEditPost response for editing Slack webhook
+func SlackHooksEditPost(ctx *context.Context) {
+ editWebhook(ctx, slackHookParams(ctx))
+}
+
+func slackHookParams(ctx *context.Context) webhookParams {
form := web.GetForm(ctx).(*forms.NewSlackHookForm)
- ctx.Data["Title"] = ctx.Tr("repo.settings")
- ctx.Data["PageIsSettingsHooks"] = true
- ctx.Data["PageIsSettingsHooksNew"] = true
- ctx.Data["Webhook"] = webhook.Webhook{HookEvent: &webhook.HookEvent{}}
- ctx.Data["HookType"] = webhook.SLACK
- orCtx, err := getOrgRepoCtx(ctx)
- if err != nil {
- ctx.ServerError("getOrgRepoCtx", err)
- return
+ return webhookParams{
+ Type: webhook.SLACK,
+ URL: form.PayloadURL,
+ ContentType: webhook.ContentTypeJSON,
+ WebhookForm: form.WebhookForm,
+ Meta: &webhook_service.SlackMeta{
+ Channel: strings.TrimSpace(form.Channel),
+ Username: form.Username,
+ IconURL: form.IconURL,
+ Color: form.Color,
+ },
}
-
- if ctx.HasError() {
- ctx.HTML(http.StatusOK, orCtx.NewTemplate)
- return
- }
-
- if form.HasInvalidChannel() {
- ctx.Flash.Error(ctx.Tr("repo.settings.add_webhook.invalid_channel_name"))
- ctx.Redirect(orCtx.LinkNew + "/slack/new")
- return
- }
-
- meta, err := json.Marshal(&webhook_service.SlackMeta{
- Channel: strings.TrimSpace(form.Channel),
- Username: form.Username,
- IconURL: form.IconURL,
- Color: form.Color,
- })
- if err != nil {
- ctx.ServerError("Marshal", err)
- return
- }
-
- w := &webhook.Webhook{
- RepoID: orCtx.RepoID,
- URL: form.PayloadURL,
- ContentType: webhook.ContentTypeJSON,
- HookEvent: ParseHookEvent(form.WebhookForm),
- IsActive: form.Active,
- Type: webhook.SLACK,
- Meta: string(meta),
- OrgID: orCtx.OrgID,
- IsSystemWebhook: orCtx.IsSystemWebhook,
- }
- if err := w.UpdateEvent(); err != nil {
- ctx.ServerError("UpdateEvent", err)
- return
- } else if err := webhook.CreateWebhook(ctx, w); err != nil {
- ctx.ServerError("CreateWebhook", err)
- return
- }
-
- ctx.Flash.Success(ctx.Tr("repo.settings.add_hook_success"))
- ctx.Redirect(orCtx.Link)
}
-// FeishuHooksNewPost response for creating feishu hook
+// FeishuHooksNewPost response for creating Feishu webhook
func FeishuHooksNewPost(ctx *context.Context) {
- form := web.GetForm(ctx).(*forms.NewFeishuHookForm)
- ctx.Data["Title"] = ctx.Tr("repo.settings")
- ctx.Data["PageIsSettingsHooks"] = true
- ctx.Data["PageIsSettingsHooksNew"] = true
- ctx.Data["Webhook"] = webhook.Webhook{HookEvent: &webhook.HookEvent{}}
- ctx.Data["HookType"] = webhook.FEISHU
-
- orCtx, err := getOrgRepoCtx(ctx)
- if err != nil {
- ctx.ServerError("getOrgRepoCtx", err)
- return
- }
-
- if ctx.HasError() {
- ctx.HTML(http.StatusOK, orCtx.NewTemplate)
- return
- }
-
- w := &webhook.Webhook{
- RepoID: orCtx.RepoID,
- URL: form.PayloadURL,
- ContentType: webhook.ContentTypeJSON,
- HookEvent: ParseHookEvent(form.WebhookForm),
- IsActive: form.Active,
- Type: webhook.FEISHU,
- Meta: "",
- OrgID: orCtx.OrgID,
- IsSystemWebhook: orCtx.IsSystemWebhook,
- }
- if err := w.UpdateEvent(); err != nil {
- ctx.ServerError("UpdateEvent", err)
- return
- } else if err := webhook.CreateWebhook(ctx, w); err != nil {
- ctx.ServerError("CreateWebhook", err)
- return
- }
-
- ctx.Flash.Success(ctx.Tr("repo.settings.add_hook_success"))
- ctx.Redirect(orCtx.Link)
+ createWebhook(ctx, feishuHookParams(ctx))
}
-// WechatworkHooksNewPost response for creating wechatwork hook
+// FeishuHooksEditPost response for editing Feishu webhook
+func FeishuHooksEditPost(ctx *context.Context) {
+ editWebhook(ctx, feishuHookParams(ctx))
+}
+
+func feishuHookParams(ctx *context.Context) webhookParams {
+ form := web.GetForm(ctx).(*forms.NewFeishuHookForm)
+
+ return webhookParams{
+ Type: webhook.FEISHU,
+ URL: form.PayloadURL,
+ ContentType: webhook.ContentTypeJSON,
+ WebhookForm: form.WebhookForm,
+ }
+}
+
+// WechatworkHooksNewPost response for creating Wechatwork webhook
func WechatworkHooksNewPost(ctx *context.Context) {
+ createWebhook(ctx, wechatworkHookParams(ctx))
+}
+
+// WechatworkHooksEditPost response for editing Wechatwork webhook
+func WechatworkHooksEditPost(ctx *context.Context) {
+ editWebhook(ctx, wechatworkHookParams(ctx))
+}
+
+func wechatworkHookParams(ctx *context.Context) webhookParams {
form := web.GetForm(ctx).(*forms.NewWechatWorkHookForm)
- ctx.Data["Title"] = ctx.Tr("repo.settings")
- ctx.Data["PageIsSettingsHooks"] = true
- ctx.Data["PageIsSettingsHooksNew"] = true
- ctx.Data["Webhook"] = webhook.Webhook{HookEvent: &webhook.HookEvent{}}
- ctx.Data["HookType"] = webhook.WECHATWORK
-
- orCtx, err := getOrgRepoCtx(ctx)
- if err != nil {
- ctx.ServerError("getOrgRepoCtx", err)
- return
+ return webhookParams{
+ Type: webhook.WECHATWORK,
+ URL: form.PayloadURL,
+ ContentType: webhook.ContentTypeJSON,
+ WebhookForm: form.WebhookForm,
}
-
- if ctx.HasError() {
- ctx.HTML(http.StatusOK, orCtx.NewTemplate)
- return
- }
-
- w := &webhook.Webhook{
- RepoID: orCtx.RepoID,
- URL: form.PayloadURL,
- ContentType: webhook.ContentTypeJSON,
- HookEvent: ParseHookEvent(form.WebhookForm),
- IsActive: form.Active,
- Type: webhook.WECHATWORK,
- Meta: "",
- OrgID: orCtx.OrgID,
- IsSystemWebhook: orCtx.IsSystemWebhook,
- }
- if err := w.UpdateEvent(); err != nil {
- ctx.ServerError("UpdateEvent", err)
- return
- } else if err := webhook.CreateWebhook(ctx, w); err != nil {
- ctx.ServerError("CreateWebhook", err)
- return
- }
-
- ctx.Flash.Success(ctx.Tr("repo.settings.add_hook_success"))
- ctx.Redirect(orCtx.Link)
}
-// PackagistHooksNewPost response for creating packagist hook
+// PackagistHooksNewPost response for creating Packagist webhook
func PackagistHooksNewPost(ctx *context.Context) {
+ createWebhook(ctx, packagistHookParams(ctx))
+}
+
+// PackagistHooksEditPost response for editing Packagist webhook
+func PackagistHooksEditPost(ctx *context.Context) {
+ editWebhook(ctx, packagistHookParams(ctx))
+}
+
+func packagistHookParams(ctx *context.Context) webhookParams {
form := web.GetForm(ctx).(*forms.NewPackagistHookForm)
- ctx.Data["Title"] = ctx.Tr("repo.settings")
- ctx.Data["PageIsSettingsHooks"] = true
- ctx.Data["PageIsSettingsHooksNew"] = true
- ctx.Data["Webhook"] = webhook.Webhook{HookEvent: &webhook.HookEvent{}}
- ctx.Data["HookType"] = webhook.PACKAGIST
- orCtx, err := getOrgRepoCtx(ctx)
- if err != nil {
- ctx.ServerError("getOrgRepoCtx", err)
- return
+ return webhookParams{
+ Type: webhook.PACKAGIST,
+ URL: fmt.Sprintf("https://packagist.org/api/update-package?username=%s&apiToken=%s", url.QueryEscape(form.Username), url.QueryEscape(form.APIToken)),
+ ContentType: webhook.ContentTypeJSON,
+ WebhookForm: form.WebhookForm,
+ Meta: &webhook_service.PackagistMeta{
+ Username: form.Username,
+ APIToken: form.APIToken,
+ PackageURL: form.PackageURL,
+ },
}
-
- if ctx.HasError() {
- ctx.HTML(http.StatusOK, orCtx.NewTemplate)
- return
- }
-
- meta, err := json.Marshal(&webhook_service.PackagistMeta{
- Username: form.Username,
- APIToken: form.APIToken,
- PackageURL: form.PackageURL,
- })
- if err != nil {
- ctx.ServerError("Marshal", err)
- return
- }
-
- w := &webhook.Webhook{
- RepoID: orCtx.RepoID,
- URL: fmt.Sprintf("https://packagist.org/api/update-package?username=%s&apiToken=%s", url.QueryEscape(form.Username), url.QueryEscape(form.APIToken)),
- ContentType: webhook.ContentTypeJSON,
- HookEvent: ParseHookEvent(form.WebhookForm),
- IsActive: form.Active,
- Type: webhook.PACKAGIST,
- Meta: string(meta),
- OrgID: orCtx.OrgID,
- IsSystemWebhook: orCtx.IsSystemWebhook,
- }
- if err := w.UpdateEvent(); err != nil {
- ctx.ServerError("UpdateEvent", err)
- return
- } else if err := webhook.CreateWebhook(ctx, w); err != nil {
- ctx.ServerError("CreateWebhook", err)
- return
- }
-
- ctx.Flash.Success(ctx.Tr("repo.settings.add_hook_success"))
- ctx.Redirect(orCtx.Link)
}
func checkWebhook(ctx *context.Context) (*orgRepoCtx, *webhook.Webhook) {
@@ -795,444 +628,6 @@ func WebHooksEdit(ctx *context.Context) {
ctx.HTML(http.StatusOK, orCtx.NewTemplate)
}
-// WebHooksEditPost response for editing web hook
-func WebHooksEditPost(ctx *context.Context) {
- form := web.GetForm(ctx).(*forms.NewWebhookForm)
- ctx.Data["Title"] = ctx.Tr("repo.settings.update_webhook")
- ctx.Data["PageIsSettingsHooks"] = true
- ctx.Data["PageIsSettingsHooksEdit"] = true
-
- orCtx, w := checkWebhook(ctx)
- if ctx.Written() {
- return
- }
- ctx.Data["Webhook"] = w
-
- if ctx.HasError() {
- ctx.HTML(http.StatusOK, orCtx.NewTemplate)
- return
- }
-
- contentType := webhook.ContentTypeJSON
- if webhook.HookContentType(form.ContentType) == webhook.ContentTypeForm {
- contentType = webhook.ContentTypeForm
- }
-
- w.URL = form.PayloadURL
- w.ContentType = contentType
- w.Secret = form.Secret
- w.HookEvent = ParseHookEvent(form.WebhookForm)
- w.IsActive = form.Active
- w.HTTPMethod = form.HTTPMethod
- if err := w.UpdateEvent(); err != nil {
- ctx.ServerError("UpdateEvent", err)
- return
- } else if err := webhook.UpdateWebhook(w); err != nil {
- ctx.ServerError("WebHooksEditPost", err)
- return
- }
-
- ctx.Flash.Success(ctx.Tr("repo.settings.update_hook_success"))
- ctx.Redirect(fmt.Sprintf("%s/%d", orCtx.Link, w.ID))
-}
-
-// GogsHooksEditPost response for editing gogs hook
-func GogsHooksEditPost(ctx *context.Context) {
- form := web.GetForm(ctx).(*forms.NewGogshookForm)
- ctx.Data["Title"] = ctx.Tr("repo.settings.update_webhook")
- ctx.Data["PageIsSettingsHooks"] = true
- ctx.Data["PageIsSettingsHooksEdit"] = true
-
- orCtx, w := checkWebhook(ctx)
- if ctx.Written() {
- return
- }
- ctx.Data["Webhook"] = w
-
- if ctx.HasError() {
- ctx.HTML(http.StatusOK, orCtx.NewTemplate)
- return
- }
-
- contentType := webhook.ContentTypeJSON
- if webhook.HookContentType(form.ContentType) == webhook.ContentTypeForm {
- contentType = webhook.ContentTypeForm
- }
-
- w.URL = form.PayloadURL
- w.ContentType = contentType
- w.Secret = form.Secret
- w.HookEvent = ParseHookEvent(form.WebhookForm)
- w.IsActive = form.Active
- if err := w.UpdateEvent(); err != nil {
- ctx.ServerError("UpdateEvent", err)
- return
- } else if err := webhook.UpdateWebhook(w); err != nil {
- ctx.ServerError("GogsHooksEditPost", err)
- return
- }
-
- ctx.Flash.Success(ctx.Tr("repo.settings.update_hook_success"))
- ctx.Redirect(fmt.Sprintf("%s/%d", orCtx.Link, w.ID))
-}
-
-// SlackHooksEditPost response for editing slack hook
-func SlackHooksEditPost(ctx *context.Context) {
- form := web.GetForm(ctx).(*forms.NewSlackHookForm)
- ctx.Data["Title"] = ctx.Tr("repo.settings")
- ctx.Data["PageIsSettingsHooks"] = true
- ctx.Data["PageIsSettingsHooksEdit"] = true
-
- orCtx, w := checkWebhook(ctx)
- if ctx.Written() {
- return
- }
- ctx.Data["Webhook"] = w
-
- if ctx.HasError() {
- ctx.HTML(http.StatusOK, orCtx.NewTemplate)
- return
- }
-
- if form.HasInvalidChannel() {
- ctx.Flash.Error(ctx.Tr("repo.settings.add_webhook.invalid_channel_name"))
- ctx.Redirect(fmt.Sprintf("%s/%d", orCtx.Link, w.ID))
- return
- }
-
- meta, err := json.Marshal(&webhook_service.SlackMeta{
- Channel: strings.TrimSpace(form.Channel),
- Username: form.Username,
- IconURL: form.IconURL,
- Color: form.Color,
- })
- if err != nil {
- ctx.ServerError("Marshal", err)
- return
- }
-
- w.URL = form.PayloadURL
- w.Meta = string(meta)
- w.HookEvent = ParseHookEvent(form.WebhookForm)
- w.IsActive = form.Active
- if err := w.UpdateEvent(); err != nil {
- ctx.ServerError("UpdateEvent", err)
- return
- } else if err := webhook.UpdateWebhook(w); err != nil {
- ctx.ServerError("UpdateWebhook", err)
- return
- }
-
- ctx.Flash.Success(ctx.Tr("repo.settings.update_hook_success"))
- ctx.Redirect(fmt.Sprintf("%s/%d", orCtx.Link, w.ID))
-}
-
-// DiscordHooksEditPost response for editing discord hook
-func DiscordHooksEditPost(ctx *context.Context) {
- form := web.GetForm(ctx).(*forms.NewDiscordHookForm)
- ctx.Data["Title"] = ctx.Tr("repo.settings")
- ctx.Data["PageIsSettingsHooks"] = true
- ctx.Data["PageIsSettingsHooksEdit"] = true
-
- orCtx, w := checkWebhook(ctx)
- if ctx.Written() {
- return
- }
- ctx.Data["Webhook"] = w
-
- if ctx.HasError() {
- ctx.HTML(http.StatusOK, orCtx.NewTemplate)
- return
- }
-
- meta, err := json.Marshal(&webhook_service.DiscordMeta{
- Username: form.Username,
- IconURL: form.IconURL,
- })
- if err != nil {
- ctx.ServerError("Marshal", err)
- return
- }
-
- w.URL = form.PayloadURL
- w.Meta = string(meta)
- w.HookEvent = ParseHookEvent(form.WebhookForm)
- w.IsActive = form.Active
- if err := w.UpdateEvent(); err != nil {
- ctx.ServerError("UpdateEvent", err)
- return
- } else if err := webhook.UpdateWebhook(w); err != nil {
- ctx.ServerError("UpdateWebhook", err)
- return
- }
-
- ctx.Flash.Success(ctx.Tr("repo.settings.update_hook_success"))
- ctx.Redirect(fmt.Sprintf("%s/%d", orCtx.Link, w.ID))
-}
-
-// DingtalkHooksEditPost response for editing discord hook
-func DingtalkHooksEditPost(ctx *context.Context) {
- form := web.GetForm(ctx).(*forms.NewDingtalkHookForm)
- ctx.Data["Title"] = ctx.Tr("repo.settings")
- ctx.Data["PageIsSettingsHooks"] = true
- ctx.Data["PageIsSettingsHooksEdit"] = true
-
- orCtx, w := checkWebhook(ctx)
- if ctx.Written() {
- return
- }
- ctx.Data["Webhook"] = w
-
- if ctx.HasError() {
- ctx.HTML(http.StatusOK, orCtx.NewTemplate)
- return
- }
-
- w.URL = form.PayloadURL
- w.HookEvent = ParseHookEvent(form.WebhookForm)
- w.IsActive = form.Active
- if err := w.UpdateEvent(); err != nil {
- ctx.ServerError("UpdateEvent", err)
- return
- } else if err := webhook.UpdateWebhook(w); err != nil {
- ctx.ServerError("UpdateWebhook", err)
- return
- }
-
- ctx.Flash.Success(ctx.Tr("repo.settings.update_hook_success"))
- ctx.Redirect(fmt.Sprintf("%s/%d", orCtx.Link, w.ID))
-}
-
-// TelegramHooksEditPost response for editing discord hook
-func TelegramHooksEditPost(ctx *context.Context) {
- form := web.GetForm(ctx).(*forms.NewTelegramHookForm)
- ctx.Data["Title"] = ctx.Tr("repo.settings")
- ctx.Data["PageIsSettingsHooks"] = true
- ctx.Data["PageIsSettingsHooksEdit"] = true
-
- orCtx, w := checkWebhook(ctx)
- if ctx.Written() {
- return
- }
- ctx.Data["Webhook"] = w
-
- if ctx.HasError() {
- ctx.HTML(http.StatusOK, orCtx.NewTemplate)
- return
- }
-
- meta, err := json.Marshal(&webhook_service.TelegramMeta{
- BotToken: form.BotToken,
- ChatID: form.ChatID,
- })
- if err != nil {
- ctx.ServerError("Marshal", err)
- return
- }
- w.Meta = string(meta)
- w.URL = fmt.Sprintf("https://api.telegram.org/bot%s/sendMessage?chat_id=%s", url.PathEscape(form.BotToken), url.QueryEscape(form.ChatID))
- w.HookEvent = ParseHookEvent(form.WebhookForm)
- w.IsActive = form.Active
- if err := w.UpdateEvent(); err != nil {
- ctx.ServerError("UpdateEvent", err)
- return
- } else if err := webhook.UpdateWebhook(w); err != nil {
- ctx.ServerError("UpdateWebhook", err)
- return
- }
-
- ctx.Flash.Success(ctx.Tr("repo.settings.update_hook_success"))
- ctx.Redirect(fmt.Sprintf("%s/%d", orCtx.Link, w.ID))
-}
-
-// MatrixHooksEditPost response for editing a Matrix hook
-func MatrixHooksEditPost(ctx *context.Context) {
- form := web.GetForm(ctx).(*forms.NewMatrixHookForm)
- ctx.Data["Title"] = ctx.Tr("repo.settings")
- ctx.Data["PageIsSettingsHooks"] = true
- ctx.Data["PageIsSettingsHooksEdit"] = true
-
- orCtx, w := checkWebhook(ctx)
- if ctx.Written() {
- return
- }
- ctx.Data["Webhook"] = w
-
- if ctx.HasError() {
- ctx.HTML(http.StatusOK, orCtx.NewTemplate)
- return
- }
-
- meta, err := json.Marshal(&webhook_service.MatrixMeta{
- HomeserverURL: form.HomeserverURL,
- Room: form.RoomID,
- AccessToken: form.AccessToken,
- MessageType: form.MessageType,
- })
- if err != nil {
- ctx.ServerError("Marshal", err)
- return
- }
- w.Meta = string(meta)
- w.URL = fmt.Sprintf("%s/_matrix/client/r0/rooms/%s/send/m.room.message", form.HomeserverURL, url.PathEscape(form.RoomID))
-
- w.HookEvent = ParseHookEvent(form.WebhookForm)
- w.IsActive = form.Active
- if err := w.UpdateEvent(); err != nil {
- ctx.ServerError("UpdateEvent", err)
- return
- } else if err := webhook.UpdateWebhook(w); err != nil {
- ctx.ServerError("UpdateWebhook", err)
- return
- }
-
- ctx.Flash.Success(ctx.Tr("repo.settings.update_hook_success"))
- ctx.Redirect(fmt.Sprintf("%s/%d", orCtx.Link, w.ID))
-}
-
-// MSTeamsHooksEditPost response for editing MS Teams hook
-func MSTeamsHooksEditPost(ctx *context.Context) {
- form := web.GetForm(ctx).(*forms.NewMSTeamsHookForm)
- ctx.Data["Title"] = ctx.Tr("repo.settings")
- ctx.Data["PageIsSettingsHooks"] = true
- ctx.Data["PageIsSettingsHooksEdit"] = true
-
- orCtx, w := checkWebhook(ctx)
- if ctx.Written() {
- return
- }
- ctx.Data["Webhook"] = w
-
- if ctx.HasError() {
- ctx.HTML(http.StatusOK, orCtx.NewTemplate)
- return
- }
-
- w.URL = form.PayloadURL
- w.HookEvent = ParseHookEvent(form.WebhookForm)
- w.IsActive = form.Active
- if err := w.UpdateEvent(); err != nil {
- ctx.ServerError("UpdateEvent", err)
- return
- } else if err := webhook.UpdateWebhook(w); err != nil {
- ctx.ServerError("UpdateWebhook", err)
- return
- }
-
- ctx.Flash.Success(ctx.Tr("repo.settings.update_hook_success"))
- ctx.Redirect(fmt.Sprintf("%s/%d", orCtx.Link, w.ID))
-}
-
-// FeishuHooksEditPost response for editing feishu hook
-func FeishuHooksEditPost(ctx *context.Context) {
- form := web.GetForm(ctx).(*forms.NewFeishuHookForm)
- ctx.Data["Title"] = ctx.Tr("repo.settings")
- ctx.Data["PageIsSettingsHooks"] = true
- ctx.Data["PageIsSettingsHooksEdit"] = true
-
- orCtx, w := checkWebhook(ctx)
- if ctx.Written() {
- return
- }
- ctx.Data["Webhook"] = w
-
- if ctx.HasError() {
- ctx.HTML(http.StatusOK, orCtx.NewTemplate)
- return
- }
-
- w.URL = form.PayloadURL
- w.HookEvent = ParseHookEvent(form.WebhookForm)
- w.IsActive = form.Active
- if err := w.UpdateEvent(); err != nil {
- ctx.ServerError("UpdateEvent", err)
- return
- } else if err := webhook.UpdateWebhook(w); err != nil {
- ctx.ServerError("UpdateWebhook", err)
- return
- }
-
- ctx.Flash.Success(ctx.Tr("repo.settings.update_hook_success"))
- ctx.Redirect(fmt.Sprintf("%s/%d", orCtx.Link, w.ID))
-}
-
-// WechatworkHooksEditPost response for editing wechatwork hook
-func WechatworkHooksEditPost(ctx *context.Context) {
- form := web.GetForm(ctx).(*forms.NewWechatWorkHookForm)
- ctx.Data["Title"] = ctx.Tr("repo.settings")
- ctx.Data["PageIsSettingsHooks"] = true
- ctx.Data["PageIsSettingsHooksEdit"] = true
-
- orCtx, w := checkWebhook(ctx)
- if ctx.Written() {
- return
- }
- ctx.Data["Webhook"] = w
-
- if ctx.HasError() {
- ctx.HTML(http.StatusOK, orCtx.NewTemplate)
- return
- }
-
- w.URL = form.PayloadURL
- w.HookEvent = ParseHookEvent(form.WebhookForm)
- w.IsActive = form.Active
- if err := w.UpdateEvent(); err != nil {
- ctx.ServerError("UpdateEvent", err)
- return
- } else if err := webhook.UpdateWebhook(w); err != nil {
- ctx.ServerError("UpdateWebhook", err)
- return
- }
-
- ctx.Flash.Success(ctx.Tr("repo.settings.update_hook_success"))
- ctx.Redirect(fmt.Sprintf("%s/%d", orCtx.Link, w.ID))
-}
-
-// PackagistHooksEditPost response for editing packagist hook
-func PackagistHooksEditPost(ctx *context.Context) {
- form := web.GetForm(ctx).(*forms.NewPackagistHookForm)
- ctx.Data["Title"] = ctx.Tr("repo.settings")
- ctx.Data["PageIsSettingsHooks"] = true
- ctx.Data["PageIsSettingsHooksEdit"] = true
-
- orCtx, w := checkWebhook(ctx)
- if ctx.Written() {
- return
- }
- ctx.Data["Webhook"] = w
-
- if ctx.HasError() {
- ctx.HTML(http.StatusOK, orCtx.NewTemplate)
- return
- }
-
- meta, err := json.Marshal(&webhook_service.PackagistMeta{
- Username: form.Username,
- APIToken: form.APIToken,
- PackageURL: form.PackageURL,
- })
- if err != nil {
- ctx.ServerError("Marshal", err)
- return
- }
-
- w.Meta = string(meta)
- w.URL = fmt.Sprintf("https://packagist.org/api/update-package?username=%s&apiToken=%s", url.QueryEscape(form.Username), url.QueryEscape(form.APIToken))
- w.HookEvent = ParseHookEvent(form.WebhookForm)
- w.IsActive = form.Active
- if err := w.UpdateEvent(); err != nil {
- ctx.ServerError("UpdateEvent", err)
- return
- } else if err := webhook.UpdateWebhook(w); err != nil {
- ctx.ServerError("UpdateWebhook", err)
- return
- }
-
- ctx.Flash.Success(ctx.Tr("repo.settings.update_hook_success"))
- ctx.Redirect(fmt.Sprintf("%s/%d", orCtx.Link, w.ID))
-}
-
// TestWebhook test if web hook is work fine
func TestWebhook(ctx *context.Context) {
hookID := ctx.ParamsInt64(":id")
@@ -1271,15 +666,18 @@ func TestWebhook(ctx *context.Context) {
},
}
+ commitID := commit.ID.String()
p := &api.PushPayload{
- Ref: git.BranchPrefix + ctx.Repo.Repository.DefaultBranch,
- Before: commit.ID.String(),
- After: commit.ID.String(),
- Commits: []*api.PayloadCommit{apiCommit},
- HeadCommit: apiCommit,
- Repo: convert.ToRepo(ctx.Repo.Repository, perm.AccessModeNone),
- Pusher: apiUser,
- Sender: apiUser,
+ Ref: git.BranchPrefix + ctx.Repo.Repository.DefaultBranch,
+ Before: commitID,
+ After: commitID,
+ CompareURL: setting.AppURL + ctx.Repo.Repository.ComposeCompareURL(commitID, commitID),
+ Commits: []*api.PayloadCommit{apiCommit},
+ TotalCommits: 1,
+ HeadCommit: apiCommit,
+ Repo: convert.ToRepo(ctx.Repo.Repository, perm.AccessModeNone),
+ Pusher: apiUser,
+ Sender: apiUser,
}
if err := webhook_service.PrepareWebhook(w, ctx.Repo.Repository, webhook.HookEventPush, p); err != nil {
ctx.Flash.Error("PrepareWebhook: " + err.Error())
diff --git a/routers/web/repo/wiki.go b/routers/web/repo/wiki.go
index e4134028aa..a10e12ee63 100644
--- a/routers/web/repo/wiki.go
+++ b/routers/web/repo/wiki.go
@@ -15,8 +15,8 @@ import (
"strings"
"time"
- "code.gitea.io/gitea/models"
git_model "code.gitea.io/gitea/models/git"
+ repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/charset"
@@ -25,6 +25,7 @@ import (
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/markup"
"code.gitea.io/gitea/modules/markup/markdown"
+ "code.gitea.io/gitea/modules/notification"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util"
@@ -98,7 +99,7 @@ func findWikiRepoCommit(ctx *context.Context) (*git.Repository, *git.Commit, err
return nil, nil, err
}
- commit, err := wikiRepo.GetBranchCommit("master")
+ commit, err := wikiRepo.GetBranchCommit(wiki_service.DefaultBranch)
if err != nil {
return wikiRepo, nil, err
}
@@ -164,7 +165,7 @@ func renderViewPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) {
}
wikiName, err := wiki_service.FilenameToName(entry.Name())
if err != nil {
- if models.IsErrWikiInvalidFileName(err) {
+ if repo_model.IsErrWikiInvalidFileName(err) {
continue
}
if wikiRepo != nil {
@@ -239,9 +240,28 @@ func renderViewPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) {
Metas: ctx.Repo.Repository.ComposeDocumentMetas(),
IsWiki: true,
}
+ buf := &strings.Builder{}
- var buf strings.Builder
- if err := markdown.Render(rctx, bytes.NewReader(data), &buf); err != nil {
+ renderFn := func(data []byte) (escaped *charset.EscapeStatus, output string, err error) {
+ markupRd, markupWr := io.Pipe()
+ defer markupWr.Close()
+ done := make(chan struct{})
+ go func() {
+ // We allow NBSP here this is rendered
+ escaped, _ = charset.EscapeControlReader(markupRd, buf, ctx.Locale, charset.RuneNBSP)
+ output = buf.String()
+ buf.Reset()
+ close(done)
+ }()
+
+ err = markdown.Render(rctx, bytes.NewReader(data), markupWr)
+ _ = markupWr.CloseWithError(err)
+ <-done
+ return escaped, output, err
+ }
+
+ ctx.Data["EscapeStatus"], ctx.Data["content"], err = renderFn(data)
+ if err != nil {
if wikiRepo != nil {
wikiRepo.Close()
}
@@ -249,11 +269,10 @@ func renderViewPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) {
return nil, nil
}
- ctx.Data["EscapeStatus"], ctx.Data["content"] = charset.EscapeControlString(buf.String())
-
if !isSideBar {
buf.Reset()
- if err := markdown.Render(rctx, bytes.NewReader(sidebarContent), &buf); err != nil {
+ ctx.Data["sidebarEscapeStatus"], ctx.Data["sidebarContent"], err = renderFn(sidebarContent)
+ if err != nil {
if wikiRepo != nil {
wikiRepo.Close()
}
@@ -261,14 +280,14 @@ func renderViewPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) {
return nil, nil
}
ctx.Data["sidebarPresent"] = sidebarContent != nil
- ctx.Data["sidebarEscapeStatus"], ctx.Data["sidebarContent"] = charset.EscapeControlString(buf.String())
} else {
ctx.Data["sidebarPresent"] = false
}
if !isFooter {
buf.Reset()
- if err := markdown.Render(rctx, bytes.NewReader(footerContent), &buf); err != nil {
+ ctx.Data["footerEscapeStatus"], ctx.Data["footerContent"], err = renderFn(footerContent)
+ if err != nil {
if wikiRepo != nil {
wikiRepo.Close()
}
@@ -276,7 +295,6 @@ func renderViewPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) {
return nil, nil
}
ctx.Data["footerPresent"] = footerContent != nil
- ctx.Data["footerEscapeStatus"], ctx.Data["footerContent"] = charset.EscapeControlString(buf.String())
} else {
ctx.Data["footerPresent"] = false
}
@@ -284,7 +302,7 @@ func renderViewPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) {
ctx.Data["toc"] = rctx.TableOfContents
// get commit count - wiki revisions
- commitsCount, _ := wikiRepo.FileCommitsCount("master", pageFilename)
+ commitsCount, _ := wikiRepo.FileCommitsCount(wiki_service.DefaultBranch, pageFilename)
ctx.Data["CommitCount"] = commitsCount
return wikiRepo, entry
@@ -333,7 +351,7 @@ func renderRevisionPage(ctx *context.Context) (*git.Repository, *git.TreeEntry)
ctx.Data["footerContent"] = ""
// get commit count - wiki revisions
- commitsCount, _ := wikiRepo.FileCommitsCount("master", pageFilename)
+ commitsCount, _ := wikiRepo.FileCommitsCount(wiki_service.DefaultBranch, pageFilename)
ctx.Data["CommitCount"] = commitsCount
// get page
@@ -343,12 +361,12 @@ func renderRevisionPage(ctx *context.Context) (*git.Repository, *git.TreeEntry)
}
// get Commit Count
- commitsHistory, err := wikiRepo.CommitsByFileAndRangeNoFollow("master", pageFilename, page)
+ commitsHistory, err := wikiRepo.CommitsByFileAndRange(wiki_service.DefaultBranch, pageFilename, page)
if err != nil {
if wikiRepo != nil {
wikiRepo.Close()
}
- ctx.ServerError("CommitsByFileAndRangeNoFollow", err)
+ ctx.ServerError("CommitsByFileAndRange", err)
return nil, nil
}
ctx.Data["Commits"] = git_model.ConvertFromGitCommit(commitsHistory, ctx.Repo.Repository)
@@ -571,7 +589,7 @@ func WikiPages(ctx *context.Context) {
}
wikiName, err := wiki_service.FilenameToName(entry.Name())
if err != nil {
- if models.IsErrWikiInvalidFileName(err) {
+ if repo_model.IsErrWikiInvalidFileName(err) {
continue
}
ctx.ServerError("WikiFilenameToName", err)
@@ -676,10 +694,10 @@ func NewWikiPost(ctx *context.Context) {
}
if err := wiki_service.AddWikiPage(ctx, ctx.Doer, ctx.Repo.Repository, wikiName, form.Content, form.Message); err != nil {
- if models.IsErrWikiReservedName(err) {
+ if repo_model.IsErrWikiReservedName(err) {
ctx.Data["Err_Title"] = true
ctx.RenderWithErr(ctx.Tr("repo.wiki.reserved_page", wikiName), tplWikiNew, &form)
- } else if models.IsErrWikiAlreadyExist(err) {
+ } else if repo_model.IsErrWikiAlreadyExist(err) {
ctx.Data["Err_Title"] = true
ctx.RenderWithErr(ctx.Tr("repo.wiki.page_already_exists"), tplWikiNew, &form)
} else {
@@ -688,6 +706,8 @@ func NewWikiPost(ctx *context.Context) {
return
}
+ notification.NotifyNewWikiPage(ctx.Doer, ctx.Repo.Repository, wikiName, form.Message)
+
ctx.Redirect(ctx.Repo.RepoLink + "/wiki/" + wiki_service.NameToSubURL(wikiName))
}
@@ -730,6 +750,8 @@ func EditWikiPost(ctx *context.Context) {
return
}
+ notification.NotifyEditWikiPage(ctx.Doer, ctx.Repo.Repository, newWikiName, form.Message)
+
ctx.Redirect(ctx.Repo.RepoLink + "/wiki/" + wiki_service.NameToSubURL(newWikiName))
}
@@ -745,6 +767,8 @@ func DeleteWikiPagePost(ctx *context.Context) {
return
}
+ notification.NotifyDeleteWikiPage(ctx.Doer, ctx.Repo.Repository, wikiName)
+
ctx.JSON(http.StatusOK, map[string]interface{}{
"redirect": ctx.Repo.RepoLink + "/wiki/",
})
diff --git a/routers/web/user/avatar.go b/routers/web/user/avatar.go
index 53a603fab0..05896299d2 100644
--- a/routers/web/user/avatar.go
+++ b/routers/web/user/avatar.go
@@ -31,6 +31,10 @@ func AvatarByUserName(ctx *context.Context) {
if strings.ToLower(userName) != "ghost" {
var err error
if user, err = user_model.GetUserByName(ctx, userName); err != nil {
+ if user_model.IsErrUserNotExist(err) {
+ ctx.NotFound("GetUserByName", err)
+ return
+ }
ctx.ServerError("Invalid user: "+userName, err)
return
}
diff --git a/routers/web/user/code.go b/routers/web/user/code.go
new file mode 100644
index 0000000000..89bd23588b
--- /dev/null
+++ b/routers/web/user/code.go
@@ -0,0 +1,114 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package user
+
+import (
+ "net/http"
+
+ repo_model "code.gitea.io/gitea/models/repo"
+ "code.gitea.io/gitea/modules/base"
+ "code.gitea.io/gitea/modules/context"
+ code_indexer "code.gitea.io/gitea/modules/indexer/code"
+ "code.gitea.io/gitea/modules/setting"
+)
+
+const (
+ tplUserCode base.TplName = "user/code"
+)
+
+// CodeSearch render user/organization code search page
+func CodeSearch(ctx *context.Context) {
+ if !setting.Indexer.RepoIndexerEnabled {
+ ctx.Redirect(ctx.ContextUser.HomeLink())
+ return
+ }
+
+ ctx.Data["IsPackageEnabled"] = setting.Packages.Enabled
+ ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
+ ctx.Data["Title"] = ctx.Tr("code.title")
+ ctx.Data["ContextUser"] = ctx.ContextUser
+
+ language := ctx.FormTrim("l")
+ keyword := ctx.FormTrim("q")
+
+ queryType := ctx.FormTrim("t")
+ isMatch := queryType == "match"
+
+ ctx.Data["Keyword"] = keyword
+ ctx.Data["Language"] = language
+ ctx.Data["queryType"] = queryType
+ ctx.Data["IsCodePage"] = true
+
+ if keyword == "" {
+ ctx.HTML(http.StatusOK, tplUserCode)
+ return
+ }
+
+ var (
+ repoIDs []int64
+ err error
+ )
+
+ page := ctx.FormInt("page")
+ if page <= 0 {
+ page = 1
+ }
+
+ repoIDs, err = repo_model.FindUserCodeAccessibleOwnerRepoIDs(ctx, ctx.ContextUser.ID, ctx.Doer)
+ if err != nil {
+ ctx.ServerError("FindUserCodeAccessibleOwnerRepoIDs", err)
+ return
+ }
+
+ var (
+ total int
+ searchResults []*code_indexer.Result
+ searchResultLanguages []*code_indexer.SearchResultLanguages
+ )
+
+ if len(repoIDs) > 0 {
+ total, searchResults, searchResultLanguages, err = code_indexer.PerformSearch(ctx, repoIDs, language, keyword, page, setting.UI.RepoSearchPagingNum, isMatch)
+ if err != nil {
+ if code_indexer.IsAvailable() {
+ ctx.ServerError("SearchResults", err)
+ return
+ }
+ ctx.Data["CodeIndexerUnavailable"] = true
+ } else {
+ ctx.Data["CodeIndexerUnavailable"] = !code_indexer.IsAvailable()
+ }
+
+ loadRepoIDs := make([]int64, 0, len(searchResults))
+ for _, result := range searchResults {
+ var find bool
+ for _, id := range loadRepoIDs {
+ if id == result.RepoID {
+ find = true
+ break
+ }
+ }
+ if !find {
+ loadRepoIDs = append(loadRepoIDs, result.RepoID)
+ }
+ }
+
+ repoMaps, err := repo_model.GetRepositoriesMapByIDs(loadRepoIDs)
+ if err != nil {
+ ctx.ServerError("GetRepositoriesMapByIDs", err)
+ return
+ }
+
+ ctx.Data["RepoMaps"] = repoMaps
+ }
+ ctx.Data["SearchResults"] = searchResults
+ ctx.Data["SearchResultLanguages"] = searchResultLanguages
+
+ pager := context.NewPagination(total, setting.UI.RepoSearchPagingNum, page, 5)
+ pager.SetDefaultParams(ctx)
+ pager.AddParam(ctx, "l", "Language")
+ ctx.Data["Page"] = pager
+
+ ctx.HTML(http.StatusOK, tplUserCode)
+}
diff --git a/routers/web/user/home.go b/routers/web/user/home.go
index 698117e957..837caedc84 100644
--- a/routers/web/user/home.go
+++ b/routers/web/user/home.go
@@ -14,7 +14,7 @@ import (
"strconv"
"strings"
- "code.gitea.io/gitea/models"
+ activities_model "code.gitea.io/gitea/models/activities"
asymkey_model "code.gitea.io/gitea/models/asymkey"
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
@@ -56,7 +56,7 @@ func getDashboardContextUser(ctx *context.Context) *user_model.User {
}
ctx.Data["ContextUser"] = ctxUser
- orgs, err := models.GetUserOrgsList(ctx.Doer)
+ orgs, err := organization.GetUserOrgsList(ctx.Doer)
if err != nil {
ctx.ServerError("GetUserOrgsList", err)
return nil
@@ -91,7 +91,7 @@ func Dashboard(ctx *context.Context) {
}
if setting.Service.EnableUserHeatmap {
- data, err := models.GetUserHeatmapDataByUserTeam(ctxUser, ctx.Org.Team, ctx.Doer)
+ data, err := activities_model.GetUserHeatmapDataByUserTeam(ctxUser, ctx.Org.Team, ctx.Doer)
if err != nil {
ctx.ServerError("GetUserHeatmapDataByUserTeam", err)
return
@@ -100,40 +100,7 @@ func Dashboard(ctx *context.Context) {
}
var err error
- var mirrors []*repo_model.Repository
- if ctxUser.IsOrganization() {
- var env organization.AccessibleReposEnvironment
- if ctx.Org.Team != nil {
- env = organization.OrgFromUser(ctxUser).AccessibleTeamReposEnv(ctx.Org.Team)
- } else {
- env, err = organization.AccessibleReposEnv(ctx, organization.OrgFromUser(ctxUser), ctx.Doer.ID)
- if err != nil {
- ctx.ServerError("AccessibleReposEnv", err)
- return
- }
- }
- mirrors, err = env.MirrorRepos()
- if err != nil {
- ctx.ServerError("env.MirrorRepos", err)
- return
- }
- } else {
- mirrors, err = repo_model.GetUserMirrorRepositories(ctxUser.ID)
- if err != nil {
- ctx.ServerError("GetUserMirrorRepositories", err)
- return
- }
- }
- ctx.Data["MaxShowRepoNum"] = setting.UI.User.RepoPagingNum
-
- if err := repo_model.MirrorRepositoryList(mirrors).LoadAttributes(); err != nil {
- ctx.ServerError("MirrorRepositoryList.LoadAttributes", err)
- return
- }
- ctx.Data["MirrorCount"] = len(mirrors)
- ctx.Data["Mirrors"] = mirrors
-
- ctx.Data["Feeds"], err = models.GetFeeds(ctx, models.GetFeedsOptions{
+ ctx.Data["Feeds"], err = activities_model.GetFeeds(ctx, activities_model.GetFeedsOptions{
RequestedUser: ctxUser,
RequestedTeam: ctx.Org.Team,
Actor: ctx.Doer,
@@ -334,6 +301,7 @@ func Pulls(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("pull_requests")
ctx.Data["PageIsPulls"] = true
+ ctx.Data["SingleRepoAction"] = "pull"
buildIssueOverview(ctx, unit.TypePullRequests)
}
@@ -347,6 +315,7 @@ func Issues(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("issues")
ctx.Data["PageIsIssues"] = true
+ ctx.Data["SingleRepoAction"] = "issue"
buildIssueOverview(ctx, unit.TypeIssues)
}
@@ -591,6 +560,7 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
LabelIDs: opts.LabelIDs,
Org: org,
Team: team,
+ RepoCond: opts.RepoCond,
}
issueStats, err = issues_model.GetUserIssueStats(statsOpts)
@@ -606,10 +576,8 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
var shownIssues int
if !isShowClosed {
shownIssues = int(issueStats.OpenCount)
- ctx.Data["TotalIssueCount"] = shownIssues
} else {
shownIssues = int(issueStats.ClosedCount)
- ctx.Data["TotalIssueCount"] = shownIssues
}
if len(repoIDs) != 0 {
shownIssues = 0
@@ -618,6 +586,19 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
}
}
+ var allIssueCount int64
+ for _, issueCount := range issueCountByRepo {
+ allIssueCount += issueCount
+ }
+ ctx.Data["TotalIssueCount"] = allIssueCount
+
+ if len(repoIDs) == 1 {
+ repo := showReposMap[repoIDs[0]]
+ if repo != nil {
+ ctx.Data["SingleRepoLink"] = repo.Link()
+ }
+ }
+
ctx.Data["IsShowClosed"] = isShowClosed
ctx.Data["IssueRefEndNames"], ctx.Data["IssueRefURLs"] = issue_service.GetRefEndNamesAndURLs(issues, ctx.FormString("RepoLink"))
diff --git a/routers/web/user/home_test.go b/routers/web/user/home_test.go
index 9ad0711dc0..36e99bba5e 100644
--- a/routers/web/user/home_test.go
+++ b/routers/web/user/home_test.go
@@ -76,7 +76,7 @@ func TestPulls(t *testing.T) {
Pulls(ctx)
assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
- assert.Len(t, ctx.Data["Issues"], 3)
+ assert.Len(t, ctx.Data["Issues"], 4)
}
func TestMilestones(t *testing.T) {
diff --git a/routers/web/user/notification.go b/routers/web/user/notification.go
index 9e658bcb14..b4753a603e 100644
--- a/routers/web/user/notification.go
+++ b/routers/web/user/notification.go
@@ -12,17 +12,24 @@ import (
"net/url"
"strings"
- "code.gitea.io/gitea/models"
+ activities_model "code.gitea.io/gitea/models/activities"
+ "code.gitea.io/gitea/models/db"
+ issues_model "code.gitea.io/gitea/models/issues"
+ repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/structs"
+ "code.gitea.io/gitea/modules/util"
+ issue_service "code.gitea.io/gitea/services/issue"
+ pull_service "code.gitea.io/gitea/services/pull"
)
const (
- tplNotification base.TplName = "user/notification/notification"
- tplNotificationDiv base.TplName = "user/notification/notification_div"
+ tplNotification base.TplName = "user/notification/notification"
+ tplNotificationDiv base.TplName = "user/notification/notification_div"
+ tplNotificationSubscriptions base.TplName = "user/notification/notification_subscriptions"
)
// GetNotificationCount is the middleware that sets the notification count in the context
@@ -36,7 +43,7 @@ func GetNotificationCount(c *context.Context) {
}
c.Data["NotificationUnreadCount"] = func() int64 {
- count, err := models.GetNotificationCount(c, c.Doer, models.NotificationStatusUnread)
+ count, err := activities_model.GetNotificationCount(c, c.Doer, activities_model.NotificationStatusUnread)
if err != nil {
if err != goctx.Canceled {
log.Error("Unable to GetNotificationCount for user:%-v: %v", c.Doer, err)
@@ -65,7 +72,7 @@ func Notifications(c *context.Context) {
func getNotifications(c *context.Context) {
var (
keyword = c.FormTrim("q")
- status models.NotificationStatus
+ status activities_model.NotificationStatus
page = c.FormInt("page")
perPage = c.FormInt("perPage")
)
@@ -78,12 +85,12 @@ func getNotifications(c *context.Context) {
switch keyword {
case "read":
- status = models.NotificationStatusRead
+ status = activities_model.NotificationStatusRead
default:
- status = models.NotificationStatusUnread
+ status = activities_model.NotificationStatusUnread
}
- total, err := models.GetNotificationCount(c, c.Doer, status)
+ total, err := activities_model.GetNotificationCount(c, c.Doer, status)
if err != nil {
c.ServerError("ErrGetNotificationCount", err)
return
@@ -96,8 +103,8 @@ func getNotifications(c *context.Context) {
return
}
- statuses := []models.NotificationStatus{status, models.NotificationStatusPinned}
- notifications, err := models.NotificationsForUser(c, c.Doer, statuses, page, perPage)
+ statuses := []activities_model.NotificationStatus{status, activities_model.NotificationStatusPinned}
+ notifications, err := activities_model.NotificationsForUser(c, c.Doer, statuses, page, perPage)
if err != nil {
c.ServerError("ErrNotificationsForUser", err)
return
@@ -151,22 +158,22 @@ func NotificationStatusPost(c *context.Context) {
var (
notificationID = c.FormInt64("notification_id")
statusStr = c.FormString("status")
- status models.NotificationStatus
+ status activities_model.NotificationStatus
)
switch statusStr {
case "read":
- status = models.NotificationStatusRead
+ status = activities_model.NotificationStatusRead
case "unread":
- status = models.NotificationStatusUnread
+ status = activities_model.NotificationStatusUnread
case "pinned":
- status = models.NotificationStatusPinned
+ status = activities_model.NotificationStatusPinned
default:
c.ServerError("InvalidNotificationStatus", errors.New("Invalid notification status"))
return
}
- if _, err := models.SetNotificationStatus(notificationID, c.Doer, status); err != nil {
+ if _, err := activities_model.SetNotificationStatus(notificationID, c.Doer, status); err != nil {
c.ServerError("SetNotificationStatus", err)
return
}
@@ -188,7 +195,7 @@ func NotificationStatusPost(c *context.Context) {
// NotificationPurgePost is a route for 'purging' the list of notifications - marking all unread as read
func NotificationPurgePost(c *context.Context) {
- err := models.UpdateNotificationStatuses(c.Doer, models.NotificationStatusUnread, models.NotificationStatusRead)
+ err := activities_model.UpdateNotificationStatuses(c.Doer, activities_model.NotificationStatusUnread, activities_model.NotificationStatusRead)
if err != nil {
c.ServerError("ErrUpdateNotificationStatuses", err)
return
@@ -197,7 +204,209 @@ func NotificationPurgePost(c *context.Context) {
c.Redirect(setting.AppSubURL+"/notifications", http.StatusSeeOther)
}
+// NotificationSubscriptions returns the list of subscribed issues
+func NotificationSubscriptions(c *context.Context) {
+ page := c.FormInt("page")
+ if page < 1 {
+ page = 1
+ }
+
+ sortType := c.FormString("sort")
+ c.Data["SortType"] = sortType
+
+ state := c.FormString("state")
+ if !util.IsStringInSlice(state, []string{"all", "open", "closed"}, true) {
+ state = "all"
+ }
+ c.Data["State"] = state
+ var showClosed util.OptionalBool
+ switch state {
+ case "all":
+ showClosed = util.OptionalBoolNone
+ case "closed":
+ showClosed = util.OptionalBoolTrue
+ case "open":
+ showClosed = util.OptionalBoolFalse
+ }
+
+ var issueTypeBool util.OptionalBool
+ issueType := c.FormString("issueType")
+ switch issueType {
+ case "issues":
+ issueTypeBool = util.OptionalBoolFalse
+ case "pulls":
+ issueTypeBool = util.OptionalBoolTrue
+ default:
+ issueTypeBool = util.OptionalBoolNone
+ }
+ c.Data["IssueType"] = issueType
+
+ var labelIDs []int64
+ selectedLabels := c.FormString("labels")
+ c.Data["Labels"] = selectedLabels
+ if len(selectedLabels) > 0 && selectedLabels != "0" {
+ var err error
+ labelIDs, err = base.StringsToInt64s(strings.Split(selectedLabels, ","))
+ if err != nil {
+ c.ServerError("StringsToInt64s", err)
+ return
+ }
+ }
+
+ count, err := issues_model.CountIssues(&issues_model.IssuesOptions{
+ SubscriberID: c.Doer.ID,
+ IsClosed: showClosed,
+ IsPull: issueTypeBool,
+ LabelIDs: labelIDs,
+ })
+ if err != nil {
+ c.ServerError("CountIssues", err)
+ return
+ }
+ issues, err := issues_model.Issues(&issues_model.IssuesOptions{
+ ListOptions: db.ListOptions{
+ PageSize: setting.UI.IssuePagingNum,
+ Page: page,
+ },
+ SubscriberID: c.Doer.ID,
+ SortType: sortType,
+ IsClosed: showClosed,
+ IsPull: issueTypeBool,
+ LabelIDs: labelIDs,
+ })
+ if err != nil {
+ c.ServerError("Issues", err)
+ return
+ }
+
+ commitStatuses, lastStatus, err := pull_service.GetIssuesAllCommitStatus(c, issues)
+ if err != nil {
+ c.ServerError("GetIssuesAllCommitStatus", err)
+ return
+ }
+ c.Data["CommitLastStatus"] = lastStatus
+ c.Data["CommitStatuses"] = commitStatuses
+ c.Data["Issues"] = issues
+
+ c.Data["IssueRefEndNames"], c.Data["IssueRefURLs"] = issue_service.GetRefEndNamesAndURLs(issues, "")
+
+ commitStatus, err := pull_service.GetIssuesLastCommitStatus(c, issues)
+ if err != nil {
+ c.ServerError("GetIssuesLastCommitStatus", err)
+ return
+ }
+ c.Data["CommitStatus"] = commitStatus
+
+ issueList := issues_model.IssueList(issues)
+ approvalCounts, err := issueList.GetApprovalCounts(c)
+ if err != nil {
+ c.ServerError("ApprovalCounts", err)
+ return
+ }
+ c.Data["ApprovalCounts"] = func(issueID int64, typ string) int64 {
+ counts, ok := approvalCounts[issueID]
+ if !ok || len(counts) == 0 {
+ return 0
+ }
+ reviewTyp := issues_model.ReviewTypeApprove
+ if typ == "reject" {
+ reviewTyp = issues_model.ReviewTypeReject
+ } else if typ == "waiting" {
+ reviewTyp = issues_model.ReviewTypeRequest
+ }
+ for _, count := range counts {
+ if count.Type == reviewTyp {
+ return count.Count
+ }
+ }
+ return 0
+ }
+
+ c.Data["Status"] = 1
+ c.Data["Title"] = c.Tr("notification.subscriptions")
+
+ // redirect to last page if request page is more than total pages
+ pager := context.NewPagination(int(count), setting.UI.IssuePagingNum, page, 5)
+ if pager.Paginater.Current() < page {
+ c.Redirect(fmt.Sprintf("/notifications/subscriptions?page=%d", pager.Paginater.Current()))
+ return
+ }
+ pager.AddParam(c, "sort", "SortType")
+ pager.AddParam(c, "state", "State")
+ c.Data["Page"] = pager
+
+ c.HTML(http.StatusOK, tplNotificationSubscriptions)
+}
+
+// NotificationWatching returns the list of watching repos
+func NotificationWatching(c *context.Context) {
+ page := c.FormInt("page")
+ if page < 1 {
+ page = 1
+ }
+
+ var orderBy db.SearchOrderBy
+ c.Data["SortType"] = c.FormString("sort")
+ switch c.FormString("sort") {
+ case "newest":
+ orderBy = db.SearchOrderByNewest
+ case "oldest":
+ orderBy = db.SearchOrderByOldest
+ case "recentupdate":
+ orderBy = db.SearchOrderByRecentUpdated
+ case "leastupdate":
+ orderBy = db.SearchOrderByLeastUpdated
+ case "reversealphabetically":
+ orderBy = db.SearchOrderByAlphabeticallyReverse
+ case "alphabetically":
+ orderBy = db.SearchOrderByAlphabetically
+ case "moststars":
+ orderBy = db.SearchOrderByStarsReverse
+ case "feweststars":
+ orderBy = db.SearchOrderByStars
+ case "mostforks":
+ orderBy = db.SearchOrderByForksReverse
+ case "fewestforks":
+ orderBy = db.SearchOrderByForks
+ default:
+ c.Data["SortType"] = "recentupdate"
+ orderBy = db.SearchOrderByRecentUpdated
+ }
+
+ repos, count, err := repo_model.SearchRepository(&repo_model.SearchRepoOptions{
+ ListOptions: db.ListOptions{
+ PageSize: setting.UI.User.RepoPagingNum,
+ Page: page,
+ },
+ Actor: c.Doer,
+ Keyword: c.FormTrim("q"),
+ OrderBy: orderBy,
+ Private: c.IsSigned,
+ WatchedByID: c.Doer.ID,
+ Collaborate: util.OptionalBoolFalse,
+ TopicOnly: c.FormBool("topic"),
+ IncludeDescription: setting.UI.SearchRepoDescription,
+ })
+ if err != nil {
+ c.ServerError("ErrSearchRepository", err)
+ return
+ }
+ total := int(count)
+ c.Data["Total"] = total
+ c.Data["Repos"] = repos
+
+ // redirect to last page if request page is more than total pages
+ pager := context.NewPagination(total, setting.UI.User.RepoPagingNum, page, 5)
+ pager.SetDefaultParams(c)
+ c.Data["Page"] = pager
+
+ c.Data["Status"] = 2
+ c.Data["Title"] = c.Tr("notification.watching")
+
+ c.HTML(http.StatusOK, tplNotificationSubscriptions)
+}
+
// NewAvailable returns the notification counts
func NewAvailable(ctx *context.Context) {
- ctx.JSON(http.StatusOK, structs.NotificationCount{New: models.CountUnread(ctx, ctx.Doer.ID)})
+ ctx.JSON(http.StatusOK, structs.NotificationCount{New: activities_model.CountUnread(ctx, ctx.Doer.ID)})
}
diff --git a/routers/web/user/package.go b/routers/web/user/package.go
index b2b550cb73..c72592e728 100644
--- a/routers/web/user/package.go
+++ b/routers/web/user/package.go
@@ -8,6 +8,7 @@ import (
"net/http"
"code.gitea.io/gitea/models/db"
+ org_model "code.gitea.io/gitea/models/organization"
packages_model "code.gitea.io/gitea/models/packages"
container_model "code.gitea.io/gitea/models/packages/container"
"code.gitea.io/gitea/models/perm"
@@ -17,6 +18,7 @@ import (
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/modules/web"
"code.gitea.io/gitea/services/forms"
packages_service "code.gitea.io/gitea/services/packages"
@@ -43,9 +45,10 @@ func ListPackages(ctx *context.Context) {
PageSize: setting.UI.PackagesPagingNum,
Page: page,
},
- OwnerID: ctx.ContextUser.ID,
- Type: packages_model.Type(packageType),
- Name: packages_model.SearchValue{Value: query},
+ OwnerID: ctx.ContextUser.ID,
+ Type: packages_model.Type(packageType),
+ Name: packages_model.SearchValue{Value: query},
+ IsInternal: util.OptionalBoolFalse,
})
if err != nil {
ctx.ServerError("SearchLatestVersions", err)
@@ -83,6 +86,7 @@ func ListPackages(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("packages.title")
ctx.Data["IsPackagesPage"] = true
+ ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
ctx.Data["ContextUser"] = ctx.ContextUser
ctx.Data["Query"] = query
ctx.Data["PackageType"] = packageType
@@ -91,6 +95,21 @@ func ListPackages(ctx *context.Context) {
ctx.Data["Total"] = total
ctx.Data["RepositoryAccessMap"] = repositoryAccessMap
+ // TODO: context/org -> HandleOrgAssignment() can not be used
+ if ctx.ContextUser.IsOrganization() {
+ org := org_model.OrgFromUser(ctx.ContextUser)
+ ctx.Data["Org"] = org
+ ctx.Data["OrgLink"] = ctx.ContextUser.OrganisationLink()
+
+ if ctx.Doer != nil {
+ ctx.Data["IsOrganizationMember"], _ = org_model.IsOrganizationMember(ctx, org.ID, ctx.Doer.ID)
+ ctx.Data["IsOrganizationOwner"], _ = org_model.IsOrganizationOwner(ctx, org.ID, ctx.Doer.ID)
+ } else {
+ ctx.Data["IsOrganizationMember"] = false
+ ctx.Data["IsOrganizationOwner"] = false
+ }
+ }
+
pager := context.NewPagination(int(total), setting.UI.PackagesPagingNum, page, 5)
pager.AddParam(ctx, "q", "Query")
pager.AddParam(ctx, "type", "PackageType")
@@ -112,7 +131,8 @@ func RedirectToLastVersion(ctx *context.Context) {
}
pvs, _, err := packages_model.SearchLatestVersions(ctx, &packages_model.PackageSearchOptions{
- PackageID: p.ID,
+ PackageID: p.ID,
+ IsInternal: util.OptionalBoolFalse,
})
if err != nil {
ctx.ServerError("GetPackageByName", err)
@@ -138,6 +158,7 @@ func ViewPackageVersion(ctx *context.Context) {
ctx.Data["Title"] = pd.Package.Name
ctx.Data["IsPackagesPage"] = true
+ ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
ctx.Data["ContextUser"] = ctx.ContextUser
ctx.Data["PackageDescriptor"] = pd
@@ -157,8 +178,9 @@ func ViewPackageVersion(ctx *context.Context) {
})
default:
pvs, total, err = packages_model.SearchVersions(ctx, &packages_model.PackageSearchOptions{
- Paginator: db.NewAbsoluteListOptions(0, 5),
- PackageID: pd.Package.ID,
+ Paginator: db.NewAbsoluteListOptions(0, 5),
+ PackageID: pd.Package.ID,
+ IsInternal: util.OptionalBoolFalse,
})
if err != nil {
ctx.ServerError("SearchVersions", err)
@@ -214,6 +236,7 @@ func ListPackageVersions(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("packages.title")
ctx.Data["IsPackagesPage"] = true
+ ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
ctx.Data["ContextUser"] = ctx.ContextUser
ctx.Data["PackageDescriptor"] = &packages_model.PackageDescriptor{
Package: p,
@@ -254,6 +277,7 @@ func ListPackageVersions(ctx *context.Context) {
ExactMatch: false,
Value: query,
},
+ IsInternal: util.OptionalBoolFalse,
})
if err != nil {
ctx.ServerError("SearchVersions", err)
@@ -284,6 +308,7 @@ func PackageSettings(ctx *context.Context) {
ctx.Data["Title"] = pd.Package.Name
ctx.Data["IsPackagesPage"] = true
+ ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
ctx.Data["ContextUser"] = ctx.ContextUser
ctx.Data["PackageDescriptor"] = pd
@@ -372,5 +397,5 @@ func DownloadPackageFile(ctx *context.Context) {
}
defer s.Close()
- ctx.ServeStream(s, pf.Name)
+ ctx.ServeContent(pf.Name, s, pf.CreatedUnix.AsLocalTime())
}
diff --git a/routers/web/user/profile.go b/routers/web/user/profile.go
index 6f23d239e2..6e16b377db 100644
--- a/routers/web/user/profile.go
+++ b/routers/web/user/profile.go
@@ -10,7 +10,7 @@ import (
"net/http"
"strings"
- "code.gitea.io/gitea/models"
+ activities_model "code.gitea.io/gitea/models/activities"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/organization"
project_model "code.gitea.io/gitea/models/project"
@@ -69,7 +69,7 @@ func Profile(ctx *context.Context) {
ctx.Data["IsFollowing"] = isFollowing
if setting.Service.EnableUserHeatmap {
- data, err := models.GetUserHeatmapDataByUser(ctx.ContextUser, ctx.Doer)
+ data, err := activities_model.GetUserHeatmapDataByUser(ctx.ContextUser, ctx.Doer)
if err != nil {
ctx.ServerError("GetUserHeatmapDataByUser", err)
return
@@ -105,6 +105,13 @@ func Profile(ctx *context.Context) {
ctx.Data["Orgs"] = orgs
ctx.Data["HasOrgsVisible"] = organization.HasOrgsVisible(orgs, ctx.Doer)
+ badges, _, err := user_model.GetUserBadges(ctx, ctx.ContextUser)
+ if err != nil {
+ ctx.ServerError("GetUserBadges", err)
+ return
+ }
+ ctx.Data["Badges"] = badges
+
tab := ctx.FormString("tab")
ctx.Data["TabName"] = tab
@@ -181,7 +188,7 @@ func Profile(ctx *context.Context) {
total = int(count)
case "activity":
- ctx.Data["Feeds"], err = models.GetFeeds(ctx, models.GetFeedsOptions{
+ ctx.Data["Feeds"], err = activities_model.GetFeeds(ctx, activities_model.GetFeedsOptions{
RequestedUser: ctx.ContextUser,
Actor: ctx.Doer,
IncludePrivate: showPrivate,
@@ -283,6 +290,7 @@ func Profile(ctx *context.Context) {
}
ctx.Data["Page"] = pager
ctx.Data["IsPackageEnabled"] = setting.Packages.Enabled
+ ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
ctx.Data["ShowUserEmail"] = len(ctx.ContextUser.Email) > 0 && ctx.IsSigned && (!ctx.ContextUser.KeepEmailPrivate || ctx.ContextUser.ID == ctx.Doer.ID)
diff --git a/routers/web/user/setting/account.go b/routers/web/user/setting/account.go
index cdb24c6066..8b95caf2fc 100644
--- a/routers/web/user/setting/account.go
+++ b/routers/web/user/setting/account.go
@@ -156,7 +156,8 @@ func EmailPost(ctx *context.Context) {
preference := ctx.FormString("preference")
if !(preference == user_model.EmailNotificationsEnabled ||
preference == user_model.EmailNotificationsOnMention ||
- preference == user_model.EmailNotificationsDisabled) {
+ preference == user_model.EmailNotificationsDisabled ||
+ preference == user_model.EmailNotificationsAndYourOwn) {
log.Error("Email notifications preference change returned unrecognized option %s: %s", preference, ctx.Doer.Name)
ctx.ServerError("SetEmailPreference", errors.New("option unrecognized"))
return
diff --git a/routers/web/user/setting/adopt.go b/routers/web/user/setting/adopt.go
index c7139f8bb1..a92aa6e989 100644
--- a/routers/web/user/setting/adopt.go
+++ b/routers/web/user/setting/adopt.go
@@ -7,10 +7,10 @@ package setting
import (
"path/filepath"
- "code.gitea.io/gitea/models"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/context"
+ repo_module "code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
repo_service "code.gitea.io/gitea/services/repository"
@@ -46,7 +46,7 @@ func AdoptOrDeleteRepository(ctx *context.Context) {
if has || !isDir {
// Fallthrough to failure mode
} else if action == "adopt" && allowAdopt {
- if _, err := repo_service.AdoptRepository(ctxUser, ctxUser, models.CreateRepoOptions{
+ if _, err := repo_service.AdoptRepository(ctxUser, ctxUser, repo_module.CreateRepoOptions{
Name: dir,
IsPrivate: true,
}); err != nil {
diff --git a/routers/web/user/setting/applications.go b/routers/web/user/setting/applications.go
index 4ffec47801..e9572a07a6 100644
--- a/routers/web/user/setting/applications.go
+++ b/routers/web/user/setting/applications.go
@@ -8,8 +8,7 @@ package setting
import (
"net/http"
- "code.gitea.io/gitea/models"
- "code.gitea.io/gitea/models/auth"
+ auth_model "code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/setting"
@@ -44,12 +43,12 @@ func ApplicationsPost(ctx *context.Context) {
return
}
- t := &models.AccessToken{
+ t := &auth_model.AccessToken{
UID: ctx.Doer.ID,
Name: form.Name,
}
- exist, err := models.AccessTokenByNameExists(t)
+ exist, err := auth_model.AccessTokenByNameExists(t)
if err != nil {
ctx.ServerError("AccessTokenByNameExists", err)
return
@@ -60,7 +59,7 @@ func ApplicationsPost(ctx *context.Context) {
return
}
- if err := models.NewAccessToken(t); err != nil {
+ if err := auth_model.NewAccessToken(t); err != nil {
ctx.ServerError("NewAccessToken", err)
return
}
@@ -73,7 +72,7 @@ func ApplicationsPost(ctx *context.Context) {
// DeleteApplication response for delete user access token
func DeleteApplication(ctx *context.Context) {
- if err := models.DeleteAccessTokenByID(ctx.FormInt64("id"), ctx.Doer.ID); err != nil {
+ if err := auth_model.DeleteAccessTokenByID(ctx.FormInt64("id"), ctx.Doer.ID); err != nil {
ctx.Flash.Error("DeleteAccessTokenByID: " + err.Error())
} else {
ctx.Flash.Success(ctx.Tr("settings.delete_token_success"))
@@ -85,7 +84,7 @@ func DeleteApplication(ctx *context.Context) {
}
func loadApplicationsData(ctx *context.Context) {
- tokens, err := models.ListAccessTokens(models.ListAccessTokensOptions{UserID: ctx.Doer.ID})
+ tokens, err := auth_model.ListAccessTokens(auth_model.ListAccessTokensOptions{UserID: ctx.Doer.ID})
if err != nil {
ctx.ServerError("ListAccessTokens", err)
return
@@ -93,12 +92,12 @@ func loadApplicationsData(ctx *context.Context) {
ctx.Data["Tokens"] = tokens
ctx.Data["EnableOAuth2"] = setting.OAuth2.Enable
if setting.OAuth2.Enable {
- ctx.Data["Applications"], err = auth.GetOAuth2ApplicationsByUserID(ctx, ctx.Doer.ID)
+ ctx.Data["Applications"], err = auth_model.GetOAuth2ApplicationsByUserID(ctx, ctx.Doer.ID)
if err != nil {
ctx.ServerError("GetOAuth2ApplicationsByUserID", err)
return
}
- ctx.Data["Grants"], err = auth.GetOAuth2GrantsByUserID(ctx, ctx.Doer.ID)
+ ctx.Data["Grants"], err = auth_model.GetOAuth2GrantsByUserID(ctx, ctx.Doer.ID)
if err != nil {
ctx.ServerError("GetOAuth2GrantsByUserID", err)
return
diff --git a/routers/web/user/setting/oauth2.go b/routers/web/user/setting/oauth2.go
index db76a12f18..0cc05dd040 100644
--- a/routers/web/user/setting/oauth2.go
+++ b/routers/web/user/setting/oauth2.go
@@ -5,79 +5,40 @@
package setting
import (
- "fmt"
- "net/http"
-
- "code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/context"
- "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/web"
- "code.gitea.io/gitea/services/forms"
)
const (
- tplSettingsOAuthApplications base.TplName = "user/settings/applications_oauth2_edit"
+ tplSettingsOAuthApplicationEdit base.TplName = "user/settings/applications_oauth2_edit"
)
+func newOAuth2CommonHandlers(userID int64) *OAuth2CommonHandlers {
+ return &OAuth2CommonHandlers{
+ OwnerID: userID,
+ BasePathList: setting.AppSubURL + "/user/settings/applications",
+ BasePathEditPrefix: setting.AppSubURL + "/user/settings/applications/oauth2",
+ TplAppEdit: tplSettingsOAuthApplicationEdit,
+ }
+}
+
// OAuthApplicationsPost response for adding a oauth2 application
func OAuthApplicationsPost(ctx *context.Context) {
- form := web.GetForm(ctx).(*forms.EditOAuth2ApplicationForm)
ctx.Data["Title"] = ctx.Tr("settings")
ctx.Data["PageIsSettingsApplications"] = true
- if ctx.HasError() {
- loadApplicationsData(ctx)
-
- ctx.HTML(http.StatusOK, tplSettingsApplications)
- return
- }
- // TODO validate redirect URI
- app, err := auth.CreateOAuth2Application(ctx, auth.CreateOAuth2ApplicationOptions{
- Name: form.Name,
- RedirectURIs: []string{form.RedirectURI},
- UserID: ctx.Doer.ID,
- })
- if err != nil {
- ctx.ServerError("CreateOAuth2Application", err)
- return
- }
- ctx.Flash.Success(ctx.Tr("settings.create_oauth2_application_success"))
- ctx.Data["App"] = app
- ctx.Data["ClientSecret"], err = app.GenerateClientSecret()
- if err != nil {
- ctx.ServerError("GenerateClientSecret", err)
- return
- }
- ctx.HTML(http.StatusOK, tplSettingsOAuthApplications)
+ oa := newOAuth2CommonHandlers(ctx.Doer.ID)
+ oa.AddApp(ctx)
}
// OAuthApplicationsEdit response for editing oauth2 application
func OAuthApplicationsEdit(ctx *context.Context) {
- form := web.GetForm(ctx).(*forms.EditOAuth2ApplicationForm)
ctx.Data["Title"] = ctx.Tr("settings")
ctx.Data["PageIsSettingsApplications"] = true
- if ctx.HasError() {
- loadApplicationsData(ctx)
-
- ctx.HTML(http.StatusOK, tplSettingsApplications)
- return
- }
- // TODO validate redirect URI
- var err error
- if ctx.Data["App"], err = auth.UpdateOAuth2Application(auth.UpdateOAuth2ApplicationOptions{
- ID: ctx.ParamsInt64("id"),
- Name: form.Name,
- RedirectURIs: []string{form.RedirectURI},
- UserID: ctx.Doer.ID,
- }); err != nil {
- ctx.ServerError("UpdateOAuth2Application", err)
- return
- }
- ctx.Flash.Success(ctx.Tr("settings.update_oauth2_application_success"))
- ctx.HTML(http.StatusOK, tplSettingsOAuthApplications)
+ oa := newOAuth2CommonHandlers(ctx.Doer.ID)
+ oa.EditSave(ctx)
}
// OAuthApplicationsRegenerateSecret handles the post request for regenerating the secret
@@ -85,75 +46,24 @@ func OAuthApplicationsRegenerateSecret(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("settings")
ctx.Data["PageIsSettingsApplications"] = true
- app, err := auth.GetOAuth2ApplicationByID(ctx, ctx.ParamsInt64("id"))
- if err != nil {
- if auth.IsErrOAuthApplicationNotFound(err) {
- ctx.NotFound("Application not found", err)
- return
- }
- ctx.ServerError("GetOAuth2ApplicationByID", err)
- return
- }
- if app.UID != ctx.Doer.ID {
- ctx.NotFound("Application not found", nil)
- return
- }
- ctx.Data["App"] = app
- ctx.Data["ClientSecret"], err = app.GenerateClientSecret()
- if err != nil {
- ctx.ServerError("GenerateClientSecret", err)
- return
- }
- ctx.Flash.Success(ctx.Tr("settings.update_oauth2_application_success"))
- ctx.HTML(http.StatusOK, tplSettingsOAuthApplications)
+ oa := newOAuth2CommonHandlers(ctx.Doer.ID)
+ oa.RegenerateSecret(ctx)
}
// OAuth2ApplicationShow displays the given application
func OAuth2ApplicationShow(ctx *context.Context) {
- app, err := auth.GetOAuth2ApplicationByID(ctx, ctx.ParamsInt64("id"))
- if err != nil {
- if auth.IsErrOAuthApplicationNotFound(err) {
- ctx.NotFound("Application not found", err)
- return
- }
- ctx.ServerError("GetOAuth2ApplicationByID", err)
- return
- }
- if app.UID != ctx.Doer.ID {
- ctx.NotFound("Application not found", nil)
- return
- }
- ctx.Data["App"] = app
- ctx.HTML(http.StatusOK, tplSettingsOAuthApplications)
+ oa := newOAuth2CommonHandlers(ctx.Doer.ID)
+ oa.EditShow(ctx)
}
// DeleteOAuth2Application deletes the given oauth2 application
func DeleteOAuth2Application(ctx *context.Context) {
- if err := auth.DeleteOAuth2Application(ctx.FormInt64("id"), ctx.Doer.ID); err != nil {
- ctx.ServerError("DeleteOAuth2Application", err)
- return
- }
- log.Trace("OAuth2 Application deleted: %s", ctx.Doer.Name)
-
- ctx.Flash.Success(ctx.Tr("settings.remove_oauth2_application_success"))
- ctx.JSON(http.StatusOK, map[string]interface{}{
- "redirect": setting.AppSubURL + "/user/settings/applications",
- })
+ oa := newOAuth2CommonHandlers(ctx.Doer.ID)
+ oa.DeleteApp(ctx)
}
// RevokeOAuth2Grant revokes the grant with the given id
func RevokeOAuth2Grant(ctx *context.Context) {
- if ctx.Doer.ID == 0 || ctx.FormInt64("id") == 0 {
- ctx.ServerError("RevokeOAuth2Grant", fmt.Errorf("user id or grant id is zero"))
- return
- }
- if err := auth.RevokeOAuth2Grant(ctx, ctx.FormInt64("id"), ctx.Doer.ID); err != nil {
- ctx.ServerError("RevokeOAuth2Grant", err)
- return
- }
-
- ctx.Flash.Success(ctx.Tr("settings.revoke_oauth2_grant_success"))
- ctx.JSON(http.StatusOK, map[string]interface{}{
- "redirect": setting.AppSubURL + "/user/settings/applications",
- })
+ oa := newOAuth2CommonHandlers(ctx.Doer.ID)
+ oa.RevokeGrant(ctx)
}
diff --git a/routers/web/user/setting/oauth2_common.go b/routers/web/user/setting/oauth2_common.go
new file mode 100644
index 0000000000..f02f6ab041
--- /dev/null
+++ b/routers/web/user/setting/oauth2_common.go
@@ -0,0 +1,150 @@
+// Copyright 2019 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package setting
+
+import (
+ "fmt"
+ "net/http"
+
+ "code.gitea.io/gitea/models/auth"
+ "code.gitea.io/gitea/modules/base"
+ "code.gitea.io/gitea/modules/context"
+ "code.gitea.io/gitea/modules/web"
+ "code.gitea.io/gitea/services/forms"
+)
+
+type OAuth2CommonHandlers struct {
+ OwnerID int64 // 0 for instance-wide, otherwise OrgID or UserID
+ BasePathList string // the base URL for the application list page, eg: "/user/setting/applications"
+ BasePathEditPrefix string // the base URL for the application edit page, will be appended with app id, eg: "/user/setting/applications/oauth2"
+ TplAppEdit base.TplName // the template for the application edit page
+}
+
+func (oa *OAuth2CommonHandlers) renderEditPage(ctx *context.Context) {
+ app := ctx.Data["App"].(*auth.OAuth2Application)
+ ctx.Data["FormActionPath"] = fmt.Sprintf("%s/%d", oa.BasePathEditPrefix, app.ID)
+ ctx.HTML(http.StatusOK, oa.TplAppEdit)
+}
+
+// AddApp adds an oauth2 application
+func (oa *OAuth2CommonHandlers) AddApp(ctx *context.Context) {
+ form := web.GetForm(ctx).(*forms.EditOAuth2ApplicationForm)
+ if ctx.HasError() {
+ // go to the application list page
+ ctx.Redirect(oa.BasePathList)
+ return
+ }
+
+ // TODO validate redirect URI
+ app, err := auth.CreateOAuth2Application(ctx, auth.CreateOAuth2ApplicationOptions{
+ Name: form.Name,
+ RedirectURIs: []string{form.RedirectURI},
+ UserID: oa.OwnerID,
+ })
+ if err != nil {
+ ctx.ServerError("CreateOAuth2Application", err)
+ return
+ }
+
+ // render the edit page with secret
+ ctx.Flash.Success(ctx.Tr("settings.create_oauth2_application_success"), true)
+ ctx.Data["App"] = app
+ ctx.Data["ClientSecret"], err = app.GenerateClientSecret()
+ if err != nil {
+ ctx.ServerError("GenerateClientSecret", err)
+ return
+ }
+ oa.renderEditPage(ctx)
+}
+
+// EditShow displays the given application
+func (oa *OAuth2CommonHandlers) EditShow(ctx *context.Context) {
+ app, err := auth.GetOAuth2ApplicationByID(ctx, ctx.ParamsInt64("id"))
+ if err != nil {
+ if auth.IsErrOAuthApplicationNotFound(err) {
+ ctx.NotFound("Application not found", err)
+ return
+ }
+ ctx.ServerError("GetOAuth2ApplicationByID", err)
+ return
+ }
+ if app.UID != oa.OwnerID {
+ ctx.NotFound("Application not found", nil)
+ return
+ }
+ ctx.Data["App"] = app
+ oa.renderEditPage(ctx)
+}
+
+// EditSave saves the oauth2 application
+func (oa *OAuth2CommonHandlers) EditSave(ctx *context.Context) {
+ form := web.GetForm(ctx).(*forms.EditOAuth2ApplicationForm)
+
+ if ctx.HasError() {
+ oa.renderEditPage(ctx)
+ return
+ }
+
+ // TODO validate redirect URI
+ var err error
+ if ctx.Data["App"], err = auth.UpdateOAuth2Application(auth.UpdateOAuth2ApplicationOptions{
+ ID: ctx.ParamsInt64("id"),
+ Name: form.Name,
+ RedirectURIs: []string{form.RedirectURI},
+ UserID: oa.OwnerID,
+ }); err != nil {
+ ctx.ServerError("UpdateOAuth2Application", err)
+ return
+ }
+ ctx.Flash.Success(ctx.Tr("settings.update_oauth2_application_success"))
+ ctx.Redirect(oa.BasePathList)
+}
+
+// RegenerateSecret regenerates the secret
+func (oa *OAuth2CommonHandlers) RegenerateSecret(ctx *context.Context) {
+ app, err := auth.GetOAuth2ApplicationByID(ctx, ctx.ParamsInt64("id"))
+ if err != nil {
+ if auth.IsErrOAuthApplicationNotFound(err) {
+ ctx.NotFound("Application not found", err)
+ return
+ }
+ ctx.ServerError("GetOAuth2ApplicationByID", err)
+ return
+ }
+ if app.UID != oa.OwnerID {
+ ctx.NotFound("Application not found", nil)
+ return
+ }
+ ctx.Data["App"] = app
+ ctx.Data["ClientSecret"], err = app.GenerateClientSecret()
+ if err != nil {
+ ctx.ServerError("GenerateClientSecret", err)
+ return
+ }
+ ctx.Flash.Success(ctx.Tr("settings.update_oauth2_application_success"), true)
+ oa.renderEditPage(ctx)
+}
+
+// DeleteApp deletes the given oauth2 application
+func (oa *OAuth2CommonHandlers) DeleteApp(ctx *context.Context) {
+ if err := auth.DeleteOAuth2Application(ctx.ParamsInt64("id"), oa.OwnerID); err != nil {
+ ctx.ServerError("DeleteOAuth2Application", err)
+ return
+ }
+
+ ctx.Flash.Success(ctx.Tr("settings.remove_oauth2_application_success"))
+ ctx.JSON(http.StatusOK, map[string]interface{}{"redirect": oa.BasePathList})
+}
+
+// RevokeGrant revokes the grant
+func (oa *OAuth2CommonHandlers) RevokeGrant(ctx *context.Context) {
+ if err := auth.RevokeOAuth2Grant(ctx, ctx.ParamsInt64("grantId"), oa.OwnerID); err != nil {
+ ctx.ServerError("RevokeOAuth2Grant", err)
+ return
+ }
+
+ ctx.Flash.Success(ctx.Tr("settings.revoke_oauth2_grant_success"))
+ ctx.JSON(http.StatusOK, map[string]interface{}{"redirect": oa.BasePathList})
+}
diff --git a/routers/web/user/setting/profile.go b/routers/web/user/setting/profile.go
index b07813e725..c9a7afe982 100644
--- a/routers/web/user/setting/profile.go
+++ b/routers/web/user/setting/profile.go
@@ -30,6 +30,7 @@ import (
"code.gitea.io/gitea/modules/web/middleware"
"code.gitea.io/gitea/services/agit"
"code.gitea.io/gitea/services/forms"
+ container_service "code.gitea.io/gitea/services/packages/container"
user_service "code.gitea.io/gitea/services/user"
)
@@ -90,6 +91,11 @@ func HandleUsernameChange(ctx *context.Context, user *user_model.User, newName s
return err
}
+ if err := container_service.UpdateRepositoryNames(ctx, user, newName); err != nil {
+ ctx.ServerError("UpdateRepositoryNames", err)
+ return err
+ }
+
log.Trace("User name changed: %s -> %s", user.Name, newName)
return nil
}
diff --git a/routers/web/user/setting/security/security.go b/routers/web/user/setting/security/security.go
index 218cf57ab7..57ea24eeb1 100644
--- a/routers/web/user/setting/security/security.go
+++ b/routers/web/user/setting/security/security.go
@@ -8,8 +8,7 @@ package security
import (
"net/http"
- "code.gitea.io/gitea/models"
- "code.gitea.io/gitea/models/auth"
+ auth_model "code.gitea.io/gitea/models/auth"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/context"
@@ -56,21 +55,21 @@ func DeleteAccountLink(ctx *context.Context) {
}
func loadSecurityData(ctx *context.Context) {
- enrolled, err := auth.HasTwoFactorByUID(ctx.Doer.ID)
+ enrolled, err := auth_model.HasTwoFactorByUID(ctx.Doer.ID)
if err != nil {
ctx.ServerError("SettingsTwoFactor", err)
return
}
ctx.Data["TOTPEnrolled"] = enrolled
- credentials, err := auth.GetWebAuthnCredentialsByUID(ctx.Doer.ID)
+ credentials, err := auth_model.GetWebAuthnCredentialsByUID(ctx.Doer.ID)
if err != nil {
ctx.ServerError("GetWebAuthnCredentialsByUID", err)
return
}
ctx.Data["WebAuthnCredentials"] = credentials
- tokens, err := models.ListAccessTokens(models.ListAccessTokensOptions{UserID: ctx.Doer.ID})
+ tokens, err := auth_model.ListAccessTokens(auth_model.ListAccessTokensOptions{UserID: ctx.Doer.ID})
if err != nil {
ctx.ServerError("ListAccessTokens", err)
return
@@ -84,9 +83,9 @@ func loadSecurityData(ctx *context.Context) {
}
// map the provider display name with the AuthSource
- sources := make(map[*auth.Source]string)
+ sources := make(map[*auth_model.Source]string)
for _, externalAccount := range accountLinks {
- if authSource, err := auth.GetSourceByID(externalAccount.LoginSourceID); err == nil {
+ if authSource, err := auth_model.GetSourceByID(externalAccount.LoginSourceID); err == nil {
var providerDisplayName string
type DisplayNamed interface {
diff --git a/routers/web/user/task.go b/routers/web/user/task.go
index fd561cdd4c..7f5ef792ad 100644
--- a/routers/web/user/task.go
+++ b/routers/web/user/task.go
@@ -8,16 +8,16 @@ import (
"net/http"
"strconv"
- "code.gitea.io/gitea/models"
+ admin_model "code.gitea.io/gitea/models/admin"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/json"
)
// TaskStatus returns task's status
func TaskStatus(ctx *context.Context) {
- task, opts, err := models.GetMigratingTaskByID(ctx.ParamsInt64("task"), ctx.Doer.ID)
+ task, opts, err := admin_model.GetMigratingTaskByID(ctx.ParamsInt64("task"), ctx.Doer.ID)
if err != nil {
- if models.IsErrTaskDoesNotExist(err) {
+ if admin_model.IsErrTaskDoesNotExist(err) {
ctx.JSON(http.StatusNotFound, map[string]interface{}{
"error": "task `" + strconv.FormatInt(ctx.ParamsInt64("task"), 10) + "` does not exist",
})
@@ -33,9 +33,9 @@ func TaskStatus(ctx *context.Context) {
if task.Message != "" && task.Message[0] == '{' {
// assume message is actually a translatable string
- var translatableMessage models.TranslatableMessage
+ var translatableMessage admin_model.TranslatableMessage
if err := json.Unmarshal([]byte(message), &translatableMessage); err != nil {
- translatableMessage = models.TranslatableMessage{
+ translatableMessage = admin_model.TranslatableMessage{
Format: "migrate.migrating_failed.error",
Args: []interface{}{task.Message},
}
diff --git a/routers/web/web.go b/routers/web/web.go
index fbece620b1..8859ec5850 100644
--- a/routers/web/web.go
+++ b/routers/web/web.go
@@ -12,6 +12,7 @@ import (
"code.gitea.io/gitea/models/perm"
"code.gitea.io/gitea/models/unit"
+ "code.gitea.io/gitea/modules/cache"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/httpcache"
@@ -42,7 +43,6 @@ import (
context_service "code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/forms"
"code.gitea.io/gitea/services/lfs"
- "code.gitea.io/gitea/services/mailer"
_ "code.gitea.io/gitea/modules/session" // to registers all internal adapters
@@ -98,7 +98,7 @@ func buildAuthGroup() *auth_service.Group {
}
// Routes returns all web routes
-func Routes() *web.Route {
+func Routes(ctx gocontext.Context) *web.Route {
routes := web.NewRoute()
routes.Use(web.WrapWithPrefix(public.AssetsURLPathPrefix, public.AssetsHandlerFunc(&public.Options{
@@ -120,7 +120,9 @@ func Routes() *web.Route {
})
routes.Use(sessioner)
- routes.Use(Recovery())
+ ctx, _ = templates.HTMLRenderer(ctx)
+
+ routes.Use(Recovery(ctx))
// We use r.Route here over r.Use because this prevents requests that are not for avatars having to go through this additional handler
routes.Route("/avatars/*", "GET, HEAD", storageHandler(setting.Avatar.Storage, "avatars", storage.Avatars))
@@ -151,8 +153,6 @@ func Routes() *web.Route {
common = append(common, h)
}
- mailer.InitMailRender(templates.Mailer())
-
if setting.Service.EnableCaptcha {
// The captcha http.Handler should only fire on /captcha/* so we can just mount this on that url
routes.Route("/captcha/*", "GET,HEAD", append(common, captcha.Captchaer(context.GetImageCaptcha()))...)
@@ -195,10 +195,10 @@ func Routes() *web.Route {
routes.Get("/api/healthz", healthcheck.Check)
// Removed: toolbox.Toolboxer middleware will provide debug information which seems unnecessary
- common = append(common, context.Contexter())
+ common = append(common, context.Contexter(ctx))
group := buildAuthGroup()
- if err := group.Init(); err != nil {
+ if err := group.Init(ctx); err != nil {
log.Error("Could not initialize '%s' auth method, error: %s", group.Name(), err)
}
@@ -289,6 +289,13 @@ func RegisterRoutes(m *web.Route) {
}
}
+ dlSourceEnabled := func(ctx *context.Context) {
+ if setting.Repository.DisableDownloadSourceArchives {
+ ctx.Error(http.StatusNotFound)
+ return
+ }
+ }
+
// FIXME: not all routes need go through same middleware.
// Especially some AJAX requests, we can reduce middleware number to improve performance.
// Routers.
@@ -420,8 +427,8 @@ func RegisterRoutes(m *web.Route) {
m.Post("/{id}", bindIgnErr(forms.EditOAuth2ApplicationForm{}), user_setting.OAuthApplicationsEdit)
m.Post("/{id}/regenerate_secret", user_setting.OAuthApplicationsRegenerateSecret)
m.Post("", bindIgnErr(forms.EditOAuth2ApplicationForm{}), user_setting.OAuthApplicationsPost)
- m.Post("/delete", user_setting.DeleteOAuth2Application)
- m.Post("/revoke", user_setting.RevokeOAuth2Grant)
+ m.Post("/{id}/delete", user_setting.DeleteOAuth2Application)
+ m.Post("/{id}/revoke/{grantId}", user_setting.RevokeOAuth2Grant)
})
m.Combo("/applications").Get(user_setting.Applications).
Post(bindIgnErr(forms.NewAccessTokenForm{}), user_setting.ApplicationsPost)
@@ -466,8 +473,13 @@ func RegisterRoutes(m *web.Route) {
m.Group("/admin", func() {
m.Get("", adminReq, admin.Dashboard)
m.Post("", adminReq, bindIgnErr(forms.AdminDashboardForm{}), admin.DashboardPost)
- m.Get("/config", admin.Config)
- m.Post("/config/test_mail", admin.SendTestMail)
+
+ m.Group("/config", func() {
+ m.Get("", admin.Config)
+ m.Post("", admin.ChangeConfig)
+ m.Post("/test_mail", admin.SendTestMail)
+ })
+
m.Group("/monitor", func() {
m.Get("", admin.Monitor)
m.Get("/stacktrace", admin.GoroutineStacktrace)
@@ -521,7 +533,7 @@ func RegisterRoutes(m *web.Route) {
m.Get("", repo.WebHooksEdit)
m.Post("/replay/{uuid}", repo.ReplayWebhook)
})
- m.Post("/gitea/{id}", bindIgnErr(forms.NewWebhookForm{}), repo.WebHooksEditPost)
+ m.Post("/gitea/{id}", bindIgnErr(forms.NewWebhookForm{}), repo.GiteaHooksEditPost)
m.Post("/gogs/{id}", bindIgnErr(forms.NewGogshookForm{}), repo.GogsHooksEditPost)
m.Post("/slack/{id}", bindIgnErr(forms.NewSlackHookForm{}), repo.SlackHooksEditPost)
m.Post("/discord/{id}", bindIgnErr(forms.NewDiscordHookForm{}), repo.DiscordHooksEditPost)
@@ -562,6 +574,23 @@ func RegisterRoutes(m *web.Route) {
m.Post("/delete", admin.DeleteNotices)
m.Post("/empty", admin.EmptyNotices)
})
+
+ m.Group("/applications", func() {
+ m.Get("", admin.Applications)
+ m.Post("/oauth2", bindIgnErr(forms.EditOAuth2ApplicationForm{}), admin.ApplicationsPost)
+ m.Group("/oauth2/{id}", func() {
+ m.Combo("").Get(admin.EditApplication).Post(bindIgnErr(forms.EditOAuth2ApplicationForm{}), admin.EditApplicationPost)
+ m.Post("/regenerate_secret", admin.ApplicationsRegenerateSecret)
+ m.Post("/delete", admin.DeleteApplication)
+ })
+ }, func(ctx *context.Context) {
+ if !setting.OAuth2.Enable {
+ ctx.Error(http.StatusForbidden)
+ return
+ }
+ })
+ }, func(ctx *context.Context) {
+ ctx.Data["EnableOAuth2"] = setting.OAuth2.Enable
}, adminReq)
// ***** END: Admin *****
@@ -655,6 +684,20 @@ func RegisterRoutes(m *web.Route) {
Post(bindIgnErr(forms.UpdateOrgSettingForm{}), org.SettingsPost)
m.Post("/avatar", bindIgnErr(forms.AvatarForm{}), org.SettingsAvatar)
m.Post("/avatar/delete", org.SettingsDeleteAvatar)
+ m.Group("/applications", func() {
+ m.Get("", org.Applications)
+ m.Post("/oauth2", bindIgnErr(forms.EditOAuth2ApplicationForm{}), org.OAuthApplicationsPost)
+ m.Group("/oauth2/{id}", func() {
+ m.Combo("").Get(org.OAuth2ApplicationShow).Post(bindIgnErr(forms.EditOAuth2ApplicationForm{}), org.OAuth2ApplicationEdit)
+ m.Post("/regenerate_secret", org.OAuthApplicationsRegenerateSecret)
+ m.Post("/delete", org.DeleteOAuth2Application)
+ })
+ }, func(ctx *context.Context) {
+ if !setting.OAuth2.Enable {
+ ctx.Error(http.StatusForbidden)
+ return
+ }
+ })
m.Group("/hooks", func() {
m.Get("", org.Webhooks)
@@ -674,7 +717,7 @@ func RegisterRoutes(m *web.Route) {
m.Get("", repo.WebHooksEdit)
m.Post("/replay/{uuid}", repo.ReplayWebhook)
})
- m.Post("/gitea/{id}", bindIgnErr(forms.NewWebhookForm{}), repo.WebHooksEditPost)
+ m.Post("/gitea/{id}", bindIgnErr(forms.NewWebhookForm{}), repo.GiteaHooksEditPost)
m.Post("/gogs/{id}", bindIgnErr(forms.NewGogshookForm{}), repo.GogsHooksEditPost)
m.Post("/slack/{id}", bindIgnErr(forms.NewSlackHookForm{}), repo.SlackHooksEditPost)
m.Post("/discord/{id}", bindIgnErr(forms.NewDiscordHookForm{}), repo.DiscordHooksEditPost)
@@ -695,6 +738,8 @@ func RegisterRoutes(m *web.Route) {
})
m.Route("/delete", "GET,POST", org.SettingsDelete)
+ }, func(ctx *context.Context) {
+ ctx.Data["EnableOAuth2"] = setting.OAuth2.Enable
})
}, context.OrgAssignment(true, true))
}, reqSignIn)
@@ -731,6 +776,7 @@ func RegisterRoutes(m *web.Route) {
})
}, ignSignIn, context.PackageAssignment(), reqPackageAccess(perm.AccessModeRead))
}
+ m.Get("/code", user.CodeSearch)
}, context_service.UserAssignmentWeb())
// ***** Release Attachment Download without Signin
@@ -796,7 +842,7 @@ func RegisterRoutes(m *web.Route) {
m.Post("/test", repo.TestWebhook)
m.Post("/replay/{uuid}", repo.ReplayWebhook)
})
- m.Post("/gitea/{id}", bindIgnErr(forms.NewWebhookForm{}), repo.WebHooksEditPost)
+ m.Post("/gitea/{id}", bindIgnErr(forms.NewWebhookForm{}), repo.GiteaHooksEditPost)
m.Post("/gogs/{id}", bindIgnErr(forms.NewGogshookForm{}), repo.GogsHooksEditPost)
m.Post("/slack/{id}", bindIgnErr(forms.NewSlackHookForm{}), repo.SlackHooksEditPost)
m.Post("/discord/{id}", bindIgnErr(forms.NewDiscordHookForm{}), repo.DiscordHooksEditPost)
@@ -1011,6 +1057,7 @@ func RegisterRoutes(m *web.Route) {
return
}
ctx.Data["CommitsCount"] = ctx.Repo.CommitsCount
+ ctx.Repo.GitRepo.LastCommitCache = git.NewLastCommitCache(ctx.Repo.CommitsCount, ctx.Repo.Repository.FullName(), ctx.Repo.GitRepo, cache.GetCache())
})
}, ignSignIn, context.RepoAssignment, context.UnitTypes(), reqRepoReleaseReader)
@@ -1104,7 +1151,7 @@ func RegisterRoutes(m *web.Route) {
m.Group("/archive", func() {
m.Get("/*", repo.Download)
m.Post("/*", repo.InitiateDownload)
- }, repo.MustBeNotEmpty, reqRepoCodeReader)
+ }, repo.MustBeNotEmpty, dlSourceEnabled, reqRepoCodeReader)
m.Group("/branches", func() {
m.Get("", repo.Branches)
@@ -1261,6 +1308,8 @@ func RegisterRoutes(m *web.Route) {
m.Group("/notifications", func() {
m.Get("", user.Notifications)
+ m.Get("/subscriptions", user.NotificationSubscriptions)
+ m.Get("/watching", user.NotificationWatching)
m.Post("/status", user.NotificationStatusPost)
m.Post("/purge", user.NotificationPurgePost)
m.Get("/new", user.NewAvailable)
diff --git a/services/asymkey/ssh_key_test.go b/services/asymkey/ssh_key_test.go
index 182371271a..9bc23a719c 100644
--- a/services/asymkey/ssh_key_test.go
+++ b/services/asymkey/ssh_key_test.go
@@ -18,7 +18,7 @@ import (
func TestAddLdapSSHPublicKeys(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
+ user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
s := &auth.Source{ID: 1}
testCases := []struct {
diff --git a/services/attachment/attachment_test.go b/services/attachment/attachment_test.go
index 889151d8f3..561792db2f 100644
--- a/services/attachment/attachment_test.go
+++ b/services/attachment/attachment_test.go
@@ -26,7 +26,7 @@ func TestMain(m *testing.M) {
func TestUploadAttachment(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User)
+ user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
fPath := "./attachment_test.go"
f, err := os.Open(fPath)
diff --git a/services/auth/basic.go b/services/auth/basic.go
index 1869662e92..9b32ad29af 100644
--- a/services/auth/basic.go
+++ b/services/auth/basic.go
@@ -9,7 +9,7 @@ import (
"net/http"
"strings"
- "code.gitea.io/gitea/models"
+ auth_model "code.gitea.io/gitea/models/auth"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/log"
@@ -85,7 +85,7 @@ func (b *Basic) Verify(req *http.Request, w http.ResponseWriter, store DataStore
return u
}
- token, err := models.GetAccessTokenBySHA(authToken)
+ token, err := auth_model.GetAccessTokenBySHA(authToken)
if err == nil {
log.Trace("Basic Authorization: Valid AccessToken for user[%d]", uid)
u, err := user_model.GetUserByID(token.UID)
@@ -95,13 +95,13 @@ func (b *Basic) Verify(req *http.Request, w http.ResponseWriter, store DataStore
}
token.UpdatedUnix = timeutil.TimeStampNow()
- if err = models.UpdateAccessToken(token); err != nil {
+ if err = auth_model.UpdateAccessToken(token); err != nil {
log.Error("UpdateAccessToken: %v", err)
}
store.GetData()["IsApiToken"] = true
return u
- } else if !models.IsErrAccessTokenNotExist(err) && !models.IsErrAccessTokenEmpty(err) {
+ } else if !auth_model.IsErrAccessTokenNotExist(err) && !auth_model.IsErrAccessTokenEmpty(err) {
log.Error("GetAccessTokenBySha: %v", err)
}
diff --git a/services/auth/group.go b/services/auth/group.go
index 0f40e1a76c..bbafe64b49 100644
--- a/services/auth/group.go
+++ b/services/auth/group.go
@@ -5,6 +5,7 @@
package auth
import (
+ "context"
"net/http"
"reflect"
"strings"
@@ -51,14 +52,14 @@ func (b *Group) Name() string {
}
// Init does nothing as the Basic implementation does not need to allocate any resources
-func (b *Group) Init() error {
+func (b *Group) Init(ctx context.Context) error {
for _, method := range b.methods {
initializable, ok := method.(Initializable)
if !ok {
continue
}
- if err := initializable.Init(); err != nil {
+ if err := initializable.Init(ctx); err != nil {
return err
}
}
diff --git a/services/auth/interface.go b/services/auth/interface.go
index a05ece2078..ecc9ad2ca6 100644
--- a/services/auth/interface.go
+++ b/services/auth/interface.go
@@ -34,7 +34,7 @@ type Method interface {
type Initializable interface {
// Init should be called exactly once before using any of the other methods,
// in order to allow the plugin to allocate necessary resources
- Init() error
+ Init(ctx context.Context) error
}
// Named represents a named thing
diff --git a/services/auth/oauth2.go b/services/auth/oauth2.go
index 68638a0806..8f038d6104 100644
--- a/services/auth/oauth2.go
+++ b/services/auth/oauth2.go
@@ -10,8 +10,7 @@ import (
"strings"
"time"
- "code.gitea.io/gitea/models"
- "code.gitea.io/gitea/models/auth"
+ auth_model "code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/models/db"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/log"
@@ -37,8 +36,8 @@ func CheckOAuthAccessToken(accessToken string) int64 {
log.Trace("oauth2.ParseToken: %v", err)
return 0
}
- var grant *auth.OAuth2Grant
- if grant, err = auth.GetOAuth2GrantByID(db.DefaultContext, token.GrantID); err != nil || grant == nil {
+ var grant *auth_model.OAuth2Grant
+ if grant, err = auth_model.GetOAuth2GrantByID(db.DefaultContext, token.GrantID); err != nil || grant == nil {
return 0
}
if token.Type != oauth2.TypeAccessToken {
@@ -91,15 +90,15 @@ func (o *OAuth2) userIDFromToken(req *http.Request, store DataStore) int64 {
}
return uid
}
- t, err := models.GetAccessTokenBySHA(tokenSHA)
+ t, err := auth_model.GetAccessTokenBySHA(tokenSHA)
if err != nil {
- if !models.IsErrAccessTokenNotExist(err) && !models.IsErrAccessTokenEmpty(err) {
+ if !auth_model.IsErrAccessTokenNotExist(err) && !auth_model.IsErrAccessTokenEmpty(err) {
log.Error("GetAccessTokenBySHA: %v", err)
}
return 0
}
t.UpdatedUnix = timeutil.TimeStampNow()
- if err = models.UpdateAccessToken(t); err != nil {
+ if err = auth_model.UpdateAccessToken(t); err != nil {
log.Error("UpdateAccessToken: %v", err)
}
store.GetData()["IsApiToken"] = true
diff --git a/services/auth/reverseproxy.go b/services/auth/reverseproxy.go
index 05d6af78f1..8dec1c8ea7 100644
--- a/services/auth/reverseproxy.go
+++ b/services/auth/reverseproxy.go
@@ -37,11 +37,7 @@ type ReverseProxy struct{}
// getUserName extracts the username from the "setting.ReverseProxyAuthUser" header
func (r *ReverseProxy) getUserName(req *http.Request) string {
- webAuthUser := strings.TrimSpace(req.Header.Get(setting.ReverseProxyAuthUser))
- if len(webAuthUser) == 0 {
- return ""
- }
- return webAuthUser
+ return strings.TrimSpace(req.Header.Get(setting.ReverseProxyAuthUser))
}
// Name represents the name of auth method
@@ -49,14 +45,14 @@ func (r *ReverseProxy) Name() string {
return ReverseProxyMethodName
}
-// Verify extracts the username from the "setting.ReverseProxyAuthUser" header
+// getUserFromAuthUser extracts the username from the "setting.ReverseProxyAuthUser" header
// of the request and returns the corresponding user object for that name.
// Verification of header data is not performed as it should have already been done by
-// the revese proxy.
+// the reverse proxy.
// If a username is available in the "setting.ReverseProxyAuthUser" header an existing
// user object is returned (populated with username or email found in header).
// Returns nil if header is empty.
-func (r *ReverseProxy) Verify(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) *user_model.User {
+func (r *ReverseProxy) getUserFromAuthUser(req *http.Request) *user_model.User {
username := r.getUserName(req)
if len(username) == 0 {
return nil
@@ -71,6 +67,54 @@ func (r *ReverseProxy) Verify(req *http.Request, w http.ResponseWriter, store Da
}
user = r.newUser(req)
}
+ return user
+}
+
+// getEmail extracts the email from the "setting.ReverseProxyAuthEmail" header
+func (r *ReverseProxy) getEmail(req *http.Request) string {
+ return strings.TrimSpace(req.Header.Get(setting.ReverseProxyAuthEmail))
+}
+
+// getUserFromAuthEmail extracts the username from the "setting.ReverseProxyAuthEmail" header
+// of the request and returns the corresponding user object for that email.
+// Verification of header data is not performed as it should have already been done by
+// the reverse proxy.
+// If an email is available in the "setting.ReverseProxyAuthEmail" header an existing
+// user object is returned (populated with the email found in header).
+// Returns nil if header is empty or if "setting.EnableReverseProxyEmail" is disabled.
+func (r *ReverseProxy) getUserFromAuthEmail(req *http.Request) *user_model.User {
+ if !setting.Service.EnableReverseProxyEmail {
+ return nil
+ }
+ email := r.getEmail(req)
+ if len(email) == 0 {
+ return nil
+ }
+ log.Trace("ReverseProxy Authorization: Found email: %s", email)
+
+ user, err := user_model.GetUserByEmail(email)
+ if err != nil {
+ // Do not allow auto-registration, we don't have a username here
+ if !user_model.IsErrUserNotExist(err) {
+ log.Error("GetUserByEmail: %v", err)
+ }
+ return nil
+ }
+ return user
+}
+
+// Verify attempts to load a user object based on headers sent by the reverse proxy.
+// First it will attempt to load it based on the username (see docs for getUserFromAuthUser),
+// and failing that it will attempt to load it based on the email (see docs for getUserFromAuthEmail).
+// Returns nil if the headers are empty or the user is not found.
+func (r *ReverseProxy) Verify(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) *user_model.User {
+ user := r.getUserFromAuthUser(req)
+ if user == nil {
+ user = r.getUserFromAuthEmail(req)
+ if user == nil {
+ return nil
+ }
+ }
// Make sure requests to API paths, attachment downloads, git and LFS do not create a new session
if !middleware.IsAPIPath(req) && !isAttachmentDownload(req) && !isGitRawReleaseOrLFSPath(req) {
@@ -105,9 +149,15 @@ func (r *ReverseProxy) newUser(req *http.Request) *user_model.User {
}
}
+ var fullname string
+ if setting.Service.EnableReverseProxyFullName {
+ fullname = req.Header.Get(setting.ReverseProxyAuthFullName)
+ }
+
user := &user_model.User{
- Name: username,
- Email: email,
+ Name: username,
+ Email: email,
+ FullName: fullname,
}
overwriteDefault := user_model.CreateUserOverwriteOptions{
diff --git a/services/auth/session.go b/services/auth/session.go
index 6a23a17665..1ec94aa0af 100644
--- a/services/auth/session.go
+++ b/services/auth/session.go
@@ -39,6 +39,10 @@ func (s *Session) Verify(req *http.Request, w http.ResponseWriter, store DataSto
// SessionUser returns the user object corresponding to the "uid" session variable.
func SessionUser(sess SessionStore) *user_model.User {
+ if sess == nil {
+ return nil
+ }
+
// Get user ID
uid := sess.Get("uid")
if uid == nil {
diff --git a/services/auth/source/oauth2/jwtsigningkey.go b/services/auth/source/oauth2/jwtsigningkey.go
index 24f2c41119..d9312ee820 100644
--- a/services/auth/source/oauth2/jwtsigningkey.go
+++ b/services/auth/source/oauth2/jwtsigningkey.go
@@ -31,11 +31,11 @@ import (
// ErrInvalidAlgorithmType represents an invalid algorithm error.
type ErrInvalidAlgorithmType struct {
- Algorightm string
+ Algorithm string
}
func (err ErrInvalidAlgorithmType) Error() string {
- return fmt.Sprintf("JWT signing algorithm is not supported: %s", err.Algorightm)
+ return fmt.Sprintf("JWT signing algorithm is not supported: %s", err.Algorithm)
}
// JWTSigningKey represents a algorithm/key pair to sign JWTs
@@ -364,7 +364,7 @@ func loadOrCreateSymmetricKey() (interface{}, error) {
return nil, err
}
- setting.CreateOrAppendToCustomConf(func(cfg *ini.File) {
+ setting.CreateOrAppendToCustomConf("oauth2.JWT_SECRET", func(cfg *ini.File) {
secretBase64 := base64.RawURLEncoding.EncodeToString(key)
cfg.Section("oauth2").Key("JWT_SECRET").SetValue(secretBase64)
})
diff --git a/services/auth/source/smtp/auth.go b/services/auth/source/smtp/auth.go
index 8d0cbb11cd..a9e4b0e5f4 100644
--- a/services/auth/source/smtp/auth.go
+++ b/services/auth/source/smtp/auth.go
@@ -58,10 +58,10 @@ var ErrUnsupportedLoginType = errors.New("Login source is unknown")
func Authenticate(a smtp.Auth, source *Source) error {
tlsConfig := &tls.Config{
InsecureSkipVerify: source.SkipVerify,
- ServerName: source.Host,
+ ServerName: source.Addr,
}
- conn, err := net.Dial("tcp", net.JoinHostPort(source.Host, strconv.Itoa(source.Port)))
+ conn, err := net.Dial("tcp", net.JoinHostPort(source.Addr, strconv.Itoa(source.Port)))
if err != nil {
return err
}
@@ -71,7 +71,7 @@ func Authenticate(a smtp.Auth, source *Source) error {
conn = tls.Client(conn, tlsConfig)
}
- client, err := smtp.NewClient(conn, source.Host)
+ client, err := smtp.NewClient(conn, source.Addr)
if err != nil {
return fmt.Errorf("failed to create NewClient: %w", err)
}
diff --git a/services/auth/source/smtp/source.go b/services/auth/source/smtp/source.go
index 5e69f912da..b2286d42a0 100644
--- a/services/auth/source/smtp/source.go
+++ b/services/auth/source/smtp/source.go
@@ -19,7 +19,7 @@ import (
// Source holds configuration for the SMTP login source.
type Source struct {
Auth string
- Host string
+ Addr string
Port int
AllowedDomains string `xorm:"TEXT"`
ForceSMTPS bool
diff --git a/services/auth/source/smtp/source_authenticate.go b/services/auth/source/smtp/source_authenticate.go
index dff24d494e..63fd3e5511 100644
--- a/services/auth/source/smtp/source_authenticate.go
+++ b/services/auth/source/smtp/source_authenticate.go
@@ -32,7 +32,7 @@ func (source *Source) Authenticate(user *user_model.User, userName, password str
var auth smtp.Auth
switch source.Auth {
case PlainAuthentication:
- auth = smtp.PlainAuth("", userName, password, source.Host)
+ auth = smtp.PlainAuth("", userName, password, source.Addr)
case LoginAuthentication:
auth = &loginAuthenticator{userName, password}
case CRAMMD5Authentication:
diff --git a/services/auth/sspi_windows.go b/services/auth/sspi_windows.go
index 7e31378b6c..757d596c4c 100644
--- a/services/auth/sspi_windows.go
+++ b/services/auth/sspi_windows.go
@@ -5,6 +5,7 @@
package auth
import (
+ "context"
"errors"
"net/http"
"strings"
@@ -52,21 +53,14 @@ type SSPI struct {
}
// Init creates a new global websspi.Authenticator object
-func (s *SSPI) Init() error {
+func (s *SSPI) Init(ctx context.Context) error {
config := websspi.NewConfig()
var err error
sspiAuth, err = websspi.New(config)
if err != nil {
return err
}
- s.rnd = render.New(render.Options{
- Extensions: []string{".tmpl"},
- Directory: "templates",
- Funcs: templates.NewFuncMap(),
- Asset: templates.GetAsset,
- AssetNames: templates.GetAssetNames,
- IsDevelopment: !setting.IsProd,
- })
+ _, s.rnd = templates.HTMLRenderer(ctx)
return nil
}
diff --git a/services/cron/tasks.go b/services/cron/tasks.go
index c26e47e0ce..6ff5964d1e 100644
--- a/services/cron/tasks.go
+++ b/services/cron/tasks.go
@@ -10,8 +10,8 @@ import (
"reflect"
"sync"
- admin_model "code.gitea.io/gitea/models/admin"
"code.gitea.io/gitea/models/db"
+ system_model "code.gitea.io/gitea/models/system"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/graceful"
"code.gitea.io/gitea/modules/log"
@@ -114,7 +114,7 @@ func (t *Task) RunWithUser(doer *user_model.User, config Config) {
t.LastDoer = doerName
t.lock.Unlock()
- if err := admin_model.CreateNotice(ctx, admin_model.NoticeTask, config.FormatMessage(translation.NewLocale("en-US"), t.Name, "cancelled", doerName, message)); err != nil {
+ if err := system_model.CreateNotice(ctx, system_model.NoticeTask, config.FormatMessage(translation.NewLocale("en-US"), t.Name, "cancelled", doerName, message)); err != nil {
log.Error("CreateNotice: %v", err)
}
return
@@ -127,7 +127,7 @@ func (t *Task) RunWithUser(doer *user_model.User, config Config) {
t.lock.Unlock()
if config.DoNoticeOnSuccess() {
- if err := admin_model.CreateNotice(ctx, admin_model.NoticeTask, config.FormatMessage(translation.NewLocale("en-US"), t.Name, "finished", doerName)); err != nil {
+ if err := system_model.CreateNotice(ctx, system_model.NoticeTask, config.FormatMessage(translation.NewLocale("en-US"), t.Name, "finished", doerName)); err != nil {
log.Error("CreateNotice: %v", err)
}
}
diff --git a/services/cron/tasks_extended.go b/services/cron/tasks_extended.go
index 41bd5c4420..04b9560be3 100644
--- a/services/cron/tasks_extended.go
+++ b/services/cron/tasks_extended.go
@@ -8,10 +8,10 @@ import (
"context"
"time"
- "code.gitea.io/gitea/models"
- "code.gitea.io/gitea/models/admin"
+ activities_model "code.gitea.io/gitea/models/activities"
asymkey_model "code.gitea.io/gitea/models/asymkey"
"code.gitea.io/gitea/models/db"
+ "code.gitea.io/gitea/models/system"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/updatechecker"
@@ -134,7 +134,7 @@ func registerDeleteOldActions() {
OlderThan: 365 * 24 * time.Hour,
}, func(ctx context.Context, _ *user_model.User, config Config) error {
olderThanConfig := config.(*OlderThanConfig)
- return models.DeleteOldActions(olderThanConfig.OlderThan)
+ return activities_model.DeleteOldActions(olderThanConfig.OlderThan)
})
}
@@ -166,7 +166,7 @@ func registerDeleteOldSystemNotices() {
OlderThan: 365 * 24 * time.Hour,
}, func(ctx context.Context, _ *user_model.User, config Config) error {
olderThanConfig := config.(*OlderThanConfig)
- return admin.DeleteOldSystemNotices(olderThanConfig.OlderThan)
+ return system.DeleteOldSystemNotices(olderThanConfig.OlderThan)
})
}
diff --git a/services/forms/auth_form.go b/services/forms/auth_form.go
index 7e7c756752..9064be2cca 100644
--- a/services/forms/auth_form.go
+++ b/services/forms/auth_form.go
@@ -45,7 +45,7 @@ type AuthenticationForm struct {
IsActive bool
IsSyncEnabled bool
SMTPAuth string
- SMTPHost string
+ SMTPAddr string
SMTPPort int
AllowedDomains string
SecurityProtocol int `binding:"Range(0,2)"`
diff --git a/services/forms/repo_branch_form.go b/services/forms/repo_branch_form.go
index f9262aaede..011926092f 100644
--- a/services/forms/repo_branch_form.go
+++ b/services/forms/repo_branch_form.go
@@ -16,6 +16,7 @@ import (
// NewBranchForm form for creating a new branch
type NewBranchForm struct {
NewBranchName string `binding:"Required;MaxSize(100);GitRefName"`
+ CurrentPath string
CreateTag bool
}
diff --git a/services/forms/repo_form.go b/services/forms/repo_form.go
index afecc205f3..c1e9cb3197 100644
--- a/services/forms/repo_form.go
+++ b/services/forms/repo_form.go
@@ -17,7 +17,7 @@ import (
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/web/middleware"
- "code.gitea.io/gitea/routers/utils"
+ "code.gitea.io/gitea/services/webhook"
"gitea.com/go-chi/binding"
)
@@ -34,7 +34,7 @@ type CreateRepoForm struct {
UID int64 `binding:"Required"`
RepoName string `binding:"Required;AlphaDashDot;MaxSize(100)"`
Private bool
- Description string `binding:"MaxSize(255)"`
+ Description string `binding:"MaxSize(2048)"`
DefaultBranch string `binding:"GitRefName;MaxSize(100)"`
AutoInit bool
Gitignores string
@@ -76,7 +76,7 @@ type MigrateRepoForm struct {
LFS bool `json:"lfs"`
LFSEndpoint string `json:"lfs_endpoint"`
Private bool `json:"private"`
- Description string `json:"description" binding:"MaxSize(255)"`
+ Description string `json:"description" binding:"MaxSize(2048)"`
Wiki bool `json:"wiki"`
Milestones bool `json:"milestones"`
Labels bool `json:"labels"`
@@ -116,8 +116,8 @@ func ParseRemoteAddr(remoteAddr, authUsername, authPassword string) (string, err
// RepoSettingForm form for changing repository settings
type RepoSettingForm struct {
RepoName string `binding:"Required;AlphaDashDot;MaxSize(100)"`
- Description string `binding:"MaxSize(255)"`
- Website string `binding:"ValidUrl;MaxSize(255)"`
+ Description string `binding:"MaxSize(2048)"`
+ Website string `binding:"ValidUrl;MaxSize(1024)"`
Interval string
MirrorAddress string
MirrorUsername string
@@ -215,12 +215,12 @@ func (f *ProtectBranchForm) Validate(req *http.Request, errs binding.Errors) bin
return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
}
-// __ __ ___. .__ .__ __
-// / \ / \ ____\_ |__ | |__ | |__ ____ | | __
-// \ \/\/ // __ \| __ \| | \| | \ / _ \| |/ /
-// \ /\ ___/| \_\ \ Y \ Y ( <_> ) <
-// \__/\ / \___ >___ /___| /___| /\____/|__|_ \
-// \/ \/ \/ \/ \/ \/
+// __ __ ___. .__ __
+// / \ / \ ____\_ |__ | |__ ____ ____ | | __
+// \ \/\/ // __ \| __ \| | \ / _ \ / _ \| |/ /
+// \ /\ ___/| \_\ \ Y ( <_> | <_> ) <
+// \__/\ / \___ >___ /___| /\____/ \____/|__|_ \
+// \/ \/ \/ \/ \/
// WebhookForm form for changing web hook
type WebhookForm struct {
@@ -242,6 +242,7 @@ type WebhookForm struct {
PullRequestComment bool
PullRequestReview bool
PullRequestSync bool
+ Wiki bool
Repository bool
Package bool
Active bool
@@ -305,14 +306,16 @@ type NewSlackHookForm struct {
// Validate validates the fields
func (f *NewSlackHookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
ctx := context.GetContext(req)
+ if !webhook.IsValidSlackChannel(strings.TrimSpace(f.Channel)) {
+ errs = append(errs, binding.Error{
+ FieldNames: []string{"Channel"},
+ Classification: "",
+ Message: ctx.Tr("repo.settings.add_webhook.invalid_channel_name"),
+ })
+ }
return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
}
-// HasInvalidChannel validates the channel name is in the right format
-func (f NewSlackHookForm) HasInvalidChannel() bool {
- return !utils.IsValidSlackChannel(f.Channel)
-}
-
// NewDiscordHookForm form for creating discord hook
type NewDiscordHookForm struct {
PayloadURL string `binding:"Required;ValidUrl"`
diff --git a/services/forms/user_form.go b/services/forms/user_form.go
index 405b4a9a49..8ce1d85c57 100644
--- a/services/forms/user_form.go
+++ b/services/forms/user_form.go
@@ -40,7 +40,8 @@ type InstallForm struct {
AppURL string `binding:"Required"`
LogRootPath string `binding:"Required"`
- SMTPHost string
+ SMTPAddr string
+ SMTPPort string
SMTPFrom string
SMTPUser string `binding:"OmitEmpty;MaxSize(254)" locale:"install.mailer_user"`
SMTPPasswd string
@@ -95,6 +96,7 @@ type RegisterForm struct {
Retype string
GRecaptchaResponse string `form:"g-recaptcha-response"`
HcaptchaResponse string `form:"h-captcha-response"`
+ McaptchaResponse string `form:"m-captcha-response"`
}
// Validate validates the fields
diff --git a/services/forms/user_form_auth_openid.go b/services/forms/user_form_auth_openid.go
index fd3368d303..992517f34f 100644
--- a/services/forms/user_form_auth_openid.go
+++ b/services/forms/user_form_auth_openid.go
@@ -31,6 +31,7 @@ type SignUpOpenIDForm struct {
Email string `binding:"Required;Email;MaxSize(254)"`
GRecaptchaResponse string `form:"g-recaptcha-response"`
HcaptchaResponse string `form:"h-captcha-response"`
+ McaptchaResponse string `form:"m-captcha-response"`
}
// Validate validates the fields
diff --git a/services/gitdiff/gitdiff.go b/services/gitdiff/gitdiff.go
index 6e8c149dab..e7362cdcdd 100644
--- a/services/gitdiff/gitdiff.go
+++ b/services/gitdiff/gitdiff.go
@@ -15,7 +15,6 @@ import (
"io"
"net/url"
"os"
- "regexp"
"sort"
"strings"
"time"
@@ -33,6 +32,7 @@ import (
"code.gitea.io/gitea/modules/lfs"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/translation"
"github.com/sergi/go-diff/diffmatchpatch"
stdcharset "golang.org/x/net/html/charset"
@@ -40,7 +40,7 @@ import (
"golang.org/x/text/transform"
)
-// DiffLineType represents the type of a DiffLine.
+// DiffLineType represents the type of DiffLine.
type DiffLineType uint8
// DiffLineType possible values.
@@ -51,7 +51,7 @@ const (
DiffLineSection
)
-// DiffFileType represents the type of a DiffFile.
+// DiffFileType represents the type of DiffFile.
type DiffFileType uint8
// DiffFileType possible values.
@@ -100,12 +100,12 @@ type DiffLineSectionInfo struct {
// BlobExcerptChunkSize represent max lines of excerpt
const BlobExcerptChunkSize = 20
-// GetType returns the type of a DiffLine.
+// GetType returns the type of DiffLine.
func (d *DiffLine) GetType() int {
return int(d.Type)
}
-// CanComment returns whether or not a line can get commented
+// CanComment returns whether a line can get commented
func (d *DiffLine) CanComment() bool {
return len(d.Comments) == 0 && d.Type != DiffLineSection
}
@@ -170,11 +170,11 @@ func getDiffLineSectionInfo(treePath, line string, lastLeftIdx, lastRightIdx int
}
// escape a line's content or return
needed for copy/paste purposes
-func getLineContent(content string) DiffInline {
+func getLineContent(content string, locale translation.Locale) DiffInline {
if len(content) > 0 {
- return DiffInlineWithUnicodeEscape(template.HTML(html.EscapeString(content)))
+ return DiffInlineWithUnicodeEscape(template.HTML(html.EscapeString(content)), locale)
}
- return DiffInline{Content: "
"}
+ return DiffInline{EscapeStatus: &charset.EscapeStatus{}, Content: "
"}
}
// DiffSection represents a section of a DiffFile.
@@ -191,287 +191,13 @@ var (
codeTagSuffix = []byte(`
`)
)
-var (
- unfinishedtagRegex = regexp.MustCompile(`<[^>]*$`)
- trailingSpanRegex = regexp.MustCompile(`]?$`)
- entityRegex = regexp.MustCompile(`&[#]*?[0-9[:alpha:]]*$`)
-)
-
-// shouldWriteInline represents combinations where we manually write inline changes
-func shouldWriteInline(diff diffmatchpatch.Diff, lineType DiffLineType) bool {
- if true &&
- diff.Type == diffmatchpatch.DiffEqual ||
- diff.Type == diffmatchpatch.DiffInsert && lineType == DiffLineAdd ||
- diff.Type == diffmatchpatch.DiffDelete && lineType == DiffLineDel {
- return true
- }
- return false
-}
-
-func fixupBrokenSpans(diffs []diffmatchpatch.Diff) []diffmatchpatch.Diff {
- // Create a new array to store our fixed up blocks
- fixedup := make([]diffmatchpatch.Diff, 0, len(diffs))
-
- // semantically label some numbers
- const insert, delete, equal = 0, 1, 2
-
- // record the positions of the last type of each block in the fixedup blocks
- last := []int{-1, -1, -1}
- operation := []diffmatchpatch.Operation{diffmatchpatch.DiffInsert, diffmatchpatch.DiffDelete, diffmatchpatch.DiffEqual}
-
- // create a writer for insert and deletes
- toWrite := []strings.Builder{
- {},
- {},
- }
-
- // make some flags for insert and delete
- unfinishedTag := []bool{false, false}
- unfinishedEnt := []bool{false, false}
-
- // store stores the provided text in the writer for the typ
- store := func(text string, typ int) {
- (&(toWrite[typ])).WriteString(text)
- }
-
- // hasStored returns true if there is stored content
- hasStored := func(typ int) bool {
- return (&toWrite[typ]).Len() > 0
- }
-
- // stored will return that content
- stored := func(typ int) string {
- return (&toWrite[typ]).String()
- }
-
- // empty will empty the stored content
- empty := func(typ int) {
- (&toWrite[typ]).Reset()
- }
-
- // pop will remove the stored content appending to a diff block for that typ
- pop := func(typ int, fixedup []diffmatchpatch.Diff) []diffmatchpatch.Diff {
- if hasStored(typ) {
- if last[typ] > last[equal] {
- fixedup[last[typ]].Text += stored(typ)
- } else {
- fixedup = append(fixedup, diffmatchpatch.Diff{
- Type: operation[typ],
- Text: stored(typ),
- })
- }
- empty(typ)
- }
- return fixedup
- }
-
- // Now we walk the provided diffs and check the type of each block in turn
- for _, diff := range diffs {
-
- typ := delete // flag for handling insert or delete typs
- switch diff.Type {
- case diffmatchpatch.DiffEqual:
- // First check if there is anything stored
- if hasStored(insert) || hasStored(delete) {
- // There are two reasons for storing content:
- // 1. Unfinished Entity <- Could be more efficient here by not doing this if we're looking for a tag
- if unfinishedEnt[insert] || unfinishedEnt[delete] {
- // we look for a ';' to finish an entity
- idx := strings.IndexRune(diff.Text, ';')
- if idx >= 0 {
- // if we find a ';' store the preceding content to both insert and delete
- store(diff.Text[:idx+1], insert)
- store(diff.Text[:idx+1], delete)
-
- // and remove it from this block
- diff.Text = diff.Text[idx+1:]
-
- // reset the ent flags
- unfinishedEnt[insert] = false
- unfinishedEnt[delete] = false
- } else {
- // otherwise store it all on insert and delete
- store(diff.Text, insert)
- store(diff.Text, delete)
- // and empty this block
- diff.Text = ""
- }
- }
- // 2. Unfinished Tag
- if unfinishedTag[insert] || unfinishedTag[delete] {
- // we look for a '>' to finish a tag
- idx := strings.IndexRune(diff.Text, '>')
- if idx >= 0 {
- store(diff.Text[:idx+1], insert)
- store(diff.Text[:idx+1], delete)
- diff.Text = diff.Text[idx+1:]
- unfinishedTag[insert] = false
- unfinishedTag[delete] = false
- } else {
- store(diff.Text, insert)
- store(diff.Text, delete)
- diff.Text = ""
- }
- }
-
- // If we've completed the required tag/entities
- if !(unfinishedTag[insert] || unfinishedTag[delete] || unfinishedEnt[insert] || unfinishedEnt[delete]) {
- // pop off the stack
- fixedup = pop(insert, fixedup)
- fixedup = pop(delete, fixedup)
- }
-
- // If that has left this diff block empty then shortcut
- if len(diff.Text) == 0 {
- continue
- }
- }
-
- // check if this block ends in an unfinished tag?
- idx := unfinishedtagRegex.FindStringIndex(diff.Text)
- if idx != nil {
- unfinishedTag[insert] = true
- unfinishedTag[delete] = true
- } else {
- // otherwise does it end in an unfinished entity?
- idx = entityRegex.FindStringIndex(diff.Text)
- if idx != nil {
- unfinishedEnt[insert] = true
- unfinishedEnt[delete] = true
- }
- }
-
- // If there is an unfinished component
- if idx != nil {
- // Store the fragment
- store(diff.Text[idx[0]:], insert)
- store(diff.Text[idx[0]:], delete)
- // and remove it from this block
- diff.Text = diff.Text[:idx[0]]
- }
-
- // If that hasn't left the block empty
- if len(diff.Text) > 0 {
- // store the position of the last equal block and store it in our diffs
- last[equal] = len(fixedup)
- fixedup = append(fixedup, diff)
- }
- continue
- case diffmatchpatch.DiffInsert:
- typ = insert
- fallthrough
- case diffmatchpatch.DiffDelete:
- // First check if there is anything stored for this type
- if hasStored(typ) {
- // if there is prepend it to this block, empty the storage and reset our flags
- diff.Text = stored(typ) + diff.Text
- empty(typ)
- unfinishedEnt[typ] = false
- unfinishedTag[typ] = false
- }
-
- // check if this block ends in an unfinished tag
- idx := unfinishedtagRegex.FindStringIndex(diff.Text)
- if idx != nil {
- unfinishedTag[typ] = true
- } else {
- // otherwise does it end in an unfinished entity
- idx = entityRegex.FindStringIndex(diff.Text)
- if idx != nil {
- unfinishedEnt[typ] = true
- }
- }
-
- // If there is an unfinished component
- if idx != nil {
- // Store the fragment
- store(diff.Text[idx[0]:], typ)
- // and remove it from this block
- diff.Text = diff.Text[:idx[0]]
- }
-
- // If that hasn't left the block empty
- if len(diff.Text) > 0 {
- // if the last block of this type was after the last equal block
- if last[typ] > last[equal] {
- // store this blocks content on that block
- fixedup[last[typ]].Text += diff.Text
- } else {
- // otherwise store the position of the last block of this type and store the block
- last[typ] = len(fixedup)
- fixedup = append(fixedup, diff)
- }
- }
- continue
- }
- }
-
- // pop off any remaining stored content
- fixedup = pop(insert, fixedup)
- fixedup = pop(delete, fixedup)
-
- return fixedup
-}
-
-func diffToHTML(fileName string, diffs []diffmatchpatch.Diff, lineType DiffLineType) DiffInline {
+func diffToHTML(lineWrapperTags []string, diffs []diffmatchpatch.Diff, lineType DiffLineType) string {
buf := bytes.NewBuffer(nil)
- match := ""
-
- diffs = fixupBrokenSpans(diffs)
-
+ // restore the line wrapper tags and , if necessary
+ for _, tag := range lineWrapperTags {
+ buf.WriteString(tag)
+ }
for _, diff := range diffs {
- if shouldWriteInline(diff, lineType) {
- if len(match) > 0 {
- diff.Text = match + diff.Text
- match = ""
- }
- // Chroma HTML syntax highlighting is done before diffing individual lines in order to maintain consistency.
- // Since inline changes might split in the middle of a chroma span tag or HTML entity, make we manually put it back together
- // before writing so we don't try insert added/removed code spans in the middle of one of those
- // and create broken HTML. This is done by moving incomplete HTML forward until it no longer matches our pattern of
- // a line ending with an incomplete HTML entity or partial/opening .
-
- // EX:
- // diffs[{Type: dmp.DiffDelete, Text: "language}]
-
- // After first iteration
- // diffs[{Type: dmp.DiffDelete, Text: "language"}, //write out
- // {Type: dmp.DiffEqual, Text: ",}]
-
- // After second iteration
- // {Type: dmp.DiffEqual, Text: ""}, // write out
- // {Type: dmp.DiffDelete, Text: ",}]
-
- // Final
- // {Type: dmp.DiffDelete, Text: ",}]
- // end up writing ,
- // Instead of lass="p",
-
- m := trailingSpanRegex.FindStringSubmatchIndex(diff.Text)
- if m != nil {
- match = diff.Text[m[0]:m[1]]
- diff.Text = strings.TrimSuffix(diff.Text, match)
- }
- m = entityRegex.FindStringSubmatchIndex(diff.Text)
- if m != nil {
- match = diff.Text[m[0]:m[1]]
- diff.Text = strings.TrimSuffix(diff.Text, match)
- }
- // Print an existing closing span first before opening added/remove-code span so it doesn't unintentionally close it
- if strings.HasPrefix(diff.Text, "") {
- buf.WriteString("")
- diff.Text = strings.TrimPrefix(diff.Text, "")
- }
- // If we weren't able to fix it then this should avoid broken HTML by not inserting more spans below
- // The previous/next diff section will contain the rest of the tag that is missing here
- if strings.Count(diff.Text, "<") != strings.Count(diff.Text, ">") {
- buf.WriteString(diff.Text)
- continue
- }
- }
switch {
case diff.Type == diffmatchpatch.DiffEqual:
buf.WriteString(diff.Text)
@@ -485,7 +211,10 @@ func diffToHTML(fileName string, diffs []diffmatchpatch.Diff, lineType DiffLineT
buf.Write(codeTagSuffix)
}
}
- return DiffInlineWithUnicodeEscape(template.HTML(buf.String()))
+ for range lineWrapperTags {
+ buf.WriteString("")
+ }
+ return buf.String()
}
// GetLine gets a specific line by type (add or del) and file line number
@@ -539,26 +268,26 @@ func init() {
// DiffInline is a struct that has a content and escape status
type DiffInline struct {
- EscapeStatus charset.EscapeStatus
+ EscapeStatus *charset.EscapeStatus
Content template.HTML
}
// DiffInlineWithUnicodeEscape makes a DiffInline with hidden unicode characters escaped
-func DiffInlineWithUnicodeEscape(s template.HTML) DiffInline {
- status, content := charset.EscapeControlString(string(s))
+func DiffInlineWithUnicodeEscape(s template.HTML, locale translation.Locale) DiffInline {
+ status, content := charset.EscapeControlHTML(string(s), locale)
return DiffInline{EscapeStatus: status, Content: template.HTML(content)}
}
// DiffInlineWithHighlightCode makes a DiffInline with code highlight and hidden unicode characters escaped
-func DiffInlineWithHighlightCode(fileName, language, code string) DiffInline {
- status, content := charset.EscapeControlString(highlight.Code(fileName, language, code))
+func DiffInlineWithHighlightCode(fileName, language, code string, locale translation.Locale) DiffInline {
+ status, content := charset.EscapeControlHTML(highlight.Code(fileName, language, code), locale)
return DiffInline{EscapeStatus: status, Content: template.HTML(content)}
}
// GetComputedInlineDiffFor computes inline diff for the given line.
-func (diffSection *DiffSection) GetComputedInlineDiffFor(diffLine *DiffLine) DiffInline {
+func (diffSection *DiffSection) GetComputedInlineDiffFor(diffLine *DiffLine, locale translation.Locale) DiffInline {
if setting.Git.DisableDiffHighlight {
- return getLineContent(diffLine.Content[1:])
+ return getLineContent(diffLine.Content[1:], locale)
}
var (
@@ -575,32 +304,34 @@ func (diffSection *DiffSection) GetComputedInlineDiffFor(diffLine *DiffLine) Dif
// try to find equivalent diff line. ignore, otherwise
switch diffLine.Type {
case DiffLineSection:
- return getLineContent(diffLine.Content[1:])
+ return getLineContent(diffLine.Content[1:], locale)
case DiffLineAdd:
compareDiffLine = diffSection.GetLine(DiffLineDel, diffLine.RightIdx)
if compareDiffLine == nil {
- return DiffInlineWithHighlightCode(diffSection.FileName, language, diffLine.Content[1:])
+ return DiffInlineWithHighlightCode(diffSection.FileName, language, diffLine.Content[1:], locale)
}
diff1 = compareDiffLine.Content
diff2 = diffLine.Content
case DiffLineDel:
compareDiffLine = diffSection.GetLine(DiffLineAdd, diffLine.LeftIdx)
if compareDiffLine == nil {
- return DiffInlineWithHighlightCode(diffSection.FileName, language, diffLine.Content[1:])
+ return DiffInlineWithHighlightCode(diffSection.FileName, language, diffLine.Content[1:], locale)
}
diff1 = diffLine.Content
diff2 = compareDiffLine.Content
default:
if strings.IndexByte(" +-", diffLine.Content[0]) > -1 {
- return DiffInlineWithHighlightCode(diffSection.FileName, language, diffLine.Content[1:])
+ return DiffInlineWithHighlightCode(diffSection.FileName, language, diffLine.Content[1:], locale)
}
- return DiffInlineWithHighlightCode(diffSection.FileName, language, diffLine.Content)
+ return DiffInlineWithHighlightCode(diffSection.FileName, language, diffLine.Content, locale)
}
- diffRecord := diffMatchPatch.DiffMain(highlight.Code(diffSection.FileName, language, diff1[1:]), highlight.Code(diffSection.FileName, language, diff2[1:]), true)
- diffRecord = diffMatchPatch.DiffCleanupEfficiency(diffRecord)
-
- return diffToHTML(diffSection.FileName, diffRecord, diffLine.Type)
+ hcd := newHighlightCodeDiff()
+ diffRecord := hcd.diffWithHighlight(diffSection.FileName, language, diff1[1:], diff2[1:])
+ // it seems that Gitea doesn't need the line wrapper of Chroma, so do not add them back
+ // if the line wrappers are still needed in the future, it can be added back by "diffToHTML(hcd.lineWrapperTags. ...)"
+ diffHTML := diffToHTML(nil, diffRecord, diffLine.Type)
+ return DiffInlineWithUnicodeEscape(template.HTML(diffHTML), locale)
}
// DiffFile represents a file diff.
@@ -1289,7 +1020,7 @@ func readFileName(rd *strings.Reader) (string, bool) {
if char == '"' {
fmt.Fscanf(rd, "%q ", &name)
if len(name) == 0 {
- log.Error("Reader has no file name: %v", rd)
+ log.Error("Reader has no file name: reader=%+v", rd)
return "", true
}
@@ -1311,7 +1042,7 @@ func readFileName(rd *strings.Reader) (string, bool) {
}
}
if len(name) < 2 {
- log.Error("Unable to determine name from reader: %v", rd)
+ log.Error("Unable to determine name from reader: reader=%+v", rd)
return "", true
}
return name[2:], ambiguity
@@ -1447,8 +1178,6 @@ func GetDiff(gitRepo *git.Repository, opts *DiffOptions, files ...string) (*Diff
} else if language, has := attrs["gitlab-language"]; has && language != "unspecified" && language != "" {
diffFile.Language = language
}
- } else {
- log.Error("Unexpected error: %v", err)
}
}
diff --git a/services/gitdiff/gitdiff_test.go b/services/gitdiff/gitdiff_test.go
index caca0e91d8..dfdd4df9c4 100644
--- a/services/gitdiff/gitdiff_test.go
+++ b/services/gitdiff/gitdiff_test.go
@@ -7,7 +7,6 @@ package gitdiff
import (
"fmt"
- "html/template"
"strconv"
"strings"
"testing"
@@ -17,93 +16,27 @@ import (
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/highlight"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/setting"
dmp "github.com/sergi/go-diff/diffmatchpatch"
"github.com/stretchr/testify/assert"
- "gopkg.in/ini.v1"
)
-func assertEqual(t *testing.T, s1 string, s2 template.HTML) {
- if s1 != string(s2) {
- t.Errorf("Did not receive expected results:\nExpected: %s\nActual: %s", s1, s2)
- }
-}
-
func TestDiffToHTML(t *testing.T) {
- setting.Cfg = ini.Empty()
- assertEqual(t, "foo bar biz", diffToHTML("", []dmp.Diff{
+ assert.Equal(t, "foo bar biz", diffToHTML(nil, []dmp.Diff{
{Type: dmp.DiffEqual, Text: "foo "},
{Type: dmp.DiffInsert, Text: "bar"},
{Type: dmp.DiffDelete, Text: " baz"},
{Type: dmp.DiffEqual, Text: " biz"},
- }, DiffLineAdd).Content)
+ }, DiffLineAdd))
- assertEqual(t, "foo bar biz", diffToHTML("", []dmp.Diff{
+ assert.Equal(t, "foo bar biz", diffToHTML(nil, []dmp.Diff{
{Type: dmp.DiffEqual, Text: "foo "},
{Type: dmp.DiffDelete, Text: "bar"},
{Type: dmp.DiffInsert, Text: " baz"},
{Type: dmp.DiffEqual, Text: " biz"},
- }, DiffLineDel).Content)
-
- assertEqual(t, "if !nohl && (lexer != nil || r.GuessLanguage) {", diffToHTML("", []dmp.Diff{
- {Type: dmp.DiffEqual, Text: "if !nohl && (lexer != nil"},
- {Type: dmp.DiffInsert, Text: " || r.GuessLanguage)"},
- {Type: dmp.DiffEqual, Text: " {"},
- }, DiffLineAdd).Content)
-
- assertEqual(t, "tagURL := fmt.Sprintf("## [%s](%s/%s/%s/%s?q=&type=all&state=closed&milestone=%d) - %s", ge.Milestone\", ge.BaseURL, ge.Owner, ge.Repo, from, milestoneID, time.Now().Format("2006-01-02"))", diffToHTML("", []dmp.Diff{
- {Type: dmp.DiffEqual, Text: "tagURL := fmt.Sprintf("## [%s](%s/%s/%s/%s?q=&type=all&state=closed&milestone=%d) - %s", ge.Milestone\""},
- {Type: dmp.DiffInsert, Text: "f\">getGiteaTagURL(client"},
- {Type: dmp.DiffEqual, Text: ", ge.BaseURL, ge.Owner, ge.Repo, "},
- {Type: dmp.DiffDelete, Text: "from, milestoneID, time.Now().Format("2006-01-02")"},
- {Type: dmp.DiffInsert, Text: "ge.Milestone, from, milestoneID"},
- {Type: dmp.DiffEqual, Text: ")"},
- }, DiffLineDel).Content)
-
- assertEqual(t, "r.WrapperRenderer(w, language, true, attrs, false)", diffToHTML("", []dmp.Diff{
- {Type: dmp.DiffEqual, Text: "r.WrapperRenderer(w, "},
- {Type: dmp.DiffDelete, Text: "language, true, attrs"},
- {Type: dmp.DiffEqual, Text: ", false)"},
- }, DiffLineDel).Content)
-
- assertEqual(t, "language, true, attrs, false)", diffToHTML("", []dmp.Diff{
- {Type: dmp.DiffInsert, Text: "language, true, attrs"},
- {Type: dmp.DiffEqual, Text: ", false)"},
- }, DiffLineAdd).Content)
-
- assertEqual(t, "print("// ", sys.argv)", diffToHTML("", []dmp.Diff{
- {Type: dmp.DiffEqual, Text: "print"},
- {Type: dmp.DiffInsert, Text: "("},
- {Type: dmp.DiffEqual, Text: ""// ", sys.argv"},
- {Type: dmp.DiffInsert, Text: ")"},
- }, DiffLineAdd).Content)
-
- assertEqual(t, "sh 'useradd -u $(stat -c "%u" .gitignore) jenkins'", diffToHTML("", []dmp.Diff{
- {Type: dmp.DiffEqual, Text: "sh "},
- {Type: dmp.DiffDelete, Text: "4;useradd -u 111 jenkins""},
- {Type: dmp.DiffInsert, Text: "9;useradd -u $(stat -c "%u" .gitignore) jenkins'"},
- {Type: dmp.DiffEqual, Text: ";"},
- }, DiffLineAdd).Content)
-
- assertEqual(t, " <h4 class="release-list-title df ac">", diffToHTML("", []dmp.Diff{
- {Type: dmp.DiffEqual, Text: " <h"},
- {Type: dmp.DiffInsert, Text: "4 class="},
- {Type: dmp.DiffEqual, Text: "3"},
- {Type: dmp.DiffInsert, Text: "4;release-list-title df ac""},
- {Type: dmp.DiffEqual, Text: ">"},
- }, DiffLineAdd).Content)
+ }, DiffLineDel))
}
func TestParsePatch_skipTo(t *testing.T) {
@@ -592,7 +525,6 @@ index 0000000..6bb8f39
if err != nil {
t.Errorf("ParsePatch failed: %s", err)
}
- println(result)
diff2 := `diff --git "a/A \\ B" "b/A \\ B"
--- "a/A \\ B"
@@ -669,8 +601,8 @@ func setupDefaultDiff() *Diff {
func TestDiff_LoadComments(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 2}).(*issues_model.Issue)
- user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User)
+ issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 2})
+ user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
diff := setupDefaultDiff()
assert.NoError(t, diff.LoadComments(db.DefaultContext, issue, user))
assert.Len(t, diff.Files[0].Sections[0].Lines[0].Comments, 2)
@@ -712,18 +644,6 @@ func TestGetDiffRangeWithWhitespaceBehavior(t *testing.T) {
}
}
-func TestDiffToHTML_14231(t *testing.T) {
- setting.Cfg = ini.Empty()
- diffRecord := diffMatchPatch.DiffMain(highlight.Code("main.v", "", " run()\n"), highlight.Code("main.v", "", " run(db)\n"), true)
- diffRecord = diffMatchPatch.DiffCleanupEfficiency(diffRecord)
-
- expected := ` run(db)
-`
- output := diffToHTML("main.v", diffRecord, DiffLineAdd)
-
- assertEqual(t, expected, output.Content)
-}
-
func TestNoCrashes(t *testing.T) {
type testcase struct {
gitdiff string
diff --git a/services/gitdiff/highlightdiff.go b/services/gitdiff/highlightdiff.go
new file mode 100644
index 0000000000..4ceada4d7e
--- /dev/null
+++ b/services/gitdiff/highlightdiff.go
@@ -0,0 +1,223 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package gitdiff
+
+import (
+ "strings"
+
+ "code.gitea.io/gitea/modules/highlight"
+
+ "github.com/sergi/go-diff/diffmatchpatch"
+)
+
+// token is a html tag or entity, eg: "", "", "<"
+func extractHTMLToken(s string) (before, token, after string, valid bool) {
+ for pos1 := 0; pos1 < len(s); pos1++ {
+ if s[pos1] == '<' {
+ pos2 := strings.IndexByte(s[pos1:], '>')
+ if pos2 == -1 {
+ return "", "", s, false
+ }
+ return s[:pos1], s[pos1 : pos1+pos2+1], s[pos1+pos2+1:], true
+ } else if s[pos1] == '&' {
+ pos2 := strings.IndexByte(s[pos1:], ';')
+ if pos2 == -1 {
+ return "", "", s, false
+ }
+ return s[:pos1], s[pos1 : pos1+pos2+1], s[pos1+pos2+1:], true
+ }
+ }
+ return "", "", s, true
+}
+
+// highlightCodeDiff is used to do diff with highlighted HTML code.
+// It totally depends on Chroma's valid HTML output and its structure, do not use these functions for other purposes.
+// The HTML tags and entities will be replaced by Unicode placeholders: "{TEXT}" => "\uE000{TEXT}\uE001"
+// These Unicode placeholders are friendly to the diff.
+// Then after diff, the placeholders in diff result will be recovered to the HTML tags and entities.
+// It's guaranteed that the tags in final diff result are paired correctly.
+type highlightCodeDiff struct {
+ placeholderBegin rune
+ placeholderMaxCount int
+ placeholderIndex int
+ placeholderTokenMap map[rune]string
+ tokenPlaceholderMap map[string]rune
+
+ placeholderOverflowCount int
+
+ lineWrapperTags []string
+}
+
+func newHighlightCodeDiff() *highlightCodeDiff {
+ return &highlightCodeDiff{
+ placeholderBegin: rune(0x100000), // Plane 16: Supplementary Private Use Area B (U+100000..U+10FFFD)
+ placeholderMaxCount: 64000,
+ placeholderTokenMap: map[rune]string{},
+ tokenPlaceholderMap: map[string]rune{},
+ }
+}
+
+// nextPlaceholder returns 0 if no more placeholder can be used
+// the diff is done line by line, usually there are only a few (no more than 10) placeholders in one line
+// so the placeholderMaxCount is impossible to be exhausted in real cases.
+func (hcd *highlightCodeDiff) nextPlaceholder() rune {
+ for hcd.placeholderIndex < hcd.placeholderMaxCount {
+ r := hcd.placeholderBegin + rune(hcd.placeholderIndex)
+ hcd.placeholderIndex++
+ // only use non-existing (not used by code) rune as placeholders
+ if _, ok := hcd.placeholderTokenMap[r]; !ok {
+ return r
+ }
+ }
+ return 0 // no more available placeholder
+}
+
+func (hcd *highlightCodeDiff) isInPlaceholderRange(r rune) bool {
+ return hcd.placeholderBegin <= r && r < hcd.placeholderBegin+rune(hcd.placeholderMaxCount)
+}
+
+func (hcd *highlightCodeDiff) collectUsedRunes(code string) {
+ for _, r := range code {
+ if hcd.isInPlaceholderRange(r) {
+ // put the existing rune (used by code) in map, then this rune won't be used a placeholder anymore.
+ hcd.placeholderTokenMap[r] = ""
+ }
+ }
+}
+
+func (hcd *highlightCodeDiff) diffWithHighlight(filename, language, codeA, codeB string) []diffmatchpatch.Diff {
+ hcd.collectUsedRunes(codeA)
+ hcd.collectUsedRunes(codeB)
+
+ highlightCodeA := highlight.Code(filename, language, codeA)
+ highlightCodeB := highlight.Code(filename, language, codeB)
+
+ highlightCodeA = hcd.convertToPlaceholders(highlightCodeA)
+ highlightCodeB = hcd.convertToPlaceholders(highlightCodeB)
+
+ diffs := diffMatchPatch.DiffMain(highlightCodeA, highlightCodeB, true)
+ diffs = diffMatchPatch.DiffCleanupEfficiency(diffs)
+
+ for i := range diffs {
+ hcd.recoverOneDiff(&diffs[i])
+ }
+ return diffs
+}
+
+// convertToPlaceholders totally depends on Chroma's valid HTML output and its structure, do not use these functions for other purposes.
+func (hcd *highlightCodeDiff) convertToPlaceholders(htmlCode string) string {
+ var tagStack []string
+ res := strings.Builder{}
+
+ firstRunForLineTags := hcd.lineWrapperTags == nil
+
+ var beforeToken, token string
+ var valid bool
+
+ // the standard chroma highlight HTML is " ... "
+ for {
+ beforeToken, token, htmlCode, valid = extractHTMLToken(htmlCode)
+ if !valid || token == "" {
+ break
+ }
+ // write the content before the token into result string, and consume the token in the string
+ res.WriteString(beforeToken)
+
+ // the line wrapper tags should be removed before diff
+ if strings.HasPrefix(token, `")
+ continue
+ }
+
+ var tokenInMap string
+ if strings.HasSuffix(token, "") { // for closing tag
+ if len(tagStack) == 0 {
+ break // invalid diff result, no opening tag but see closing tag
+ }
+ // make sure the closing tag in map is related to the open tag, to make the diff algorithm can match the opening/closing tags
+ // the closing tag will be recorded in the map by key "" for ""
+ tokenInMap = token + ""
+ tagStack = tagStack[:len(tagStack)-1]
+ } else if token[0] == '<' { // for opening tag
+ tokenInMap = token
+ tagStack = append(tagStack, token)
+ } else if token[0] == '&' { // for html entity
+ tokenInMap = token
+ } // else: impossible
+
+ // remember the placeholder and token in the map
+ placeholder, ok := hcd.tokenPlaceholderMap[tokenInMap]
+ if !ok {
+ placeholder = hcd.nextPlaceholder()
+ if placeholder != 0 {
+ hcd.tokenPlaceholderMap[tokenInMap] = placeholder
+ hcd.placeholderTokenMap[placeholder] = tokenInMap
+ }
+ }
+
+ if placeholder != 0 {
+ res.WriteRune(placeholder) // use the placeholder to replace the token
+ } else {
+ // unfortunately, all private use runes has been exhausted, no more placeholder could be used, no more converting
+ // usually, the exhausting won't occur in real cases, the magnitude of used placeholders is not larger than that of the CSS classes outputted by chroma.
+ hcd.placeholderOverflowCount++
+ if strings.HasPrefix(token, "&") {
+ // when the token is a html entity, something must be outputted even if there is no placeholder.
+ res.WriteRune(0xFFFD) // replacement character TODO: how to handle this case more gracefully?
+ res.WriteString(token[1:]) // still output the entity code part, otherwise there will be no diff result.
+ }
+ }
+ }
+
+ // write the remaining string
+ res.WriteString(htmlCode)
+ return res.String()
+}
+
+func (hcd *highlightCodeDiff) recoverOneDiff(diff *diffmatchpatch.Diff) {
+ sb := strings.Builder{}
+ var tagStack []string
+
+ for _, r := range diff.Text {
+ token, ok := hcd.placeholderTokenMap[r]
+ if !ok || token == "" {
+ sb.WriteRune(r) // if the rune is not a placeholder, write it as it is
+ continue
+ }
+ var tokenToRecover string
+ if strings.HasPrefix(token, "") { // for closing tag
+ // only get the tag itself, ignore the trailing comment (for how the comment is generated, see the code in `convert` function)
+ tokenToRecover = token[:strings.IndexByte(token, '>')+1]
+ if len(tagStack) == 0 {
+ continue // if no opening tag in stack yet, skip the closing tag
+ }
+ tagStack = tagStack[:len(tagStack)-1]
+ } else if token[0] == '<' { // for opening tag
+ tokenToRecover = token
+ tagStack = append(tagStack, token)
+ } else if token[0] == '&' { // for html entity
+ tokenToRecover = token
+ } // else: impossible
+ sb.WriteString(tokenToRecover)
+ }
+
+ if len(tagStack) > 0 {
+ // close all opening tags
+ for i := len(tagStack) - 1; i >= 0; i-- {
+ tagToClose := tagStack[i]
+ // get the closing tag "" from "" or ""
+ pos := strings.IndexAny(tagToClose, " >")
+ if pos != -1 {
+ sb.WriteString("" + tagToClose[1:pos] + ">")
+ } // else: impossible. every tag was pushed into the stack by the code above and is valid HTML opening tag
+ }
+ }
+
+ diff.Text = sb.String()
+}
diff --git a/services/gitdiff/highlightdiff_test.go b/services/gitdiff/highlightdiff_test.go
new file mode 100644
index 0000000000..205f9a0773
--- /dev/null
+++ b/services/gitdiff/highlightdiff_test.go
@@ -0,0 +1,126 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package gitdiff
+
+import (
+ "fmt"
+ "strings"
+ "testing"
+
+ "github.com/sergi/go-diff/diffmatchpatch"
+ "github.com/stretchr/testify/assert"
+)
+
+func TestDiffWithHighlight(t *testing.T) {
+ hcd := newHighlightCodeDiff()
+ diffs := hcd.diffWithHighlight(
+ "main.v", "",
+ " run('<>')\n",
+ " run(db)\n",
+ )
+
+ expected := ` run('<>')`
+ output := diffToHTML(nil, diffs, DiffLineDel)
+ assert.Equal(t, expected, output)
+
+ expected = ` run(db)`
+ output = diffToHTML(nil, diffs, DiffLineAdd)
+ assert.Equal(t, expected, output)
+
+ hcd = newHighlightCodeDiff()
+ hcd.placeholderTokenMap['O'] = ""
+ hcd.placeholderTokenMap['C'] = ""
+ diff := diffmatchpatch.Diff{}
+
+ diff.Text = "OC"
+ hcd.recoverOneDiff(&diff)
+ assert.Equal(t, "", diff.Text)
+
+ diff.Text = "O"
+ hcd.recoverOneDiff(&diff)
+ assert.Equal(t, "", diff.Text)
+
+ diff.Text = "C"
+ hcd.recoverOneDiff(&diff)
+ assert.Equal(t, "", diff.Text)
+}
+
+func TestDiffWithHighlightPlaceholder(t *testing.T) {
+ hcd := newHighlightCodeDiff()
+ diffs := hcd.diffWithHighlight(
+ "main.js", "",
+ "a='\U00100000'",
+ "a='\U0010FFFD''",
+ )
+ assert.Equal(t, "", hcd.placeholderTokenMap[0x00100000])
+ assert.Equal(t, "", hcd.placeholderTokenMap[0x0010FFFD])
+
+ expected := fmt.Sprintf(`a='%s'`, "\U00100000")
+ output := diffToHTML(hcd.lineWrapperTags, diffs, DiffLineDel)
+ assert.Equal(t, expected, output)
+
+ hcd = newHighlightCodeDiff()
+ diffs = hcd.diffWithHighlight(
+ "main.js", "",
+ "a='\U00100000'",
+ "a='\U0010FFFD'",
+ )
+ expected = fmt.Sprintf(`a='%s'`, "\U0010FFFD")
+ output = diffToHTML(nil, diffs, DiffLineAdd)
+ assert.Equal(t, expected, output)
+}
+
+func TestDiffWithHighlightPlaceholderExhausted(t *testing.T) {
+ hcd := newHighlightCodeDiff()
+ hcd.placeholderMaxCount = 0
+ diffs := hcd.diffWithHighlight(
+ "main.js", "",
+ "'",
+ ``,
+ )
+ output := diffToHTML(nil, diffs, DiffLineDel)
+ expected := fmt.Sprintf(`%s#39;`, "\uFFFD")
+ assert.Equal(t, expected, output)
+
+ hcd = newHighlightCodeDiff()
+ hcd.placeholderMaxCount = 0
+ diffs = hcd.diffWithHighlight(
+ "main.js", "",
+ "a < b",
+ "a > b",
+ )
+ output = diffToHTML(nil, diffs, DiffLineDel)
+ expected = fmt.Sprintf(`a %slt; b`, "\uFFFD")
+ assert.Equal(t, expected, output)
+
+ output = diffToHTML(nil, diffs, DiffLineAdd)
+ expected = fmt.Sprintf(`a %sgt; b`, "\uFFFD")
+ assert.Equal(t, expected, output)
+}
+
+func TestDiffWithHighlightTagMatch(t *testing.T) {
+ totalOverflow := 0
+ for i := 0; i < 100; i++ {
+ hcd := newHighlightCodeDiff()
+ hcd.placeholderMaxCount = i
+ diffs := hcd.diffWithHighlight(
+ "main.js", "",
+ "a='1'",
+ "b='2'",
+ )
+ totalOverflow += hcd.placeholderOverflowCount
+
+ output := diffToHTML(nil, diffs, DiffLineDel)
+ c1 := strings.Count(output, " 0 && len(ref.Name) > 0 {
- refRepo, err = models.GetRepositoryFromMatch(ref.Owner, ref.Name)
+ refRepo, err = repo_model.GetRepositoryByOwnerAndName(ref.Owner, ref.Name)
if err != nil {
+ if repo_model.IsErrRepoNotExist(err) {
+ log.Warn("Repository referenced in commit but does not exist: %v", err)
+ } else {
+ log.Error("repo_model.GetRepositoryByOwnerAndName: %v", err)
+ }
continue
}
} else {
@@ -139,10 +145,9 @@ func UpdateIssuesCommit(doer *user_model.User, repo *repo_model.Repository, comm
}
key := markKey{ID: refIssue.ID, Action: ref.Action}
- if refMarked[key] {
+ if !refMarked.Add(key) {
continue
}
- refMarked[key] = true
// FIXME: this kind of condition is all over the code, it should be consolidated in a single place
canclose := perm.IsAdmin() || perm.IsOwner() || perm.CanWriteIssuesOrPulls(refIssue.IsPull) || refIssue.PosterID == doer.ID
diff --git a/services/issue/commit_test.go b/services/issue/commit_test.go
index ce3f913627..8469bf1ac1 100644
--- a/services/issue/commit_test.go
+++ b/services/issue/commit_test.go
@@ -7,7 +7,7 @@ package issue
import (
"testing"
- "code.gitea.io/gitea/models"
+ activities_model "code.gitea.io/gitea/models/activities"
issues_model "code.gitea.io/gitea/models/issues"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
@@ -47,8 +47,8 @@ func TestUpdateIssuesCommit(t *testing.T) {
},
}
- user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository)
+ user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
repo.Owner = user
commentBean := &issues_model.Comment{
@@ -64,7 +64,7 @@ func TestUpdateIssuesCommit(t *testing.T) {
assert.NoError(t, UpdateIssuesCommit(user, repo, pushCommits, repo.DefaultBranch))
unittest.AssertExistsAndLoadBean(t, commentBean)
unittest.AssertExistsAndLoadBean(t, issueBean, "is_closed=1")
- unittest.CheckConsistencyFor(t, &models.Action{})
+ unittest.CheckConsistencyFor(t, &activities_model.Action{})
// Test that push to a non-default branch closes no issue.
pushCommits = []*repository.PushCommit{
@@ -77,7 +77,7 @@ func TestUpdateIssuesCommit(t *testing.T) {
Message: "close #1",
},
}
- repo = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3}).(*repo_model.Repository)
+ repo = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3})
commentBean = &issues_model.Comment{
Type: issues_model.CommentTypeCommitRef,
CommitSHA: "abcdef1",
@@ -91,7 +91,7 @@ func TestUpdateIssuesCommit(t *testing.T) {
assert.NoError(t, UpdateIssuesCommit(user, repo, pushCommits, "non-existing-branch"))
unittest.AssertExistsAndLoadBean(t, commentBean)
unittest.AssertNotExistsBean(t, issueBean, "is_closed=1")
- unittest.CheckConsistencyFor(t, &models.Action{})
+ unittest.CheckConsistencyFor(t, &activities_model.Action{})
pushCommits = []*repository.PushCommit{
{
@@ -103,7 +103,7 @@ func TestUpdateIssuesCommit(t *testing.T) {
Message: "close " + setting.AppURL + repo.FullName() + "/pulls/1",
},
}
- repo = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3}).(*repo_model.Repository)
+ repo = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3})
commentBean = &issues_model.Comment{
Type: issues_model.CommentTypeCommitRef,
CommitSHA: "abcdef3",
@@ -117,7 +117,7 @@ func TestUpdateIssuesCommit(t *testing.T) {
assert.NoError(t, UpdateIssuesCommit(user, repo, pushCommits, repo.DefaultBranch))
unittest.AssertExistsAndLoadBean(t, commentBean)
unittest.AssertExistsAndLoadBean(t, issueBean, "is_closed=1")
- unittest.CheckConsistencyFor(t, &models.Action{})
+ unittest.CheckConsistencyFor(t, &activities_model.Action{})
}
func TestUpdateIssuesCommit_Colon(t *testing.T) {
@@ -133,8 +133,8 @@ func TestUpdateIssuesCommit_Colon(t *testing.T) {
},
}
- user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository)
+ user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
repo.Owner = user
issueBean := &issues_model.Issue{RepoID: repo.ID, Index: 4}
@@ -142,12 +142,12 @@ func TestUpdateIssuesCommit_Colon(t *testing.T) {
unittest.AssertNotExistsBean(t, &issues_model.Issue{RepoID: repo.ID, Index: 2}, "is_closed=1")
assert.NoError(t, UpdateIssuesCommit(user, repo, pushCommits, repo.DefaultBranch))
unittest.AssertExistsAndLoadBean(t, issueBean, "is_closed=1")
- unittest.CheckConsistencyFor(t, &models.Action{})
+ unittest.CheckConsistencyFor(t, &activities_model.Action{})
}
func TestUpdateIssuesCommit_Issue5957(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
+ user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
// Test that push to a non-default branch closes an issue.
pushCommits := []*repository.PushCommit{
@@ -161,7 +161,7 @@ func TestUpdateIssuesCommit_Issue5957(t *testing.T) {
},
}
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}).(*repo_model.Repository)
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2})
commentBean := &issues_model.Comment{
Type: issues_model.CommentTypeCommitRef,
CommitSHA: "abcdef1",
@@ -176,12 +176,12 @@ func TestUpdateIssuesCommit_Issue5957(t *testing.T) {
assert.NoError(t, UpdateIssuesCommit(user, repo, pushCommits, "non-existing-branch"))
unittest.AssertExistsAndLoadBean(t, commentBean)
unittest.AssertExistsAndLoadBean(t, issueBean, "is_closed=1")
- unittest.CheckConsistencyFor(t, &models.Action{})
+ unittest.CheckConsistencyFor(t, &activities_model.Action{})
}
func TestUpdateIssuesCommit_AnotherRepo(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
+ user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
// Test that a push to default branch closes issue in another repo
// If the user also has push permissions to that repo
@@ -196,7 +196,7 @@ func TestUpdateIssuesCommit_AnotherRepo(t *testing.T) {
},
}
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}).(*repo_model.Repository)
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2})
commentBean := &issues_model.Comment{
Type: issues_model.CommentTypeCommitRef,
CommitSHA: "abcdef1",
@@ -211,12 +211,12 @@ func TestUpdateIssuesCommit_AnotherRepo(t *testing.T) {
assert.NoError(t, UpdateIssuesCommit(user, repo, pushCommits, repo.DefaultBranch))
unittest.AssertExistsAndLoadBean(t, commentBean)
unittest.AssertExistsAndLoadBean(t, issueBean, "is_closed=1")
- unittest.CheckConsistencyFor(t, &models.Action{})
+ unittest.CheckConsistencyFor(t, &activities_model.Action{})
}
func TestUpdateIssuesCommit_AnotherRepo_FullAddress(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
+ user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
// Test that a push to default branch closes issue in another repo
// If the user also has push permissions to that repo
@@ -231,7 +231,7 @@ func TestUpdateIssuesCommit_AnotherRepo_FullAddress(t *testing.T) {
},
}
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}).(*repo_model.Repository)
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2})
commentBean := &issues_model.Comment{
Type: issues_model.CommentTypeCommitRef,
CommitSHA: "abcdef1",
@@ -246,12 +246,12 @@ func TestUpdateIssuesCommit_AnotherRepo_FullAddress(t *testing.T) {
assert.NoError(t, UpdateIssuesCommit(user, repo, pushCommits, repo.DefaultBranch))
unittest.AssertExistsAndLoadBean(t, commentBean)
unittest.AssertExistsAndLoadBean(t, issueBean, "is_closed=1")
- unittest.CheckConsistencyFor(t, &models.Action{})
+ unittest.CheckConsistencyFor(t, &activities_model.Action{})
}
func TestUpdateIssuesCommit_AnotherRepoNoPermission(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 10}).(*user_model.User)
+ user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 10})
// Test that a push with close reference *can not* close issue
// If the committer doesn't have push rights in that repo
@@ -274,7 +274,7 @@ func TestUpdateIssuesCommit_AnotherRepoNoPermission(t *testing.T) {
},
}
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 6}).(*repo_model.Repository)
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 6})
commentBean := &issues_model.Comment{
Type: issues_model.CommentTypeCommitRef,
CommitSHA: "abcdef3",
@@ -297,5 +297,5 @@ func TestUpdateIssuesCommit_AnotherRepoNoPermission(t *testing.T) {
unittest.AssertNotExistsBean(t, commentBean)
unittest.AssertNotExistsBean(t, commentBean2)
unittest.AssertNotExistsBean(t, issueBean, "is_closed=1")
- unittest.CheckConsistencyFor(t, &models.Action{})
+ unittest.CheckConsistencyFor(t, &activities_model.Action{})
}
diff --git a/services/issue/issue.go b/services/issue/issue.go
index 2ea7f06b15..f3ab2e3e7a 100644
--- a/services/issue/issue.go
+++ b/services/issue/issue.go
@@ -7,13 +7,13 @@ package issue
import (
"fmt"
- "code.gitea.io/gitea/models"
- admin_model "code.gitea.io/gitea/models/admin"
+ activities_model "code.gitea.io/gitea/models/activities"
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
access_model "code.gitea.io/gitea/models/perm/access"
project_model "code.gitea.io/gitea/models/project"
repo_model "code.gitea.io/gitea/models/repo"
+ system_model "code.gitea.io/gitea/models/system"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/notification"
@@ -223,7 +223,7 @@ func deleteIssue(issue *issues_model.Issue) error {
return err
}
- if err := models.DeleteIssueActions(ctx, issue.RepoID, issue.ID); err != nil {
+ if err := activities_model.DeleteIssueActions(ctx, issue.RepoID, issue.ID); err != nil {
return err
}
@@ -233,7 +233,7 @@ func deleteIssue(issue *issues_model.Issue) error {
}
for i := range issue.Attachments {
- admin_model.RemoveStorageWithNotice(ctx, storage.Attachments, "Delete issue attachment", issue.Attachments[i].RelativePath())
+ system_model.RemoveStorageWithNotice(ctx, storage.Attachments, "Delete issue attachment", issue.Attachments[i].RelativePath())
}
// delete all database data still assigned to this issue
@@ -244,7 +244,7 @@ func deleteIssue(issue *issues_model.Issue) error {
&issues_model.IssueDependency{},
&issues_model.IssueAssignees{},
&issues_model.IssueUser{},
- &models.Notification{},
+ &activities_model.Notification{},
&issues_model.Reaction{},
&issues_model.IssueWatch{},
&issues_model.Stopwatch{},
diff --git a/services/issue/label_test.go b/services/issue/label_test.go
index 120c9ea4f1..482db95139 100644
--- a/services/issue/label_test.go
+++ b/services/issue/label_test.go
@@ -27,12 +27,12 @@ func TestIssue_AddLabels(t *testing.T) {
}
for _, test := range tests {
assert.NoError(t, unittest.PrepareTestDatabase())
- issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: test.issueID}).(*issues_model.Issue)
+ issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: test.issueID})
labels := make([]*issues_model.Label, len(test.labelIDs))
for i, labelID := range test.labelIDs {
- labels[i] = unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: labelID}).(*issues_model.Label)
+ labels[i] = unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: labelID})
}
- doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: test.doerID}).(*user_model.User)
+ doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: test.doerID})
assert.NoError(t, AddLabels(issue, doer, labels))
for _, labelID := range test.labelIDs {
unittest.AssertExistsAndLoadBean(t, &issues_model.IssueLabel{IssueID: test.issueID, LabelID: labelID})
@@ -53,9 +53,9 @@ func TestIssue_AddLabel(t *testing.T) {
}
for _, test := range tests {
assert.NoError(t, unittest.PrepareTestDatabase())
- issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: test.issueID}).(*issues_model.Issue)
- label := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: test.labelID}).(*issues_model.Label)
- doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: test.doerID}).(*user_model.User)
+ issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: test.issueID})
+ label := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: test.labelID})
+ doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: test.doerID})
assert.NoError(t, AddLabel(issue, doer, label))
unittest.AssertExistsAndLoadBean(t, &issues_model.IssueLabel{IssueID: test.issueID, LabelID: test.labelID})
}
diff --git a/services/issue/milestone_test.go b/services/issue/milestone_test.go
index d08b1ae8c7..087c256700 100644
--- a/services/issue/milestone_test.go
+++ b/services/issue/milestone_test.go
@@ -16,8 +16,8 @@ import (
func TestChangeMilestoneAssign(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{RepoID: 1}).(*issues_model.Issue)
- doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
+ issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{RepoID: 1})
+ doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
assert.NotNil(t, issue)
assert.NotNil(t, doer)
diff --git a/services/mailer/mail.go b/services/mailer/mail.go
index b8d79bd818..a5bfa496f9 100644
--- a/services/mailer/mail.go
+++ b/services/mailer/mail.go
@@ -17,7 +17,7 @@ import (
texttmpl "text/template"
"time"
- "code.gitea.io/gitea/models"
+ activities_model "code.gitea.io/gitea/models/activities"
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
repo_model "code.gitea.io/gitea/models/repo"
@@ -55,12 +55,6 @@ var (
subjectRemoveSpaces = regexp.MustCompile(`[\s]+`)
)
-// InitMailRender initializes the mail renderer
-func InitMailRender(subjectTpl *texttmpl.Template, bodyTpl *template.Template) {
- subjectTemplates = subjectTpl
- bodyTemplates = bodyTpl
-}
-
// SendTestMail sends a test mail
func SendTestMail(email string) error {
if setting.MailService == nil {
@@ -308,7 +302,7 @@ func composeIssueCommentMessages(ctx *mailCommentContext, lang string, recipient
// Make sure to compose independent messages to avoid leaking user emails
msgID := createReference(ctx.Issue, ctx.Comment, ctx.ActionType)
- reference := createReference(ctx.Issue, nil, models.ActionType(0))
+ reference := createReference(ctx.Issue, nil, activities_model.ActionType(0))
msgs := make([]*Message, 0, len(recipients))
for _, recipient := range recipients {
@@ -329,7 +323,7 @@ func composeIssueCommentMessages(ctx *mailCommentContext, lang string, recipient
return msgs, nil
}
-func createReference(issue *issues_model.Issue, comment *issues_model.Comment, actionType models.ActionType) string {
+func createReference(issue *issues_model.Issue, comment *issues_model.Comment, actionType activities_model.ActionType) string {
var path string
if issue.IsPull {
path = "pulls"
@@ -342,13 +336,13 @@ func createReference(issue *issues_model.Issue, comment *issues_model.Comment, a
extra = fmt.Sprintf("/comment/%d", comment.ID)
} else {
switch actionType {
- case models.ActionCloseIssue, models.ActionClosePullRequest:
+ case activities_model.ActionCloseIssue, activities_model.ActionClosePullRequest:
extra = fmt.Sprintf("/close/%d", time.Now().UnixNano()/1e6)
- case models.ActionReopenIssue, models.ActionReopenPullRequest:
+ case activities_model.ActionReopenIssue, activities_model.ActionReopenPullRequest:
extra = fmt.Sprintf("/reopen/%d", time.Now().UnixNano()/1e6)
- case models.ActionMergePullRequest:
+ case activities_model.ActionMergePullRequest:
extra = fmt.Sprintf("/merge/%d", time.Now().UnixNano()/1e6)
- case models.ActionPullRequestReadyForReview:
+ case activities_model.ActionPullRequestReadyForReview:
extra = fmt.Sprintf("/ready/%d", time.Now().UnixNano()/1e6)
}
}
@@ -426,7 +420,7 @@ func SendIssueAssignedMail(issue *issues_model.Issue, doer *user_model.User, con
Context: context.TODO(), // TODO: use a correct context
Issue: issue,
Doer: doer,
- ActionType: models.ActionType(0),
+ ActionType: activities_model.ActionType(0),
Content: content,
Comment: comment,
}, lang, tos, false, "issue assigned")
@@ -439,8 +433,8 @@ func SendIssueAssignedMail(issue *issues_model.Issue, doer *user_model.User, con
}
// actionToTemplate returns the type and name of the action facing the user
-// (slightly different from models.ActionType) and the name of the template to use (based on availability)
-func actionToTemplate(issue *issues_model.Issue, actionType models.ActionType,
+// (slightly different from activities_model.ActionType) and the name of the template to use (based on availability)
+func actionToTemplate(issue *issues_model.Issue, actionType activities_model.ActionType,
commentType issues_model.CommentType, reviewType issues_model.ReviewType,
) (typeName, name, template string) {
if issue.IsPull {
@@ -449,19 +443,19 @@ func actionToTemplate(issue *issues_model.Issue, actionType models.ActionType,
typeName = "issue"
}
switch actionType {
- case models.ActionCreateIssue, models.ActionCreatePullRequest:
+ case activities_model.ActionCreateIssue, activities_model.ActionCreatePullRequest:
name = "new"
- case models.ActionCommentIssue, models.ActionCommentPull:
+ case activities_model.ActionCommentIssue, activities_model.ActionCommentPull:
name = "comment"
- case models.ActionCloseIssue, models.ActionClosePullRequest:
+ case activities_model.ActionCloseIssue, activities_model.ActionClosePullRequest:
name = "close"
- case models.ActionReopenIssue, models.ActionReopenPullRequest:
+ case activities_model.ActionReopenIssue, activities_model.ActionReopenPullRequest:
name = "reopen"
- case models.ActionMergePullRequest:
+ case activities_model.ActionMergePullRequest:
name = "merge"
- case models.ActionPullReviewDismissed:
+ case activities_model.ActionPullReviewDismissed:
name = "review_dismissed"
- case models.ActionPullRequestReadyForReview:
+ case activities_model.ActionPullRequestReadyForReview:
name = "ready_for_review"
default:
switch commentType {
diff --git a/services/mailer/mail_comment.go b/services/mailer/mail_comment.go
index 95d11ae8a1..af07821c29 100644
--- a/services/mailer/mail_comment.go
+++ b/services/mailer/mail_comment.go
@@ -7,15 +7,16 @@ package mailer
import (
"context"
- "code.gitea.io/gitea/models"
+ activities_model "code.gitea.io/gitea/models/activities"
issues_model "code.gitea.io/gitea/models/issues"
user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
)
// MailParticipantsComment sends new comment emails to repository watchers and mentioned people.
-func MailParticipantsComment(ctx context.Context, c *issues_model.Comment, opType models.ActionType, issue *issues_model.Issue, mentions []*user_model.User) error {
+func MailParticipantsComment(ctx context.Context, c *issues_model.Comment, opType activities_model.ActionType, issue *issues_model.Issue, mentions []*user_model.User) error {
if setting.MailService == nil {
// No mail service configured
return nil
@@ -46,14 +47,14 @@ func MailMentionsComment(ctx context.Context, pr *issues_model.PullRequest, c *i
return nil
}
- visited := make(map[int64]bool, len(mentions)+1)
- visited[c.Poster.ID] = true
+ visited := make(container.Set[int64], len(mentions)+1)
+ visited.Add(c.Poster.ID)
if err = mailIssueCommentBatch(
&mailCommentContext{
Context: ctx,
Issue: pr.Issue,
Doer: c.Poster,
- ActionType: models.ActionCommentPull,
+ ActionType: activities_model.ActionCommentPull,
Content: c.Content,
Comment: c,
}, mentions, visited, true); err != nil {
diff --git a/services/mailer/mail_issue.go b/services/mailer/mail_issue.go
index 5c330f6e00..15bfa4af41 100644
--- a/services/mailer/mail_issue.go
+++ b/services/mailer/mail_issue.go
@@ -8,11 +8,13 @@ import (
"context"
"fmt"
- "code.gitea.io/gitea/models"
+ activities_model "code.gitea.io/gitea/models/activities"
issues_model "code.gitea.io/gitea/models/issues"
+ 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/modules/container"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting"
)
@@ -25,7 +27,7 @@ type mailCommentContext struct {
context.Context
Issue *issues_model.Issue
Doer *user_model.User
- ActionType models.ActionType
+ ActionType activities_model.ActionType
Content string
Comment *issues_model.Comment
}
@@ -80,7 +82,7 @@ func mailIssueCommentToParticipants(ctx *mailCommentContext, mentions []*user_mo
// =========== Repo watchers ===========
// Make repo watchers last, since it's likely the list with the most users
- if !(ctx.Issue.IsPull && ctx.Issue.PullRequest.IsWorkInProgress() && ctx.ActionType != models.ActionCreatePullRequest) {
+ if !(ctx.Issue.IsPull && ctx.Issue.PullRequest.IsWorkInProgress() && ctx.ActionType != activities_model.ActionCreatePullRequest) {
ids, err = repo_model.GetRepoWatchersIDs(ctx, ctx.Issue.RepoID)
if err != nil {
return fmt.Errorf("GetRepoWatchersIDs(%d): %v", ctx.Issue.RepoID, err)
@@ -88,10 +90,12 @@ func mailIssueCommentToParticipants(ctx *mailCommentContext, mentions []*user_mo
unfiltered = append(ids, unfiltered...)
}
- visited := make(map[int64]bool, len(unfiltered)+len(mentions)+1)
+ visited := make(container.Set[int64], len(unfiltered)+len(mentions)+1)
// Avoid mailing the doer
- visited[ctx.Doer.ID] = true
+ if ctx.Doer.EmailNotificationsPreference != user_model.EmailNotificationsAndYourOwn {
+ visited.Add(ctx.Doer.ID)
+ }
// =========== Mentions ===========
if err = mailIssueCommentBatch(ctx, mentions, visited, true); err != nil {
@@ -103,9 +107,7 @@ func mailIssueCommentToParticipants(ctx *mailCommentContext, mentions []*user_mo
if err != nil {
return fmt.Errorf("GetIssueWatchersIDs(%d): %v", ctx.Issue.ID, err)
}
- for _, i := range ids {
- visited[i] = true
- }
+ visited.AddMultiple(ids...)
unfilteredUsers, err := user_model.GetMaileableUsersByIDs(unfiltered, false)
if err != nil {
@@ -118,7 +120,7 @@ func mailIssueCommentToParticipants(ctx *mailCommentContext, mentions []*user_mo
return nil
}
-func mailIssueCommentBatch(ctx *mailCommentContext, users []*user_model.User, visited map[int64]bool, fromMention bool) error {
+func mailIssueCommentBatch(ctx *mailCommentContext, users []*user_model.User, visited container.Set[int64], fromMention bool) error {
checkUnit := unit.TypeIssues
if ctx.Issue.IsPull {
checkUnit = unit.TypePullRequests
@@ -133,20 +135,18 @@ func mailIssueCommentBatch(ctx *mailCommentContext, users []*user_model.User, vi
// At this point we exclude:
// user that don't have all mails enabled or users only get mail on mention and this is one ...
if !(user.EmailNotificationsPreference == user_model.EmailNotificationsEnabled ||
+ user.EmailNotificationsPreference == user_model.EmailNotificationsAndYourOwn ||
fromMention && user.EmailNotificationsPreference == user_model.EmailNotificationsOnMention) {
continue
}
// if we have already visited this user we exclude them
- if _, ok := visited[user.ID]; ok {
+ if !visited.Add(user.ID) {
continue
}
- // now mark them as visited
- visited[user.ID] = true
-
// test if this user is allowed to see the issue/pull
- if !models.CheckRepoUnitUser(ctx, ctx.Issue.Repo, user, checkUnit) {
+ if !access_model.CheckRepoUnitUser(ctx, ctx.Issue.Repo, user, checkUnit) {
continue
}
@@ -172,16 +172,16 @@ func mailIssueCommentBatch(ctx *mailCommentContext, users []*user_model.User, vi
// MailParticipants sends new issue thread created emails to repository watchers
// and mentioned people.
-func MailParticipants(issue *issues_model.Issue, doer *user_model.User, opType models.ActionType, mentions []*user_model.User) error {
+func MailParticipants(issue *issues_model.Issue, doer *user_model.User, opType activities_model.ActionType, mentions []*user_model.User) error {
if setting.MailService == nil {
// No mail service configured
return nil
}
content := issue.Content
- if opType == models.ActionCloseIssue || opType == models.ActionClosePullRequest ||
- opType == models.ActionReopenIssue || opType == models.ActionReopenPullRequest ||
- opType == models.ActionMergePullRequest {
+ if opType == activities_model.ActionCloseIssue || opType == activities_model.ActionClosePullRequest ||
+ opType == activities_model.ActionReopenIssue || opType == activities_model.ActionReopenPullRequest ||
+ opType == activities_model.ActionMergePullRequest {
content = ""
}
if err := mailIssueCommentToParticipants(
diff --git a/services/mailer/mail_release.go b/services/mailer/mail_release.go
index dd9f78612c..7c44f93929 100644
--- a/services/mailer/mail_release.go
+++ b/services/mailer/mail_release.go
@@ -8,7 +8,6 @@ import (
"bytes"
"context"
- "code.gitea.io/gitea/models"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/base"
@@ -25,7 +24,7 @@ const (
)
// MailNewRelease send new release notify to all all repo watchers.
-func MailNewRelease(ctx context.Context, rel *models.Release) {
+func MailNewRelease(ctx context.Context, rel *repo_model.Release) {
if setting.MailService == nil {
// No mail service configured
return
@@ -55,7 +54,7 @@ func MailNewRelease(ctx context.Context, rel *models.Release) {
}
}
-func mailNewRelease(ctx context.Context, lang string, tos []string, rel *models.Release) {
+func mailNewRelease(ctx context.Context, lang string, tos []string, rel *repo_model.Release) {
locale := translation.NewLocale(lang)
var err error
diff --git a/services/mailer/mail_test.go b/services/mailer/mail_test.go
index 93837ba8c4..acb1f69961 100644
--- a/services/mailer/mail_test.go
+++ b/services/mailer/mail_test.go
@@ -13,7 +13,7 @@ import (
"testing"
texttmpl "text/template"
- "code.gitea.io/gitea/models"
+ activities_model "code.gitea.io/gitea/models/activities"
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
repo_model "code.gitea.io/gitea/models/repo"
@@ -56,25 +56,24 @@ func prepareMailerTest(t *testing.T) (doer *user_model.User, repo *repo_model.Re
setting.MailService = &mailService
setting.Domain = "localhost"
- doer = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
- repo = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1, Owner: doer}).(*repo_model.Repository)
- issue = unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1, Repo: repo, Poster: doer}).(*issues_model.Issue)
+ doer = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
+ repo = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1, Owner: doer})
+ issue = unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 1, Repo: repo, Poster: doer})
assert.NoError(t, issue.LoadRepo(db.DefaultContext))
- comment = unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: 2, Issue: issue}).(*issues_model.Comment)
+ comment = unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: 2, Issue: issue})
return doer, repo, issue, comment
}
func TestComposeIssueCommentMessage(t *testing.T) {
doer, _, issue, comment := prepareMailerTest(t)
- stpl := texttmpl.Must(texttmpl.New("issue/comment").Parse(subjectTpl))
- btpl := template.Must(template.New("issue/comment").Parse(bodyTpl))
- InitMailRender(stpl, btpl)
+ subjectTemplates = texttmpl.Must(texttmpl.New("issue/comment").Parse(subjectTpl))
+ bodyTemplates = template.Must(template.New("issue/comment").Parse(bodyTpl))
recipients := []*user_model.User{{Name: "Test", Email: "test@gitea.com"}, {Name: "Test2", Email: "test2@gitea.com"}}
msgs, err := composeIssueCommentMessages(&mailCommentContext{
Context: context.TODO(), // TODO: use a correct context
- Issue: issue, Doer: doer, ActionType: models.ActionCommentIssue,
+ Issue: issue, Doer: doer, ActionType: activities_model.ActionCommentIssue,
Content: "test body", Comment: comment,
}, "en-US", recipients, false, "issue comment")
assert.NoError(t, err)
@@ -97,14 +96,13 @@ func TestComposeIssueCommentMessage(t *testing.T) {
func TestComposeIssueMessage(t *testing.T) {
doer, _, issue, _ := prepareMailerTest(t)
- stpl := texttmpl.Must(texttmpl.New("issue/new").Parse(subjectTpl))
- btpl := template.Must(template.New("issue/new").Parse(bodyTpl))
- InitMailRender(stpl, btpl)
+ subjectTemplates = texttmpl.Must(texttmpl.New("issue/new").Parse(subjectTpl))
+ bodyTemplates = template.Must(template.New("issue/new").Parse(bodyTpl))
recipients := []*user_model.User{{Name: "Test", Email: "test@gitea.com"}, {Name: "Test2", Email: "test2@gitea.com"}}
msgs, err := composeIssueCommentMessages(&mailCommentContext{
Context: context.TODO(), // TODO: use a correct context
- Issue: issue, Doer: doer, ActionType: models.ActionCreateIssue,
+ Issue: issue, Doer: doer, ActionType: activities_model.ActionCreateIssue,
Content: "test body",
}, "en-US", recipients, false, "issue create")
assert.NoError(t, err)
@@ -128,17 +126,15 @@ func TestTemplateSelection(t *testing.T) {
doer, repo, issue, comment := prepareMailerTest(t)
recipients := []*user_model.User{{Name: "Test", Email: "test@gitea.com"}}
- stpl := texttmpl.Must(texttmpl.New("issue/default").Parse("issue/default/subject"))
- texttmpl.Must(stpl.New("issue/new").Parse("issue/new/subject"))
- texttmpl.Must(stpl.New("pull/comment").Parse("pull/comment/subject"))
- texttmpl.Must(stpl.New("issue/close").Parse("")) // Must default to fallback subject
+ subjectTemplates = texttmpl.Must(texttmpl.New("issue/default").Parse("issue/default/subject"))
+ texttmpl.Must(subjectTemplates.New("issue/new").Parse("issue/new/subject"))
+ texttmpl.Must(subjectTemplates.New("pull/comment").Parse("pull/comment/subject"))
+ texttmpl.Must(subjectTemplates.New("issue/close").Parse("")) // Must default to fallback subject
- btpl := template.Must(template.New("issue/default").Parse("issue/default/body"))
- template.Must(btpl.New("issue/new").Parse("issue/new/body"))
- template.Must(btpl.New("pull/comment").Parse("pull/comment/body"))
- template.Must(btpl.New("issue/close").Parse("issue/close/body"))
-
- InitMailRender(stpl, btpl)
+ bodyTemplates = template.Must(template.New("issue/default").Parse("issue/default/body"))
+ template.Must(bodyTemplates.New("issue/new").Parse("issue/new/body"))
+ template.Must(bodyTemplates.New("pull/comment").Parse("pull/comment/body"))
+ template.Must(bodyTemplates.New("issue/close").Parse("issue/close/body"))
expect := func(t *testing.T, msg *Message, expSubject, expBody string) {
subject := msg.ToMessage().GetHeader("Subject")
@@ -151,30 +147,30 @@ func TestTemplateSelection(t *testing.T) {
msg := testComposeIssueCommentMessage(t, &mailCommentContext{
Context: context.TODO(), // TODO: use a correct context
- Issue: issue, Doer: doer, ActionType: models.ActionCreateIssue,
+ Issue: issue, Doer: doer, ActionType: activities_model.ActionCreateIssue,
Content: "test body",
}, recipients, false, "TestTemplateSelection")
expect(t, msg, "issue/new/subject", "issue/new/body")
msg = testComposeIssueCommentMessage(t, &mailCommentContext{
Context: context.TODO(), // TODO: use a correct context
- Issue: issue, Doer: doer, ActionType: models.ActionCommentIssue,
+ Issue: issue, Doer: doer, ActionType: activities_model.ActionCommentIssue,
Content: "test body", Comment: comment,
}, recipients, false, "TestTemplateSelection")
expect(t, msg, "issue/default/subject", "issue/default/body")
- pull := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 2, Repo: repo, Poster: doer}).(*issues_model.Issue)
- comment = unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: 4, Issue: pull}).(*issues_model.Comment)
+ pull := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 2, Repo: repo, Poster: doer})
+ comment = unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: 4, Issue: pull})
msg = testComposeIssueCommentMessage(t, &mailCommentContext{
Context: context.TODO(), // TODO: use a correct context
- Issue: pull, Doer: doer, ActionType: models.ActionCommentPull,
+ Issue: pull, Doer: doer, ActionType: activities_model.ActionCommentPull,
Content: "test body", Comment: comment,
}, recipients, false, "TestTemplateSelection")
expect(t, msg, "pull/comment/subject", "pull/comment/body")
msg = testComposeIssueCommentMessage(t, &mailCommentContext{
Context: context.TODO(), // TODO: use a correct context
- Issue: issue, Doer: doer, ActionType: models.ActionCloseIssue,
+ Issue: issue, Doer: doer, ActionType: activities_model.ActionCloseIssue,
Content: "test body", Comment: comment,
}, recipients, false, "TestTemplateSelection")
expect(t, msg, "Re: [user2/repo1] issue1 (#1)", "issue/close/body")
@@ -185,11 +181,10 @@ func TestTemplateServices(t *testing.T) {
assert.NoError(t, issue.LoadRepo(db.DefaultContext))
expect := func(t *testing.T, issue *issues_model.Issue, comment *issues_model.Comment, doer *user_model.User,
- actionType models.ActionType, fromMention bool, tplSubject, tplBody, expSubject, expBody string,
+ actionType activities_model.ActionType, fromMention bool, tplSubject, tplBody, expSubject, expBody string,
) {
- stpl := texttmpl.Must(texttmpl.New("issue/default").Parse(tplSubject))
- btpl := template.Must(template.New("issue/default").Parse(tplBody))
- InitMailRender(stpl, btpl)
+ subjectTemplates = texttmpl.Must(texttmpl.New("issue/default").Parse(tplSubject))
+ bodyTemplates = template.Must(template.New("issue/default").Parse(tplBody))
recipients := []*user_model.User{{Name: "Test", Email: "test@gitea.com"}}
msg := testComposeIssueCommentMessage(t, &mailCommentContext{
@@ -207,19 +202,19 @@ func TestTemplateServices(t *testing.T) {
assert.Contains(t, wholemsg, "\r\n"+expBody+"\r\n")
}
- expect(t, issue, comment, doer, models.ActionCommentIssue, false,
+ expect(t, issue, comment, doer, activities_model.ActionCommentIssue, false,
"{{.SubjectPrefix}}[{{.Repo}}]: @{{.Doer.Name}} commented on #{{.Issue.Index}} - {{.Issue.Title}}",
"//{{.ActionType}},{{.ActionName}},{{if .IsMention}}norender{{end}}//",
"Re: [user2/repo1]: @user2 commented on #1 - issue1",
"//issue,comment,//")
- expect(t, issue, comment, doer, models.ActionCommentIssue, true,
+ expect(t, issue, comment, doer, activities_model.ActionCommentIssue, true,
"{{if .IsMention}}must render{{end}}",
"//subject is: {{.Subject}}//",
"must render",
"//subject is: must render//")
- expect(t, issue, comment, doer, models.ActionCommentIssue, true,
+ expect(t, issue, comment, doer, activities_model.ActionCommentIssue, true,
"{{.FallbackSubject}}",
"//{{.SubjectPrefix}}//",
"Re: [user2/repo1] issue1 (#1)",
@@ -271,7 +266,7 @@ func Test_createReference(t *testing.T) {
type args struct {
issue *issues_model.Issue
comment *issues_model.Comment
- actionType models.ActionType
+ actionType activities_model.ActionType
}
tests := []struct {
name string
@@ -283,7 +278,7 @@ func Test_createReference(t *testing.T) {
name: "Open Issue",
args: args{
issue: issue,
- actionType: models.ActionCreateIssue,
+ actionType: activities_model.ActionCreateIssue,
},
prefix: fmt.Sprintf("%s/issues/%d@%s", issue.Repo.FullName(), issue.Index, setting.Domain),
},
@@ -291,7 +286,7 @@ func Test_createReference(t *testing.T) {
name: "Open Pull",
args: args{
issue: pullIssue,
- actionType: models.ActionCreatePullRequest,
+ actionType: activities_model.ActionCreatePullRequest,
},
prefix: fmt.Sprintf("%s/pulls/%d@%s", issue.Repo.FullName(), issue.Index, setting.Domain),
},
@@ -300,7 +295,7 @@ func Test_createReference(t *testing.T) {
args: args{
issue: issue,
comment: comment,
- actionType: models.ActionCommentIssue,
+ actionType: activities_model.ActionCommentIssue,
},
prefix: fmt.Sprintf("%s/issues/%d/comment/%d@%s", issue.Repo.FullName(), issue.Index, comment.ID, setting.Domain),
},
@@ -309,7 +304,7 @@ func Test_createReference(t *testing.T) {
args: args{
issue: pullIssue,
comment: comment,
- actionType: models.ActionCommentPull,
+ actionType: activities_model.ActionCommentPull,
},
prefix: fmt.Sprintf("%s/pulls/%d/comment/%d@%s", issue.Repo.FullName(), issue.Index, comment.ID, setting.Domain),
},
@@ -317,7 +312,7 @@ func Test_createReference(t *testing.T) {
name: "Close Issue",
args: args{
issue: issue,
- actionType: models.ActionCloseIssue,
+ actionType: activities_model.ActionCloseIssue,
},
prefix: fmt.Sprintf("%s/issues/%d/close/", issue.Repo.FullName(), issue.Index),
},
@@ -325,7 +320,7 @@ func Test_createReference(t *testing.T) {
name: "Close Pull",
args: args{
issue: pullIssue,
- actionType: models.ActionClosePullRequest,
+ actionType: activities_model.ActionClosePullRequest,
},
prefix: fmt.Sprintf("%s/pulls/%d/close/", issue.Repo.FullName(), issue.Index),
},
@@ -333,7 +328,7 @@ func Test_createReference(t *testing.T) {
name: "Reopen Issue",
args: args{
issue: issue,
- actionType: models.ActionReopenIssue,
+ actionType: activities_model.ActionReopenIssue,
},
prefix: fmt.Sprintf("%s/issues/%d/reopen/", issue.Repo.FullName(), issue.Index),
},
@@ -341,7 +336,7 @@ func Test_createReference(t *testing.T) {
name: "Reopen Pull",
args: args{
issue: pullIssue,
- actionType: models.ActionReopenPullRequest,
+ actionType: activities_model.ActionReopenPullRequest,
},
prefix: fmt.Sprintf("%s/pulls/%d/reopen/", issue.Repo.FullName(), issue.Index),
},
@@ -349,7 +344,7 @@ func Test_createReference(t *testing.T) {
name: "Merge Pull",
args: args{
issue: pullIssue,
- actionType: models.ActionMergePullRequest,
+ actionType: activities_model.ActionMergePullRequest,
},
prefix: fmt.Sprintf("%s/pulls/%d/merge/", issue.Repo.FullName(), issue.Index),
},
@@ -357,7 +352,7 @@ func Test_createReference(t *testing.T) {
name: "Ready Pull",
args: args{
issue: pullIssue,
- actionType: models.ActionPullRequestReadyForReview,
+ actionType: activities_model.ActionPullRequestReadyForReview,
},
prefix: fmt.Sprintf("%s/pulls/%d/ready/", issue.Repo.FullName(), issue.Index),
},
diff --git a/services/mailer/mailer.go b/services/mailer/mailer.go
index f4bc2ddc63..1f43c7f827 100644
--- a/services/mailer/mailer.go
+++ b/services/mailer/mailer.go
@@ -7,6 +7,7 @@ package mailer
import (
"bytes"
+ "context"
"crypto/tls"
"fmt"
"hash/fnv"
@@ -24,6 +25,7 @@ import (
"code.gitea.io/gitea/modules/process"
"code.gitea.io/gitea/modules/queue"
"code.gitea.io/gitea/modules/setting"
+ "code.gitea.io/gitea/modules/templates"
"github.com/jaytaylor/html2text"
"gopkg.in/gomail.v2"
@@ -147,65 +149,82 @@ type smtpSender struct{}
func (s *smtpSender) Send(from string, to []string, msg io.WriterTo) error {
opts := setting.MailService
- host, port, err := net.SplitHostPort(opts.Host)
+ var network string
+ var address string
+ if opts.Protocol == "smtp+unix" {
+ network = "unix"
+ address = opts.SMTPAddr
+ } else {
+ network = "tcp"
+ address = net.JoinHostPort(opts.SMTPAddr, opts.SMTPPort)
+ }
+
+ conn, err := net.Dial(network, address)
if err != nil {
- return err
- }
-
- tlsconfig := &tls.Config{
- InsecureSkipVerify: opts.SkipVerify,
- ServerName: host,
- }
-
- if opts.UseCertificate {
- cert, err := tls.LoadX509KeyPair(opts.CertFile, opts.KeyFile)
- if err != nil {
- return err
- }
- tlsconfig.Certificates = []tls.Certificate{cert}
- }
-
- conn, err := net.Dial("tcp", net.JoinHostPort(host, port))
- if err != nil {
- return err
+ return fmt.Errorf("failed to establish network connection to SMTP server: %v", err)
}
defer conn.Close()
- isSecureConn := opts.IsTLSEnabled || (strings.HasSuffix(port, "465"))
- // Start TLS directly if the port ends with 465 (SMTPS protocol)
- if isSecureConn {
+ var tlsconfig *tls.Config
+ if opts.Protocol == "smtps" || opts.Protocol == "smtp+startls" {
+ tlsconfig = &tls.Config{
+ InsecureSkipVerify: opts.ForceTrustServerCert,
+ ServerName: opts.SMTPAddr,
+ }
+
+ if opts.UseClientCert {
+ cert, err := tls.LoadX509KeyPair(opts.ClientCertFile, opts.ClientKeyFile)
+ if err != nil {
+ return fmt.Errorf("could not load SMTP client certificate: %v", err)
+ }
+ tlsconfig.Certificates = []tls.Certificate{cert}
+ }
+ }
+
+ if opts.Protocol == "smtps" {
conn = tls.Client(conn, tlsconfig)
}
+ host := "localhost"
+ if opts.Protocol == "smtp+unix" {
+ host = opts.SMTPAddr
+ }
client, err := smtp.NewClient(conn, host)
if err != nil {
- return fmt.Errorf("NewClient: %v", err)
+ return fmt.Errorf("could not initiate SMTP session: %v", err)
}
- if !opts.DisableHelo {
+ if opts.EnableHelo {
hostname := opts.HeloHostname
if len(hostname) == 0 {
hostname, err = os.Hostname()
if err != nil {
- return err
+ return fmt.Errorf("could not retrieve system hostname: %v", err)
}
}
if err = client.Hello(hostname); err != nil {
- return fmt.Errorf("Hello: %v", err)
+ return fmt.Errorf("failed to issue HELO command: %v", err)
}
}
- // If not using SMTPS, always use STARTTLS if available
- hasStartTLS, _ := client.Extension("STARTTLS")
- if !isSecureConn && hasStartTLS {
- if err = client.StartTLS(tlsconfig); err != nil {
- return fmt.Errorf("StartTLS: %v", err)
+ if opts.Protocol == "smtp+startls" {
+ hasStartTLS, _ := client.Extension("STARTTLS")
+ if hasStartTLS {
+ if err = client.StartTLS(tlsconfig); err != nil {
+ return fmt.Errorf("failed to start TLS connection: %v", err)
+ }
+ } else {
+ log.Warn("StartTLS requested, but SMTP server does not support it; falling back to regular SMTP")
}
}
canAuth, options := client.Extension("AUTH")
- if canAuth && len(opts.User) > 0 {
+ if len(opts.User) > 0 {
+ if !canAuth {
+ return fmt.Errorf("SMTP server does not support AUTH, but credentials provided")
+ }
+
var auth smtp.Auth
if strings.Contains(options, "CRAM-MD5") {
@@ -219,34 +238,34 @@ func (s *smtpSender) Send(from string, to []string, msg io.WriterTo) error {
if auth != nil {
if err = client.Auth(auth); err != nil {
- return fmt.Errorf("Auth: %v", err)
+ return fmt.Errorf("failed to authenticate SMTP: %v", err)
}
}
}
if opts.OverrideEnvelopeFrom {
if err = client.Mail(opts.EnvelopeFrom); err != nil {
- return fmt.Errorf("Mail: %v", err)
+ return fmt.Errorf("failed to issue MAIL command: %v", err)
}
} else {
if err = client.Mail(from); err != nil {
- return fmt.Errorf("Mail: %v", err)
+ return fmt.Errorf("failed to issue MAIL command: %v", err)
}
}
for _, rec := range to {
if err = client.Rcpt(rec); err != nil {
- return fmt.Errorf("Rcpt: %v", err)
+ return fmt.Errorf("failed to issue RCPT command: %v", err)
}
}
w, err := client.Data()
if err != nil {
- return fmt.Errorf("Data: %v", err)
+ return fmt.Errorf("failed to issue DATA command: %v", err)
} else if _, err = msg.WriteTo(w); err != nil {
- return fmt.Errorf("WriteTo: %v", err)
+ return fmt.Errorf("SMTP write failed: %v", err)
} else if err = w.Close(); err != nil {
- return fmt.Errorf("Close: %v", err)
+ return fmt.Errorf("SMTP close failed: %v", err)
}
return client.Quit()
@@ -330,7 +349,7 @@ var mailQueue queue.Queue
var Sender gomail.Sender
// NewContext start mail queue service
-func NewContext() {
+func NewContext(ctx context.Context) {
// Need to check if mailQueue is nil because in during reinstall (user had installed
// before but switched install lock off), this function will be called again
// while mail queue is already processing tasks, and produces a race condition.
@@ -338,13 +357,13 @@ func NewContext() {
return
}
- switch setting.MailService.MailerType {
- case "smtp":
- Sender = &smtpSender{}
+ switch setting.MailService.Protocol {
case "sendmail":
Sender = &sendmailSender{}
case "dummy":
Sender = &dummySender{}
+ default:
+ Sender = &smtpSender{}
}
mailQueue = queue.CreateQueue("mail", func(data ...queue.Data) []queue.Data {
@@ -362,6 +381,8 @@ func NewContext() {
}, &Message{})
go graceful.GetManager().RunWithShutdownFns(mailQueue.Run)
+
+ subjectTemplates, bodyTemplates = templates.Mailer(ctx)
}
// SendAsync send mail asynchronously
diff --git a/services/migrations/codebase.go b/services/migrations/codebase.go
index bb74c0a49d..edeb276773 100644
--- a/services/migrations/codebase.go
+++ b/services/migrations/codebase.go
@@ -107,9 +107,24 @@ func NewCodebaseDownloader(ctx context.Context, projectURL *url.URL, project, re
commitMap: make(map[string]string),
}
+ log.Trace("Create Codebase downloader. BaseURL: %s Project: %s RepoName: %s", baseURL, project, repoName)
return downloader
}
+// String implements Stringer
+func (d *CodebaseDownloader) String() string {
+ return fmt.Sprintf("migration from codebase server %s %s/%s", d.baseURL, d.project, d.repoName)
+}
+
+// ColorFormat provides a basic color format for a GogsDownloader
+func (d *CodebaseDownloader) ColorFormat(s fmt.State) {
+ if d == nil {
+ log.ColorFprintf(s, "")
+ return
+ }
+ log.ColorFprintf(s, "migration from codebase server %s %s/%s", d.baseURL, d.project, d.repoName)
+}
+
// FormatCloneURL add authentication into remote URLs
func (d *CodebaseDownloader) FormatCloneURL(opts base.MigrateOptions, remoteAddr string) (string, error) {
return opts.CloneAddr, nil
@@ -451,8 +466,8 @@ func (d *CodebaseDownloader) GetPullRequests(page, perPage int) ([]*base.PullReq
Value int64 `xml:",chardata"`
Type string `xml:"type,attr"`
} `xml:"id"`
- SourceRef string `xml:"source-ref"`
- TargetRef string `xml:"target-ref"`
+ SourceRef string `xml:"source-ref"` // NOTE: from the documentation these are actually just branches NOT full refs
+ TargetRef string `xml:"target-ref"` // NOTE: from the documentation these are actually just branches NOT full refs
Subject string `xml:"subject"`
Status string `xml:"status"`
UserID struct {
@@ -564,6 +579,9 @@ func (d *CodebaseDownloader) GetPullRequests(page, perPage int) ([]*base.PullReq
Comments: comments[1:],
},
})
+
+ // SECURITY: Ensure that the PR is safe
+ _ = CheckAndEnsureSafePR(pullRequests[len(pullRequests)-1], d.baseURL.String(), d)
}
return pullRequests, true, nil
diff --git a/services/migrations/common.go b/services/migrations/common.go
new file mode 100644
index 0000000000..052975c9e7
--- /dev/null
+++ b/services/migrations/common.go
@@ -0,0 +1,82 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package migrations
+
+import (
+ "fmt"
+ "strings"
+
+ system_model "code.gitea.io/gitea/models/system"
+ "code.gitea.io/gitea/modules/git"
+ "code.gitea.io/gitea/modules/log"
+ base "code.gitea.io/gitea/modules/migration"
+)
+
+// WarnAndNotice will log the provided message and send a repository notice
+func WarnAndNotice(fmtStr string, args ...interface{}) {
+ log.Warn(fmtStr, args...)
+ if err := system_model.CreateRepositoryNotice(fmt.Sprintf(fmtStr, args...)); err != nil {
+ log.Error("create repository notice failed: ", err)
+ }
+}
+
+func hasBaseURL(toCheck, baseURL string) bool {
+ if len(baseURL) > 0 && baseURL[len(baseURL)-1] != '/' {
+ baseURL += "/"
+ }
+ return strings.HasPrefix(toCheck, baseURL)
+}
+
+// CheckAndEnsureSafePR will check that a given PR is safe to download
+func CheckAndEnsureSafePR(pr *base.PullRequest, commonCloneBaseURL string, g base.Downloader) bool {
+ valid := true
+ // SECURITY: the patchURL must be checked to have the same baseURL as the current to prevent open redirect
+ if pr.PatchURL != "" && !hasBaseURL(pr.PatchURL, commonCloneBaseURL) {
+ // TODO: Should we check that this url has the expected format for a patch url?
+ WarnAndNotice("PR #%d in %s has invalid PatchURL: %s baseURL: %s", pr.Number, g, pr.PatchURL, commonCloneBaseURL)
+ pr.PatchURL = ""
+ valid = false
+ }
+
+ // SECURITY: the headCloneURL must be checked to have the same baseURL as the current to prevent open redirect
+ if pr.Head.CloneURL != "" && !hasBaseURL(pr.Head.CloneURL, commonCloneBaseURL) {
+ // TODO: Should we check that this url has the expected format for a patch url?
+ WarnAndNotice("PR #%d in %s has invalid HeadCloneURL: %s baseURL: %s", pr.Number, g, pr.Head.CloneURL, commonCloneBaseURL)
+ pr.Head.CloneURL = ""
+ valid = false
+ }
+
+ // SECURITY: SHAs Must be a SHA
+ if pr.MergeCommitSHA != "" && !git.IsValidSHAPattern(pr.MergeCommitSHA) {
+ WarnAndNotice("PR #%d in %s has invalid MergeCommitSHA: %s", pr.Number, g, pr.MergeCommitSHA)
+ pr.MergeCommitSHA = ""
+ }
+ if pr.Head.SHA != "" && !git.IsValidSHAPattern(pr.Head.SHA) {
+ WarnAndNotice("PR #%d in %s has invalid HeadSHA: %s", pr.Number, g, pr.Head.SHA)
+ pr.Head.SHA = ""
+ valid = false
+ }
+ if pr.Base.SHA != "" && !git.IsValidSHAPattern(pr.Base.SHA) {
+ WarnAndNotice("PR #%d in %s has invalid BaseSHA: %s", pr.Number, g, pr.Base.SHA)
+ pr.Base.SHA = ""
+ valid = false
+ }
+
+ // SECURITY: Refs must be valid refs or SHAs
+ if pr.Head.Ref != "" && !git.IsValidRefPattern(pr.Head.Ref) {
+ WarnAndNotice("PR #%d in %s has invalid HeadRef: %s", pr.Number, g, pr.Head.Ref)
+ pr.Head.Ref = ""
+ valid = false
+ }
+ if pr.Base.Ref != "" && !git.IsValidRefPattern(pr.Base.Ref) {
+ WarnAndNotice("PR #%d in %s has invalid BaseRef: %s", pr.Number, g, pr.Base.Ref)
+ pr.Base.Ref = ""
+ valid = false
+ }
+
+ pr.EnsuredSafe = true
+
+ return valid
+}
diff --git a/services/migrations/dump.go b/services/migrations/dump.go
index a9ec459519..188f2775e0 100644
--- a/services/migrations/dump.go
+++ b/services/migrations/dump.go
@@ -12,7 +12,6 @@ import (
"net/http"
"net/url"
"os"
- "path"
"path/filepath"
"strconv"
"strings"
@@ -26,6 +25,7 @@ import (
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/structs"
+ "github.com/google/uuid"
"gopkg.in/yaml.v2"
)
@@ -47,7 +47,7 @@ type RepositoryDumper struct {
reviewFiles map[int64]*os.File
gitRepo *git.Repository
- prHeadCache map[string]struct{}
+ prHeadCache map[string]string
}
// NewRepositoryDumper creates an gitea Uploader
@@ -62,7 +62,7 @@ func NewRepositoryDumper(ctx context.Context, baseDir, repoOwner, repoName strin
baseDir: baseDir,
repoOwner: repoOwner,
repoName: repoName,
- prHeadCache: make(map[string]struct{}),
+ prHeadCache: make(map[string]string),
commentFiles: make(map[int64]*os.File),
reviewFiles: make(map[int64]*os.File),
}, nil
@@ -296,8 +296,10 @@ func (g *RepositoryDumper) CreateReleases(releases ...*base.Release) error {
}
for _, asset := range release.Assets {
attachLocalPath := filepath.Join(attachDir, asset.Name)
- // download attachment
+ // SECURITY: We cannot check the DownloadURL and DownloadFunc are safe here
+ // ... we must assume that they are safe and simply download the attachment
+ // download attachment
err := func(attachPath string) error {
var rc io.ReadCloser
var err error
@@ -317,7 +319,7 @@ func (g *RepositoryDumper) CreateReleases(releases ...*base.Release) error {
fw, err := os.Create(attachPath)
if err != nil {
- return fmt.Errorf("Create: %v", err)
+ return fmt.Errorf("create: %w", err)
}
defer fw.Close()
@@ -385,22 +387,7 @@ func (g *RepositoryDumper) createItems(dir string, itemFiles map[int64]*os.File,
}
for number, items := range itemsMap {
- var err error
- itemFile := itemFiles[number]
- if itemFile == nil {
- itemFile, err = os.Create(filepath.Join(dir, fmt.Sprintf("%d.yml", number)))
- if err != nil {
- return err
- }
- itemFiles[number] = itemFile
- }
-
- bs, err := yaml.Marshal(items)
- if err != nil {
- return err
- }
-
- if _, err := itemFile.Write(bs); err != nil {
+ if err := g.encodeItems(number, items, dir, itemFiles); err != nil {
return err
}
}
@@ -408,6 +395,23 @@ func (g *RepositoryDumper) createItems(dir string, itemFiles map[int64]*os.File,
return nil
}
+func (g *RepositoryDumper) encodeItems(number int64, items []interface{}, dir string, itemFiles map[int64]*os.File) error {
+ itemFile := itemFiles[number]
+ if itemFile == nil {
+ var err error
+ itemFile, err = os.Create(filepath.Join(dir, fmt.Sprintf("%d.yml", number)))
+ if err != nil {
+ return err
+ }
+ itemFiles[number] = itemFile
+ }
+
+ encoder := yaml.NewEncoder(itemFile)
+ defer encoder.Close()
+
+ return encoder.Encode(items)
+}
+
// CreateComments creates comments of issues
func (g *RepositoryDumper) CreateComments(comments ...*base.Comment) error {
commentsMap := make(map[int64][]interface{}, len(comments))
@@ -418,102 +422,175 @@ func (g *RepositoryDumper) CreateComments(comments ...*base.Comment) error {
return g.createItems(g.commentDir(), g.commentFiles, commentsMap)
}
-// CreatePullRequests creates pull requests
-func (g *RepositoryDumper) CreatePullRequests(prs ...*base.PullRequest) error {
- for _, pr := range prs {
- // download patch file
- err := func() error {
- u, err := g.setURLToken(pr.PatchURL)
- if err != nil {
- return err
- }
- resp, err := http.Get(u)
- if err != nil {
- return err
- }
- defer resp.Body.Close()
- pullDir := filepath.Join(g.gitPath(), "pulls")
- if err = os.MkdirAll(pullDir, os.ModePerm); err != nil {
- return err
- }
- fPath := filepath.Join(pullDir, fmt.Sprintf("%d.patch", pr.Number))
- f, err := os.Create(fPath)
- if err != nil {
- return err
- }
- defer f.Close()
- if _, err = io.Copy(f, resp.Body); err != nil {
- return err
- }
- pr.PatchURL = "git/pulls/" + fmt.Sprintf("%d.patch", pr.Number)
-
- return nil
- }()
- if err != nil {
- return err
- }
-
- // set head information
- pullHead := filepath.Join(g.gitPath(), "refs", "pull", fmt.Sprintf("%d", pr.Number))
- if err := os.MkdirAll(pullHead, os.ModePerm); err != nil {
- return err
- }
- p, err := os.Create(filepath.Join(pullHead, "head"))
- if err != nil {
- return err
- }
- _, err = p.WriteString(pr.Head.SHA)
- p.Close()
- if err != nil {
- return err
- }
-
- if pr.IsForkPullRequest() && pr.State != "closed" {
- if pr.Head.OwnerName != "" {
- remote := pr.Head.OwnerName
- _, ok := g.prHeadCache[remote]
- if !ok {
- // git remote add
- // TODO: how to handle private CloneURL?
- err := g.gitRepo.AddRemote(remote, pr.Head.CloneURL, true)
- if err != nil {
- log.Error("AddRemote failed: %s", err)
- } else {
- g.prHeadCache[remote] = struct{}{}
- ok = true
- }
- }
-
- if ok {
- _, _, err = git.NewCommand(g.ctx, "fetch", remote, pr.Head.Ref).RunStdString(&git.RunOpts{Dir: g.gitPath()})
- if err != nil {
- log.Error("Fetch branch from %s failed: %v", pr.Head.CloneURL, err)
- } else {
- // a new branch name with will be created to as new head branch
- ref := path.Join(pr.Head.OwnerName, pr.Head.Ref)
- headBranch := filepath.Join(g.gitPath(), "refs", "heads", ref)
- if err := os.MkdirAll(filepath.Dir(headBranch), os.ModePerm); err != nil {
- return err
- }
- b, err := os.Create(headBranch)
- if err != nil {
- return err
- }
- _, err = b.WriteString(pr.Head.SHA)
- b.Close()
- if err != nil {
- return err
- }
- pr.Head.Ref = ref
- }
- }
- }
- }
- // whatever it's a forked repo PR, we have to change head info as the same as the base info
- pr.Head.OwnerName = pr.Base.OwnerName
- pr.Head.RepoName = pr.Base.RepoName
+func (g *RepositoryDumper) handlePullRequest(pr *base.PullRequest) error {
+ // SECURITY: this pr must have been ensured safe
+ if !pr.EnsuredSafe {
+ log.Error("PR #%d in %s/%s has not been checked for safety ... We will ignore this.", pr.Number, g.repoOwner, g.repoName)
+ return fmt.Errorf("unsafe PR #%d", pr.Number)
}
+ // First we download the patch file
+ err := func() error {
+ // if the patchURL is empty there is nothing to download
+ if pr.PatchURL == "" {
+ return nil
+ }
+
+ // SECURITY: We will assume that the pr.PatchURL has been checked
+ // pr.PatchURL maybe a local file - but note EnsureSafe should be asserting that this safe
+ u, err := g.setURLToken(pr.PatchURL)
+ if err != nil {
+ return err
+ }
+
+ // SECURITY: We will assume that the pr.PatchURL has been checked
+ // pr.PatchURL maybe a local file - but note EnsureSafe should be asserting that this safe
+ resp, err := http.Get(u) // TODO: This probably needs to use the downloader as there may be rate limiting issues here
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+ pullDir := filepath.Join(g.gitPath(), "pulls")
+ if err = os.MkdirAll(pullDir, os.ModePerm); err != nil {
+ return err
+ }
+ fPath := filepath.Join(pullDir, fmt.Sprintf("%d.patch", pr.Number))
+ f, err := os.Create(fPath)
+ if err != nil {
+ return err
+ }
+ defer f.Close()
+
+ // TODO: Should there be limits on the size of this file?
+ if _, err = io.Copy(f, resp.Body); err != nil {
+ return err
+ }
+ pr.PatchURL = "git/pulls/" + fmt.Sprintf("%d.patch", pr.Number)
+
+ return nil
+ }()
+ if err != nil {
+ log.Error("PR #%d in %s/%s unable to download patch: %v", pr.Number, g.repoOwner, g.repoName, err)
+ return err
+ }
+
+ isFork := pr.IsForkPullRequest()
+
+ // Even if it's a forked repo PR, we have to change head info as the same as the base info
+ oldHeadOwnerName := pr.Head.OwnerName
+ pr.Head.OwnerName, pr.Head.RepoName = pr.Base.OwnerName, pr.Base.RepoName
+
+ if !isFork || pr.State == "closed" {
+ return nil
+ }
+
+ // OK we want to fetch the current head as a branch from its CloneURL
+
+ // 1. Is there a head clone URL available?
+ // 2. Is there a head ref available?
+ if pr.Head.CloneURL == "" || pr.Head.Ref == "" {
+ // Set head information if pr.Head.SHA is available
+ if pr.Head.SHA != "" {
+ _, _, err = git.NewCommand(g.ctx, "update-ref", "--no-deref", pr.GetGitRefName(), pr.Head.SHA).RunStdString(&git.RunOpts{Dir: g.gitPath()})
+ if err != nil {
+ log.Error("PR #%d in %s/%s unable to update-ref for pr HEAD: %v", pr.Number, g.repoOwner, g.repoName, err)
+ }
+ }
+ return nil
+ }
+
+ // 3. We need to create a remote for this clone url
+ // ... maybe we already have a name for this remote
+ remote, ok := g.prHeadCache[pr.Head.CloneURL+":"]
+ if !ok {
+ // ... let's try ownername as a reasonable name
+ remote = oldHeadOwnerName
+ if !git.IsValidRefPattern(remote) {
+ // ... let's try something less nice
+ remote = "head-pr-" + strconv.FormatInt(pr.Number, 10)
+ }
+ // ... now add the remote
+ err := g.gitRepo.AddRemote(remote, pr.Head.CloneURL, true)
+ if err != nil {
+ log.Error("PR #%d in %s/%s AddRemote[%s] failed: %v", pr.Number, g.repoOwner, g.repoName, remote, err)
+ } else {
+ g.prHeadCache[pr.Head.CloneURL+":"] = remote
+ ok = true
+ }
+ }
+ if !ok {
+ // Set head information if pr.Head.SHA is available
+ if pr.Head.SHA != "" {
+ _, _, err = git.NewCommand(g.ctx, "update-ref", "--no-deref", pr.GetGitRefName(), pr.Head.SHA).RunStdString(&git.RunOpts{Dir: g.gitPath()})
+ if err != nil {
+ log.Error("PR #%d in %s/%s unable to update-ref for pr HEAD: %v", pr.Number, g.repoOwner, g.repoName, err)
+ }
+ }
+
+ return nil
+ }
+
+ // 4. Check if we already have this ref?
+ localRef, ok := g.prHeadCache[pr.Head.CloneURL+":"+pr.Head.Ref]
+ if !ok {
+ // ... We would normally name this migrated branch as / but we need to ensure that is safe
+ localRef = git.SanitizeRefPattern(oldHeadOwnerName + "/" + pr.Head.Ref)
+
+ // ... Now we must assert that this does not exist
+ if g.gitRepo.IsBranchExist(localRef) {
+ localRef = "head-pr-" + strconv.FormatInt(pr.Number, 10) + "/" + localRef
+ i := 0
+ for g.gitRepo.IsBranchExist(localRef) {
+ if i > 5 {
+ // ... We tried, we really tried but this is just a seriously unfriendly repo
+ return fmt.Errorf("unable to create unique local reference from %s", pr.Head.Ref)
+ }
+ // OK just try some uuids!
+ localRef = git.SanitizeRefPattern("head-pr-" + strconv.FormatInt(pr.Number, 10) + uuid.New().String())
+ i++
+ }
+ }
+
+ fetchArg := pr.Head.Ref + ":" + git.BranchPrefix + localRef
+ if strings.HasPrefix(fetchArg, "-") {
+ fetchArg = git.BranchPrefix + fetchArg
+ }
+
+ _, _, err = git.NewCommand(g.ctx, "fetch", "--no-tags", "--", remote, fetchArg).RunStdString(&git.RunOpts{Dir: g.gitPath()})
+ if err != nil {
+ log.Error("Fetch branch from %s failed: %v", pr.Head.CloneURL, err)
+ // We need to continue here so that the Head.Ref is reset and we attempt to set the gitref for the PR
+ // (This last step will likely fail but we should try to do as much as we can.)
+ } else {
+ // Cache the localRef as the Head.Ref - if we've failed we can always try again.
+ g.prHeadCache[pr.Head.CloneURL+":"+pr.Head.Ref] = localRef
+ }
+ }
+
+ // Set the pr.Head.Ref to the localRef
+ pr.Head.Ref = localRef
+
+ // 5. Now if pr.Head.SHA == "" we should recover this to the head of this branch
+ if pr.Head.SHA == "" {
+ headSha, err := g.gitRepo.GetBranchCommitID(localRef)
+ if err != nil {
+ log.Error("unable to get head SHA of local head for PR #%d from %s in %s/%s. Error: %v", pr.Number, pr.Head.Ref, g.repoOwner, g.repoName, err)
+ return nil
+ }
+ pr.Head.SHA = headSha
+ }
+ if pr.Head.SHA != "" {
+ _, _, err = git.NewCommand(g.ctx, "update-ref", "--no-deref", pr.GetGitRefName(), pr.Head.SHA).RunStdString(&git.RunOpts{Dir: g.gitPath()})
+ if err != nil {
+ log.Error("unable to set %s as the local head for PR #%d from %s in %s/%s. Error: %v", pr.Head.SHA, pr.Number, pr.Head.Ref, g.repoOwner, g.repoName, err)
+ }
+ }
+
+ return nil
+}
+
+// CreatePullRequests creates pull requests
+func (g *RepositoryDumper) CreatePullRequests(prs ...*base.PullRequest) error {
var err error
if g.pullrequestFile == nil {
if err := os.MkdirAll(g.baseDir, os.ModePerm); err != nil {
@@ -525,16 +602,22 @@ func (g *RepositoryDumper) CreatePullRequests(prs ...*base.PullRequest) error {
}
}
- bs, err := yaml.Marshal(prs)
- if err != nil {
- return err
- }
+ encoder := yaml.NewEncoder(g.pullrequestFile)
+ defer encoder.Close()
- if _, err := g.pullrequestFile.Write(bs); err != nil {
- return err
+ count := 0
+ for i := 0; i < len(prs); i++ {
+ pr := prs[i]
+ if err := g.handlePullRequest(pr); err != nil {
+ log.Error("PR #%d in %s/%s failed - skipping", pr.Number, g.repoOwner, g.repoName, err)
+ continue
+ }
+ prs[count] = pr
+ count++
}
+ prs = prs[:count]
- return nil
+ return encoder.Encode(prs)
}
// CreateReviews create pull request reviews
@@ -560,6 +643,10 @@ func (g *RepositoryDumper) Finish() error {
// DumpRepository dump repository according MigrateOptions to a local directory
func DumpRepository(ctx context.Context, baseDir, ownerName string, opts base.MigrateOptions) error {
+ doer, err := user_model.GetAdminUser()
+ if err != nil {
+ return err
+ }
downloader, err := newDownloader(ctx, ownerName, opts)
if err != nil {
return err
@@ -569,7 +656,7 @@ func DumpRepository(ctx context.Context, baseDir, ownerName string, opts base.Mi
return err
}
- if err := migrateRepository(downloader, uploader, opts, nil); err != nil {
+ if err := migrateRepository(doer, downloader, uploader, opts, nil); err != nil {
if err1 := uploader.Rollback(); err1 != nil {
log.Error("rollback failed: %v", err1)
}
@@ -641,7 +728,7 @@ func RestoreRepository(ctx context.Context, baseDir, ownerName, repoName string,
return err
}
- if err = migrateRepository(downloader, uploader, migrateOpts, nil); err != nil {
+ if err = migrateRepository(doer, downloader, uploader, migrateOpts, nil); err != nil {
if err1 := uploader.Rollback(); err1 != nil {
log.Error("rollback failed: %v", err1)
}
diff --git a/services/migrations/gitbucket.go b/services/migrations/gitbucket.go
index 92b6cac738..21d8c672dd 100644
--- a/services/migrations/gitbucket.go
+++ b/services/migrations/gitbucket.go
@@ -6,9 +6,11 @@ package migrations
import (
"context"
+ "fmt"
"net/url"
"strings"
+ "code.gitea.io/gitea/modules/log"
base "code.gitea.io/gitea/modules/migration"
"code.gitea.io/gitea/modules/structs"
)
@@ -37,6 +39,7 @@ func (f *GitBucketDownloaderFactory) New(ctx context.Context, opts base.MigrateO
oldOwner := fields[1]
oldName := strings.TrimSuffix(fields[2], ".git")
+ log.Trace("Create GitBucket downloader. BaseURL: %s RepoOwner: %s RepoName: %s", baseURL, oldOwner, oldName)
return NewGitBucketDownloader(ctx, baseURL, opts.AuthUsername, opts.AuthPassword, opts.AuthToken, oldOwner, oldName), nil
}
@@ -51,6 +54,20 @@ type GitBucketDownloader struct {
*GithubDownloaderV3
}
+// String implements Stringer
+func (g *GitBucketDownloader) String() string {
+ return fmt.Sprintf("migration from gitbucket server %s %s/%s", g.baseURL, g.repoOwner, g.repoName)
+}
+
+// ColorFormat provides a basic color format for a GitBucketDownloader
+func (g *GitBucketDownloader) ColorFormat(s fmt.State) {
+ if g == nil {
+ log.ColorFprintf(s, "")
+ return
+ }
+ log.ColorFprintf(s, "migration from gitbucket server %s %s/%s", g.baseURL, g.repoOwner, g.repoName)
+}
+
// NewGitBucketDownloader creates a GitBucket downloader
func NewGitBucketDownloader(ctx context.Context, baseURL, userName, password, token, repoOwner, repoName string) *GitBucketDownloader {
githubDownloader := NewGithubDownloaderV3(ctx, baseURL, userName, password, token, repoOwner, repoName)
diff --git a/services/migrations/gitea_downloader.go b/services/migrations/gitea_downloader.go
index 4ad55894ee..c52f302691 100644
--- a/services/migrations/gitea_downloader.go
+++ b/services/migrations/gitea_downloader.go
@@ -14,7 +14,6 @@ import (
"strings"
"time"
- admin_model "code.gitea.io/gitea/models/admin"
"code.gitea.io/gitea/modules/log"
base "code.gitea.io/gitea/modules/migration"
"code.gitea.io/gitea/modules/structs"
@@ -71,6 +70,7 @@ type GiteaDownloader struct {
base.NullDownloader
ctx context.Context
client *gitea_sdk.Client
+ baseURL string
repoOwner string
repoName string
pagination bool
@@ -78,8 +78,9 @@ type GiteaDownloader struct {
}
// NewGiteaDownloader creates a gitea Downloader via gitea API
-// Use either a username/password or personal token. token is preferred
-// Note: Public access only allows very basic access
+//
+// Use either a username/password or personal token. token is preferred
+// Note: Public access only allows very basic access
func NewGiteaDownloader(ctx context.Context, baseURL, repoPath, username, password, token string) (*GiteaDownloader, error) {
giteaClient, err := gitea_sdk.NewClient(
baseURL,
@@ -116,6 +117,7 @@ func NewGiteaDownloader(ctx context.Context, baseURL, repoPath, username, passwo
return &GiteaDownloader{
ctx: ctx,
client: giteaClient,
+ baseURL: baseURL,
repoOwner: path[0],
repoName: path[1],
pagination: paginationSupport,
@@ -128,6 +130,20 @@ func (g *GiteaDownloader) SetContext(ctx context.Context) {
g.ctx = ctx
}
+// String implements Stringer
+func (g *GiteaDownloader) String() string {
+ return fmt.Sprintf("migration from gitea server %s %s/%s", g.baseURL, g.repoOwner, g.repoName)
+}
+
+// ColorFormat provides a basic color format for a GiteaDownloader
+func (g *GiteaDownloader) ColorFormat(s fmt.State) {
+ if g == nil {
+ log.ColorFprintf(s, "")
+ return
+ }
+ log.ColorFprintf(s, "migration from gitea server %s %s/%s", g.baseURL, g.repoOwner, g.repoName)
+}
+
// GetRepoInfo returns a repository information
func (g *GiteaDownloader) GetRepoInfo() (*base.Repository, error) {
if g == nil {
@@ -283,6 +299,12 @@ func (g *GiteaDownloader) convertGiteaRelease(rel *gitea_sdk.Release) *base.Rele
if err != nil {
return nil, err
}
+
+ if !hasBaseURL(asset.DownloadURL, g.baseURL) {
+ WarnAndNotice("Unexpected AssetURL for assetID[%d] in %s: %s", asset.ID, g, asset.DownloadURL)
+ return io.NopCloser(strings.NewReader(asset.DownloadURL)), nil
+ }
+
// FIXME: for a private download?
req, err := http.NewRequest("GET", asset.DownloadURL, nil)
if err != nil {
@@ -402,11 +424,7 @@ func (g *GiteaDownloader) GetIssues(page, perPage int) ([]*base.Issue, bool, err
reactions, err := g.getIssueReactions(issue.Index)
if err != nil {
- log.Warn("Unable to load reactions during migrating issue #%d to %s/%s. Error: %v", issue.Index, g.repoOwner, g.repoName, err)
- if err2 := admin_model.CreateRepositoryNotice(
- fmt.Sprintf("Unable to load reactions during migrating issue #%d to %s/%s. Error: %v", issue.Index, g.repoOwner, g.repoName, err)); err2 != nil {
- log.Error("create repository notice failed: ", err2)
- }
+ WarnAndNotice("Unable to load reactions during migrating issue #%d in %s. Error: %v", issue.Index, g, err)
}
var assignees []string
@@ -464,11 +482,7 @@ func (g *GiteaDownloader) GetComments(commentable base.Commentable) ([]*base.Com
for _, comment := range comments {
reactions, err := g.getCommentReactions(comment.ID)
if err != nil {
- log.Warn("Unable to load comment reactions during migrating issue #%d for comment %d to %s/%s. Error: %v", commentable.GetForeignIndex(), comment.ID, g.repoOwner, g.repoName, err)
- if err2 := admin_model.CreateRepositoryNotice(
- fmt.Sprintf("Unable to load reactions during migrating issue #%d for comment %d to %s/%s. Error: %v", commentable.GetForeignIndex(), comment.ID, g.repoOwner, g.repoName, err)); err2 != nil {
- log.Error("create repository notice failed: ", err2)
- }
+ WarnAndNotice("Unable to load comment reactions during migrating issue #%d for comment %d in %s. Error: %v", commentable.GetForeignIndex(), comment.ID, g, err)
}
allComments = append(allComments, &base.Comment{
@@ -543,11 +557,7 @@ func (g *GiteaDownloader) GetPullRequests(page, perPage int) ([]*base.PullReques
reactions, err := g.getIssueReactions(pr.Index)
if err != nil {
- log.Warn("Unable to load reactions during migrating pull #%d to %s/%s. Error: %v", pr.Index, g.repoOwner, g.repoName, err)
- if err2 := admin_model.CreateRepositoryNotice(
- fmt.Sprintf("Unable to load reactions during migrating pull #%d to %s/%s. Error: %v", pr.Index, g.repoOwner, g.repoName, err)); err2 != nil {
- log.Error("create repository notice failed: ", err2)
- }
+ WarnAndNotice("Unable to load reactions during migrating pull #%d in %s. Error: %v", pr.Index, g, err)
}
var assignees []string
@@ -604,6 +614,8 @@ func (g *GiteaDownloader) GetPullRequests(page, perPage int) ([]*base.PullReques
},
ForeignIndex: pr.Index,
})
+ // SECURITY: Ensure that the PR is safe
+ _ = CheckAndEnsureSafePR(allPRs[len(allPRs)-1], g.baseURL, g)
}
isEnd := len(prs) < perPage
diff --git a/services/migrations/gitea_uploader.go b/services/migrations/gitea_uploader.go
index c7a6f9b02f..83388391da 100644
--- a/services/migrations/gitea_uploader.go
+++ b/services/migrations/gitea_uploader.go
@@ -10,6 +10,7 @@ import (
"fmt"
"io"
"os"
+ "path"
"path/filepath"
"strconv"
"strings"
@@ -32,7 +33,7 @@ import (
"code.gitea.io/gitea/modules/uri"
"code.gitea.io/gitea/services/pull"
- gouuid "github.com/google/uuid"
+ "github.com/google/uuid"
)
var _ base.Uploader = &GiteaLocalUploader{}
@@ -48,7 +49,7 @@ type GiteaLocalUploader struct {
milestones map[string]int64
issues map[int64]*issues_model.Issue
gitRepo *git.Repository
- prHeadCache map[string]struct{}
+ prHeadCache map[string]string
sameApp bool
userMap map[int64]int64 // external user id mapping to user id
prCache map[int64]*issues_model.PullRequest
@@ -65,7 +66,7 @@ func NewGiteaLocalUploader(ctx context.Context, doer *user_model.User, repoOwner
labels: make(map[string]*issues_model.Label),
milestones: make(map[string]int64),
issues: make(map[int64]*issues_model.Issue),
- prHeadCache: make(map[string]struct{}),
+ prHeadCache: make(map[string]string),
userMap: make(map[int64]int64),
prCache: make(map[int64]*issues_model.PullRequest),
}
@@ -83,7 +84,7 @@ func (g *GiteaLocalUploader) MaxBatchInsertSize(tp string) int {
case "label":
return db.MaxBatchInsertSize(new(issues_model.Label))
case "release":
- return db.MaxBatchInsertSize(new(models.Release))
+ return db.MaxBatchInsertSize(new(repo_model.Release))
case "pullrequest":
return db.MaxBatchInsertSize(new(issues_model.PullRequest))
}
@@ -99,7 +100,7 @@ func (g *GiteaLocalUploader) CreateRepo(repo *base.Repository, opts base.Migrate
var r *repo_model.Repository
if opts.MigrateToRepoID <= 0 {
- r, err = repo_module.CreateRepository(g.doer, owner, models.CreateRepoOptions{
+ r, err = repo_module.CreateRepository(g.doer, owner, repo_module.CreateRepoOptions{
Name: g.repoName,
Description: repo.Description,
OriginalURL: repo.OriginalURL,
@@ -125,7 +126,7 @@ func (g *GiteaLocalUploader) CreateRepo(repo *base.Repository, opts base.Migrate
Mirror: repo.IsMirror,
LFS: opts.LFS,
LFSEndpoint: opts.LFSEndpoint,
- CloneAddr: repo.CloneURL,
+ CloneAddr: repo.CloneURL, // SECURITY: we will assume that this has already been checked
Private: repo.IsPrivate,
Wiki: opts.Wiki,
Releases: opts.Releases, // if didn't get releases, then sync them from tags
@@ -150,13 +151,15 @@ func (g *GiteaLocalUploader) Close() {
// CreateTopics creates topics
func (g *GiteaLocalUploader) CreateTopics(topics ...string) error {
- // ignore topics to long for the db
+ // Ignore topics too long for the db
c := 0
- for i := range topics {
- if len(topics[i]) <= 50 {
- topics[c] = topics[i]
- c++
+ for _, topic := range topics {
+ if len(topic) > 50 {
+ continue
}
+
+ topics[c] = topic
+ c++
}
topics = topics[:c]
return repo_model.SaveTopics(g.repo.ID, topics...)
@@ -217,11 +220,17 @@ func (g *GiteaLocalUploader) CreateMilestones(milestones ...*base.Milestone) err
func (g *GiteaLocalUploader) CreateLabels(labels ...*base.Label) error {
lbs := make([]*issues_model.Label, 0, len(labels))
for _, label := range labels {
+ // We must validate color here:
+ if !issues_model.LabelColorPattern.MatchString("#" + label.Color) {
+ log.Warn("Invalid label color: #%s for label: %s in migration to %s/%s", label.Color, label.Name, g.repoOwner, g.repoName)
+ label.Color = "ffffff"
+ }
+
lbs = append(lbs, &issues_model.Label{
RepoID: g.repo.ID,
Name: label.Name,
Description: label.Description,
- Color: fmt.Sprintf("#%s", label.Color),
+ Color: "#" + label.Color,
})
}
@@ -237,7 +246,7 @@ func (g *GiteaLocalUploader) CreateLabels(labels ...*base.Label) error {
// CreateReleases creates releases
func (g *GiteaLocalUploader) CreateReleases(releases ...*base.Release) error {
- rels := make([]*models.Release, 0, len(releases))
+ rels := make([]*repo_model.Release, 0, len(releases))
for _, release := range releases {
if release.Created.IsZero() {
if !release.Published.IsZero() {
@@ -247,7 +256,17 @@ func (g *GiteaLocalUploader) CreateReleases(releases ...*base.Release) error {
}
}
- rel := models.Release{
+ // SECURITY: The TagName must be a valid git ref
+ if release.TagName != "" && !git.IsValidRefPattern(release.TagName) {
+ release.TagName = ""
+ }
+
+ // SECURITY: The TargetCommitish must be a valid git ref
+ if release.TargetCommitish != "" && !git.IsValidRefPattern(release.TargetCommitish) {
+ release.TargetCommitish = ""
+ }
+
+ rel := repo_model.Release{
RepoID: g.repo.ID,
TagName: release.TagName,
LowerTagName: strings.ToLower(release.TagName),
@@ -288,14 +307,15 @@ func (g *GiteaLocalUploader) CreateReleases(releases ...*base.Release) error {
}
}
attach := repo_model.Attachment{
- UUID: gouuid.New().String(),
+ UUID: uuid.New().String(),
Name: asset.Name,
DownloadCount: int64(*asset.DownloadCount),
Size: int64(*asset.Size),
CreatedUnix: timeutil.TimeStamp(asset.Created.Unix()),
}
- // download attachment
+ // SECURITY: We cannot check the DownloadURL and DownloadFunc are safe here
+ // ... we must assume that they are safe and simply download the attachment
err := func() error {
// asset.DownloadURL maybe a local file
var rc io.ReadCloser
@@ -365,6 +385,12 @@ func (g *GiteaLocalUploader) CreateIssues(issues ...*base.Issue) error {
}
}
+ // SECURITY: issue.Ref needs to be a valid reference
+ if !git.IsValidRefPattern(issue.Ref) {
+ log.Warn("Invalid issue.Ref[%s] in issue #%d in %s/%s", issue.Ref, issue.Number, g.repoOwner, g.repoName)
+ issue.Ref = ""
+ }
+
is := issues_model.Issue{
RepoID: g.repo.ID,
Repo: g.repo,
@@ -386,6 +412,10 @@ func (g *GiteaLocalUploader) CreateIssues(issues ...*base.Issue) error {
},
}
+ if is.ForeignReference.ForeignIndex == "0" {
+ is.ForeignReference.ForeignIndex = strconv.FormatInt(is.Index, 10)
+ }
+
if err := g.remapUser(issue, &is); err != nil {
return err
}
@@ -496,102 +526,151 @@ func (g *GiteaLocalUploader) CreatePullRequests(prs ...*base.PullRequest) error
}
func (g *GiteaLocalUploader) updateGitForPullRequest(pr *base.PullRequest) (head string, err error) {
- // download patch file
+ // SECURITY: this pr must have been must have been ensured safe
+ if !pr.EnsuredSafe {
+ log.Error("PR #%d in %s/%s has not been checked for safety.", pr.Number, g.repoOwner, g.repoName)
+ return "", fmt.Errorf("the PR[%d] was not checked for safety", pr.Number)
+ }
+
+ // Anonymous function to download the patch file (allows us to use defer)
err = func() error {
+ // if the patchURL is empty there is nothing to download
if pr.PatchURL == "" {
return nil
}
- // pr.PatchURL maybe a local file
- ret, err := uri.Open(pr.PatchURL)
+
+ // SECURITY: We will assume that the pr.PatchURL has been checked
+ // pr.PatchURL maybe a local file - but note EnsureSafe should be asserting that this safe
+ ret, err := uri.Open(pr.PatchURL) // TODO: This probably needs to use the downloader as there may be rate limiting issues here
if err != nil {
return err
}
defer ret.Close()
+
pullDir := filepath.Join(g.repo.RepoPath(), "pulls")
if err = os.MkdirAll(pullDir, os.ModePerm); err != nil {
return err
}
+
f, err := os.Create(filepath.Join(pullDir, fmt.Sprintf("%d.patch", pr.Number)))
if err != nil {
return err
}
defer f.Close()
+
+ // TODO: Should there be limits on the size of this file?
_, err = io.Copy(f, ret)
+
return err
}()
if err != nil {
return "", err
}
- // set head information
- pullHead := filepath.Join(g.repo.RepoPath(), "refs", "pull", fmt.Sprintf("%d", pr.Number))
- if err := os.MkdirAll(pullHead, os.ModePerm); err != nil {
- return "", err
- }
- p, err := os.Create(filepath.Join(pullHead, "head"))
- if err != nil {
- return "", err
- }
- _, err = p.WriteString(pr.Head.SHA)
- p.Close()
- if err != nil {
- return "", err
- }
-
head = "unknown repository"
if pr.IsForkPullRequest() && pr.State != "closed" {
- if pr.Head.OwnerName != "" {
- remote := pr.Head.OwnerName
- _, ok := g.prHeadCache[remote]
- if !ok {
- // git remote add
- err := g.gitRepo.AddRemote(remote, pr.Head.CloneURL, true)
- if err != nil {
- log.Error("AddRemote failed: %s", err)
- } else {
- g.prHeadCache[remote] = struct{}{}
- ok = true
+ // OK we want to fetch the current head as a branch from its CloneURL
+
+ // 1. Is there a head clone URL available?
+ // 2. Is there a head ref available?
+ if pr.Head.CloneURL == "" || pr.Head.Ref == "" {
+ return head, nil
+ }
+
+ // 3. We need to create a remote for this clone url
+ // ... maybe we already have a name for this remote
+ remote, ok := g.prHeadCache[pr.Head.CloneURL+":"]
+ if !ok {
+ // ... let's try ownername as a reasonable name
+ remote = pr.Head.OwnerName
+ if !git.IsValidRefPattern(remote) {
+ // ... let's try something less nice
+ remote = "head-pr-" + strconv.FormatInt(pr.Number, 10)
+ }
+ // ... now add the remote
+ err := g.gitRepo.AddRemote(remote, pr.Head.CloneURL, true)
+ if err != nil {
+ log.Error("PR #%d in %s/%s AddRemote[%s] failed: %v", pr.Number, g.repoOwner, g.repoName, remote, err)
+ } else {
+ g.prHeadCache[pr.Head.CloneURL+":"] = remote
+ ok = true
+ }
+ }
+ if !ok {
+ return head, nil
+ }
+
+ // 4. Check if we already have this ref?
+ localRef, ok := g.prHeadCache[pr.Head.CloneURL+":"+pr.Head.Ref]
+ if !ok {
+ // ... We would normally name this migrated branch as / but we need to ensure that is safe
+ localRef = git.SanitizeRefPattern(pr.Head.OwnerName + "/" + pr.Head.Ref)
+
+ // ... Now we must assert that this does not exist
+ if g.gitRepo.IsBranchExist(localRef) {
+ localRef = "head-pr-" + strconv.FormatInt(pr.Number, 10) + "/" + localRef
+ i := 0
+ for g.gitRepo.IsBranchExist(localRef) {
+ if i > 5 {
+ // ... We tried, we really tried but this is just a seriously unfriendly repo
+ return head, nil
+ }
+ // OK just try some uuids!
+ localRef = git.SanitizeRefPattern("head-pr-" + strconv.FormatInt(pr.Number, 10) + uuid.New().String())
+ i++
}
}
- if ok {
- _, _, err = git.NewCommand(g.ctx, "fetch", "--no-tags", "--", remote, pr.Head.Ref).RunStdString(&git.RunOpts{Dir: g.repo.RepoPath()})
- if err != nil {
- log.Error("Fetch branch from %s failed: %v", pr.Head.CloneURL, err)
- } else {
- headBranch := filepath.Join(g.repo.RepoPath(), "refs", "heads", pr.Head.OwnerName, pr.Head.Ref)
- if err := os.MkdirAll(filepath.Dir(headBranch), os.ModePerm); err != nil {
- return "", err
- }
- b, err := os.Create(headBranch)
- if err != nil {
- return "", err
- }
- _, err = b.WriteString(pr.Head.SHA)
- b.Close()
- if err != nil {
- return "", err
- }
- head = pr.Head.OwnerName + "/" + pr.Head.Ref
- }
+ fetchArg := pr.Head.Ref + ":" + git.BranchPrefix + localRef
+ if strings.HasPrefix(fetchArg, "-") {
+ fetchArg = git.BranchPrefix + fetchArg
}
+
+ _, _, err = git.NewCommand(g.ctx, "fetch", "--no-tags", "--", remote, fetchArg).RunStdString(&git.RunOpts{Dir: g.repo.RepoPath()})
+ if err != nil {
+ log.Error("Fetch branch from %s failed: %v", pr.Head.CloneURL, err)
+ return head, nil
+ }
+ g.prHeadCache[pr.Head.CloneURL+":"+pr.Head.Ref] = localRef
+ head = localRef
}
- } else {
+
+ // 5. Now if pr.Head.SHA == "" we should recover this to the head of this branch
+ if pr.Head.SHA == "" {
+ headSha, err := g.gitRepo.GetBranchCommitID(localRef)
+ if err != nil {
+ log.Error("unable to get head SHA of local head for PR #%d from %s in %s/%s. Error: %v", pr.Number, pr.Head.Ref, g.repoOwner, g.repoName, err)
+ return head, nil
+ }
+ pr.Head.SHA = headSha
+ }
+
+ _, _, err = git.NewCommand(g.ctx, "update-ref", "--no-deref", pr.GetGitRefName(), pr.Head.SHA).RunStdString(&git.RunOpts{Dir: g.repo.RepoPath()})
+ if err != nil {
+ return "", err
+ }
+
+ return head, nil
+ }
+
+ if pr.Head.Ref != "" {
head = pr.Head.Ref
- // Ensure the closed PR SHA still points to an existing ref
+ }
+
+ // Ensure the closed PR SHA still points to an existing ref
+ if pr.Head.SHA == "" {
+ // The SHA is empty
+ log.Warn("Empty reference, no pull head for PR #%d in %s/%s", pr.Number, g.repoOwner, g.repoName)
+ } else {
_, _, err = git.NewCommand(g.ctx, "rev-list", "--quiet", "-1", pr.Head.SHA).RunStdString(&git.RunOpts{Dir: g.repo.RepoPath()})
if err != nil {
- if pr.Head.SHA != "" {
- // Git update-ref remove bad references with a relative path
- log.Warn("Deprecated local head, removing : %v", pr.Head.SHA)
- err = g.gitRepo.RemoveReference(pr.GetGitRefName())
- } else {
- // The SHA is empty, remove the head file
- log.Warn("Empty reference, removing : %v", pullHead)
- err = os.Remove(filepath.Join(pullHead, "head"))
- }
+ // Git update-ref remove bad references with a relative path
+ log.Warn("Deprecated local head %s for PR #%d in %s/%s, removing %s", pr.Head.SHA, pr.Number, g.repoOwner, g.repoName, pr.GetGitRefName())
+ } else {
+ // set head information
+ _, _, err = git.NewCommand(g.ctx, "update-ref", "--no-deref", pr.GetGitRefName(), pr.Head.SHA).RunStdString(&git.RunOpts{Dir: g.repo.RepoPath()})
if err != nil {
- log.Error("Cannot remove local head ref, %v", err)
+ log.Error("unable to set %s as the local head for PR #%d from %s in %s/%s. Error: %v", pr.Head.SHA, pr.Number, pr.Head.Ref, g.repoOwner, g.repoName, err)
}
}
}
@@ -615,6 +694,20 @@ func (g *GiteaLocalUploader) newPullRequest(pr *base.PullRequest) (*issues_model
return nil, fmt.Errorf("updateGitForPullRequest: %w", err)
}
+ // Now we may need to fix the mergebase
+ if pr.Base.SHA == "" {
+ if pr.Base.Ref != "" && pr.Head.SHA != "" {
+ // A PR against a tag base does not make sense - therefore pr.Base.Ref must be a branch
+ // TODO: should we be checking for the refs/heads/ prefix on the pr.Base.Ref? (i.e. are these actually branches or refs)
+ pr.Base.SHA, _, err = g.gitRepo.GetMergeBase("", git.BranchPrefix+pr.Base.Ref, pr.Head.SHA)
+ if err != nil {
+ log.Error("Cannot determine the merge base for PR #%d in %s/%s. Error: %v", pr.Number, g.repoOwner, g.repoName, err)
+ }
+ } else {
+ log.Error("Cannot determine the merge base for PR #%d in %s/%s. Not enough information", pr.Number, g.repoOwner, g.repoName)
+ }
+ }
+
if pr.Created.IsZero() {
if pr.Closed != nil {
pr.Created = *pr.Closed
@@ -728,6 +821,8 @@ func (g *GiteaLocalUploader) CreateReviews(reviews ...*base.Review) error {
return err
}
+ cms = append(cms, &cm)
+
// get pr
pr, ok := g.prCache[issue.ID]
if !ok {
@@ -738,6 +833,17 @@ func (g *GiteaLocalUploader) CreateReviews(reviews ...*base.Review) error {
}
g.prCache[issue.ID] = pr
}
+ if pr.MergeBase == "" {
+ // No mergebase -> no basis for any patches
+ log.Warn("PR #%d in %s/%s: does not have a merge base, all review comments will be ignored", pr.Index, g.repoOwner, g.repoName)
+ continue
+ }
+
+ headCommitID, err := g.gitRepo.GetRefCommitID(pr.GetGitRefName())
+ if err != nil {
+ log.Warn("PR #%d GetRefCommitID[%s] in %s/%s: %v, all review comments will be ignored", pr.Index, pr.GetGitRefName(), g.repoOwner, g.repoName, err)
+ continue
+ }
for _, comment := range review.Comments {
line := comment.Line
@@ -746,11 +852,9 @@ func (g *GiteaLocalUploader) CreateReviews(reviews ...*base.Review) error {
} else {
_, _, line, _ = git.ParseDiffHunkString(comment.DiffHunk)
}
- headCommitID, err := g.gitRepo.GetRefCommitID(pr.GetGitRefName())
- if err != nil {
- log.Warn("GetRefCommitID[%s]: %v, the review comment will be ignored", pr.GetGitRefName(), err)
- continue
- }
+
+ // SECURITY: The TreePath must be cleaned!
+ comment.TreePath = path.Clean("/" + comment.TreePath)[1:]
var patch string
reader, writer := io.Pipe()
@@ -775,6 +879,11 @@ func (g *GiteaLocalUploader) CreateReviews(reviews ...*base.Review) error {
comment.UpdatedAt = comment.CreatedAt
}
+ if !git.IsValidSHAPattern(comment.CommitID) {
+ log.Warn("Invalid comment CommitID[%s] on comment[%d] in PR #%d of %s/%s replaced with %s", comment.CommitID, pr.Index, g.repoOwner, g.repoName, headCommitID)
+ comment.CommitID = headCommitID
+ }
+
c := issues_model.Comment{
Type: issues_model.CommentTypeCode,
IssueID: issue.ID,
@@ -793,8 +902,6 @@ func (g *GiteaLocalUploader) CreateReviews(reviews ...*base.Review) error {
cm.Comments = append(cm.Comments, &c)
}
-
- cms = append(cms, &cm)
}
return issues_model.InsertReviews(cms)
diff --git a/services/migrations/gitea_uploader_test.go b/services/migrations/gitea_uploader_test.go
index 6ea1c20592..af6230decb 100644
--- a/services/migrations/gitea_uploader_test.go
+++ b/services/migrations/gitea_uploader_test.go
@@ -15,7 +15,6 @@ import (
"testing"
"time"
- "code.gitea.io/gitea/models"
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
repo_model "code.gitea.io/gitea/models/repo"
@@ -37,7 +36,7 @@ func TestGiteaUploadRepo(t *testing.T) {
unittest.PrepareTestEnv(t)
- user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User)
+ user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
var (
ctx = context.Background()
@@ -46,7 +45,7 @@ func TestGiteaUploadRepo(t *testing.T) {
uploader = NewGiteaLocalUploader(graceful.GetManager().HammerContext(), user, user.Name, repoName)
)
- err := migrateRepository(downloader, uploader, base.MigrateOptions{
+ err := migrateRepository(user, downloader, uploader, base.MigrateOptions{
CloneAddr: "https://github.com/go-xorm/builder",
RepoName: repoName,
AuthUsername: "",
@@ -63,7 +62,7 @@ func TestGiteaUploadRepo(t *testing.T) {
}, nil)
assert.NoError(t, err)
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerID: user.ID, Name: repoName}).(*repo_model.Repository)
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerID: user.ID, Name: repoName})
assert.True(t, repo.HasWiki())
assert.EqualValues(t, repo_model.RepositoryReady, repo.Status)
@@ -85,7 +84,7 @@ func TestGiteaUploadRepo(t *testing.T) {
assert.NoError(t, err)
assert.Len(t, labels, 12)
- releases, err := models.GetReleasesByRepoID(repo.ID, models.FindReleasesOptions{
+ releases, err := repo_model.GetReleasesByRepoID(repo.ID, repo_model.FindReleasesOptions{
ListOptions: db.ListOptions{
PageSize: 10,
Page: 0,
@@ -95,7 +94,7 @@ func TestGiteaUploadRepo(t *testing.T) {
assert.NoError(t, err)
assert.Len(t, releases, 8)
- releases, err = models.GetReleasesByRepoID(repo.ID, models.FindReleasesOptions{
+ releases, err = repo_model.GetReleasesByRepoID(repo.ID, repo_model.FindReleasesOptions{
ListOptions: db.ListOptions{
PageSize: 10,
Page: 0,
@@ -127,8 +126,8 @@ func TestGiteaUploadRepo(t *testing.T) {
func TestGiteaUploadRemapLocalUser(t *testing.T) {
unittest.PrepareTestEnv(t)
- doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User)
- user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
+ doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
+ user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
repoName := "migrated"
uploader := NewGiteaLocalUploader(context.Background(), doer, doer.Name, repoName)
@@ -146,7 +145,7 @@ func TestGiteaUploadRemapLocalUser(t *testing.T) {
// The externalID does not match any existing user, everything
// belongs to the doer
//
- target := models.Release{}
+ target := repo_model.Release{}
uploader.userMap = make(map[int64]int64)
err := uploader.remapUser(&source, &target)
assert.NoError(t, err)
@@ -157,7 +156,7 @@ func TestGiteaUploadRemapLocalUser(t *testing.T) {
// everything belongs to the doer
//
source.PublisherID = user.ID
- target = models.Release{}
+ target = repo_model.Release{}
uploader.userMap = make(map[int64]int64)
err = uploader.remapUser(&source, &target)
assert.NoError(t, err)
@@ -168,7 +167,7 @@ func TestGiteaUploadRemapLocalUser(t *testing.T) {
// belongs to the existing user
//
source.PublisherName = user.Name
- target = models.Release{}
+ target = repo_model.Release{}
uploader.userMap = make(map[int64]int64)
err = uploader.remapUser(&source, &target)
assert.NoError(t, err)
@@ -177,7 +176,7 @@ func TestGiteaUploadRemapLocalUser(t *testing.T) {
func TestGiteaUploadRemapExternalUser(t *testing.T) {
unittest.PrepareTestEnv(t)
- doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}).(*user_model.User)
+ doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
repoName := "migrated"
uploader := NewGiteaLocalUploader(context.Background(), doer, doer.Name, repoName)
@@ -197,7 +196,7 @@ func TestGiteaUploadRemapExternalUser(t *testing.T) {
// by the doer
//
uploader.userMap = make(map[int64]int64)
- target := models.Release{}
+ target := repo_model.Release{}
err := uploader.remapUser(&source, &target)
assert.NoError(t, err)
assert.EqualValues(t, doer.ID, target.GetUserID())
@@ -205,7 +204,7 @@ func TestGiteaUploadRemapExternalUser(t *testing.T) {
//
// Link the external ID to an existing user
//
- linkedUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
+ linkedUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
externalLoginUser := &user_model.ExternalLoginUser{
ExternalID: strconv.FormatInt(externalID, 10),
UserID: linkedUser.ID,
@@ -220,7 +219,7 @@ func TestGiteaUploadRemapExternalUser(t *testing.T) {
// the migrated data
//
uploader.userMap = make(map[int64]int64)
- target = models.Release{}
+ target = repo_model.Release{}
err = uploader.remapUser(&source, &target)
assert.NoError(t, err)
assert.EqualValues(t, linkedUser.ID, target.GetUserID())
@@ -232,7 +231,7 @@ func TestGiteaUploadUpdateGitForPullRequest(t *testing.T) {
//
// fromRepo master
//
- fromRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository)
+ fromRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
baseRef := "master"
assert.NoError(t, git.InitRepository(git.DefaultContext, fromRepo.RepoPath(), false))
err := git.NewCommand(git.DefaultContext, "symbolic-ref", "HEAD", git.BranchPrefix+baseRef).Run(&git.RunOpts{Dir: fromRepo.RepoPath()})
@@ -273,13 +272,13 @@ func TestGiteaUploadUpdateGitForPullRequest(t *testing.T) {
headSHA, err := fromGitRepo.GetBranchCommitID(headRef)
assert.NoError(t, err)
- fromRepoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: fromRepo.OwnerID}).(*user_model.User)
+ fromRepoOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: fromRepo.OwnerID})
//
// forkRepo branch2
//
forkHeadRef := "branch2"
- forkRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 8}).(*repo_model.Repository)
+ forkRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 8})
assert.NoError(t, git.CloneWithArgs(git.DefaultContext, fromRepo.RepoPath(), forkRepo.RepoPath(), []string{}, git.CloneRepoOptions{
Branch: headRef,
}))
@@ -391,7 +390,7 @@ func TestGiteaUploadUpdateGitForPullRequest(t *testing.T) {
},
},
assertContent: func(t *testing.T, content string) {
- assert.Contains(t, content, "AddRemote failed")
+ assert.Contains(t, content, "AddRemote")
},
},
{
@@ -440,7 +439,7 @@ func TestGiteaUploadUpdateGitForPullRequest(t *testing.T) {
},
},
assertContent: func(t *testing.T, content string) {
- assert.Contains(t, content, "Empty reference, removing")
+ assert.Contains(t, content, "Empty reference")
assert.NotContains(t, content, "Cannot remove local head")
},
},
@@ -468,7 +467,6 @@ func TestGiteaUploadUpdateGitForPullRequest(t *testing.T) {
},
assertContent: func(t *testing.T, content string) {
assert.Contains(t, content, "Deprecated local head")
- assert.Contains(t, content, "Cannot remove local head")
},
},
{
@@ -505,6 +503,8 @@ func TestGiteaUploadUpdateGitForPullRequest(t *testing.T) {
logger.SetLogger("buffer", "buffer", "{}")
defer logger.DelLogger("buffer")
+ testCase.pr.EnsuredSafe = true
+
head, err := uploader.updateGitForPullRequest(&testCase.pr)
assert.NoError(t, err)
assert.EqualValues(t, testCase.head, head)
diff --git a/services/migrations/github.go b/services/migrations/github.go
index 5f5b430fa9..0ffdbb042a 100644
--- a/services/migrations/github.go
+++ b/services/migrations/github.go
@@ -51,7 +51,7 @@ func (f *GithubDownloaderV3Factory) New(ctx context.Context, opts base.MigrateOp
oldOwner := fields[1]
oldName := strings.TrimSuffix(fields[2], ".git")
- log.Trace("Create github downloader: %s/%s", oldOwner, oldName)
+ log.Trace("Create github downloader BaseURL: %s %s/%s", baseURL, oldOwner, oldName)
return NewGithubDownloaderV3(ctx, baseURL, opts.AuthUsername, opts.AuthPassword, opts.AuthToken, oldOwner, oldName), nil
}
@@ -67,6 +67,7 @@ type GithubDownloaderV3 struct {
base.NullDownloader
ctx context.Context
clients []*github.Client
+ baseURL string
repoOwner string
repoName string
userName string
@@ -81,6 +82,7 @@ type GithubDownloaderV3 struct {
func NewGithubDownloaderV3(ctx context.Context, baseURL, userName, password, token, repoOwner, repoName string) *GithubDownloaderV3 {
downloader := GithubDownloaderV3{
userName: userName,
+ baseURL: baseURL,
password: password,
ctx: ctx,
repoOwner: repoOwner,
@@ -118,6 +120,20 @@ func NewGithubDownloaderV3(ctx context.Context, baseURL, userName, password, tok
return &downloader
}
+// String implements Stringer
+func (g *GithubDownloaderV3) String() string {
+ return fmt.Sprintf("migration from github server %s %s/%s", g.baseURL, g.repoOwner, g.repoName)
+}
+
+// ColorFormat provides a basic color format for a GithubDownloader
+func (g *GithubDownloaderV3) ColorFormat(s fmt.State) {
+ if g == nil {
+ log.ColorFprintf(s, "")
+ return
+ }
+ log.ColorFprintf(s, "migration from github server %s %s/%s", g.baseURL, g.repoOwner, g.repoName)
+}
+
func (g *GithubDownloaderV3) addClient(client *http.Client, baseURL string) {
githubClient := github.NewClient(client)
if baseURL != "https://github.com" {
@@ -322,33 +338,44 @@ func (g *GithubDownloaderV3) convertGithubRelease(rel *github.RepositoryRelease)
Updated: asset.UpdatedAt.Time,
DownloadFunc: func() (io.ReadCloser, error) {
g.waitAndPickClient()
- asset, redirectURL, err := g.getClient().Repositories.DownloadReleaseAsset(g.ctx, g.repoOwner, g.repoName, assetID, nil)
+ readCloser, redirectURL, err := g.getClient().Repositories.DownloadReleaseAsset(g.ctx, g.repoOwner, g.repoName, assetID, nil)
if err != nil {
return nil, err
}
if err := g.RefreshRate(); err != nil {
log.Error("g.getClient().RateLimits: %s", err)
}
- if asset == nil {
- if redirectURL != "" {
- g.waitAndPickClient()
- req, err := http.NewRequestWithContext(g.ctx, "GET", redirectURL, nil)
- if err != nil {
- return nil, err
- }
- resp, err := httpClient.Do(req)
- err1 := g.RefreshRate()
- if err1 != nil {
- log.Error("g.getClient().RateLimits: %s", err1)
- }
- if err != nil {
- return nil, err
- }
- return resp.Body, nil
- }
- return nil, fmt.Errorf("No release asset found for %d", assetID)
+
+ if readCloser != nil {
+ return readCloser, nil
}
- return asset, nil
+
+ if redirectURL == "" {
+ return nil, fmt.Errorf("no release asset found for %d", assetID)
+ }
+
+ // Prevent open redirect
+ if !hasBaseURL(redirectURL, g.baseURL) &&
+ !hasBaseURL(redirectURL, "https://objects.githubusercontent.com/") {
+ WarnAndNotice("Unexpected AssetURL for assetID[%d] in %s: %s", asset.GetID(), g, redirectURL)
+
+ return io.NopCloser(strings.NewReader(redirectURL)), nil
+ }
+
+ g.waitAndPickClient()
+ req, err := http.NewRequestWithContext(g.ctx, "GET", redirectURL, nil)
+ if err != nil {
+ return nil, err
+ }
+ resp, err := httpClient.Do(req)
+ err1 := g.RefreshRate()
+ if err1 != nil {
+ log.Error("g.RefreshRate(): %s", err1)
+ }
+ if err != nil {
+ return nil, err
+ }
+ return resp.Body, nil
},
})
}
@@ -697,7 +724,7 @@ func (g *GithubDownloaderV3) GetPullRequests(page, perPage int) ([]*base.PullReq
SHA: pr.GetHead().GetSHA(),
OwnerName: pr.GetHead().GetUser().GetLogin(),
RepoName: pr.GetHead().GetRepo().GetName(),
- CloneURL: pr.GetHead().GetRepo().GetCloneURL(),
+ CloneURL: pr.GetHead().GetRepo().GetCloneURL(), // see below for SECURITY related issues here
},
Base: base.PullRequestBranch{
Ref: pr.GetBase().GetRef(),
@@ -705,10 +732,13 @@ func (g *GithubDownloaderV3) GetPullRequests(page, perPage int) ([]*base.PullReq
RepoName: pr.GetBase().GetRepo().GetName(),
OwnerName: pr.GetBase().GetUser().GetLogin(),
},
- PatchURL: pr.GetPatchURL(),
+ PatchURL: pr.GetPatchURL(), // see below for SECURITY related issues here
Reactions: reactions,
ForeignIndex: int64(*pr.Number),
})
+
+ // SECURITY: Ensure that the PR is safe
+ _ = CheckAndEnsureSafePR(allPRs[len(allPRs)-1], g.baseURL, g)
}
return allPRs, len(prs) < perPage, nil
diff --git a/services/migrations/gitlab.go b/services/migrations/gitlab.go
index 549e3cb659..95bec59e83 100644
--- a/services/migrations/gitlab.go
+++ b/services/migrations/gitlab.go
@@ -63,6 +63,7 @@ type GitlabDownloader struct {
base.NullDownloader
ctx context.Context
client *gitlab.Client
+ baseURL string
repoID int
repoName string
issueCount int64
@@ -70,8 +71,9 @@ type GitlabDownloader struct {
}
// NewGitlabDownloader creates a gitlab Downloader via gitlab API
-// Use either a username/password, personal token entered into the username field, or anonymous/public access
-// Note: Public access only allows very basic access
+//
+// Use either a username/password, personal token entered into the username field, or anonymous/public access
+// Note: Public access only allows very basic access
func NewGitlabDownloader(ctx context.Context, baseURL, repoPath, username, password, token string) (*GitlabDownloader, error) {
gitlabClient, err := gitlab.NewClient(token, gitlab.WithBaseURL(baseURL), gitlab.WithHTTPClient(NewMigrationHTTPClient()))
// Only use basic auth if token is blank and password is NOT
@@ -124,12 +126,27 @@ func NewGitlabDownloader(ctx context.Context, baseURL, repoPath, username, passw
return &GitlabDownloader{
ctx: ctx,
client: gitlabClient,
+ baseURL: baseURL,
repoID: gr.ID,
repoName: gr.Name,
maxPerPage: 100,
}, nil
}
+// String implements Stringer
+func (g *GitlabDownloader) String() string {
+ return fmt.Sprintf("migration from gitlab server %s [%d]/%s", g.baseURL, g.repoID, g.repoName)
+}
+
+// ColorFormat provides a basic color format for a GitlabDownloader
+func (g *GitlabDownloader) ColorFormat(s fmt.State) {
+ if g == nil {
+ log.ColorFprintf(s, "")
+ return
+ }
+ log.ColorFprintf(s, "migration from gitlab server %s [%d]/%s", g.baseURL, g.repoID, g.repoName)
+}
+
// SetContext set context
func (g *GitlabDownloader) SetContext(ctx context.Context) {
g.ctx = ctx
@@ -307,6 +324,11 @@ func (g *GitlabDownloader) convertGitlabRelease(rel *gitlab.Release) *base.Relea
return nil, err
}
+ if !hasBaseURL(link.URL, g.baseURL) {
+ WarnAndNotice("Unexpected AssetURL for assetID[%d] in %s: %s", asset.ID, g, link.URL)
+ return io.NopCloser(strings.NewReader(link.URL)), nil
+ }
+
req, err := http.NewRequest("GET", link.URL, nil)
if err != nil {
return nil, err
@@ -353,7 +375,8 @@ type gitlabIssueContext struct {
}
// GetIssues returns issues according start and limit
-// Note: issue label description and colors are not supported by the go-gitlab library at this time
+//
+// Note: issue label description and colors are not supported by the go-gitlab library at this time
func (g *GitlabDownloader) GetIssues(page, perPage int) ([]*base.Issue, bool, error) {
state := "all"
sort := "asc"
@@ -610,6 +633,9 @@ func (g *GitlabDownloader) GetPullRequests(page, perPage int) ([]*base.PullReque
ForeignIndex: int64(pr.IID),
Context: gitlabIssueContext{IsMergeRequest: true},
})
+
+ // SECURITY: Ensure that the PR is safe
+ _ = CheckAndEnsureSafePR(allPRs[len(allPRs)-1], g.baseURL, g)
}
return allPRs, len(prs) < perPage, nil
diff --git a/services/migrations/gogs.go b/services/migrations/gogs.go
index a28033218e..46cc3ca416 100644
--- a/services/migrations/gogs.go
+++ b/services/migrations/gogs.go
@@ -73,6 +73,20 @@ type GogsDownloader struct {
transport http.RoundTripper
}
+// String implements Stringer
+func (g *GogsDownloader) String() string {
+ return fmt.Sprintf("migration from gogs server %s %s/%s", g.baseURL, g.repoOwner, g.repoName)
+}
+
+// ColorFormat provides a basic color format for a GogsDownloader
+func (g *GogsDownloader) ColorFormat(s fmt.State) {
+ if g == nil {
+ log.ColorFprintf(s, "")
+ return
+ }
+ log.ColorFprintf(s, "migration from gogs server %s %s/%s", g.baseURL, g.repoOwner, g.repoName)
+}
+
// SetContext set context
func (g *GogsDownloader) SetContext(ctx context.Context) {
g.ctx = ctx
diff --git a/services/migrations/migrate.go b/services/migrations/migrate.go
index f2542173a0..dfb21b884b 100644
--- a/services/migrations/migrate.go
+++ b/services/migrations/migrate.go
@@ -14,8 +14,8 @@ import (
"strings"
"code.gitea.io/gitea/models"
- admin_model "code.gitea.io/gitea/models/admin"
repo_model "code.gitea.io/gitea/models/repo"
+ system_model "code.gitea.io/gitea/models/system"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/hostmatcher"
"code.gitea.io/gitea/modules/log"
@@ -128,11 +128,11 @@ func MigrateRepository(ctx context.Context, doer *user_model.User, ownerName str
uploader := NewGiteaLocalUploader(ctx, doer, ownerName, opts.RepoName)
uploader.gitServiceType = opts.GitServiceType
- if err := migrateRepository(downloader, uploader, opts, messenger); err != nil {
+ if err := migrateRepository(doer, downloader, uploader, opts, messenger); err != nil {
if err1 := uploader.Rollback(); err1 != nil {
log.Error("rollback failed: %v", err1)
}
- if err2 := admin_model.CreateRepositoryNotice(fmt.Sprintf("Migrate repository from %s failed: %v", opts.OriginalURL, err)); err2 != nil {
+ if err2 := system_model.CreateRepositoryNotice(fmt.Sprintf("Migrate repository from %s failed: %v", opts.OriginalURL, err)); err2 != nil {
log.Error("create respotiry notice failed: ", err2)
}
return nil, err
@@ -177,7 +177,7 @@ func newDownloader(ctx context.Context, ownerName string, opts base.MigrateOptio
// migrateRepository will download information and then upload it to Uploader, this is a simple
// process for small repository. For a big repository, save all the data to disk
// before upload is better
-func migrateRepository(downloader base.Downloader, uploader base.Uploader, opts base.MigrateOptions, messenger base.Messenger) error {
+func migrateRepository(doer *user_model.User, downloader base.Downloader, uploader base.Uploader, opts base.MigrateOptions, messenger base.Messenger) error {
if messenger == nil {
messenger = base.NilMessenger
}
@@ -198,6 +198,27 @@ func migrateRepository(downloader base.Downloader, uploader base.Uploader, opts
return err
}
+ // SECURITY: If the downloader is not a RepositoryRestorer then we need to recheck the CloneURL
+ if _, ok := downloader.(*RepositoryRestorer); !ok {
+ // Now the clone URL can be rewritten by the downloader so we must recheck
+ if err := IsMigrateURLAllowed(repo.CloneURL, doer); err != nil {
+ return err
+ }
+
+ // SECURITY: Ensure that we haven't been redirected from an external to a local filesystem
+ // Now we know all of these must parse
+ cloneAddrURL, _ := url.Parse(opts.CloneAddr)
+ cloneURL, _ := url.Parse(repo.CloneURL)
+
+ if cloneURL.Scheme == "file" || cloneURL.Scheme == "" {
+ if cloneAddrURL.Scheme != "file" && cloneAddrURL.Scheme != "" {
+ return fmt.Errorf("repo info has changed from external to local filesystem")
+ }
+ }
+
+ // We don't actually need to check the OriginalURL as it isn't used anywhere
+ }
+
log.Trace("migrating git data from %s", repo.CloneURL)
messenger("repo.migrate.migrating_git")
if err = uploader.CreateRepo(repo, opts); err != nil {
@@ -479,5 +500,10 @@ func Init() error {
}
// TODO: at the moment, if ALLOW_LOCALNETWORKS=false, ALLOWED_DOMAINS=domain.com, and domain.com has IP 127.0.0.1, then it's still allowed.
// if we want to block such case, the private&loopback should be added to the blockList when ALLOW_LOCALNETWORKS=false
+
+ if setting.Proxy.Enabled && setting.Proxy.ProxyURLFixed != nil {
+ allowList.AppendPattern(setting.Proxy.ProxyURLFixed.Host)
+ }
+
return nil
}
diff --git a/services/migrations/migrate_test.go b/services/migrations/migrate_test.go
index 53cfe6d3eb..3fc4034777 100644
--- a/services/migrations/migrate_test.go
+++ b/services/migrations/migrate_test.go
@@ -19,8 +19,8 @@ import (
func TestMigrateWhiteBlocklist(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- adminUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "user1"}).(*user_model.User)
- nonAdminUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "user2"}).(*user_model.User)
+ adminUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "user1"})
+ nonAdminUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "user2"})
setting.Migrations.AllowedDomains = "github.com"
setting.Migrations.AllowLocalNetworks = false
diff --git a/services/migrations/onedev.go b/services/migrations/onedev.go
index a46ba35f72..8cc826c3b4 100644
--- a/services/migrations/onedev.go
+++ b/services/migrations/onedev.go
@@ -110,6 +110,20 @@ func NewOneDevDownloader(ctx context.Context, baseURL *url.URL, username, passwo
return downloader
}
+// String implements Stringer
+func (d *OneDevDownloader) String() string {
+ return fmt.Sprintf("migration from oneDev server %s [%d]/%s", d.baseURL, d.repoID, d.repoName)
+}
+
+// ColorFormat provides a basic color format for a OneDevDownloader
+func (d *OneDevDownloader) ColorFormat(s fmt.State) {
+ if d == nil {
+ log.ColorFprintf(s, "")
+ return
+ }
+ log.ColorFprintf(s, "migration from oneDev server %s [%d]/%s", d.baseURL, d.repoID, d.repoName)
+}
+
func (d *OneDevDownloader) callAPI(endpoint string, parameter map[string]string, result interface{}) error {
u, err := d.baseURL.Parse(endpoint)
if err != nil {
@@ -542,6 +556,9 @@ func (d *OneDevDownloader) GetPullRequests(page, perPage int) ([]*base.PullReque
ForeignIndex: pr.ID,
Context: onedevIssueContext{IsPullRequest: true},
})
+
+ // SECURITY: Ensure that the PR is safe
+ _ = CheckAndEnsureSafePR(pullRequests[len(pullRequests)-1], d.baseURL.String(), d)
}
return pullRequests, len(pullRequests) == 0, nil
diff --git a/services/migrations/restore.go b/services/migrations/restore.go
index 8c9654a7e3..c3fbcbb25f 100644
--- a/services/migrations/restore.go
+++ b/services/migrations/restore.go
@@ -243,6 +243,7 @@ func (r *RepositoryRestorer) GetPullRequests(page, perPage int) ([]*base.PullReq
}
for _, pr := range pulls {
pr.PatchURL = "file://" + filepath.Join(r.baseDir, pr.PatchURL)
+ CheckAndEnsureSafePR(pr, "", r)
}
return pulls, true, nil
}
diff --git a/services/mirror/mirror.go b/services/mirror/mirror.go
index 8321829ad2..3b4a8e5f8a 100644
--- a/services/mirror/mirror.go
+++ b/services/mirror/mirror.go
@@ -106,7 +106,7 @@ func Update(ctx context.Context, pullLimit, pushLimit int) error {
pushMirrorsRequested := 0
if pushLimit != 0 {
- if err := repo_model.PushMirrorsIterate(pushLimit, func(idx int, bean interface{}) error {
+ if err := repo_model.PushMirrorsIterate(ctx, pushLimit, func(idx int, bean interface{}) error {
if err := handler(idx, bean); err != nil {
return err
}
diff --git a/services/mirror/mirror_pull.go b/services/mirror/mirror_pull.go
index f4c527bbdc..a72e7f72eb 100644
--- a/services/mirror/mirror_pull.go
+++ b/services/mirror/mirror_pull.go
@@ -10,9 +10,9 @@ import (
"strings"
"time"
- admin_model "code.gitea.io/gitea/models/admin"
"code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo"
+ system_model "code.gitea.io/gitea/models/system"
"code.gitea.io/gitea/modules/cache"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/lfs"
@@ -188,7 +188,7 @@ func pruneBrokenReferences(ctx context.Context,
log.Error("Failed to prune mirror repository %s%-v references:\nStdout: %s\nStderr: %s\nErr: %v", wiki, m.Repo, stdoutMessage, stderrMessage, pruneErr)
desc := fmt.Sprintf("Failed to prune mirror repository %s'%s' references: %s", wiki, repoPath, stderrMessage)
- if err := admin_model.CreateRepositoryNotice(desc); err != nil {
+ if err := system_model.CreateRepositoryNotice(desc); err != nil {
log.Error("CreateRepositoryNotice: %v", err)
}
// this if will only be reached on a successful prune so try to get the mirror again
@@ -267,7 +267,7 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo
if err != nil {
log.Error("SyncMirrors [repo: %-v]: failed to update mirror repository:\nStdout: %s\nStderr: %s\nErr: %v", m.Repo, stdoutMessage, stderrMessage, err)
desc := fmt.Sprintf("Failed to update mirror repository '%s': %s", repoPath, stderrMessage)
- if err = admin_model.CreateRepositoryNotice(desc); err != nil {
+ if err = system_model.CreateRepositoryNotice(desc); err != nil {
log.Error("CreateRepositoryNotice: %v", err)
}
return nil, false
@@ -356,7 +356,7 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo
if err != nil {
log.Error("SyncMirrors [repo: %-v Wiki]: failed to update mirror repository wiki:\nStdout: %s\nStderr: %s\nErr: %v", m.Repo, stdoutMessage, stderrMessage, err)
desc := fmt.Sprintf("Failed to update mirror repository wiki '%s': %s", wikiPath, stderrMessage)
- if err = admin_model.CreateRepositoryNotice(desc); err != nil {
+ if err = system_model.CreateRepositoryNotice(desc); err != nil {
log.Error("CreateRepositoryNotice: %v", err)
}
return nil, false
@@ -568,7 +568,7 @@ func checkAndUpdateEmptyRepository(m *repo_model.Mirror, gitRepo *git.Repository
if !git.IsErrUnsupportedVersion(err) {
log.Error("Failed to update default branch of underlying git repository %-v. Error: %v", m.Repo, err)
desc := fmt.Sprintf("Failed to uupdate default branch of underlying git repository '%s': %v", m.Repo.RepoPath(), err)
- if err = admin_model.CreateRepositoryNotice(desc); err != nil {
+ if err = system_model.CreateRepositoryNotice(desc); err != nil {
log.Error("CreateRepositoryNotice: %v", err)
}
return false
@@ -579,7 +579,7 @@ func checkAndUpdateEmptyRepository(m *repo_model.Mirror, gitRepo *git.Repository
if err := repo_model.UpdateRepositoryCols(db.DefaultContext, m.Repo, "default_branch", "is_empty"); err != nil {
log.Error("Failed to update default branch of repository %-v. Error: %v", m.Repo, err)
desc := fmt.Sprintf("Failed to uupdate default branch of repository '%s': %v", m.Repo.RepoPath(), err)
- if err = admin_model.CreateRepositoryNotice(desc); err != nil {
+ if err = system_model.CreateRepositoryNotice(desc); err != nil {
log.Error("CreateRepositoryNotice: %v", err)
}
return false
diff --git a/services/mirror/mirror_push.go b/services/mirror/mirror_push.go
index 2927bed72b..0c8960d78b 100644
--- a/services/mirror/mirror_push.go
+++ b/services/mirror/mirror_push.go
@@ -94,7 +94,7 @@ func SyncPushMirror(ctx context.Context, mirrorID int64) bool {
log.Error("PANIC whilst syncPushMirror[%d] Panic: %v\nStacktrace: %s", mirrorID, err, log.Stack(2))
}()
- m, err := repo_model.GetPushMirrorByID(mirrorID)
+ m, err := repo_model.GetPushMirror(ctx, repo_model.PushMirrorOptions{ID: mirrorID})
if err != nil {
log.Error("GetPushMirrorByID [%d]: %v", mirrorID, err)
return false
@@ -116,7 +116,7 @@ func SyncPushMirror(ctx context.Context, mirrorID int64) bool {
m.LastUpdateUnix = timeutil.TimeStampNow()
- if err := repo_model.UpdatePushMirror(m); err != nil {
+ if err := repo_model.UpdatePushMirror(ctx, m); err != nil {
log.Error("UpdatePushMirror [%d]: %v", m.ID, err)
return false
diff --git a/services/org/org_test.go b/services/org/org_test.go
index 7f90d85807..c4e6088a71 100644
--- a/services/org/org_test.go
+++ b/services/org/org_test.go
@@ -24,18 +24,18 @@ func TestMain(m *testing.M) {
func TestDeleteOrganization(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- org := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 6}).(*organization.Organization)
+ org := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 6})
assert.NoError(t, DeleteOrganization(org))
unittest.AssertNotExistsBean(t, &organization.Organization{ID: 6})
unittest.AssertNotExistsBean(t, &organization.OrgUser{OrgID: 6})
unittest.AssertNotExistsBean(t, &organization.Team{OrgID: 6})
- org = unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 3}).(*organization.Organization)
+ org = unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 3})
err := DeleteOrganization(org)
assert.Error(t, err)
assert.True(t, models.IsErrUserOwnRepos(err))
- user := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 5}).(*organization.Organization)
+ user := unittest.AssertExistsAndLoadBean(t, &organization.Organization{ID: 5})
assert.Error(t, DeleteOrganization(user))
unittest.CheckConsistencyFor(t, &user_model.User{}, &organization.Team{})
}
diff --git a/services/org/repo.go b/services/org/repo.go
new file mode 100644
index 0000000000..769419d45b
--- /dev/null
+++ b/services/org/repo.go
@@ -0,0 +1,28 @@
+// Copyright 2022 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package org
+
+import (
+ "context"
+ "errors"
+
+ "code.gitea.io/gitea/models"
+ "code.gitea.io/gitea/models/db"
+ "code.gitea.io/gitea/models/organization"
+ repo_model "code.gitea.io/gitea/models/repo"
+)
+
+// TeamAddRepository adds new repository to team of organization.
+func TeamAddRepository(t *organization.Team, repo *repo_model.Repository) (err error) {
+ if repo.OwnerID != t.OrgID {
+ return errors.New("repository does not belong to organization")
+ } else if models.HasRepository(t, repo.ID) {
+ return nil
+ }
+
+ return db.WithTx(func(ctx context.Context) error {
+ return models.AddRepository(ctx, t, repo)
+ })
+}
diff --git a/services/org/repo_test.go b/services/org/repo_test.go
new file mode 100644
index 0000000000..21158bfa21
--- /dev/null
+++ b/services/org/repo_test.go
@@ -0,0 +1,34 @@
+// Copyright 2021 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package org
+
+import (
+ "testing"
+
+ "code.gitea.io/gitea/models/organization"
+ repo_model "code.gitea.io/gitea/models/repo"
+ "code.gitea.io/gitea/models/unittest"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestTeam_AddRepository(t *testing.T) {
+ assert.NoError(t, unittest.PrepareTestDatabase())
+
+ testSuccess := func(teamID, repoID int64) {
+ team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: teamID})
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repoID})
+ assert.NoError(t, TeamAddRepository(team, repo))
+ unittest.AssertExistsAndLoadBean(t, &organization.TeamRepo{TeamID: teamID, RepoID: repoID})
+ unittest.CheckConsistencyFor(t, &organization.Team{ID: teamID}, &repo_model.Repository{ID: repoID})
+ }
+ testSuccess(2, 3)
+ testSuccess(2, 5)
+
+ team := unittest.AssertExistsAndLoadBean(t, &organization.Team{ID: 1})
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
+ assert.Error(t, TeamAddRepository(team, repo))
+ unittest.CheckConsistencyFor(t, &organization.Team{ID: 1}, &repo_model.Repository{ID: 1})
+}
diff --git a/services/packages/container/cleanup.go b/services/packages/container/cleanup.go
index 3e44f9aa1a..d23a481f27 100644
--- a/services/packages/container/cleanup.go
+++ b/services/packages/container/cleanup.go
@@ -6,10 +6,13 @@ package container
import (
"context"
+ "strings"
"time"
packages_model "code.gitea.io/gitea/models/packages"
container_model "code.gitea.io/gitea/models/packages/container"
+ user_model "code.gitea.io/gitea/models/user"
+ container_module "code.gitea.io/gitea/modules/packages/container"
"code.gitea.io/gitea/modules/util"
)
@@ -78,3 +81,25 @@ func cleanupExpiredUploadedBlobs(ctx context.Context, olderThan time.Duration) e
return nil
}
+
+// UpdateRepositoryNames updates the repository name property for all packages of the specific owner
+func UpdateRepositoryNames(ctx context.Context, owner *user_model.User, newOwnerName string) error {
+ ps, err := packages_model.GetPackagesByType(ctx, owner.ID, packages_model.TypeContainer)
+ if err != nil {
+ return err
+ }
+
+ newOwnerName = strings.ToLower(newOwnerName)
+
+ for _, p := range ps {
+ if err := packages_model.DeletePropertyByName(ctx, packages_model.PropertyTypePackage, p.ID, container_module.PropertyRepository); err != nil {
+ return err
+ }
+
+ if _, err := packages_model.InsertProperty(ctx, packages_model.PropertyTypePackage, p.ID, container_module.PropertyRepository, newOwnerName+"/"+p.LowerName); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
diff --git a/services/packages/packages.go b/services/packages/packages.go
index 0ebf6e7df0..96132eac09 100644
--- a/services/packages/packages.go
+++ b/services/packages/packages.go
@@ -19,6 +19,7 @@ import (
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/notification"
packages_module "code.gitea.io/gitea/modules/packages"
+ "code.gitea.io/gitea/modules/util"
container_service "code.gitea.io/gitea/services/packages/container"
)
@@ -33,10 +34,11 @@ type PackageInfo struct {
// PackageCreationInfo describes a package to create
type PackageCreationInfo struct {
PackageInfo
- SemverCompatible bool
- Creator *user_model.User
- Metadata interface{}
- Properties map[string]string
+ SemverCompatible bool
+ Creator *user_model.User
+ Metadata interface{}
+ PackageProperties map[string]string
+ VersionProperties map[string]string
}
// PackageFileInfo describes a package file
@@ -109,8 +111,9 @@ func createPackageAndAddFile(pvci *PackageCreationInfo, pfci *PackageFileCreatio
}
func createPackageAndVersion(ctx context.Context, pvci *PackageCreationInfo, allowDuplicate bool) (*packages_model.PackageVersion, bool, error) {
- log.Trace("Creating package: %v, %v, %v, %s, %s, %+v, %v", pvci.Creator.ID, pvci.Owner.ID, pvci.PackageType, pvci.Name, pvci.Version, pvci.Properties, allowDuplicate)
+ log.Trace("Creating package: %v, %v, %v, %s, %s, %+v, %+v, %v", pvci.Creator.ID, pvci.Owner.ID, pvci.PackageType, pvci.Name, pvci.Version, pvci.PackageProperties, pvci.VersionProperties, allowDuplicate)
+ packageCreated := true
p := &packages_model.Package{
OwnerID: pvci.Owner.ID,
Type: pvci.PackageType,
@@ -120,18 +123,29 @@ func createPackageAndVersion(ctx context.Context, pvci *PackageCreationInfo, all
}
var err error
if p, err = packages_model.TryInsertPackage(ctx, p); err != nil {
- if err != packages_model.ErrDuplicatePackage {
+ if err == packages_model.ErrDuplicatePackage {
+ packageCreated = false
+ } else {
log.Error("Error inserting package: %v", err)
return nil, false, err
}
}
+ if packageCreated {
+ for name, value := range pvci.PackageProperties {
+ if _, err := packages_model.InsertProperty(ctx, packages_model.PropertyTypePackage, p.ID, name, value); err != nil {
+ log.Error("Error setting package property: %v", err)
+ return nil, false, err
+ }
+ }
+ }
+
metadataJSON, err := json.Marshal(pvci.Metadata)
if err != nil {
return nil, false, err
}
- created := true
+ versionCreated := true
pv := &packages_model.PackageVersion{
PackageID: p.ID,
CreatorID: pvci.Creator.ID,
@@ -141,7 +155,7 @@ func createPackageAndVersion(ctx context.Context, pvci *PackageCreationInfo, all
}
if pv, err = packages_model.GetOrInsertVersion(ctx, pv); err != nil {
if err == packages_model.ErrDuplicatePackageVersion {
- created = false
+ versionCreated = false
}
if err != packages_model.ErrDuplicatePackageVersion || !allowDuplicate {
log.Error("Error inserting package: %v", err)
@@ -149,8 +163,8 @@ func createPackageAndVersion(ctx context.Context, pvci *PackageCreationInfo, all
}
}
- if created {
- for name, value := range pvci.Properties {
+ if versionCreated {
+ for name, value := range pvci.VersionProperties {
if _, err := packages_model.InsertProperty(ctx, packages_model.PropertyTypeVersion, pv.ID, name, value); err != nil {
log.Error("Error setting package version property: %v", err)
return nil, false, err
@@ -158,7 +172,7 @@ func createPackageAndVersion(ctx context.Context, pvci *PackageCreationInfo, all
}
}
- return pv, created, nil
+ return pv, versionCreated, nil
}
// AddFileToExistingPackage adds a file to an existing package. If the package does not exist, ErrPackageNotExist is returned
@@ -349,9 +363,18 @@ func Cleanup(unused context.Context, olderThan time.Duration) error {
return err
}
- if err := packages_model.DeletePackagesIfUnreferenced(ctx); err != nil {
+ ps, err := packages_model.FindUnreferencedPackages(ctx)
+ if err != nil {
return err
}
+ for _, p := range ps {
+ if err := packages_model.DeleteAllProperties(ctx, packages_model.PropertyTypePackage, p.ID); err != nil {
+ return err
+ }
+ if err := packages_model.DeletePackageByID(ctx, p.ID); err != nil {
+ return err
+ }
+ }
pbs, err := packages_model.FindExpiredUnreferencedBlobs(ctx, olderThan)
if err != nil {
@@ -379,7 +402,7 @@ func Cleanup(unused context.Context, olderThan time.Duration) error {
}
// GetFileStreamByPackageNameAndVersion returns the content of the specific package file
-func GetFileStreamByPackageNameAndVersion(ctx context.Context, pvi *PackageInfo, pfi *PackageFileInfo) (io.ReadCloser, *packages_model.PackageFile, error) {
+func GetFileStreamByPackageNameAndVersion(ctx context.Context, pvi *PackageInfo, pfi *PackageFileInfo) (io.ReadSeekCloser, *packages_model.PackageFile, error) {
log.Trace("Getting package file stream: %v, %v, %s, %s, %s, %s", pvi.Owner.ID, pvi.PackageType, pvi.Name, pvi.Version, pfi.Filename, pfi.CompositeKey)
pv, err := packages_model.GetVersionByNameAndVersion(ctx, pvi.Owner.ID, pvi.PackageType, pvi.Name, pvi.Version)
@@ -395,7 +418,7 @@ func GetFileStreamByPackageNameAndVersion(ctx context.Context, pvi *PackageInfo,
}
// GetFileStreamByPackageVersionAndFileID returns the content of the specific package file
-func GetFileStreamByPackageVersionAndFileID(ctx context.Context, owner *user_model.User, versionID, fileID int64) (io.ReadCloser, *packages_model.PackageFile, error) {
+func GetFileStreamByPackageVersionAndFileID(ctx context.Context, owner *user_model.User, versionID, fileID int64) (io.ReadSeekCloser, *packages_model.PackageFile, error) {
log.Trace("Getting package file stream: %v, %v, %v", owner.ID, versionID, fileID)
pv, err := packages_model.GetVersionByID(ctx, versionID)
@@ -426,8 +449,8 @@ func GetFileStreamByPackageVersionAndFileID(ctx context.Context, owner *user_mod
}
// GetFileStreamByPackageVersion returns the content of the specific package file
-func GetFileStreamByPackageVersion(ctx context.Context, pv *packages_model.PackageVersion, pfi *PackageFileInfo) (io.ReadCloser, *packages_model.PackageFile, error) {
- pf, err := packages_model.GetFileForVersionByName(db.DefaultContext, pv.ID, pfi.Filename, pfi.CompositeKey)
+func GetFileStreamByPackageVersion(ctx context.Context, pv *packages_model.PackageVersion, pfi *PackageFileInfo) (io.ReadSeekCloser, *packages_model.PackageFile, error) {
+ pf, err := packages_model.GetFileForVersionByName(ctx, pv.ID, pfi.Filename, pfi.CompositeKey)
if err != nil {
return nil, nil, err
}
@@ -436,7 +459,7 @@ func GetFileStreamByPackageVersion(ctx context.Context, pv *packages_model.Packa
}
// GetPackageFileStream returns the content of the specific package file
-func GetPackageFileStream(ctx context.Context, pf *packages_model.PackageFile) (io.ReadCloser, *packages_model.PackageFile, error) {
+func GetPackageFileStream(ctx context.Context, pf *packages_model.PackageFile) (io.ReadSeekCloser, *packages_model.PackageFile, error) {
pb, err := packages_model.GetBlobByID(ctx, pf.BlobID)
if err != nil {
return nil, nil, err
@@ -462,7 +485,8 @@ func RemoveAllPackages(ctx context.Context, userID int64) (int, error) {
PageSize: repo_model.RepositoryListDefaultPageSize,
Page: 1,
},
- OwnerID: userID,
+ OwnerID: userID,
+ IsInternal: util.OptionalBoolNone,
})
if err != nil {
return count, fmt.Errorf("GetOwnedPackages[%d]: %w", userID, err)
diff --git a/services/pull/check_test.go b/services/pull/check_test.go
index 21fe675bbc..b4de02b5e2 100644
--- a/services/pull/check_test.go
+++ b/services/pull/check_test.go
@@ -43,11 +43,11 @@ func TestPullRequest_AddToTaskQueue(t *testing.T) {
prPatchCheckerQueue = q.(queue.UniqueQueue)
- pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 2}).(*issues_model.PullRequest)
+ pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 2})
AddToTaskQueue(pr)
assert.Eventually(t, func() bool {
- pr = unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 2}).(*issues_model.PullRequest)
+ pr = unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 2})
return pr.Status == issues_model.PullRequestStatusChecking
}, 1*time.Second, 100*time.Millisecond)
@@ -72,7 +72,7 @@ func TestPullRequest_AddToTaskQueue(t *testing.T) {
assert.False(t, has)
assert.NoError(t, err)
- pr = unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 2}).(*issues_model.PullRequest)
+ pr = unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 2})
assert.Equal(t, issues_model.PullRequestStatusChecking, pr.Status)
for _, callback := range queueShutdown {
diff --git a/services/pull/merge.go b/services/pull/merge.go
index 4cd4e3bd7e..6f3df6ab2a 100644
--- a/services/pull/merge.go
+++ b/services/pull/merge.go
@@ -28,6 +28,7 @@ import (
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/cache"
"code.gitea.io/gitea/modules/git"
+ "code.gitea.io/gitea/modules/graceful"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/notification"
"code.gitea.io/gitea/modules/references"
@@ -165,9 +166,10 @@ func Merge(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.U
go AddTestPullRequestTask(doer, pr.BaseRepo.ID, pr.BaseBranch, false, "", "")
}()
- // TODO: make it able to do this in a database session
- mergeCtx := context.Background()
- pr.MergedCommitID, err = rawMerge(mergeCtx, pr, doer, mergeStyle, expectedHeadCommitID, message)
+ // Run the merge in the hammer context to prevent cancellation
+ hammerCtx := graceful.GetManager().HammerContext()
+
+ pr.MergedCommitID, err = rawMerge(hammerCtx, pr, doer, mergeStyle, expectedHeadCommitID, message)
if err != nil {
return err
}
@@ -176,18 +178,18 @@ func Merge(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.U
pr.Merger = doer
pr.MergerID = doer.ID
- if _, err := pr.SetMerged(ctx); err != nil {
+ if _, err := pr.SetMerged(hammerCtx); err != nil {
log.Error("setMerged [%d]: %v", pr.ID, err)
}
- if err := pr.LoadIssueCtx(ctx); err != nil {
+ if err := pr.LoadIssueCtx(hammerCtx); err != nil {
log.Error("loadIssue [%d]: %v", pr.ID, err)
}
- if err := pr.Issue.LoadRepo(ctx); err != nil {
+ if err := pr.Issue.LoadRepo(hammerCtx); err != nil {
log.Error("loadRepo for issue [%d]: %v", pr.ID, err)
}
- if err := pr.Issue.Repo.GetOwner(ctx); err != nil {
+ if err := pr.Issue.Repo.GetOwner(hammerCtx); err != nil {
log.Error("GetOwner for issue repo [%d]: %v", pr.ID, err)
}
@@ -197,17 +199,17 @@ func Merge(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.U
cache.Remove(pr.Issue.Repo.GetCommitsCountCacheKey(pr.BaseBranch, true))
// Resolve cross references
- refs, err := pr.ResolveCrossReferences(ctx)
+ refs, err := pr.ResolveCrossReferences(hammerCtx)
if err != nil {
log.Error("ResolveCrossReferences: %v", err)
return nil
}
for _, ref := range refs {
- if err = ref.LoadIssueCtx(ctx); err != nil {
+ if err = ref.LoadIssueCtx(hammerCtx); err != nil {
return err
}
- if err = ref.Issue.LoadRepo(ctx); err != nil {
+ if err = ref.Issue.LoadRepo(hammerCtx); err != nil {
return err
}
close := ref.RefAction == references.XRefActionCloses
diff --git a/services/pull/patch.go b/services/pull/patch.go
index bb09acc89f..dafd577069 100644
--- a/services/pull/patch.go
+++ b/services/pull/patch.go
@@ -17,6 +17,7 @@ import (
"code.gitea.io/gitea/models"
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/unit"
+ "code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/graceful"
"code.gitea.io/gitea/modules/log"
@@ -124,6 +125,7 @@ func (e *errMergeConflict) Error() string {
}
func attemptMerge(ctx context.Context, file *unmergedFile, tmpBasePath string, gitRepo *git.Repository) error {
+ log.Trace("Attempt to merge:\n%v", file)
switch {
case file.stage1 != nil && (file.stage2 == nil || file.stage3 == nil):
// 1. Deleted in one or both:
@@ -295,7 +297,8 @@ func checkConflicts(ctx context.Context, pr *issues_model.PullRequest, gitRepo *
var treeHash string
treeHash, _, err = git.NewCommand(ctx, "write-tree").RunStdString(&git.RunOpts{Dir: tmpBasePath})
if err != nil {
- return false, err
+ lsfiles, _, _ := git.NewCommand(ctx, "ls-files", "-u").RunStdString(&git.RunOpts{Dir: tmpBasePath})
+ return false, fmt.Errorf("unable to write unconflicted tree: %w\n`git ls-files -u`:\n%s", err, lsfiles)
}
treeHash = strings.TrimSpace(treeHash)
baseTree, err := gitRepo.GetTree("base")
@@ -407,7 +410,7 @@ func checkConflicts(ctx context.Context, pr *issues_model.PullRequest, gitRepo *
const appliedPatchPrefix = "Applied patch to '"
const withConflicts = "' with conflicts."
- conflictMap := map[string]bool{}
+ conflicts := make(container.Set[string])
// Now scan the output from the command
scanner := bufio.NewScanner(stderrReader)
@@ -416,7 +419,7 @@ func checkConflicts(ctx context.Context, pr *issues_model.PullRequest, gitRepo *
if strings.HasPrefix(line, prefix) {
conflict = true
filepath := strings.TrimSpace(strings.Split(line[len(prefix):], ":")[0])
- conflictMap[filepath] = true
+ conflicts.Add(filepath)
} else if is3way && line == threewayFailed {
conflict = true
} else if strings.HasPrefix(line, errorPrefix) {
@@ -425,7 +428,7 @@ func checkConflicts(ctx context.Context, pr *issues_model.PullRequest, gitRepo *
if strings.HasSuffix(line, suffix) {
filepath := strings.TrimSpace(strings.TrimSuffix(line[len(errorPrefix):], suffix))
if filepath != "" {
- conflictMap[filepath] = true
+ conflicts.Add(filepath)
}
break
}
@@ -434,18 +437,18 @@ func checkConflicts(ctx context.Context, pr *issues_model.PullRequest, gitRepo *
conflict = true
filepath := strings.TrimPrefix(strings.TrimSuffix(line, withConflicts), appliedPatchPrefix)
if filepath != "" {
- conflictMap[filepath] = true
+ conflicts.Add(filepath)
}
}
// only list 10 conflicted files
- if len(conflictMap) >= 10 {
+ if len(conflicts) >= 10 {
break
}
}
- if len(conflictMap) > 0 {
- pr.ConflictedFiles = make([]string, 0, len(conflictMap))
- for key := range conflictMap {
+ if len(conflicts) > 0 {
+ pr.ConflictedFiles = make([]string, 0, len(conflicts))
+ for key := range conflicts {
pr.ConflictedFiles = append(pr.ConflictedFiles, key)
}
}
diff --git a/services/pull/patch_unmerged.go b/services/pull/patch_unmerged.go
index 3839419142..465465d0da 100644
--- a/services/pull/patch_unmerged.go
+++ b/services/pull/patch_unmerged.go
@@ -42,6 +42,17 @@ func (line *lsFileLine) SameAs(other *lsFileLine) bool {
line.path == other.path
}
+// String provides a string representation for logging
+func (line *lsFileLine) String() string {
+ if line == nil {
+ return ""
+ }
+ if line.err != nil {
+ return fmt.Sprintf("%d %s %s %s %v", line.stage, line.mode, line.path, line.sha, line.err)
+ }
+ return fmt.Sprintf("%d %s %s %s", line.stage, line.mode, line.path, line.sha)
+}
+
// readUnmergedLsFileLines calls git ls-files -u -z and parses the lines into mode-sha-stage-path quadruplets
// it will push these to the provided channel closing it at the end
func readUnmergedLsFileLines(ctx context.Context, tmpBasePath string, outputChan chan *lsFileLine) {
@@ -118,6 +129,17 @@ type unmergedFile struct {
err error
}
+// String provides a string representation of the an unmerged file for logging
+func (u *unmergedFile) String() string {
+ if u == nil {
+ return ""
+ }
+ if u.err != nil {
+ return fmt.Sprintf("error: %v\n%v\n%v\n%v", u.err, u.stage1, u.stage2, u.stage3)
+ }
+ return fmt.Sprintf("%v\n%v\n%v", u.stage1, u.stage2, u.stage3)
+}
+
// unmergedFiles will collate the output from readUnstagedLsFileLines in to file triplets and send them
// to the provided channel, closing at the end.
func unmergedFiles(ctx context.Context, tmpBasePath string, unmerged chan *unmergedFile) {
@@ -138,6 +160,7 @@ func unmergedFiles(ctx context.Context, tmpBasePath string, unmerged chan *unmer
next := &unmergedFile{}
for line := range lsFileLineChan {
+ log.Trace("Got line: %v Current State:\n%v", line, next)
if line.err != nil {
log.Error("Unable to run ls-files -u -z! Error: %v", line.err)
unmerged <- &unmergedFile{err: fmt.Errorf("unable to run ls-files -u -z! Error: %v", line.err)}
@@ -149,7 +172,7 @@ func unmergedFiles(ctx context.Context, tmpBasePath string, unmerged chan *unmer
case 0:
// Should not happen as this represents successfully merged file - we will tolerate and ignore though
case 1:
- if next.stage1 != nil {
+ if next.stage1 != nil || next.stage2 != nil || next.stage3 != nil {
// We need to handle the unstaged file stage1,stage2,stage3
unmerged <- next
}
diff --git a/services/pull/pull.go b/services/pull/pull.go
index 103fdc340d..9de7cb5d4f 100644
--- a/services/pull/pull.go
+++ b/services/pull/pull.go
@@ -20,6 +20,7 @@ import (
issues_model "code.gitea.io/gitea/models/issues"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/graceful"
"code.gitea.io/gitea/modules/json"
@@ -640,7 +641,7 @@ func GetSquashMergeCommitMessages(ctx context.Context, pr *issues_model.PullRequ
posterSig := pr.Issue.Poster.NewGitSig().String()
- authorsMap := map[string]bool{}
+ uniqueAuthors := make(container.Set[string])
authors := make([]string, 0, len(commits))
stringBuilder := strings.Builder{}
@@ -687,9 +688,8 @@ func GetSquashMergeCommitMessages(ctx context.Context, pr *issues_model.PullRequ
}
authorString := commit.Author.String()
- if !authorsMap[authorString] && authorString != posterSig {
+ if uniqueAuthors.Add(authorString) && authorString != posterSig {
authors = append(authors, authorString)
- authorsMap[authorString] = true
}
}
@@ -709,9 +709,8 @@ func GetSquashMergeCommitMessages(ctx context.Context, pr *issues_model.PullRequ
}
for _, commit := range commits {
authorString := commit.Author.String()
- if !authorsMap[authorString] && authorString != posterSig {
+ if uniqueAuthors.Add(authorString) && authorString != posterSig {
authors = append(authors, authorString)
- authorsMap[authorString] = true
}
}
skip += limit
diff --git a/services/pull/pull_test.go b/services/pull/pull_test.go
index 9160c43460..769e3c72e9 100644
--- a/services/pull/pull_test.go
+++ b/services/pull/pull_test.go
@@ -38,7 +38,7 @@ func TestPullRequest_CommitMessageTrailersPattern(t *testing.T) {
func TestPullRequest_GetDefaultMergeMessage_InternalTracker(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 2}).(*issues_model.PullRequest)
+ pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 2})
assert.NoError(t, pr.LoadBaseRepo())
gitRepo, err := git.OpenRepository(git.DefaultContext, pr.BaseRepo.RepoPath())
@@ -65,10 +65,10 @@ func TestPullRequest_GetDefaultMergeMessage_ExternalTracker(t *testing.T) {
ExternalTrackerFormat: "https://someurl.com/{user}/{repo}/{issue}",
},
}
- baseRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository)
+ baseRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
baseRepo.Units = []*repo_model.RepoUnit{&externalTracker}
- pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 2, BaseRepo: baseRepo}).(*issues_model.PullRequest)
+ pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 2, BaseRepo: baseRepo})
assert.NoError(t, pr.LoadBaseRepo())
gitRepo, err := git.OpenRepository(git.DefaultContext, pr.BaseRepo.RepoPath())
diff --git a/services/pull/update.go b/services/pull/update.go
index e5e26462e5..49258a9862 100644
--- a/services/pull/update.go
+++ b/services/pull/update.go
@@ -87,6 +87,9 @@ func IsUserAllowedToUpdate(ctx context.Context, pull *issues_model.PullRequest,
}
headRepoPerm, err := access_model.GetUserRepoPermission(ctx, pull.HeadRepo, user)
if err != nil {
+ if repo_model.IsErrUnitTypeNotExist(err) {
+ return false, false, nil
+ }
return false, false, err
}
diff --git a/services/release/release.go b/services/release/release.go
index 6fa966de1f..af1b075232 100644
--- a/services/release/release.go
+++ b/services/release/release.go
@@ -15,6 +15,7 @@ import (
git_model "code.gitea.io/gitea/models/git"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/notification"
@@ -23,7 +24,7 @@ import (
"code.gitea.io/gitea/modules/timeutil"
)
-func createTag(gitRepo *git.Repository, rel *models.Release, msg string) (bool, error) {
+func createTag(gitRepo *git.Repository, rel *repo_model.Release, msg string) (bool, error) {
var created bool
// Only actual create when publish.
if !rel.IsDraft {
@@ -37,6 +38,9 @@ func createTag(gitRepo *git.Repository, rel *models.Release, msg string) (bool,
if err != nil {
return false, fmt.Errorf("GetProtectedTags: %v", err)
}
+
+ // Trim '--' prefix to prevent command line argument vulnerability.
+ rel.TagName = strings.TrimPrefix(rel.TagName, "--")
isAllowed, err := git_model.IsUserAllowedToControlTag(protectedTags, rel.TagName, rel.PublisherID)
if err != nil {
return false, err
@@ -52,8 +56,6 @@ func createTag(gitRepo *git.Repository, rel *models.Release, msg string) (bool,
return false, fmt.Errorf("createTag::GetCommit[%v]: %v", rel.Target, err)
}
- // Trim '--' prefix to prevent command line argument vulnerability.
- rel.TagName = strings.TrimPrefix(rel.TagName, "--")
if len(msg) > 0 {
if err = gitRepo.CreateAnnotatedTag(rel.TagName, msg, commit.ID.String()); err != nil {
if strings.Contains(err.Error(), "is not a valid tag name") {
@@ -112,12 +114,12 @@ func createTag(gitRepo *git.Repository, rel *models.Release, msg string) (bool,
}
// CreateRelease creates a new release of repository.
-func CreateRelease(gitRepo *git.Repository, rel *models.Release, attachmentUUIDs []string, msg string) error {
- has, err := models.IsReleaseExist(gitRepo.Ctx, rel.RepoID, rel.TagName)
+func CreateRelease(gitRepo *git.Repository, rel *repo_model.Release, attachmentUUIDs []string, msg string) error {
+ has, err := repo_model.IsReleaseExist(gitRepo.Ctx, rel.RepoID, rel.TagName)
if err != nil {
return err
} else if has {
- return models.ErrReleaseAlreadyExist{
+ return repo_model.ErrReleaseAlreadyExist{
TagName: rel.TagName,
}
}
@@ -131,7 +133,7 @@ func CreateRelease(gitRepo *git.Repository, rel *models.Release, attachmentUUIDs
return err
}
- if err = models.AddReleaseAttachments(gitRepo.Ctx, rel.ID, attachmentUUIDs); err != nil {
+ if err = repo_model.AddReleaseAttachments(gitRepo.Ctx, rel.ID, attachmentUUIDs); err != nil {
return err
}
@@ -144,7 +146,7 @@ func CreateRelease(gitRepo *git.Repository, rel *models.Release, attachmentUUIDs
// CreateNewTag creates a new repository tag
func CreateNewTag(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, commit, tagName, msg string) error {
- has, err := models.IsReleaseExist(ctx, repo.ID, tagName)
+ has, err := repo_model.IsReleaseExist(ctx, repo.ID, tagName)
if err != nil {
return err
} else if has {
@@ -159,7 +161,7 @@ func CreateNewTag(ctx context.Context, doer *user_model.User, repo *repo_model.R
}
defer closer.Close()
- rel := &models.Release{
+ rel := &repo_model.Release{
RepoID: repo.ID,
Repo: repo,
PublisherID: doer.ID,
@@ -182,7 +184,7 @@ func CreateNewTag(ctx context.Context, doer *user_model.User, repo *repo_model.R
// addAttachmentUUIDs accept a slice of new created attachments' uuids which will be reassigned release_id as the created release
// delAttachmentUUIDs accept a slice of attachments' uuids which will be deleted from the release
// editAttachments accept a map of attachment uuid to new attachment name which will be updated with attachments.
-func UpdateRelease(doer *user_model.User, gitRepo *git.Repository, rel *models.Release,
+func UpdateRelease(doer *user_model.User, gitRepo *git.Repository, rel *repo_model.Release,
addAttachmentUUIDs, delAttachmentUUIDs []string, editAttachments map[string]string,
) (err error) {
if rel.ID == 0 {
@@ -200,15 +202,15 @@ func UpdateRelease(doer *user_model.User, gitRepo *git.Repository, rel *models.R
}
defer committer.Close()
- if err = models.UpdateRelease(ctx, rel); err != nil {
+ if err = repo_model.UpdateRelease(ctx, rel); err != nil {
return err
}
- if err = models.AddReleaseAttachments(ctx, rel.ID, addAttachmentUUIDs); err != nil {
+ if err = repo_model.AddReleaseAttachments(ctx, rel.ID, addAttachmentUUIDs); err != nil {
return fmt.Errorf("AddReleaseAttachments: %v", err)
}
- deletedUUIDsMap := make(map[string]bool)
+ deletedUUIDs := make(container.Set[string])
if len(delAttachmentUUIDs) > 0 {
// Check attachments
attachments, err := repo_model.GetAttachmentsByUUIDs(ctx, delAttachmentUUIDs)
@@ -219,7 +221,7 @@ func UpdateRelease(doer *user_model.User, gitRepo *git.Repository, rel *models.R
if attach.ReleaseID != rel.ID {
return errors.New("delete attachement of release permission denied")
}
- deletedUUIDsMap[attach.UUID] = true
+ deletedUUIDs.Add(attach.UUID)
}
if _, err := repo_model.DeleteAttachments(ctx, attachments, false); err != nil {
@@ -244,7 +246,7 @@ func UpdateRelease(doer *user_model.User, gitRepo *git.Repository, rel *models.R
}
for uuid, newName := range editAttachments {
- if !deletedUUIDsMap[uuid] {
+ if !deletedUUIDs.Contains(uuid) {
if err = repo_model.UpdateAttachmentByUUID(ctx, &repo_model.Attachment{
UUID: uuid,
Name: newName,
@@ -269,13 +271,12 @@ func UpdateRelease(doer *user_model.User, gitRepo *git.Repository, rel *models.R
}
}
- if !isCreated {
- notification.NotifyUpdateRelease(doer, rel)
- return
- }
-
if !rel.IsDraft {
- notification.NotifyNewRelease(rel)
+ if isCreated {
+ notification.NotifyNewRelease(rel)
+ } else {
+ notification.NotifyUpdateRelease(doer, rel)
+ }
}
return err
@@ -283,7 +284,7 @@ func UpdateRelease(doer *user_model.User, gitRepo *git.Repository, rel *models.R
// DeleteReleaseByID deletes a release and corresponding Git tag by given ID.
func DeleteReleaseByID(ctx context.Context, id int64, doer *user_model.User, delTag bool) error {
- rel, err := models.GetReleaseByID(ctx, id)
+ rel, err := repo_model.GetReleaseByID(ctx, id)
if err != nil {
return fmt.Errorf("GetReleaseByID: %v", err)
}
@@ -308,7 +309,7 @@ func DeleteReleaseByID(ctx context.Context, id int64, doer *user_model.User, del
}
}
- if stdout, _, err := git.NewCommand(ctx, "tag", "-d", rel.TagName).
+ if stdout, _, err := git.NewCommand(ctx, "tag", "-d", "--", rel.TagName).
SetDescription(fmt.Sprintf("DeleteReleaseByID (git tag -d): %d", rel.ID)).
RunStdString(&git.RunOpts{Dir: repo.RepoPath()}); err != nil && !strings.Contains(err.Error(), "not found") {
log.Error("DeleteReleaseByID (git tag -d): %d in %v Failed:\nStdout: %s\nError: %v", rel.ID, repo, stdout, err)
@@ -324,13 +325,13 @@ func DeleteReleaseByID(ctx context.Context, id int64, doer *user_model.User, del
}, repository.NewPushCommits())
notification.NotifyDeleteRef(doer, repo, "tag", git.TagPrefix+rel.TagName)
- if err := models.DeleteReleaseByID(id); err != nil {
+ if err := repo_model.DeleteReleaseByID(id); err != nil {
return fmt.Errorf("DeleteReleaseByID: %v", err)
}
} else {
rel.IsTag = true
- if err = models.UpdateRelease(ctx, rel); err != nil {
+ if err = repo_model.UpdateRelease(ctx, rel); err != nil {
return fmt.Errorf("Update: %v", err)
}
}
@@ -351,7 +352,9 @@ func DeleteReleaseByID(ctx context.Context, id int64, doer *user_model.User, del
}
}
- notification.NotifyDeleteRelease(doer, rel)
+ if !rel.IsDraft {
+ notification.NotifyDeleteRelease(doer, rel)
+ }
return nil
}
diff --git a/services/release/release_test.go b/services/release/release_test.go
index 0f5b74f70d..c0cafb5fcc 100644
--- a/services/release/release_test.go
+++ b/services/release/release_test.go
@@ -10,7 +10,6 @@ import (
"testing"
"time"
- "code.gitea.io/gitea/models"
"code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
@@ -30,15 +29,15 @@ func TestMain(m *testing.M) {
func TestRelease_Create(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository)
+ user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
repoPath := repo_model.RepoPath(user.Name, repo.Name)
gitRepo, err := git.OpenRepository(git.DefaultContext, repoPath)
assert.NoError(t, err)
defer gitRepo.Close()
- assert.NoError(t, CreateRelease(gitRepo, &models.Release{
+ assert.NoError(t, CreateRelease(gitRepo, &repo_model.Release{
RepoID: repo.ID,
Repo: repo,
PublisherID: user.ID,
@@ -52,7 +51,7 @@ func TestRelease_Create(t *testing.T) {
IsTag: false,
}, nil, ""))
- assert.NoError(t, CreateRelease(gitRepo, &models.Release{
+ assert.NoError(t, CreateRelease(gitRepo, &repo_model.Release{
RepoID: repo.ID,
Repo: repo,
PublisherID: user.ID,
@@ -66,7 +65,7 @@ func TestRelease_Create(t *testing.T) {
IsTag: false,
}, nil, ""))
- assert.NoError(t, CreateRelease(gitRepo, &models.Release{
+ assert.NoError(t, CreateRelease(gitRepo, &repo_model.Release{
RepoID: repo.ID,
Repo: repo,
PublisherID: user.ID,
@@ -80,7 +79,7 @@ func TestRelease_Create(t *testing.T) {
IsTag: false,
}, nil, ""))
- assert.NoError(t, CreateRelease(gitRepo, &models.Release{
+ assert.NoError(t, CreateRelease(gitRepo, &repo_model.Release{
RepoID: repo.ID,
Repo: repo,
PublisherID: user.ID,
@@ -94,7 +93,7 @@ func TestRelease_Create(t *testing.T) {
IsTag: false,
}, nil, ""))
- assert.NoError(t, CreateRelease(gitRepo, &models.Release{
+ assert.NoError(t, CreateRelease(gitRepo, &repo_model.Release{
RepoID: repo.ID,
Repo: repo,
PublisherID: user.ID,
@@ -115,7 +114,7 @@ func TestRelease_Create(t *testing.T) {
}, strings.NewReader("testtest"))
assert.NoError(t, err)
- release := models.Release{
+ release := repo_model.Release{
RepoID: repo.ID,
Repo: repo,
PublisherID: user.ID,
@@ -134,8 +133,8 @@ func TestRelease_Create(t *testing.T) {
func TestRelease_Update(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository)
+ user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
repoPath := repo_model.RepoPath(user.Name, repo.Name)
gitRepo, err := git.OpenRepository(git.DefaultContext, repoPath)
@@ -143,7 +142,7 @@ func TestRelease_Update(t *testing.T) {
defer gitRepo.Close()
// Test a changed release
- assert.NoError(t, CreateRelease(gitRepo, &models.Release{
+ assert.NoError(t, CreateRelease(gitRepo, &repo_model.Release{
RepoID: repo.ID,
Repo: repo,
PublisherID: user.ID,
@@ -156,18 +155,18 @@ func TestRelease_Update(t *testing.T) {
IsPrerelease: false,
IsTag: false,
}, nil, ""))
- release, err := models.GetRelease(repo.ID, "v1.1.1")
+ release, err := repo_model.GetRelease(repo.ID, "v1.1.1")
assert.NoError(t, err)
releaseCreatedUnix := release.CreatedUnix
time.Sleep(2 * time.Second) // sleep 2 seconds to ensure a different timestamp
release.Note = "Changed note"
assert.NoError(t, UpdateRelease(user, gitRepo, release, nil, nil, nil))
- release, err = models.GetReleaseByID(db.DefaultContext, release.ID)
+ release, err = repo_model.GetReleaseByID(db.DefaultContext, release.ID)
assert.NoError(t, err)
assert.Equal(t, int64(releaseCreatedUnix), int64(release.CreatedUnix))
// Test a changed draft
- assert.NoError(t, CreateRelease(gitRepo, &models.Release{
+ assert.NoError(t, CreateRelease(gitRepo, &repo_model.Release{
RepoID: repo.ID,
Repo: repo,
PublisherID: user.ID,
@@ -180,18 +179,18 @@ func TestRelease_Update(t *testing.T) {
IsPrerelease: false,
IsTag: false,
}, nil, ""))
- release, err = models.GetRelease(repo.ID, "v1.2.1")
+ release, err = repo_model.GetRelease(repo.ID, "v1.2.1")
assert.NoError(t, err)
releaseCreatedUnix = release.CreatedUnix
time.Sleep(2 * time.Second) // sleep 2 seconds to ensure a different timestamp
release.Title = "Changed title"
assert.NoError(t, UpdateRelease(user, gitRepo, release, nil, nil, nil))
- release, err = models.GetReleaseByID(db.DefaultContext, release.ID)
+ release, err = repo_model.GetReleaseByID(db.DefaultContext, release.ID)
assert.NoError(t, err)
assert.Less(t, int64(releaseCreatedUnix), int64(release.CreatedUnix))
// Test a changed pre-release
- assert.NoError(t, CreateRelease(gitRepo, &models.Release{
+ assert.NoError(t, CreateRelease(gitRepo, &repo_model.Release{
RepoID: repo.ID,
Repo: repo,
PublisherID: user.ID,
@@ -204,19 +203,19 @@ func TestRelease_Update(t *testing.T) {
IsPrerelease: true,
IsTag: false,
}, nil, ""))
- release, err = models.GetRelease(repo.ID, "v1.3.1")
+ release, err = repo_model.GetRelease(repo.ID, "v1.3.1")
assert.NoError(t, err)
releaseCreatedUnix = release.CreatedUnix
time.Sleep(2 * time.Second) // sleep 2 seconds to ensure a different timestamp
release.Title = "Changed title"
release.Note = "Changed note"
assert.NoError(t, UpdateRelease(user, gitRepo, release, nil, nil, nil))
- release, err = models.GetReleaseByID(db.DefaultContext, release.ID)
+ release, err = repo_model.GetReleaseByID(db.DefaultContext, release.ID)
assert.NoError(t, err)
assert.Equal(t, int64(releaseCreatedUnix), int64(release.CreatedUnix))
// Test create release
- release = &models.Release{
+ release = &repo_model.Release{
RepoID: repo.ID,
Repo: repo,
PublisherID: user.ID,
@@ -236,7 +235,7 @@ func TestRelease_Update(t *testing.T) {
tagName := release.TagName
assert.NoError(t, UpdateRelease(user, gitRepo, release, nil, nil, nil))
- release, err = models.GetReleaseByID(db.DefaultContext, release.ID)
+ release, err = repo_model.GetReleaseByID(db.DefaultContext, release.ID)
assert.NoError(t, err)
assert.Equal(t, tagName, release.TagName)
@@ -249,7 +248,7 @@ func TestRelease_Update(t *testing.T) {
assert.NoError(t, err)
assert.NoError(t, UpdateRelease(user, gitRepo, release, []string{attach.UUID}, nil, nil))
- assert.NoError(t, models.GetReleaseAttachments(db.DefaultContext, release))
+ assert.NoError(t, repo_model.GetReleaseAttachments(db.DefaultContext, release))
assert.Len(t, release.Attachments, 1)
assert.EqualValues(t, attach.UUID, release.Attachments[0].UUID)
assert.EqualValues(t, release.ID, release.Attachments[0].ReleaseID)
@@ -260,7 +259,7 @@ func TestRelease_Update(t *testing.T) {
attach.UUID: "test2.txt",
}))
release.Attachments = nil
- assert.NoError(t, models.GetReleaseAttachments(db.DefaultContext, release))
+ assert.NoError(t, repo_model.GetReleaseAttachments(db.DefaultContext, release))
assert.Len(t, release.Attachments, 1)
assert.EqualValues(t, attach.UUID, release.Attachments[0].UUID)
assert.EqualValues(t, release.ID, release.Attachments[0].ReleaseID)
@@ -269,15 +268,15 @@ func TestRelease_Update(t *testing.T) {
// delete the attachment
assert.NoError(t, UpdateRelease(user, gitRepo, release, nil, []string{attach.UUID}, nil))
release.Attachments = nil
- assert.NoError(t, models.GetReleaseAttachments(db.DefaultContext, release))
+ assert.NoError(t, repo_model.GetReleaseAttachments(db.DefaultContext, release))
assert.Empty(t, release.Attachments)
}
func TestRelease_createTag(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository)
+ user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
repoPath := repo_model.RepoPath(user.Name, repo.Name)
gitRepo, err := git.OpenRepository(git.DefaultContext, repoPath)
@@ -285,7 +284,7 @@ func TestRelease_createTag(t *testing.T) {
defer gitRepo.Close()
// Test a changed release
- release := &models.Release{
+ release := &repo_model.Release{
RepoID: repo.ID,
Repo: repo,
PublisherID: user.ID,
@@ -309,7 +308,7 @@ func TestRelease_createTag(t *testing.T) {
assert.Equal(t, int64(releaseCreatedUnix), int64(release.CreatedUnix))
// Test a changed draft
- release = &models.Release{
+ release = &repo_model.Release{
RepoID: repo.ID,
Repo: repo,
PublisherID: user.ID,
@@ -332,7 +331,7 @@ func TestRelease_createTag(t *testing.T) {
assert.Less(t, int64(releaseCreatedUnix), int64(release.CreatedUnix))
// Test a changed pre-release
- release = &models.Release{
+ release = &repo_model.Release{
RepoID: repo.ID,
Repo: repo,
PublisherID: user.ID,
@@ -358,8 +357,8 @@ func TestRelease_createTag(t *testing.T) {
func TestCreateNewTag(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository)
+ user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
assert.NoError(t, CreateNewTag(git.DefaultContext, user, repo, "master", "v2.0",
"v2.0 is released \n\n BUGFIX: .... \n\n 123"))
diff --git a/services/repository/adopt.go b/services/repository/adopt.go
index 6d6611c705..9e04c15977 100644
--- a/services/repository/adopt.go
+++ b/services/repository/adopt.go
@@ -11,10 +11,10 @@ import (
"path/filepath"
"strings"
- "code.gitea.io/gitea/models"
"code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
+ "code.gitea.io/gitea/modules/container"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/notification"
@@ -26,7 +26,7 @@ import (
)
// AdoptRepository adopts pre-existing repository files for the user/organization.
-func AdoptRepository(doer, u *user_model.User, opts models.CreateRepoOptions) (*repo_model.Repository, error) {
+func AdoptRepository(doer, u *user_model.User, opts repo_module.CreateRepoOptions) (*repo_model.Repository, error) {
if !doer.IsAdmin && !u.CanCreateRepo() {
return nil, repo_model.ErrReachLimitOfRepo{
Limit: u.MaxRepoCreation,
@@ -67,7 +67,7 @@ func AdoptRepository(doer, u *user_model.User, opts models.CreateRepoOptions) (*
}
}
- if err := models.CreateRepository(ctx, doer, u, repo, true); err != nil {
+ if err := repo_module.CreateRepositoryByExample(ctx, doer, u, repo, true); err != nil {
return err
}
if err := adoptRepository(ctx, repoPath, doer, repo, opts); err != nil {
@@ -100,7 +100,7 @@ func AdoptRepository(doer, u *user_model.User, opts models.CreateRepoOptions) (*
return repo, nil
}
-func adoptRepository(ctx context.Context, repoPath string, u *user_model.User, repo *repo_model.Repository, opts models.CreateRepoOptions) (err error) {
+func adoptRepository(ctx context.Context, repoPath string, u *user_model.User, repo *repo_model.Repository, opts repo_module.CreateRepoOptions) (err error) {
isExist, err := util.IsExist(repoPath)
if err != nil {
log.Error("Unable to check if %s exists. Error: %v", repoPath, err)
@@ -258,12 +258,12 @@ func checkUnadoptedRepositories(userName string, repoNamesToCheck []string, unad
if len(repos) == len(repoNamesToCheck) {
return nil
}
- repoNames := make(map[string]bool, len(repos))
+ repoNames := make(container.Set[string], len(repos))
for _, repo := range repos {
- repoNames[repo.LowerName] = true
+ repoNames.Add(repo.LowerName)
}
for _, repoName := range repoNamesToCheck {
- if _, ok := repoNames[repoName]; !ok {
+ if !repoNames.Contains(repoName) {
unadopted.add(filepath.Join(userName, repoName))
}
}
diff --git a/services/repository/archiver/archiver.go b/services/repository/archiver/archiver.go
index ebd3eaf236..ae43503bae 100644
--- a/services/repository/archiver/archiver.go
+++ b/services/repository/archiver/archiver.go
@@ -57,6 +57,21 @@ func (ErrUnknownArchiveFormat) Is(err error) bool {
return ok
}
+// RepoRefNotFoundError is returned when a requested reference (commit, tag) was not found.
+type RepoRefNotFoundError struct {
+ RefName string
+}
+
+// Error implements error.
+func (e RepoRefNotFoundError) Error() string {
+ return fmt.Sprintf("unrecognized repository reference: %s", e.RefName)
+}
+
+func (e RepoRefNotFoundError) Is(err error) bool {
+ _, ok := err.(RepoRefNotFoundError)
+ return ok
+}
+
// NewRequest creates an archival request, based on the URI. The
// resulting ArchiveRequest is suitable for being passed to ArchiveRepository()
// if it's determined that the request still needs to be satisfied.
@@ -103,7 +118,7 @@ func NewRequest(repoID int64, repo *git.Repository, uri string) (*ArchiveRequest
}
}
} else {
- return nil, fmt.Errorf("Unknow ref %s type", r.refName)
+ return nil, RepoRefNotFoundError{RefName: r.refName}
}
return r, nil
@@ -115,6 +130,49 @@ func (aReq *ArchiveRequest) GetArchiveName() string {
return strings.ReplaceAll(aReq.refName, "/", "-") + "." + aReq.Type.String()
}
+// Await awaits the completion of an ArchiveRequest. If the archive has
+// already been prepared the method returns immediately. Otherwise an archiver
+// process will be started and its completion awaited. On success the returned
+// RepoArchiver may be used to download the archive. Note that even if the
+// context is cancelled/times out a started archiver will still continue to run
+// in the background.
+func (aReq *ArchiveRequest) Await(ctx context.Context) (*repo_model.RepoArchiver, error) {
+ archiver, err := repo_model.GetRepoArchiver(ctx, aReq.RepoID, aReq.Type, aReq.CommitID)
+ if err != nil {
+ return nil, fmt.Errorf("models.GetRepoArchiver: %v", err)
+ }
+
+ if archiver != nil && archiver.Status == repo_model.ArchiverReady {
+ // Archive already generated, we're done.
+ return archiver, nil
+ }
+
+ if err := StartArchive(aReq); err != nil {
+ return nil, fmt.Errorf("archiver.StartArchive: %v", err)
+ }
+
+ poll := time.NewTicker(time.Second * 1)
+ defer poll.Stop()
+
+ for {
+ select {
+ case <-graceful.GetManager().HammerContext().Done():
+ // System stopped.
+ return nil, graceful.GetManager().HammerContext().Err()
+ case <-ctx.Done():
+ return nil, ctx.Err()
+ case <-poll.C:
+ archiver, err = repo_model.GetRepoArchiver(ctx, aReq.RepoID, aReq.Type, aReq.CommitID)
+ if err != nil {
+ return nil, fmt.Errorf("repo_model.GetRepoArchiver: %v", err)
+ }
+ if archiver != nil && archiver.Status == repo_model.ArchiverReady {
+ return archiver, nil
+ }
+ }
+ }
+}
+
func doArchive(r *ArchiveRequest) (*repo_model.RepoArchiver, error) {
txCtx, committer, err := db.TxContext()
if err != nil {
@@ -147,11 +205,7 @@ func doArchive(r *ArchiveRequest) (*repo_model.RepoArchiver, error) {
}
}
- rPath, err := archiver.RelativePath()
- if err != nil {
- return nil, err
- }
-
+ rPath := archiver.RelativePath()
_, err = storage.RepoArchives.Stat(rPath)
if err == nil {
if archiver.Status == repo_model.ArchiverGenerating {
@@ -284,13 +338,10 @@ func StartArchive(request *ArchiveRequest) error {
}
func deleteOldRepoArchiver(ctx context.Context, archiver *repo_model.RepoArchiver) error {
- p, err := archiver.RelativePath()
- if err != nil {
- return err
- }
if err := repo_model.DeleteRepoArchiver(ctx, archiver); err != nil {
return err
}
+ p := archiver.RelativePath()
if err := storage.RepoArchives.Delete(p); err != nil {
log.Error("delete repo archive file failed: %v", err)
}
diff --git a/services/repository/avatar.go b/services/repository/avatar.go
index dcf04c7e54..b9bd36ab66 100644
--- a/services/repository/avatar.go
+++ b/services/repository/avatar.go
@@ -96,7 +96,7 @@ func DeleteAvatar(repo *repo_model.Repository) error {
// RemoveRandomAvatars removes the randomly generated avatars that were created for repositories
func RemoveRandomAvatars(ctx context.Context) error {
- return repo_model.IterateRepository(func(repository *repo_model.Repository) error {
+ return db.IterateObjects(ctx, func(repository *repo_model.Repository) error {
select {
case <-ctx.Done():
return db.ErrCancelledf("before random avatars removed for %s", repository.FullName())
diff --git a/services/repository/avatar_test.go b/services/repository/avatar_test.go
index efad392a2d..e5d9ac9d53 100644
--- a/services/repository/avatar_test.go
+++ b/services/repository/avatar_test.go
@@ -25,7 +25,7 @@ func TestUploadAvatar(t *testing.T) {
png.Encode(&buff, myImage)
assert.NoError(t, unittest.PrepareTestDatabase())
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 10}).(*repo_model.Repository)
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 10})
err := UploadAvatar(repo, buff.Bytes())
assert.NoError(t, err)
@@ -39,7 +39,7 @@ func TestUploadBigAvatar(t *testing.T) {
png.Encode(&buff, myImage)
assert.NoError(t, unittest.PrepareTestDatabase())
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 10}).(*repo_model.Repository)
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 10})
err := UploadAvatar(repo, buff.Bytes())
assert.Error(t, err)
@@ -52,7 +52,7 @@ func TestDeleteAvatar(t *testing.T) {
png.Encode(&buff, myImage)
assert.NoError(t, unittest.PrepareTestDatabase())
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 10}).(*repo_model.Repository)
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 10})
err := UploadAvatar(repo, buff.Bytes())
assert.NoError(t, err)
diff --git a/services/repository/cache.go b/services/repository/cache.go
index 5b0c929be1..855fe7f4a0 100644
--- a/services/repository/cache.go
+++ b/services/repository/cache.go
@@ -34,15 +34,13 @@ func CacheRef(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Rep
return err
}
- commitsCount, err := cache.GetInt64(repo.GetCommitsCountCacheKey(getRefName(fullRefName), true), commit.CommitsCount)
- if err != nil {
- return err
- }
- if commitsCount < setting.CacheService.LastCommit.CommitsCount {
- return nil
+ if gitRepo.LastCommitCache == nil {
+ commitsCount, err := cache.GetInt64(repo.GetCommitsCountCacheKey(getRefName(fullRefName), true), commit.CommitsCount)
+ if err != nil {
+ return err
+ }
+ gitRepo.LastCommitCache = git.NewLastCommitCache(commitsCount, repo.FullName(), gitRepo, cache.GetCache())
}
- commitCache := git.NewLastCommitCache(repo.FullName(), gitRepo, setting.LastCommitCacheTTLSeconds, cache.GetCache())
-
- return commitCache.CacheCommit(ctx, commit)
+ return commit.CacheCommit(ctx)
}
diff --git a/services/repository/check.go b/services/repository/check.go
index 17bdf2fac1..2417db6a27 100644
--- a/services/repository/check.go
+++ b/services/repository/check.go
@@ -11,9 +11,9 @@ import (
"time"
"code.gitea.io/gitea/models"
- admin_model "code.gitea.io/gitea/models/admin"
"code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo"
+ system_model "code.gitea.io/gitea/models/system"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
@@ -42,7 +42,7 @@ func GitFsck(ctx context.Context, timeout time.Duration, args []string) error {
repoPath := repo.RepoPath()
if err := git.Fsck(ctx, repoPath, timeout, args...); err != nil {
log.Warn("Failed to health check repository (%v): %v", repo, err)
- if err = admin_model.CreateRepositoryNotice("Failed to health check repository (%s): %v", repo.FullName(), err); err != nil {
+ if err = system_model.CreateRepositoryNotice("Failed to health check repository (%s): %v", repo.FullName(), err); err != nil {
log.Error("CreateRepositoryNotice: %v", err)
}
}
@@ -83,7 +83,7 @@ func GitGcRepos(ctx context.Context, timeout time.Duration, args ...string) erro
if err != nil {
log.Error("Repository garbage collection failed for %v. Stdout: %s\nError: %v", repo, stdout, err)
desc := fmt.Sprintf("Repository garbage collection failed for %s. Stdout: %s\nError: %v", repo.RepoPath(), stdout, err)
- if err = admin_model.CreateRepositoryNotice(desc); err != nil {
+ if err = system_model.CreateRepositoryNotice(desc); err != nil {
log.Error("CreateRepositoryNotice: %v", err)
}
return fmt.Errorf("Repository garbage collection failed in repo: %s: Error: %v", repo.FullName(), err)
@@ -93,7 +93,7 @@ func GitGcRepos(ctx context.Context, timeout time.Duration, args ...string) erro
if err := repo_module.UpdateRepoSize(ctx, repo); err != nil {
log.Error("Updating size as part of garbage collection failed for %v. Stdout: %s\nError: %v", repo, stdout, err)
desc := fmt.Sprintf("Updating size as part of garbage collection failed for %s. Stdout: %s\nError: %v", repo.RepoPath(), stdout, err)
- if err = admin_model.CreateRepositoryNotice(desc); err != nil {
+ if err = system_model.CreateRepositoryNotice(desc); err != nil {
log.Error("CreateRepositoryNotice: %v", err)
}
return fmt.Errorf("Updating size as part of garbage collection failed in repo: %s: Error: %v", repo.FullName(), err)
@@ -135,7 +135,7 @@ func gatherMissingRepoRecords(ctx context.Context) ([]*repo_model.Repository, er
if strings.HasPrefix(err.Error(), "Aborted gathering missing repo") {
return nil, err
}
- if err2 := admin_model.CreateRepositoryNotice("gatherMissingRepoRecords: %v", err); err2 != nil {
+ if err2 := system_model.CreateRepositoryNotice("gatherMissingRepoRecords: %v", err); err2 != nil {
log.Error("CreateRepositoryNotice: %v", err2)
}
return nil, err
@@ -163,7 +163,7 @@ func DeleteMissingRepositories(ctx context.Context, doer *user_model.User) error
log.Trace("Deleting %d/%d...", repo.OwnerID, repo.ID)
if err := models.DeleteRepository(doer, repo.OwnerID, repo.ID); err != nil {
log.Error("Failed to DeleteRepository %s [%d]: Error: %v", repo.FullName(), repo.ID, err)
- if err2 := admin_model.CreateRepositoryNotice("Failed to DeleteRepository %s [%d]: Error: %v", repo.FullName(), repo.ID, err); err2 != nil {
+ if err2 := system_model.CreateRepositoryNotice("Failed to DeleteRepository %s [%d]: Error: %v", repo.FullName(), repo.ID, err); err2 != nil {
log.Error("CreateRepositoryNotice: %v", err)
}
}
@@ -191,7 +191,7 @@ func ReinitMissingRepositories(ctx context.Context) error {
log.Trace("Initializing %d/%d...", repo.OwnerID, repo.ID)
if err := git.InitRepository(ctx, repo.RepoPath(), true); err != nil {
log.Error("Unable (re)initialize repository %d at %s. Error: %v", repo.ID, repo.RepoPath(), err)
- if err2 := admin_model.CreateRepositoryNotice("InitRepository [%d]: %v", repo.ID, err); err2 != nil {
+ if err2 := system_model.CreateRepositoryNotice("InitRepository [%d]: %v", repo.ID, err); err2 != nil {
log.Error("CreateRepositoryNotice: %v", err2)
}
}
diff --git a/services/repository/files/content.go b/services/repository/files/content.go
index c206909289..34c8aeec25 100644
--- a/services/repository/files/content.go
+++ b/services/repository/files/content.go
@@ -165,13 +165,24 @@ func GetContents(ctx context.Context, repo *repo_model.Repository, treePath, ref
}
selfURLString := selfURL.String()
+ err = gitRepo.AddLastCommitCache(repo.GetCommitsCountCacheKey(ref, refType != git.ObjectCommit), repo.FullName(), commitID)
+ if err != nil {
+ return nil, err
+ }
+
+ lastCommit, err := commit.GetCommitByPath(treePath)
+ if err != nil {
+ return nil, err
+ }
+
// All content types have these fields in populated
contentsResponse := &api.ContentsResponse{
- Name: entry.Name(),
- Path: treePath,
- SHA: entry.ID.String(),
- Size: entry.Size(),
- URL: &selfURLString,
+ Name: entry.Name(),
+ Path: treePath,
+ SHA: entry.ID.String(),
+ LastCommitSHA: lastCommit.ID.String(),
+ Size: entry.Size(),
+ URL: &selfURLString,
Links: &api.FileLinksResponse{
Self: &selfURLString,
},
diff --git a/services/repository/files/content_test.go b/services/repository/files/content_test.go
index 342ebae329..24fcd6c4c5 100644
--- a/services/repository/files/content_test.go
+++ b/services/repository/files/content_test.go
@@ -33,17 +33,18 @@ func getExpectedReadmeContentsResponse() *api.ContentsResponse {
gitURL := "https://try.gitea.io/api/v1/repos/user2/repo1/git/blobs/" + sha
downloadURL := "https://try.gitea.io/user2/repo1/raw/branch/master/" + treePath
return &api.ContentsResponse{
- Name: treePath,
- Path: treePath,
- SHA: "4b4851ad51df6a7d9f25c979345979eaeb5b349f",
- Type: "file",
- Size: 30,
- Encoding: &encoding,
- Content: &content,
- URL: &selfURL,
- HTMLURL: &htmlURL,
- GitURL: &gitURL,
- DownloadURL: &downloadURL,
+ Name: treePath,
+ Path: treePath,
+ SHA: "4b4851ad51df6a7d9f25c979345979eaeb5b349f",
+ LastCommitSHA: "65f1bf27bc3bf70f64657658635e66094edbcb4d",
+ Type: "file",
+ Size: 30,
+ Encoding: &encoding,
+ Content: &content,
+ URL: &selfURL,
+ HTMLURL: &htmlURL,
+ GitURL: &gitURL,
+ DownloadURL: &downloadURL,
Links: &api.FileLinksResponse{
Self: &selfURL,
GitURL: &gitURL,
diff --git a/services/repository/files/file_test.go b/services/repository/files/file_test.go
index ee0582dfc2..e158c63de2 100644
--- a/services/repository/files/file_test.go
+++ b/services/repository/files/file_test.go
@@ -43,17 +43,18 @@ func getExpectedFileResponse() *api.FileResponse {
downloadURL := setting.AppURL + "user2/repo1/raw/branch/master/" + treePath
return &api.FileResponse{
Content: &api.ContentsResponse{
- Name: treePath,
- Path: treePath,
- SHA: sha,
- Type: "file",
- Size: 30,
- Encoding: &encoding,
- Content: &content,
- URL: &selfURL,
- HTMLURL: &htmlURL,
- GitURL: &gitURL,
- DownloadURL: &downloadURL,
+ Name: treePath,
+ Path: treePath,
+ SHA: sha,
+ LastCommitSHA: "65f1bf27bc3bf70f64657658635e66094edbcb4d",
+ Type: "file",
+ Size: 30,
+ Encoding: &encoding,
+ Content: &content,
+ URL: &selfURL,
+ HTMLURL: &htmlURL,
+ GitURL: &gitURL,
+ DownloadURL: &downloadURL,
Links: &api.FileLinksResponse{
Self: &selfURL,
GitURL: &gitURL,
diff --git a/services/repository/files/tree.go b/services/repository/files/tree.go
index caad732887..59e5690977 100644
--- a/services/repository/files/tree.go
+++ b/services/repository/files/tree.go
@@ -29,7 +29,7 @@ func GetTreeBySHA(ctx context.Context, repo *repo_model.Repository, gitRepo *git
tree.URL = repo.APIURL() + "/git/trees/" + url.PathEscape(tree.SHA)
var entries git.Entries
if recursive {
- entries, err = gitTree.ListEntriesRecursive()
+ entries, err = gitTree.ListEntriesRecursiveWithSize()
} else {
entries, err = gitTree.ListEntries()
}
diff --git a/services/repository/files/upload.go b/services/repository/files/upload.go
index ffc1f4efe9..327a2e121c 100644
--- a/services/repository/files/upload.go
+++ b/services/repository/files/upload.go
@@ -11,7 +11,6 @@ import (
"path"
"strings"
- "code.gitea.io/gitea/models"
git_model "code.gitea.io/gitea/models/git"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
@@ -32,7 +31,7 @@ type UploadRepoFileOptions struct {
}
type uploadInfo struct {
- upload *models.Upload
+ upload *repo_model.Upload
lfsMetaObject *git_model.LFSMetaObject
}
@@ -56,7 +55,7 @@ func UploadRepoFiles(ctx context.Context, repo *repo_model.Repository, doer *use
return nil
}
- uploads, err := models.GetUploadsByUUIDs(opts.Files)
+ uploads, err := repo_model.GetUploadsByUUIDs(opts.Files)
if err != nil {
return fmt.Errorf("GetUploadsByUUIDs [uuids: %v]: %v", opts.Files, err)
}
@@ -157,7 +156,7 @@ func UploadRepoFiles(ctx context.Context, repo *repo_model.Repository, doer *use
return err
}
- return models.DeleteUploads(uploads...)
+ return repo_model.DeleteUploads(uploads...)
}
func copyUploadedLFSFileIntoRepository(info *uploadInfo, filename2attribute2info map[string]map[string]string, t *TemporaryUploadRepository, treePath string) error {
diff --git a/services/repository/fork.go b/services/repository/fork.go
index b274585ed5..32a516b79f 100644
--- a/services/repository/fork.go
+++ b/services/repository/fork.go
@@ -10,7 +10,6 @@ import (
"strings"
"time"
- "code.gitea.io/gitea/models"
"code.gitea.io/gitea/models/db"
git_model "code.gitea.io/gitea/models/git"
repo_model "code.gitea.io/gitea/models/repo"
@@ -23,6 +22,27 @@ import (
"code.gitea.io/gitea/modules/util"
)
+// ErrForkAlreadyExist represents a "ForkAlreadyExist" kind of error.
+type ErrForkAlreadyExist struct {
+ Uname string
+ RepoName string
+ ForkName string
+}
+
+// IsErrForkAlreadyExist checks if an error is an ErrForkAlreadyExist.
+func IsErrForkAlreadyExist(err error) bool {
+ _, ok := err.(ErrForkAlreadyExist)
+ return ok
+}
+
+func (err ErrForkAlreadyExist) Error() string {
+ return fmt.Sprintf("repository is already forked by user [uname: %s, repo path: %s, fork path: %s]", err.Uname, err.RepoName, err.ForkName)
+}
+
+func (err ErrForkAlreadyExist) Unwrap() error {
+ return util.ErrAlreadyExist
+}
+
// ForkRepoOptions contains the fork repository options
type ForkRepoOptions struct {
BaseRepo *repo_model.Repository
@@ -37,7 +57,7 @@ func ForkRepository(ctx context.Context, doer, owner *user_model.User, opts Fork
return nil, err
}
if forkedRepo != nil {
- return nil, models.ErrForkAlreadyExist{
+ return nil, ErrForkAlreadyExist{
Uname: owner.Name,
RepoName: opts.BaseRepo.FullName(),
ForkName: forkedRepo.FullName(),
@@ -93,7 +113,7 @@ func ForkRepository(ctx context.Context, doer, owner *user_model.User, opts Fork
}()
err = db.WithTx(func(txCtx context.Context) error {
- if err = models.CreateRepository(txCtx, doer, owner, repo, false); err != nil {
+ if err = repo_module.CreateRepositoryByExample(txCtx, doer, owner, repo, false); err != nil {
return err
}
diff --git a/services/repository/fork_test.go b/services/repository/fork_test.go
index 965887b5d1..d4ba507351 100644
--- a/services/repository/fork_test.go
+++ b/services/repository/fork_test.go
@@ -7,7 +7,6 @@ package repository
import (
"testing"
- "code.gitea.io/gitea/models"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
@@ -20,8 +19,8 @@ func TestForkRepository(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
// user 13 has already forked repo10
- user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 13}).(*user_model.User)
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 10}).(*repo_model.Repository)
+ user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 13})
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 10})
fork, err := ForkRepository(git.DefaultContext, user, user, ForkRepoOptions{
BaseRepo: repo,
@@ -30,5 +29,5 @@ func TestForkRepository(t *testing.T) {
})
assert.Nil(t, fork)
assert.Error(t, err)
- assert.True(t, models.IsErrForkAlreadyExist(err))
+ assert.True(t, IsErrForkAlreadyExist(err))
}
diff --git a/services/repository/push.go b/services/repository/push.go
index 65ac7b660c..d645928c43 100644
--- a/services/repository/push.go
+++ b/services/repository/push.go
@@ -11,7 +11,6 @@ import (
"strings"
"time"
- "code.gitea.io/gitea/models"
"code.gitea.io/gitea/models/db"
git_model "code.gitea.io/gitea/models/git"
repo_model "code.gitea.io/gitea/models/repo"
@@ -220,10 +219,6 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error {
log.Error("updateIssuesCommit: %v", err)
}
- if len(commits.Commits) > setting.UI.FeedMaxCommitNum {
- commits.Commits = commits.Commits[:setting.UI.FeedMaxCommitNum]
- }
-
oldCommitID := opts.OldCommitID
if oldCommitID == git.EmptySHA && len(commits.Commits) > 0 {
oldCommit, err := gitRepo.GetCommit(commits.Commits[len(commits.Commits)-1].Sha1)
@@ -251,6 +246,10 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error {
commits.CompareURL = ""
}
+ if len(commits.Commits) > setting.UI.FeedMaxCommitNum {
+ commits.Commits = commits.Commits[:setting.UI.FeedMaxCommitNum]
+ }
+
notification.NotifyPushCommits(pusher, repo, opts, commits)
if err = git_model.RemoveDeletedBranchByName(repo.ID, branch); err != nil {
@@ -292,7 +291,7 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error {
// PushUpdateAddDeleteTags updates a number of added and delete tags
func PushUpdateAddDeleteTags(repo *repo_model.Repository, gitRepo *git.Repository, addTags, delTags []string) error {
return db.WithTx(func(ctx context.Context) error {
- if err := models.PushUpdateDeleteTagsContext(ctx, repo, delTags); err != nil {
+ if err := repo_model.PushUpdateDeleteTagsContext(ctx, repo, delTags); err != nil {
return err
}
return pushUpdateAddTags(ctx, repo, gitRepo, addTags)
@@ -310,16 +309,16 @@ func pushUpdateAddTags(ctx context.Context, repo *repo_model.Repository, gitRepo
lowerTags = append(lowerTags, strings.ToLower(tag))
}
- releases, err := models.GetReleasesByRepoIDAndNames(ctx, repo.ID, lowerTags)
+ releases, err := repo_model.GetReleasesByRepoIDAndNames(ctx, repo.ID, lowerTags)
if err != nil {
return fmt.Errorf("GetReleasesByRepoIDAndNames: %v", err)
}
- relMap := make(map[string]*models.Release)
+ relMap := make(map[string]*repo_model.Release)
for _, rel := range releases {
relMap[rel.LowerTagName] = rel
}
- newReleases := make([]*models.Release, 0, len(lowerTags)-len(relMap))
+ newReleases := make([]*repo_model.Release, 0, len(lowerTags)-len(relMap))
emailToUser := make(map[string]*user_model.User)
@@ -366,7 +365,7 @@ func pushUpdateAddTags(ctx context.Context, repo *repo_model.Repository, gitRepo
rel, has := relMap[lowerTag]
if !has {
- rel = &models.Release{
+ rel = &repo_model.Release{
RepoID: repo.ID,
Title: "",
TagName: tags[i],
@@ -393,7 +392,7 @@ func pushUpdateAddTags(ctx context.Context, repo *repo_model.Repository, gitRepo
if rel.IsTag && author != nil {
rel.PublisherID = author.ID
}
- if err = models.UpdateRelease(ctx, rel); err != nil {
+ if err = repo_model.UpdateRelease(ctx, rel); err != nil {
return fmt.Errorf("Update: %v", err)
}
}
diff --git a/services/repository/repository.go b/services/repository/repository.go
index 4bde6879a6..47687d9a73 100644
--- a/services/repository/repository.go
+++ b/services/repository/repository.go
@@ -9,12 +9,12 @@ import (
"fmt"
"code.gitea.io/gitea/models"
- admin_model "code.gitea.io/gitea/models/admin"
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/models/organization"
packages_model "code.gitea.io/gitea/models/packages"
repo_model "code.gitea.io/gitea/models/repo"
+ system_model "code.gitea.io/gitea/models/system"
"code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/log"
@@ -25,7 +25,7 @@ import (
)
// CreateRepository creates a repository for the user/organization.
-func CreateRepository(doer, owner *user_model.User, opts models.CreateRepoOptions) (*repo_model.Repository, error) {
+func CreateRepository(doer, owner *user_model.User, opts repo_module.CreateRepoOptions) (*repo_model.Repository, error) {
repo, err := repo_module.CreateRepository(doer, owner, opts)
if err != nil {
// No need to rollback here we should do this in CreateRepository...
@@ -69,7 +69,7 @@ func PushCreateRepo(authUser, owner *user_model.User, repoName string) (*repo_mo
}
}
- repo, err := CreateRepository(authUser, owner, models.CreateRepoOptions{
+ repo, err := CreateRepository(authUser, owner, repo_module.CreateRepoOptions{
Name: repoName,
IsPrivate: setting.Repository.DefaultPushCreatePrivate,
})
@@ -83,8 +83,8 @@ func PushCreateRepo(authUser, owner *user_model.User, repoName string) (*repo_mo
// Init start repository service
func Init() error {
repo_module.LoadRepoConfig()
- admin_model.RemoveAllWithNotice(db.DefaultContext, "Clean up temporary repository uploads", setting.Repository.Upload.TempPath)
- admin_model.RemoveAllWithNotice(db.DefaultContext, "Clean up temporary repositories", repo_module.LocalCopyPath())
+ system_model.RemoveAllWithNotice(db.DefaultContext, "Clean up temporary repository uploads", setting.Repository.Upload.TempPath)
+ system_model.RemoveAllWithNotice(db.DefaultContext, "Clean up temporary repositories", repo_module.LocalCopyPath())
return initPushQueue()
}
@@ -117,7 +117,7 @@ func LinkedRepository(a *repo_model.Attachment) (*repo_model.Repository, unit.Ty
}
return repo, unitType, err
} else if a.ReleaseID != 0 {
- rel, err := models.GetReleaseByID(db.DefaultContext, a.ReleaseID)
+ rel, err := repo_model.GetReleaseByID(db.DefaultContext, a.ReleaseID)
if err != nil {
return nil, unit.TypeReleases, err
}
diff --git a/services/repository/review_test.go b/services/repository/review_test.go
index 640657d1dd..badacf39a6 100644
--- a/services/repository/review_test.go
+++ b/services/repository/review_test.go
@@ -16,12 +16,12 @@ import (
func TestRepoGetReviewerTeams(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}).(*repo_model.Repository)
+ repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2})
teams, err := GetReviewerTeams(repo2)
assert.NoError(t, err)
assert.Empty(t, teams)
- repo3 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3}).(*repo_model.Repository)
+ repo3 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3})
teams, err = GetReviewerTeams(repo3)
assert.NoError(t, err)
assert.Len(t, teams, 2)
diff --git a/services/repository/template.go b/services/repository/template.go
index d7e8145811..b73abdce58 100644
--- a/services/repository/template.go
+++ b/services/repository/template.go
@@ -7,12 +7,10 @@ package repository
import (
"context"
- "code.gitea.io/gitea/models"
"code.gitea.io/gitea/models/db"
issues_model "code.gitea.io/gitea/models/issues"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
- "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/notification"
repo_module "code.gitea.io/gitea/modules/repository"
)
@@ -23,6 +21,11 @@ func GenerateIssueLabels(ctx context.Context, templateRepo, generateRepo *repo_m
if err != nil {
return err
}
+ // Prevent insert being called with an empty slice which would result in
+ // err "no element on slice when insert".
+ if len(templateLabels) == 0 {
+ return nil
+ }
newLabels := make([]*issues_model.Label, 0, len(templateLabels))
for _, templateLabel := range templateLabels {
@@ -95,11 +98,6 @@ func GenerateRepository(doer, owner *user_model.User, templateRepo *repo_model.R
return nil
}); err != nil {
- if generateRepo != nil && generateRepo.ID > 0 {
- if errDelete := models.DeleteRepository(doer, owner.ID, generateRepo.ID); errDelete != nil {
- log.Error("Rollback deleteRepository: %v", errDelete)
- }
- }
return nil, err
}
diff --git a/services/repository/transfer.go b/services/repository/transfer.go
index ae15383240..a0f4a7685c 100644
--- a/services/repository/transfer.go
+++ b/services/repository/transfer.go
@@ -16,6 +16,7 @@ import (
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/notification"
+ repo_module "code.gitea.io/gitea/modules/repository"
"code.gitea.io/gitea/modules/sync"
)
@@ -49,7 +50,7 @@ func TransferOwnership(doer, newOwner *user_model.User, repo *repo_model.Reposit
}
for _, team := range teams {
- if err := models.AddRepository(team, newRepo); err != nil {
+ if err := models.AddRepository(db.DefaultContext, team, newRepo); err != nil {
return err
}
}
@@ -111,7 +112,7 @@ func StartRepositoryTransfer(doer, newOwner *user_model.User, repo *repo_model.R
return err
}
if !hasAccess {
- if err := models.AddCollaborator(repo, newOwner); err != nil {
+ if err := repo_module.AddCollaborator(repo, newOwner); err != nil {
return err
}
if err := repo_model.ChangeCollaborationAccessMode(repo, newOwner.ID, perm.AccessModeRead); err != nil {
diff --git a/services/repository/transfer_test.go b/services/repository/transfer_test.go
index 8be8c5353d..bf2a0ce0a2 100644
--- a/services/repository/transfer_test.go
+++ b/services/repository/transfer_test.go
@@ -8,7 +8,7 @@ import (
"sync"
"testing"
- "code.gitea.io/gitea/models"
+ activities_model "code.gitea.io/gitea/models/activities"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/organization"
access_model "code.gitea.io/gitea/models/perm/access"
@@ -35,12 +35,12 @@ func TestTransferOwnership(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3}).(*repo_model.Repository)
- repo.Owner = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User)
+ doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3})
+ repo.Owner = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
assert.NoError(t, TransferOwnership(doer, doer, repo, nil))
- transferredRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3}).(*repo_model.Repository)
+ transferredRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3})
assert.EqualValues(t, 2, transferredRepo.OwnerID)
exist, err := util.IsExist(repo_model.RepoPath("user3", "repo3"))
@@ -49,8 +49,8 @@ func TestTransferOwnership(t *testing.T) {
exist, err = util.IsExist(repo_model.RepoPath("user2", "repo3"))
assert.NoError(t, err)
assert.True(t, exist)
- unittest.AssertExistsAndLoadBean(t, &models.Action{
- OpType: models.ActionTransferRepo,
+ unittest.AssertExistsAndLoadBean(t, &activities_model.Action{
+ OpType: activities_model.ActionTransferRepo,
ActUserID: 2,
RepoID: 3,
Content: "user3/repo3",
@@ -62,10 +62,10 @@ func TestTransferOwnership(t *testing.T) {
func TestStartRepositoryTransferSetPermission(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3}).(*user_model.User)
- recipient := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5}).(*user_model.User)
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3}).(*repo_model.Repository)
- repo.Owner = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}).(*user_model.User)
+ doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3})
+ recipient := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5})
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3})
+ repo.Owner = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID})
hasAccess, err := access_model.HasAccess(db.DefaultContext, recipient.ID, repo)
assert.NoError(t, err)
diff --git a/services/task/migrate.go b/services/task/migrate.go
index 651681ef65..775cbf6128 100644
--- a/services/task/migrate.go
+++ b/services/task/migrate.go
@@ -10,6 +10,7 @@ import (
"strings"
"code.gitea.io/gitea/models"
+ admin_model "code.gitea.io/gitea/models/admin"
"code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
@@ -40,7 +41,7 @@ func handleCreateError(owner *user_model.User, err error) error {
}
}
-func runMigrateTask(t *models.Task) (err error) {
+func runMigrateTask(t *admin_model.Task) (err error) {
defer func() {
if e := recover(); e != nil {
err = fmt.Errorf("PANIC whilst trying to do migrate task: %v", e)
@@ -48,7 +49,7 @@ func runMigrateTask(t *models.Task) (err error) {
}
if err == nil {
- err = models.FinishMigrateTask(t)
+ err = admin_model.FinishMigrateTask(t)
if err == nil {
notification.NotifyMigrateRepository(t.Doer, t.Owner, t.Repo)
return
@@ -110,7 +111,7 @@ func runMigrateTask(t *models.Task) (err error) {
}
t.Repo, err = migrations.MigrateRepository(ctx, t.Doer, t.Owner.Name, *opts, func(format string, args ...interface{}) {
- message := models.TranslatableMessage{
+ message := admin_model.TranslatableMessage{
Format: format,
Args: args,
}
diff --git a/services/task/task.go b/services/task/task.go
index 9deb0286c5..138dc88a04 100644
--- a/services/task/task.go
+++ b/services/task/task.go
@@ -7,7 +7,7 @@ package task
import (
"fmt"
- "code.gitea.io/gitea/models"
+ admin_model "code.gitea.io/gitea/models/admin"
repo_model "code.gitea.io/gitea/models/repo"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/graceful"
@@ -27,7 +27,7 @@ import (
var taskQueue queue.Queue
// Run a task
-func Run(t *models.Task) error {
+func Run(t *admin_model.Task) error {
switch t.Type {
case structs.TaskTypeMigrateRepo:
return runMigrateTask(t)
@@ -38,7 +38,7 @@ func Run(t *models.Task) error {
// Init will start the service to get all unfinished tasks and run them
func Init() error {
- taskQueue = queue.CreateQueue("task", handle, &models.Task{})
+ taskQueue = queue.CreateQueue("task", handle, &admin_model.Task{})
if taskQueue == nil {
return fmt.Errorf("Unable to create Task Queue")
@@ -51,7 +51,7 @@ func Init() error {
func handle(data ...queue.Data) []queue.Data {
for _, datum := range data {
- task := datum.(*models.Task)
+ task := datum.(*admin_model.Task)
if err := Run(task); err != nil {
log.Error("Run task failed: %v", err)
}
@@ -70,7 +70,7 @@ func MigrateRepository(doer, u *user_model.User, opts base.MigrateOptions) error
}
// CreateMigrateTask creates a migrate task
-func CreateMigrateTask(doer, u *user_model.User, opts base.MigrateOptions) (*models.Task, error) {
+func CreateMigrateTask(doer, u *user_model.User, opts base.MigrateOptions) (*admin_model.Task, error) {
// encrypt credentials for persistence
var err error
opts.CloneAddrEncrypted, err = secret.EncryptSecret(setting.SecretKey, opts.CloneAddr)
@@ -93,7 +93,7 @@ func CreateMigrateTask(doer, u *user_model.User, opts base.MigrateOptions) (*mod
return nil, err
}
- task := &models.Task{
+ task := &admin_model.Task{
DoerID: doer.ID,
OwnerID: u.ID,
Type: structs.TaskTypeMigrateRepo,
@@ -101,11 +101,11 @@ func CreateMigrateTask(doer, u *user_model.User, opts base.MigrateOptions) (*mod
PayloadContent: string(bs),
}
- if err := models.CreateTask(task); err != nil {
+ if err := admin_model.CreateTask(task); err != nil {
return nil, err
}
- repo, err := repo_module.CreateRepository(doer, u, models.CreateRepoOptions{
+ repo, err := repo_module.CreateRepository(doer, u, repo_module.CreateRepoOptions{
Name: opts.RepoName,
Description: opts.Description,
OriginalURL: opts.OriginalURL,
diff --git a/services/user/user.go b/services/user/user.go
index 1edd9294cb..dab9ac61a4 100644
--- a/services/user/user.go
+++ b/services/user/user.go
@@ -13,12 +13,12 @@ import (
"time"
"code.gitea.io/gitea/models"
- admin_model "code.gitea.io/gitea/models/admin"
asymkey_model "code.gitea.io/gitea/models/asymkey"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/organization"
packages_model "code.gitea.io/gitea/models/packages"
repo_model "code.gitea.io/gitea/models/repo"
+ system_model "code.gitea.io/gitea/models/system"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/avatar"
"code.gitea.io/gitea/modules/eventsource"
@@ -186,7 +186,7 @@ func DeleteUser(ctx context.Context, u *user_model.User, purge bool) error {
path := user_model.UserPath(u.Name)
if err := util.RemoveAll(path); err != nil {
err = fmt.Errorf("Failed to RemoveAll %s: %v", path, err)
- _ = admin_model.CreateNotice(ctx, admin_model.NoticeTask, fmt.Sprintf("delete user '%s': %v", u.Name, err))
+ _ = system_model.CreateNotice(ctx, system_model.NoticeTask, fmt.Sprintf("delete user '%s': %v", u.Name, err))
return err
}
@@ -194,7 +194,7 @@ func DeleteUser(ctx context.Context, u *user_model.User, purge bool) error {
avatarPath := u.CustomAvatarRelativePath()
if err := storage.Avatars.Delete(avatarPath); err != nil {
err = fmt.Errorf("Failed to remove %s: %v", avatarPath, err)
- _ = admin_model.CreateNotice(ctx, admin_model.NoticeTask, fmt.Sprintf("delete user '%s': %v", u.Name, err))
+ _ = system_model.CreateNotice(ctx, system_model.NoticeTask, fmt.Sprintf("delete user '%s': %v", u.Name, err))
return err
}
}
diff --git a/services/user/user_test.go b/services/user/user_test.go
index d8673593df..c07244e7e1 100644
--- a/services/user/user_test.go
+++ b/services/user/user_test.go
@@ -28,7 +28,7 @@ func TestMain(m *testing.M) {
func TestDeleteUser(t *testing.T) {
test := func(userID int64) {
assert.NoError(t, unittest.PrepareTestDatabase())
- user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: userID}).(*user_model.User)
+ user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: userID})
ownedRepos := make([]*repo_model.Repository, 0, 10)
assert.NoError(t, db.GetEngine(db.DefaultContext).Find(&ownedRepos, &repo_model.Repository{OwnerID: userID}))
@@ -56,14 +56,14 @@ func TestDeleteUser(t *testing.T) {
test(8)
test(11)
- org := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3}).(*user_model.User)
+ org := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3})
assert.Error(t, DeleteUser(db.DefaultContext, org, false))
}
func TestPurgeUser(t *testing.T) {
test := func(userID int64) {
assert.NoError(t, unittest.PrepareTestDatabase())
- user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: userID}).(*user_model.User)
+ user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: userID})
err := DeleteUser(db.DefaultContext, user, true)
assert.NoError(t, err)
@@ -76,7 +76,7 @@ func TestPurgeUser(t *testing.T) {
test(8)
test(11)
- org := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3}).(*user_model.User)
+ org := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3})
assert.Error(t, DeleteUser(db.DefaultContext, org, false))
}
diff --git a/services/webhook/dingtalk.go b/services/webhook/dingtalk.go
index 642cf6f2fd..e047e994c2 100644
--- a/services/webhook/dingtalk.go
+++ b/services/webhook/dingtalk.go
@@ -15,7 +15,7 @@ import (
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
- dingtalk "github.com/lunny/dingtalk_webhook"
+ dingtalk "gitea.com/lunny/dingtalk_webhook"
)
type (
@@ -67,14 +67,14 @@ func (d *DingtalkPayload) Push(p *api.PushPayload) (api.Payloader, error) {
)
var titleLink, linkText string
- if len(p.Commits) == 1 {
+ if p.TotalCommits == 1 {
commitDesc = "1 new commit"
titleLink = p.Commits[0].URL
- linkText = fmt.Sprintf("view commit %s", p.Commits[0].ID[:7])
+ linkText = "view commit"
} else {
- commitDesc = fmt.Sprintf("%d new commits", len(p.Commits))
+ commitDesc = fmt.Sprintf("%d new commits", p.TotalCommits)
titleLink = p.CompareURL
- linkText = fmt.Sprintf("view commit %s...%s", p.Commits[0].ID[:7], p.Commits[len(p.Commits)-1].ID[:7])
+ linkText = "view commits"
}
if titleLink == "" {
titleLink = p.Repo.HTMLURL + "/src/" + util.PathEscapeSegments(branchName)
@@ -107,6 +107,14 @@ func (d *DingtalkPayload) Issue(p *api.IssuePayload) (api.Payloader, error) {
return createDingtalkPayload(issueTitle, text+"\r\n\r\n"+attachmentText, "view issue", p.Issue.HTMLURL), nil
}
+// Wiki implements PayloadConvertor Wiki method
+func (d *DingtalkPayload) Wiki(p *api.WikiPayload) (api.Payloader, error) {
+ text, _, _ := getWikiPayloadInfo(p, noneLinkFormatter, true)
+ url := p.Repository.HTMLURL + "/wiki/" + url.PathEscape(p.Page)
+
+ return createDingtalkPayload(text, text, "view wiki", url), nil
+}
+
// IssueComment implements PayloadConvertor IssueComment method
func (d *DingtalkPayload) IssueComment(p *api.IssueCommentPayload) (api.Payloader, error) {
text, issueTitle, _ := getIssueCommentPayloadInfo(p, noneLinkFormatter, true)
diff --git a/services/webhook/dingtalk_test.go b/services/webhook/dingtalk_test.go
index b66b5e43a8..fc15380f4d 100644
--- a/services/webhook/dingtalk_test.go
+++ b/services/webhook/dingtalk_test.go
@@ -82,7 +82,7 @@ func TestDingTalkPayload(t *testing.T) {
assert.Equal(t, "[2020558](http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778) commit message - user1\r\n[2020558](http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778) commit message - user1", pl.(*DingtalkPayload).ActionCard.Text)
assert.Equal(t, "[test/repo:test] 2 new commits", pl.(*DingtalkPayload).ActionCard.Title)
- assert.Equal(t, "view commit 2020558...2020558", pl.(*DingtalkPayload).ActionCard.SingleTitle)
+ assert.Equal(t, "view commits", pl.(*DingtalkPayload).ActionCard.SingleTitle)
assert.Equal(t, "http://localhost:3000/test/repo/src/test", parseRealSingleURL(pl.(*DingtalkPayload).ActionCard.SingleURL))
})
@@ -189,6 +189,44 @@ func TestDingTalkPayload(t *testing.T) {
assert.Equal(t, "http://localhost:3000/test/repo", parseRealSingleURL(pl.(*DingtalkPayload).ActionCard.SingleURL))
})
+ t.Run("Wiki", func(t *testing.T) {
+ p := wikiTestPayload()
+
+ d := new(DingtalkPayload)
+ p.Action = api.HookWikiCreated
+ pl, err := d.Wiki(p)
+ require.NoError(t, err)
+ require.NotNil(t, pl)
+ require.IsType(t, &DingtalkPayload{}, pl)
+
+ assert.Equal(t, "[test/repo] New wiki page 'index' (Wiki change comment) by user1", pl.(*DingtalkPayload).ActionCard.Text)
+ assert.Equal(t, "[test/repo] New wiki page 'index' (Wiki change comment) by user1", pl.(*DingtalkPayload).ActionCard.Title)
+ assert.Equal(t, "view wiki", pl.(*DingtalkPayload).ActionCard.SingleTitle)
+ assert.Equal(t, "http://localhost:3000/test/repo/wiki/index", parseRealSingleURL(pl.(*DingtalkPayload).ActionCard.SingleURL))
+
+ p.Action = api.HookWikiEdited
+ pl, err = d.Wiki(p)
+ require.NoError(t, err)
+ require.NotNil(t, pl)
+ require.IsType(t, &DingtalkPayload{}, pl)
+
+ assert.Equal(t, "[test/repo] Wiki page 'index' edited (Wiki change comment) by user1", pl.(*DingtalkPayload).ActionCard.Text)
+ assert.Equal(t, "[test/repo] Wiki page 'index' edited (Wiki change comment) by user1", pl.(*DingtalkPayload).ActionCard.Title)
+ assert.Equal(t, "view wiki", pl.(*DingtalkPayload).ActionCard.SingleTitle)
+ assert.Equal(t, "http://localhost:3000/test/repo/wiki/index", parseRealSingleURL(pl.(*DingtalkPayload).ActionCard.SingleURL))
+
+ p.Action = api.HookWikiDeleted
+ pl, err = d.Wiki(p)
+ require.NoError(t, err)
+ require.NotNil(t, pl)
+ require.IsType(t, &DingtalkPayload{}, pl)
+
+ assert.Equal(t, "[test/repo] Wiki page 'index' deleted by user1", pl.(*DingtalkPayload).ActionCard.Text)
+ assert.Equal(t, "[test/repo] Wiki page 'index' deleted by user1", pl.(*DingtalkPayload).ActionCard.Title)
+ assert.Equal(t, "view wiki", pl.(*DingtalkPayload).ActionCard.SingleTitle)
+ assert.Equal(t, "http://localhost:3000/test/repo/wiki/index", parseRealSingleURL(pl.(*DingtalkPayload).ActionCard.SingleURL))
+ })
+
t.Run("Release", func(t *testing.T) {
p := pullReleaseTestPayload()
diff --git a/services/webhook/discord.go b/services/webhook/discord.go
index ae5460b9a7..22d75db893 100644
--- a/services/webhook/discord.go
+++ b/services/webhook/discord.go
@@ -7,6 +7,7 @@ package webhook
import (
"errors"
"fmt"
+ "net/url"
"strconv"
"strings"
@@ -141,11 +142,11 @@ func (d *DiscordPayload) Push(p *api.PushPayload) (api.Payloader, error) {
)
var titleLink string
- if len(p.Commits) == 1 {
+ if p.TotalCommits == 1 {
commitDesc = "1 new commit"
titleLink = p.Commits[0].URL
} else {
- commitDesc = fmt.Sprintf("%d new commits", len(p.Commits))
+ commitDesc = fmt.Sprintf("%d new commits", p.TotalCommits)
titleLink = p.CompareURL
}
if titleLink == "" {
@@ -235,6 +236,19 @@ func (d *DiscordPayload) Repository(p *api.RepositoryPayload) (api.Payloader, er
return d.createPayload(p.Sender, title, "", url, color), nil
}
+// Wiki implements PayloadConvertor Wiki method
+func (d *DiscordPayload) Wiki(p *api.WikiPayload) (api.Payloader, error) {
+ text, color, _ := getWikiPayloadInfo(p, noneLinkFormatter, false)
+ htmlLink := p.Repository.HTMLURL + "/wiki/" + url.PathEscape(p.Page)
+
+ var description string
+ if p.Action != api.HookWikiDeleted {
+ description = p.Comment
+ }
+
+ return d.createPayload(p.Sender, text, description, htmlLink, color), nil
+}
+
// Release implements PayloadConvertor Release method
func (d *DiscordPayload) Release(p *api.ReleasePayload) (api.Payloader, error) {
text, color := getReleasePayloadInfo(p, noneLinkFormatter, false)
diff --git a/services/webhook/discord_test.go b/services/webhook/discord_test.go
index 8fe20c0102..8e4e60a7ff 100644
--- a/services/webhook/discord_test.go
+++ b/services/webhook/discord_test.go
@@ -212,6 +212,53 @@ func TestDiscordPayload(t *testing.T) {
assert.Equal(t, p.Sender.AvatarURL, pl.(*DiscordPayload).Embeds[0].Author.IconURL)
})
+ t.Run("Wiki", func(t *testing.T) {
+ p := wikiTestPayload()
+
+ d := new(DiscordPayload)
+ p.Action = api.HookWikiCreated
+ pl, err := d.Wiki(p)
+ require.NoError(t, err)
+ require.NotNil(t, pl)
+ require.IsType(t, &DiscordPayload{}, pl)
+
+ assert.Len(t, pl.(*DiscordPayload).Embeds, 1)
+ assert.Equal(t, "[test/repo] New wiki page 'index' (Wiki change comment)", pl.(*DiscordPayload).Embeds[0].Title)
+ assert.Equal(t, "Wiki change comment", pl.(*DiscordPayload).Embeds[0].Description)
+ assert.Equal(t, "http://localhost:3000/test/repo/wiki/index", pl.(*DiscordPayload).Embeds[0].URL)
+ assert.Equal(t, p.Sender.UserName, pl.(*DiscordPayload).Embeds[0].Author.Name)
+ assert.Equal(t, setting.AppURL+p.Sender.UserName, pl.(*DiscordPayload).Embeds[0].Author.URL)
+ assert.Equal(t, p.Sender.AvatarURL, pl.(*DiscordPayload).Embeds[0].Author.IconURL)
+
+ p.Action = api.HookWikiEdited
+ pl, err = d.Wiki(p)
+ require.NoError(t, err)
+ require.NotNil(t, pl)
+ require.IsType(t, &DiscordPayload{}, pl)
+
+ assert.Len(t, pl.(*DiscordPayload).Embeds, 1)
+ assert.Equal(t, "[test/repo] Wiki page 'index' edited (Wiki change comment)", pl.(*DiscordPayload).Embeds[0].Title)
+ assert.Equal(t, "Wiki change comment", pl.(*DiscordPayload).Embeds[0].Description)
+ assert.Equal(t, "http://localhost:3000/test/repo/wiki/index", pl.(*DiscordPayload).Embeds[0].URL)
+ assert.Equal(t, p.Sender.UserName, pl.(*DiscordPayload).Embeds[0].Author.Name)
+ assert.Equal(t, setting.AppURL+p.Sender.UserName, pl.(*DiscordPayload).Embeds[0].Author.URL)
+ assert.Equal(t, p.Sender.AvatarURL, pl.(*DiscordPayload).Embeds[0].Author.IconURL)
+
+ p.Action = api.HookWikiDeleted
+ pl, err = d.Wiki(p)
+ require.NoError(t, err)
+ require.NotNil(t, pl)
+ require.IsType(t, &DiscordPayload{}, pl)
+
+ assert.Len(t, pl.(*DiscordPayload).Embeds, 1)
+ assert.Equal(t, "[test/repo] Wiki page 'index' deleted", pl.(*DiscordPayload).Embeds[0].Title)
+ assert.Empty(t, pl.(*DiscordPayload).Embeds[0].Description)
+ assert.Equal(t, "http://localhost:3000/test/repo/wiki/index", pl.(*DiscordPayload).Embeds[0].URL)
+ assert.Equal(t, p.Sender.UserName, pl.(*DiscordPayload).Embeds[0].Author.Name)
+ assert.Equal(t, setting.AppURL+p.Sender.UserName, pl.(*DiscordPayload).Embeds[0].Author.URL)
+ assert.Equal(t, p.Sender.AvatarURL, pl.(*DiscordPayload).Embeds[0].Author.IconURL)
+ })
+
t.Run("Release", func(t *testing.T) {
p := pullReleaseTestPayload()
diff --git a/services/webhook/feishu.go b/services/webhook/feishu.go
index 5b20c7dda7..782684d0ff 100644
--- a/services/webhook/feishu.go
+++ b/services/webhook/feishu.go
@@ -145,6 +145,13 @@ func (f *FeishuPayload) Repository(p *api.RepositoryPayload) (api.Payloader, err
return nil, nil
}
+// Wiki implements PayloadConvertor Wiki method
+func (f *FeishuPayload) Wiki(p *api.WikiPayload) (api.Payloader, error) {
+ text, _, _ := getWikiPayloadInfo(p, noneLinkFormatter, true)
+
+ return newFeishuTextPayload(text), nil
+}
+
// Release implements PayloadConvertor Release method
func (f *FeishuPayload) Release(p *api.ReleasePayload) (api.Payloader, error) {
text, _ := getReleasePayloadInfo(p, noneLinkFormatter, true)
diff --git a/services/webhook/feishu_test.go b/services/webhook/feishu_test.go
index d862b015e7..85cfb759fe 100644
--- a/services/webhook/feishu_test.go
+++ b/services/webhook/feishu_test.go
@@ -145,6 +145,35 @@ func TestFeishuPayload(t *testing.T) {
assert.Equal(t, "[test/repo] Repository created", pl.(*FeishuPayload).Content.Text)
})
+ t.Run("Wiki", func(t *testing.T) {
+ p := wikiTestPayload()
+
+ d := new(FeishuPayload)
+ p.Action = api.HookWikiCreated
+ pl, err := d.Wiki(p)
+ require.NoError(t, err)
+ require.NotNil(t, pl)
+ require.IsType(t, &FeishuPayload{}, pl)
+
+ assert.Equal(t, "[test/repo] New wiki page 'index' (Wiki change comment) by user1", pl.(*FeishuPayload).Content.Text)
+
+ p.Action = api.HookWikiEdited
+ pl, err = d.Wiki(p)
+ require.NoError(t, err)
+ require.NotNil(t, pl)
+ require.IsType(t, &FeishuPayload{}, pl)
+
+ assert.Equal(t, "[test/repo] Wiki page 'index' edited (Wiki change comment) by user1", pl.(*FeishuPayload).Content.Text)
+
+ p.Action = api.HookWikiDeleted
+ pl, err = d.Wiki(p)
+ require.NoError(t, err)
+ require.NotNil(t, pl)
+ require.IsType(t, &FeishuPayload{}, pl)
+
+ assert.Equal(t, "[test/repo] Wiki page 'index' deleted by user1", pl.(*FeishuPayload).Content.Text)
+ })
+
t.Run("Release", func(t *testing.T) {
p := pullReleaseTestPayload()
diff --git a/services/webhook/general.go b/services/webhook/general.go
index e8006fabae..5be177d339 100644
--- a/services/webhook/general.go
+++ b/services/webhook/general.go
@@ -161,6 +161,35 @@ func getReleasePayloadInfo(p *api.ReleasePayload, linkFormatter linkFormatter, w
return text, color
}
+func getWikiPayloadInfo(p *api.WikiPayload, linkFormatter linkFormatter, withSender bool) (string, int, string) {
+ repoLink := linkFormatter(p.Repository.HTMLURL, p.Repository.FullName)
+ pageLink := linkFormatter(p.Repository.HTMLURL+"/wiki/"+url.PathEscape(p.Page), p.Page)
+
+ var text string
+ color := greenColor
+
+ switch p.Action {
+ case api.HookWikiCreated:
+ text = fmt.Sprintf("[%s] New wiki page '%s'", repoLink, pageLink)
+ case api.HookWikiEdited:
+ text = fmt.Sprintf("[%s] Wiki page '%s' edited", repoLink, pageLink)
+ color = yellowColor
+ case api.HookWikiDeleted:
+ text = fmt.Sprintf("[%s] Wiki page '%s' deleted", repoLink, pageLink)
+ color = redColor
+ }
+
+ if p.Action != api.HookWikiDeleted && p.Comment != "" {
+ text += fmt.Sprintf(" (%s)", p.Comment)
+ }
+
+ if withSender {
+ text += fmt.Sprintf(" by %s", linkFormatter(setting.AppURL+url.PathEscape(p.Sender.UserName), p.Sender.UserName))
+ }
+
+ return text, color, pageLink
+}
+
func getIssueCommentPayloadInfo(p *api.IssueCommentPayload, linkFormatter linkFormatter, withSender bool) (string, string, int) {
repoLink := linkFormatter(p.Repository.HTMLURL, p.Repository.FullName)
issueTitle := fmt.Sprintf("#%d %s", p.Issue.Index, p.Issue.Title)
diff --git a/services/webhook/general_test.go b/services/webhook/general_test.go
index 4d73afe060..da3123d3ab 100644
--- a/services/webhook/general_test.go
+++ b/services/webhook/general_test.go
@@ -82,12 +82,13 @@ func pushTestPayload() *api.PushPayload {
}
return &api.PushPayload{
- Ref: "refs/heads/test",
- Before: "2020558fe2e34debb818a514715839cabd25e777",
- After: "2020558fe2e34debb818a514715839cabd25e778",
- CompareURL: "",
- HeadCommit: commit,
- Commits: []*api.PayloadCommit{commit, commit},
+ Ref: "refs/heads/test",
+ Before: "2020558fe2e34debb818a514715839cabd25e777",
+ After: "2020558fe2e34debb818a514715839cabd25e778",
+ CompareURL: "",
+ HeadCommit: commit,
+ Commits: []*api.PayloadCommit{commit, commit},
+ TotalCommits: 2,
Repo: &api.Repository{
HTMLURL: "http://localhost:3000/test/repo",
Name: "repo",
@@ -195,6 +196,22 @@ func pullRequestCommentTestPayload() *api.IssueCommentPayload {
}
}
+func wikiTestPayload() *api.WikiPayload {
+ return &api.WikiPayload{
+ Repository: &api.Repository{
+ HTMLURL: "http://localhost:3000/test/repo",
+ Name: "repo",
+ FullName: "test/repo",
+ },
+ Sender: &api.User{
+ UserName: "user1",
+ AvatarURL: "http://localhost:3000/user1/avatar",
+ },
+ Page: "index",
+ Comment: "Wiki change comment",
+ }
+}
+
func pullReleaseTestPayload() *api.ReleasePayload {
return &api.ReleasePayload{
Action: api.HookReleasePublished,
@@ -469,6 +486,44 @@ func TestGetPullRequestPayloadInfo(t *testing.T) {
}
}
+func TestGetWikiPayloadInfo(t *testing.T) {
+ p := wikiTestPayload()
+
+ cases := []struct {
+ action api.HookWikiAction
+ text string
+ color int
+ link string
+ }{
+ {
+ api.HookWikiCreated,
+ "[test/repo] New wiki page 'index' (Wiki change comment) by user1",
+ greenColor,
+ "index",
+ },
+ {
+ api.HookWikiEdited,
+ "[test/repo] Wiki page 'index' edited (Wiki change comment) by user1",
+ yellowColor,
+ "index",
+ },
+ {
+ api.HookWikiDeleted,
+ "[test/repo] Wiki page 'index' deleted by user1",
+ redColor,
+ "index",
+ },
+ }
+
+ for i, c := range cases {
+ p.Action = c.action
+ text, color, link := getWikiPayloadInfo(p, noneLinkFormatter, true)
+ assert.Equal(t, c.text, text, "case %d", i)
+ assert.Equal(t, c.color, color, "case %d", i)
+ assert.Equal(t, c.link, link, "case %d", i)
+ }
+}
+
func TestGetReleasePayloadInfo(t *testing.T) {
p := pullReleaseTestPayload()
diff --git a/services/webhook/matrix.go b/services/webhook/matrix.go
index a42ab2a93e..6bbae70422 100644
--- a/services/webhook/matrix.go
+++ b/services/webhook/matrix.go
@@ -143,6 +143,13 @@ func (m *MatrixPayloadUnsafe) IssueComment(p *api.IssueCommentPayload) (api.Payl
return getMatrixPayloadUnsafe(text, nil, m.AccessToken, m.MsgType), nil
}
+// Wiki implements PayloadConvertor Wiki method
+func (m *MatrixPayloadUnsafe) Wiki(p *api.WikiPayload) (api.Payloader, error) {
+ text, _, _ := getWikiPayloadInfo(p, MatrixLinkFormatter, true)
+
+ return getMatrixPayloadUnsafe(text, nil, m.AccessToken, m.MsgType), nil
+}
+
// Release implements PayloadConvertor Release method
func (m *MatrixPayloadUnsafe) Release(p *api.ReleasePayload) (api.Payloader, error) {
text, _ := getReleasePayloadInfo(p, MatrixLinkFormatter, true)
@@ -154,10 +161,10 @@ func (m *MatrixPayloadUnsafe) Release(p *api.ReleasePayload) (api.Payloader, err
func (m *MatrixPayloadUnsafe) Push(p *api.PushPayload) (api.Payloader, error) {
var commitDesc string
- if len(p.Commits) == 1 {
+ if p.TotalCommits == 1 {
commitDesc = "1 commit"
} else {
- commitDesc = fmt.Sprintf("%d commits", len(p.Commits))
+ commitDesc = fmt.Sprintf("%d commits", p.TotalCommits)
}
repoLink := MatrixLinkFormatter(p.Repo.HTMLURL, p.Repo.FullName)
@@ -188,7 +195,7 @@ func (m *MatrixPayloadUnsafe) PullRequest(p *api.PullRequestPayload) (api.Payloa
func (m *MatrixPayloadUnsafe) Review(p *api.PullRequestPayload, event webhook_model.HookEventType) (api.Payloader, error) {
senderLink := MatrixLinkFormatter(setting.AppURL+url.PathEscape(p.Sender.UserName), p.Sender.UserName)
title := fmt.Sprintf("#%d %s", p.Index, p.PullRequest.Title)
- titleLink := fmt.Sprintf("%s/pulls/%d", p.Repository.HTMLURL, p.Index)
+ titleLink := MatrixLinkFormatter(p.PullRequest.URL, title)
repoLink := MatrixLinkFormatter(p.Repository.HTMLURL, p.Repository.FullName)
var text string
@@ -199,7 +206,7 @@ func (m *MatrixPayloadUnsafe) Review(p *api.PullRequestPayload, event webhook_mo
return nil, err
}
- text = fmt.Sprintf("[%s] Pull request review %s: [%s](%s) by %s", repoLink, action, title, titleLink, senderLink)
+ text = fmt.Sprintf("[%s] Pull request review %s: %s by %s", repoLink, action, titleLink, senderLink)
}
return getMatrixPayloadUnsafe(text, nil, m.AccessToken, m.MsgType), nil
diff --git a/services/webhook/matrix_test.go b/services/webhook/matrix_test.go
index 34196aedf0..624986ee9b 100644
--- a/services/webhook/matrix_test.go
+++ b/services/webhook/matrix_test.go
@@ -140,7 +140,7 @@ func TestMatrixPayload(t *testing.T) {
require.IsType(t, &MatrixPayloadUnsafe{}, pl)
assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo)] Pull request review approved: [#12 Fix bug](http://localhost:3000/test/repo/pulls/12) by [user1](https://try.gitea.io/user1)", pl.(*MatrixPayloadUnsafe).Body)
- assert.Equal(t, `[test/repo] Pull request review approved: [#12 Fix bug](http://localhost:3000/test/repo/pulls/12) by user1`, pl.(*MatrixPayloadUnsafe).FormattedBody)
+ assert.Equal(t, `[test/repo] Pull request review approved: #12 Fix bug by user1`, pl.(*MatrixPayloadUnsafe).FormattedBody)
})
t.Run("Repository", func(t *testing.T) {
@@ -156,6 +156,38 @@ func TestMatrixPayload(t *testing.T) {
assert.Equal(t, `[test/repo] Repository created by user1`, pl.(*MatrixPayloadUnsafe).FormattedBody)
})
+ t.Run("Wiki", func(t *testing.T) {
+ p := wikiTestPayload()
+
+ d := new(MatrixPayloadUnsafe)
+ p.Action = api.HookWikiCreated
+ pl, err := d.Wiki(p)
+ require.NoError(t, err)
+ require.NotNil(t, pl)
+ require.IsType(t, &MatrixPayloadUnsafe{}, pl)
+
+ assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo)] New wiki page '[index](http://localhost:3000/test/repo/wiki/index)' (Wiki change comment) by [user1](https://try.gitea.io/user1)", pl.(*MatrixPayloadUnsafe).Body)
+ assert.Equal(t, `[test/repo] New wiki page 'index' (Wiki change comment) by user1`, pl.(*MatrixPayloadUnsafe).FormattedBody)
+
+ p.Action = api.HookWikiEdited
+ pl, err = d.Wiki(p)
+ require.NoError(t, err)
+ require.NotNil(t, pl)
+ require.IsType(t, &MatrixPayloadUnsafe{}, pl)
+
+ assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo)] Wiki page '[index](http://localhost:3000/test/repo/wiki/index)' edited (Wiki change comment) by [user1](https://try.gitea.io/user1)", pl.(*MatrixPayloadUnsafe).Body)
+ assert.Equal(t, `[test/repo] Wiki page 'index' edited (Wiki change comment) by user1`, pl.(*MatrixPayloadUnsafe).FormattedBody)
+
+ p.Action = api.HookWikiDeleted
+ pl, err = d.Wiki(p)
+ require.NoError(t, err)
+ require.NotNil(t, pl)
+ require.IsType(t, &MatrixPayloadUnsafe{}, pl)
+
+ assert.Equal(t, "[[test/repo](http://localhost:3000/test/repo)] Wiki page '[index](http://localhost:3000/test/repo/wiki/index)' deleted by [user1](https://try.gitea.io/user1)", pl.(*MatrixPayloadUnsafe).Body)
+ assert.Equal(t, `[test/repo] Wiki page 'index' deleted by user1`, pl.(*MatrixPayloadUnsafe).FormattedBody)
+ })
+
t.Run("Release", func(t *testing.T) {
p := pullReleaseTestPayload()
diff --git a/services/webhook/msteams.go b/services/webhook/msteams.go
index 59e2e93493..bf9e95edc5 100644
--- a/services/webhook/msteams.go
+++ b/services/webhook/msteams.go
@@ -6,6 +6,7 @@ package webhook
import (
"fmt"
+ "net/url"
"strings"
webhook_model "code.gitea.io/gitea/models/webhook"
@@ -124,11 +125,11 @@ func (m *MSTeamsPayload) Push(p *api.PushPayload) (api.Payloader, error) {
)
var titleLink string
- if len(p.Commits) == 1 {
+ if p.TotalCommits == 1 {
commitDesc = "1 new commit"
titleLink = p.Commits[0].URL
} else {
- commitDesc = fmt.Sprintf("%d new commits", len(p.Commits))
+ commitDesc = fmt.Sprintf("%d new commits", p.TotalCommits)
titleLink = p.CompareURL
}
if titleLink == "" {
@@ -155,7 +156,7 @@ func (m *MSTeamsPayload) Push(p *api.PushPayload) (api.Payloader, error) {
text,
titleLink,
greenColor,
- &MSTeamsFact{"Commit count:", fmt.Sprintf("%d", len(p.Commits))},
+ &MSTeamsFact{"Commit count:", fmt.Sprintf("%d", p.TotalCommits)},
), nil
}
@@ -266,6 +267,21 @@ func (m *MSTeamsPayload) Repository(p *api.RepositoryPayload) (api.Payloader, er
), nil
}
+// Wiki implements PayloadConvertor Wiki method
+func (m *MSTeamsPayload) Wiki(p *api.WikiPayload) (api.Payloader, error) {
+ title, color, _ := getWikiPayloadInfo(p, noneLinkFormatter, false)
+
+ return createMSTeamsPayload(
+ p.Repository,
+ p.Sender,
+ title,
+ "",
+ p.Repository.HTMLURL+"/wiki/"+url.PathEscape(p.Page),
+ color,
+ &MSTeamsFact{"Repository:", p.Repository.FullName},
+ ), nil
+}
+
// Release implements PayloadConvertor Release method
func (m *MSTeamsPayload) Release(p *api.ReleasePayload) (api.Payloader, error) {
title, color := getReleasePayloadInfo(p, noneLinkFormatter, false)
diff --git a/services/webhook/msteams_test.go b/services/webhook/msteams_test.go
index 3fdf47c1ae..8292beed7f 100644
--- a/services/webhook/msteams_test.go
+++ b/services/webhook/msteams_test.go
@@ -330,6 +330,80 @@ func TestMSTeamsPayload(t *testing.T) {
assert.Equal(t, "http://localhost:3000/test/repo", pl.(*MSTeamsPayload).PotentialAction[0].Targets[0].URI)
})
+ t.Run("Wiki", func(t *testing.T) {
+ p := wikiTestPayload()
+
+ d := new(MSTeamsPayload)
+ p.Action = api.HookWikiCreated
+ pl, err := d.Wiki(p)
+ require.NoError(t, err)
+ require.NotNil(t, pl)
+ require.IsType(t, &MSTeamsPayload{}, pl)
+
+ assert.Equal(t, "[test/repo] New wiki page 'index' (Wiki change comment)", pl.(*MSTeamsPayload).Title)
+ assert.Equal(t, "[test/repo] New wiki page 'index' (Wiki change comment)", pl.(*MSTeamsPayload).Summary)
+ assert.Len(t, pl.(*MSTeamsPayload).Sections, 1)
+ assert.Equal(t, "user1", pl.(*MSTeamsPayload).Sections[0].ActivitySubtitle)
+ assert.Equal(t, "", pl.(*MSTeamsPayload).Sections[0].Text)
+ assert.Len(t, pl.(*MSTeamsPayload).Sections[0].Facts, 2)
+ for _, fact := range pl.(*MSTeamsPayload).Sections[0].Facts {
+ if fact.Name == "Repository:" {
+ assert.Equal(t, p.Repository.FullName, fact.Value)
+ } else {
+ t.Fail()
+ }
+ }
+ assert.Len(t, pl.(*MSTeamsPayload).PotentialAction, 1)
+ assert.Len(t, pl.(*MSTeamsPayload).PotentialAction[0].Targets, 1)
+ assert.Equal(t, "http://localhost:3000/test/repo/wiki/index", pl.(*MSTeamsPayload).PotentialAction[0].Targets[0].URI)
+
+ p.Action = api.HookWikiEdited
+ pl, err = d.Wiki(p)
+ require.NoError(t, err)
+ require.NotNil(t, pl)
+ require.IsType(t, &MSTeamsPayload{}, pl)
+
+ assert.Equal(t, "[test/repo] Wiki page 'index' edited (Wiki change comment)", pl.(*MSTeamsPayload).Title)
+ assert.Equal(t, "[test/repo] Wiki page 'index' edited (Wiki change comment)", pl.(*MSTeamsPayload).Summary)
+ assert.Len(t, pl.(*MSTeamsPayload).Sections, 1)
+ assert.Equal(t, "user1", pl.(*MSTeamsPayload).Sections[0].ActivitySubtitle)
+ assert.Equal(t, "", pl.(*MSTeamsPayload).Sections[0].Text)
+ assert.Len(t, pl.(*MSTeamsPayload).Sections[0].Facts, 2)
+ for _, fact := range pl.(*MSTeamsPayload).Sections[0].Facts {
+ if fact.Name == "Repository:" {
+ assert.Equal(t, p.Repository.FullName, fact.Value)
+ } else {
+ t.Fail()
+ }
+ }
+ assert.Len(t, pl.(*MSTeamsPayload).PotentialAction, 1)
+ assert.Len(t, pl.(*MSTeamsPayload).PotentialAction[0].Targets, 1)
+ assert.Equal(t, "http://localhost:3000/test/repo/wiki/index", pl.(*MSTeamsPayload).PotentialAction[0].Targets[0].URI)
+
+ p.Action = api.HookWikiDeleted
+ pl, err = d.Wiki(p)
+ require.NoError(t, err)
+ require.NotNil(t, pl)
+ require.IsType(t, &MSTeamsPayload{}, pl)
+
+ assert.Equal(t, "[test/repo] Wiki page 'index' deleted", pl.(*MSTeamsPayload).Title)
+ assert.Equal(t, "[test/repo] Wiki page 'index' deleted", pl.(*MSTeamsPayload).Summary)
+ assert.Len(t, pl.(*MSTeamsPayload).Sections, 1)
+ assert.Equal(t, "user1", pl.(*MSTeamsPayload).Sections[0].ActivitySubtitle)
+ assert.Empty(t, pl.(*MSTeamsPayload).Sections[0].Text)
+ assert.Len(t, pl.(*MSTeamsPayload).Sections[0].Facts, 2)
+ for _, fact := range pl.(*MSTeamsPayload).Sections[0].Facts {
+ if fact.Name == "Repository:" {
+ assert.Equal(t, p.Repository.FullName, fact.Value)
+ } else {
+ t.Fail()
+ }
+ }
+ assert.Len(t, pl.(*MSTeamsPayload).PotentialAction, 1)
+ assert.Len(t, pl.(*MSTeamsPayload).PotentialAction[0].Targets, 1)
+ assert.Equal(t, "http://localhost:3000/test/repo/wiki/index", pl.(*MSTeamsPayload).PotentialAction[0].Targets[0].URI)
+ })
+
t.Run("Release", func(t *testing.T) {
p := pullReleaseTestPayload()
diff --git a/services/webhook/packagist.go b/services/webhook/packagist.go
index ace93b13ff..5badc7462a 100644
--- a/services/webhook/packagist.go
+++ b/services/webhook/packagist.go
@@ -94,6 +94,11 @@ func (f *PackagistPayload) Repository(p *api.RepositoryPayload) (api.Payloader,
return nil, nil
}
+// Wiki implements PayloadConvertor Wiki method
+func (f *PackagistPayload) Wiki(p *api.WikiPayload) (api.Payloader, error) {
+ return nil, nil
+}
+
// Release implements PayloadConvertor Release method
func (f *PackagistPayload) Release(p *api.ReleasePayload) (api.Payloader, error) {
return nil, nil
diff --git a/services/webhook/packagist_test.go b/services/webhook/packagist_test.go
index 08912924d2..4a24b76523 100644
--- a/services/webhook/packagist_test.go
+++ b/services/webhook/packagist_test.go
@@ -116,6 +116,26 @@ func TestPackagistPayload(t *testing.T) {
require.Nil(t, pl)
})
+ t.Run("Wiki", func(t *testing.T) {
+ p := wikiTestPayload()
+
+ d := new(PackagistPayload)
+ p.Action = api.HookWikiCreated
+ pl, err := d.Wiki(p)
+ require.NoError(t, err)
+ require.Nil(t, pl)
+
+ p.Action = api.HookWikiEdited
+ pl, err = d.Wiki(p)
+ require.NoError(t, err)
+ require.Nil(t, pl)
+
+ p.Action = api.HookWikiDeleted
+ pl, err = d.Wiki(p)
+ require.NoError(t, err)
+ require.Nil(t, pl)
+ })
+
t.Run("Release", func(t *testing.T) {
p := pullReleaseTestPayload()
diff --git a/services/webhook/payloader.go b/services/webhook/payloader.go
index 0e09dd1b1e..a9d01c9891 100644
--- a/services/webhook/payloader.go
+++ b/services/webhook/payloader.go
@@ -22,6 +22,7 @@ type PayloadConvertor interface {
Review(*api.PullRequestPayload, webhook_model.HookEventType) (api.Payloader, error)
Repository(*api.RepositoryPayload) (api.Payloader, error)
Release(*api.ReleasePayload) (api.Payloader, error)
+ Wiki(*api.WikiPayload) (api.Payloader, error)
}
func convertPayloader(s PayloadConvertor, p api.Payloader, event webhook_model.HookEventType) (api.Payloader, error) {
@@ -51,6 +52,8 @@ func convertPayloader(s PayloadConvertor, p api.Payloader, event webhook_model.H
return s.Repository(p.(*api.RepositoryPayload))
case webhook_model.HookEventRelease:
return s.Release(p.(*api.ReleasePayload))
+ case webhook_model.HookEventWiki:
+ return s.Wiki(p.(*api.WikiPayload))
}
return s, nil
}
diff --git a/services/webhook/slack.go b/services/webhook/slack.go
index 11e1d3c081..f5c69d74b6 100644
--- a/services/webhook/slack.go
+++ b/services/webhook/slack.go
@@ -7,6 +7,7 @@ package webhook
import (
"errors"
"fmt"
+ "regexp"
"strings"
webhook_model "code.gitea.io/gitea/models/webhook"
@@ -156,6 +157,13 @@ func (s *SlackPayload) IssueComment(p *api.IssueCommentPayload) (api.Payloader,
}}), nil
}
+// Wiki implements PayloadConvertor Wiki method
+func (s *SlackPayload) Wiki(p *api.WikiPayload) (api.Payloader, error) {
+ text, _, _ := getWikiPayloadInfo(p, SlackLinkFormatter, true)
+
+ return s.createPayload(text, nil), nil
+}
+
// Release implements PayloadConvertor Release method
func (s *SlackPayload) Release(p *api.ReleasePayload) (api.Payloader, error) {
text, _ := getReleasePayloadInfo(p, SlackLinkFormatter, true)
@@ -171,10 +179,10 @@ func (s *SlackPayload) Push(p *api.PushPayload) (api.Payloader, error) {
commitString string
)
- if len(p.Commits) == 1 {
+ if p.TotalCommits == 1 {
commitDesc = "1 new commit"
} else {
- commitDesc = fmt.Sprintf("%d new commits", len(p.Commits))
+ commitDesc = fmt.Sprintf("%d new commits", p.TotalCommits)
}
if len(p.CompareURL) > 0 {
commitString = SlackLinkFormatter(p.CompareURL, commitDesc)
@@ -286,3 +294,13 @@ func GetSlackPayload(p api.Payloader, event webhook_model.HookEventType, meta st
return convertPayloader(s, p, event)
}
+
+var slackChannel = regexp.MustCompile(`^#?[a-z0-9_-]{1,80}$`)
+
+// IsValidSlackChannel validates a channel name conforms to what slack expects:
+// https://api.slack.com/methods/conversations.rename#naming
+// Conversation names can only contain lowercase letters, numbers, hyphens, and underscores, and must be 80 characters or less.
+// Gitea accepts if it starts with a #.
+func IsValidSlackChannel(name string) bool {
+ return slackChannel.MatchString(name)
+}
diff --git a/services/webhook/slack_test.go b/services/webhook/slack_test.go
index 8278afb69a..3af38b5158 100644
--- a/services/webhook/slack_test.go
+++ b/services/webhook/slack_test.go
@@ -145,6 +145,35 @@ func TestSlackPayload(t *testing.T) {
assert.Equal(t, "[] Repository created by ", pl.(*SlackPayload).Text)
})
+ t.Run("Wiki", func(t *testing.T) {
+ p := wikiTestPayload()
+
+ d := new(SlackPayload)
+ p.Action = api.HookWikiCreated
+ pl, err := d.Wiki(p)
+ require.NoError(t, err)
+ require.NotNil(t, pl)
+ require.IsType(t, &SlackPayload{}, pl)
+
+ assert.Equal(t, "[] New wiki page '' (Wiki change comment) by ", pl.(*SlackPayload).Text)
+
+ p.Action = api.HookWikiEdited
+ pl, err = d.Wiki(p)
+ require.NoError(t, err)
+ require.NotNil(t, pl)
+ require.IsType(t, &SlackPayload{}, pl)
+
+ assert.Equal(t, "[] Wiki page '' edited (Wiki change comment) by ", pl.(*SlackPayload).Text)
+
+ p.Action = api.HookWikiDeleted
+ pl, err = d.Wiki(p)
+ require.NoError(t, err)
+ require.NotNil(t, pl)
+ require.IsType(t, &SlackPayload{}, pl)
+
+ assert.Equal(t, "[] Wiki page '' deleted by ", pl.(*SlackPayload).Text)
+ })
+
t.Run("Release", func(t *testing.T) {
p := pullReleaseTestPayload()
@@ -170,3 +199,22 @@ func TestSlackJSONPayload(t *testing.T) {
require.NoError(t, err)
assert.NotEmpty(t, json)
}
+
+func TestIsValidSlackChannel(t *testing.T) {
+ tt := []struct {
+ channelName string
+ expected bool
+ }{
+ {"gitea", true},
+ {"#gitea", true},
+ {" ", false},
+ {"#", false},
+ {" #", false},
+ {"gitea ", false},
+ {" gitea", false},
+ }
+
+ for _, v := range tt {
+ assert.Equal(t, v.expected, IsValidSlackChannel(v.channelName))
+ }
+}
diff --git a/services/webhook/telegram.go b/services/webhook/telegram.go
index 64211493ec..7ca5f61062 100644
--- a/services/webhook/telegram.go
+++ b/services/webhook/telegram.go
@@ -89,11 +89,11 @@ func (t *TelegramPayload) Push(p *api.PushPayload) (api.Payloader, error) {
)
var titleLink string
- if len(p.Commits) == 1 {
+ if p.TotalCommits == 1 {
commitDesc = "1 new commit"
titleLink = p.Commits[0].URL
} else {
- commitDesc = fmt.Sprintf("%d new commits", len(p.Commits))
+ commitDesc = fmt.Sprintf("%d new commits", p.TotalCommits)
titleLink = p.CompareURL
}
if titleLink == "" {
@@ -171,6 +171,13 @@ func (t *TelegramPayload) Repository(p *api.RepositoryPayload) (api.Payloader, e
return nil, nil
}
+// Wiki implements PayloadConvertor Wiki method
+func (t *TelegramPayload) Wiki(p *api.WikiPayload) (api.Payloader, error) {
+ text, _, _ := getWikiPayloadInfo(p, htmlLinkFormatter, true)
+
+ return createTelegramPayload(text), nil
+}
+
// Release implements PayloadConvertor Release method
func (t *TelegramPayload) Release(p *api.ReleasePayload) (api.Payloader, error) {
text, _ := getReleasePayloadInfo(p, htmlLinkFormatter, true)
diff --git a/services/webhook/telegram_test.go b/services/webhook/telegram_test.go
index 2f83090166..5ca78d0508 100644
--- a/services/webhook/telegram_test.go
+++ b/services/webhook/telegram_test.go
@@ -145,6 +145,35 @@ func TestTelegramPayload(t *testing.T) {
assert.Equal(t, `[test/repo] Repository created`, pl.(*TelegramPayload).Message)
})
+ t.Run("Wiki", func(t *testing.T) {
+ p := wikiTestPayload()
+
+ d := new(TelegramPayload)
+ p.Action = api.HookWikiCreated
+ pl, err := d.Wiki(p)
+ require.NoError(t, err)
+ require.NotNil(t, pl)
+ require.IsType(t, &TelegramPayload{}, pl)
+
+ assert.Equal(t, `[test/repo] New wiki page 'index' (Wiki change comment) by user1`, pl.(*TelegramPayload).Message)
+
+ p.Action = api.HookWikiEdited
+ pl, err = d.Wiki(p)
+ require.NoError(t, err)
+ require.NotNil(t, pl)
+ require.IsType(t, &TelegramPayload{}, pl)
+
+ assert.Equal(t, `[test/repo] Wiki page 'index' edited (Wiki change comment) by user1`, pl.(*TelegramPayload).Message)
+
+ p.Action = api.HookWikiDeleted
+ pl, err = d.Wiki(p)
+ require.NoError(t, err)
+ require.NotNil(t, pl)
+ require.IsType(t, &TelegramPayload{}, pl)
+
+ assert.Equal(t, `[test/repo] Wiki page 'index' deleted by user1`, pl.(*TelegramPayload).Message)
+ })
+
t.Run("Release", func(t *testing.T) {
p := pullReleaseTestPayload()
diff --git a/services/webhook/webhook_test.go b/services/webhook/webhook_test.go
index 85fc39770e..1887cc71fe 100644
--- a/services/webhook/webhook_test.go
+++ b/services/webhook/webhook_test.go
@@ -30,7 +30,7 @@ func TestWebhook_GetSlackHook(t *testing.T) {
func TestPrepareWebhooks(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository)
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
hookTasks := []*webhook_model.HookTask{
{RepoID: repo.ID, HookID: 1, EventType: webhook_model.HookEventPush},
}
@@ -46,7 +46,7 @@ func TestPrepareWebhooks(t *testing.T) {
func TestPrepareWebhooksBranchFilterMatch(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}).(*repo_model.Repository)
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2})
hookTasks := []*webhook_model.HookTask{
{RepoID: repo.ID, HookID: 4, EventType: webhook_model.HookEventPush},
}
@@ -63,7 +63,7 @@ func TestPrepareWebhooksBranchFilterMatch(t *testing.T) {
func TestPrepareWebhooksBranchFilterNoMatch(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}).(*repo_model.Repository)
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2})
hookTasks := []*webhook_model.HookTask{
{RepoID: repo.ID, HookID: 4, EventType: webhook_model.HookEventPush},
}
diff --git a/services/webhook/wechatwork.go b/services/webhook/wechatwork.go
index de8b777066..5344ccaa22 100644
--- a/services/webhook/wechatwork.go
+++ b/services/webhook/wechatwork.go
@@ -93,7 +93,7 @@ func (f *WechatworkPayload) Push(p *api.PushPayload) (api.Payloader, error) {
for i, commit := range p.Commits {
var authorName string
if commit.Author != nil {
- authorName = "Author:" + commit.Author.Name
+ authorName = "Author: " + commit.Author.Name
}
message := strings.ReplaceAll(commit.Message, "\n\n", "\r\n")
@@ -166,6 +166,13 @@ func (f *WechatworkPayload) Repository(p *api.RepositoryPayload) (api.Payloader,
return nil, nil
}
+// Wiki implements PayloadConvertor Wiki method
+func (f *WechatworkPayload) Wiki(p *api.WikiPayload) (api.Payloader, error) {
+ text, _, _ := getWikiPayloadInfo(p, noneLinkFormatter, true)
+
+ return newWechatworkMarkdownPayload(text), nil
+}
+
// Release implements PayloadConvertor Release method
func (f *WechatworkPayload) Release(p *api.ReleasePayload) (api.Payloader, error) {
text, _ := getReleasePayloadInfo(p, noneLinkFormatter, true)
diff --git a/services/wiki/wiki.go b/services/wiki/wiki.go
index 43e35eed69..8faada4d59 100644
--- a/services/wiki/wiki.go
+++ b/services/wiki/wiki.go
@@ -12,9 +12,8 @@ import (
"os"
"strings"
- "code.gitea.io/gitea/models"
- admin_model "code.gitea.io/gitea/models/admin"
repo_model "code.gitea.io/gitea/models/repo"
+ system_model "code.gitea.io/gitea/models/system"
"code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/git"
@@ -31,9 +30,14 @@ var (
wikiWorkingPool = sync.NewExclusivePool()
)
+const (
+ DefaultRemote = "origin"
+ DefaultBranch = "master"
+)
+
func nameAllowed(name string) error {
if util.IsStringInSlice(name, reservedWikiNames) {
- return models.ErrWikiReservedName{
+ return repo_model.ErrWikiReservedName{
Title: name,
}
}
@@ -59,7 +63,7 @@ func NameToFilename(name string) string {
// FilenameToName converts a wiki filename to its corresponding page name.
func FilenameToName(filename string) (string, error) {
if !strings.HasSuffix(filename, ".md") {
- return "", models.ErrWikiInvalidFileName{
+ return "", repo_model.ErrWikiInvalidFileName{
FileName: filename,
}
}
@@ -82,7 +86,7 @@ func InitWiki(ctx context.Context, repo *repo_model.Repository) error {
return fmt.Errorf("InitRepository: %v", err)
} else if err = repo_module.CreateDelegateHooks(repo.WikiPath()); err != nil {
return fmt.Errorf("createDelegateHooks: %v", err)
- } else if _, _, err = git.NewCommand(ctx, "symbolic-ref", "HEAD", git.BranchPrefix+"master").RunStdString(&git.RunOpts{Dir: repo.WikiPath()}); err != nil {
+ } else if _, _, err = git.NewCommand(ctx, "symbolic-ref", "HEAD", git.BranchPrefix+DefaultBranch).RunStdString(&git.RunOpts{Dir: repo.WikiPath()}); err != nil {
return fmt.Errorf("unable to set default wiki branch to master: %v", err)
}
return nil
@@ -95,7 +99,7 @@ func prepareWikiFileName(gitRepo *git.Repository, wikiName string) (bool, string
escaped := NameToFilename(wikiName)
// Look for both files
- filesInIndex, err := gitRepo.LsTree("master", unescaped, escaped)
+ filesInIndex, err := gitRepo.LsTree(DefaultBranch, unescaped, escaped)
if err != nil {
if strings.Contains(err.Error(), "Not a valid object name master") {
return false, escaped, nil
@@ -119,7 +123,7 @@ func prepareWikiFileName(gitRepo *git.Repository, wikiName string) (bool, string
return foundEscaped, escaped, nil
}
-// updateWikiPage adds a new page to the repository wiki.
+// updateWikiPage adds a new page or edits an existing page in repository wiki.
func updateWikiPage(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, oldWikiName, newWikiName, content, message string, isNew bool) (err error) {
if err = nameAllowed(newWikiName); err != nil {
return err
@@ -131,7 +135,7 @@ func updateWikiPage(ctx context.Context, doer *user_model.User, repo *repo_model
return fmt.Errorf("InitWiki: %v", err)
}
- hasMasterBranch := git.IsBranchExist(ctx, repo.WikiPath(), "master")
+ hasMasterBranch := git.IsBranchExist(ctx, repo.WikiPath(), DefaultBranch)
basePath, err := repo_module.CreateTemporaryPath("update-wiki")
if err != nil {
@@ -149,7 +153,7 @@ func updateWikiPage(ctx context.Context, doer *user_model.User, repo *repo_model
}
if hasMasterBranch {
- cloneOpts.Branch = "master"
+ cloneOpts.Branch = DefaultBranch
}
if err := git.Clone(ctx, repo.WikiPath(), basePath, cloneOpts); err != nil {
@@ -178,7 +182,7 @@ func updateWikiPage(ctx context.Context, doer *user_model.User, repo *repo_model
if isNew {
if isWikiExist {
- return models.ErrWikiAlreadyExist{
+ return repo_model.ErrWikiAlreadyExist{
Title: newWikiPath,
}
}
@@ -247,8 +251,8 @@ func updateWikiPage(ctx context.Context, doer *user_model.User, repo *repo_model
}
if err := git.Push(gitRepo.Ctx, basePath, git.PushOptions{
- Remote: "origin",
- Branch: fmt.Sprintf("%s:%s%s", commitHash.String(), git.BranchPrefix, "master"),
+ Remote: DefaultRemote,
+ Branch: fmt.Sprintf("%s:%s%s", commitHash.String(), git.BranchPrefix, DefaultBranch),
Env: repo_module.FullPushingEnvironment(
doer,
doer,
@@ -300,7 +304,7 @@ func DeleteWikiPage(ctx context.Context, doer *user_model.User, repo *repo_model
if err := git.Clone(ctx, repo.WikiPath(), basePath, git.CloneRepoOptions{
Bare: true,
Shared: true,
- Branch: "master",
+ Branch: DefaultBranch,
}); err != nil {
log.Error("Failed to clone repository: %s (%v)", repo.FullName(), err)
return fmt.Errorf("Failed to clone repository: %s (%v)", repo.FullName(), err)
@@ -361,8 +365,8 @@ func DeleteWikiPage(ctx context.Context, doer *user_model.User, repo *repo_model
}
if err := git.Push(gitRepo.Ctx, basePath, git.PushOptions{
- Remote: "origin",
- Branch: fmt.Sprintf("%s:%s%s", commitHash.String(), git.BranchPrefix, "master"),
+ Remote: DefaultRemote,
+ Branch: fmt.Sprintf("%s:%s%s", commitHash.String(), git.BranchPrefix, DefaultBranch),
Env: repo_module.PushingEnvironment(doer, repo),
}); err != nil {
if git.IsErrPushOutOfDate(err) || git.IsErrPushRejected(err) {
@@ -380,6 +384,6 @@ func DeleteWiki(ctx context.Context, repo *repo_model.Repository) error {
return err
}
- admin_model.RemoveAllWithNotice(ctx, "Delete repository wiki", repo.WikiPath())
+ system_model.RemoveAllWithNotice(ctx, "Delete repository wiki", repo.WikiPath())
return nil
}
diff --git a/services/wiki/wiki_test.go b/services/wiki/wiki_test.go
index 0c73074dbe..774ef6c283 100644
--- a/services/wiki/wiki_test.go
+++ b/services/wiki/wiki_test.go
@@ -5,16 +5,13 @@
package wiki
import (
- "os"
"path/filepath"
"testing"
- "code.gitea.io/gitea/models"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/git"
- "code.gitea.io/gitea/modules/util"
"github.com/stretchr/testify/assert"
)
@@ -90,11 +87,11 @@ func TestWikiFilenameToName(t *testing.T) {
} {
_, err := FilenameToName(badFilename)
assert.Error(t, err)
- assert.True(t, models.IsErrWikiInvalidFileName(err))
+ assert.True(t, repo_model.IsErrWikiInvalidFileName(err))
}
_, err := FilenameToName("badescaping%%.md")
assert.Error(t, err)
- assert.False(t, models.IsErrWikiInvalidFileName(err))
+ assert.False(t, repo_model.IsErrWikiInvalidFileName(err))
}
func TestWikiNameToFilenameToName(t *testing.T) {
@@ -116,11 +113,11 @@ func TestWikiNameToFilenameToName(t *testing.T) {
func TestRepository_InitWiki(t *testing.T) {
unittest.PrepareTestEnv(t)
// repo1 already has a wiki
- repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository)
+ repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
assert.NoError(t, InitWiki(git.DefaultContext, repo1))
// repo2 does not already have a wiki
- repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}).(*repo_model.Repository)
+ repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2})
assert.NoError(t, InitWiki(git.DefaultContext, repo2))
assert.True(t, repo2.HasWiki())
}
@@ -129,8 +126,8 @@ func TestRepository_AddWikiPage(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
const wikiContent = "This is the wiki content"
const commitMsg = "Commit message"
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository)
- doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
+ doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
for _, wikiName := range []string{
"Another page",
"Here's a and a/slash",
@@ -143,7 +140,7 @@ func TestRepository_AddWikiPage(t *testing.T) {
gitRepo, err := git.OpenRepository(git.DefaultContext, repo.WikiPath())
assert.NoError(t, err)
defer gitRepo.Close()
- masterTree, err := gitRepo.GetTree("master")
+ masterTree, err := gitRepo.GetTree(DefaultBranch)
assert.NoError(t, err)
wikiPath := NameToFilename(wikiName)
entry, err := masterTree.GetTreeEntryByPath(wikiPath)
@@ -157,7 +154,7 @@ func TestRepository_AddWikiPage(t *testing.T) {
// test for already-existing wiki name
err := AddWikiPage(git.DefaultContext, doer, repo, "Home", wikiContent, commitMsg)
assert.Error(t, err)
- assert.True(t, models.IsErrWikiAlreadyExist(err))
+ assert.True(t, repo_model.IsErrWikiAlreadyExist(err))
})
t.Run("check wiki reserved name", func(t *testing.T) {
@@ -165,7 +162,7 @@ func TestRepository_AddWikiPage(t *testing.T) {
// test for reserved wiki name
err := AddWikiPage(git.DefaultContext, doer, repo, "_edit", wikiContent, commitMsg)
assert.Error(t, err)
- assert.True(t, models.IsErrWikiReservedName(err))
+ assert.True(t, repo_model.IsErrWikiReservedName(err))
})
}
@@ -174,8 +171,8 @@ func TestRepository_EditWikiPage(t *testing.T) {
const newWikiContent = "This is the new content"
const commitMsg = "Commit message"
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository)
- doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
+ doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
for _, newWikiName := range []string{
"Home", // same name as before
"New home",
@@ -187,7 +184,7 @@ func TestRepository_EditWikiPage(t *testing.T) {
// Now need to show that the page has been added:
gitRepo, err := git.OpenRepository(git.DefaultContext, repo.WikiPath())
assert.NoError(t, err)
- masterTree, err := gitRepo.GetTree("master")
+ masterTree, err := gitRepo.GetTree(DefaultBranch)
assert.NoError(t, err)
wikiPath := NameToFilename(newWikiName)
entry, err := masterTree.GetTreeEntryByPath(wikiPath)
@@ -204,15 +201,15 @@ func TestRepository_EditWikiPage(t *testing.T) {
func TestRepository_DeleteWikiPage(t *testing.T) {
unittest.PrepareTestEnv(t)
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository)
- doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}).(*user_model.User)
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
+ doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
assert.NoError(t, DeleteWikiPage(git.DefaultContext, doer, repo, "Home"))
// Now need to show that the page has been added:
gitRepo, err := git.OpenRepository(git.DefaultContext, repo.WikiPath())
assert.NoError(t, err)
defer gitRepo.Close()
- masterTree, err := gitRepo.GetTree("master")
+ masterTree, err := gitRepo.GetTree(DefaultBranch)
assert.NoError(t, err)
wikiPath := NameToFilename("Home")
_, err = masterTree.GetTreeEntryByPath(wikiPath)
@@ -221,7 +218,7 @@ func TestRepository_DeleteWikiPage(t *testing.T) {
func TestPrepareWikiFileName(t *testing.T) {
unittest.PrepareTestEnv(t)
- repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}).(*repo_model.Repository)
+ repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
gitRepo, err := git.OpenRepository(git.DefaultContext, repo.WikiPath())
defer gitRepo.Close()
assert.NoError(t, err)
@@ -274,15 +271,9 @@ func TestPrepareWikiFileName_FirstPage(t *testing.T) {
unittest.PrepareTestEnv(t)
// Now create a temporaryDirectory
- tmpDir, err := os.MkdirTemp("", "empty-wiki")
- assert.NoError(t, err)
- defer func() {
- if _, err := os.Stat(tmpDir); !os.IsNotExist(err) {
- _ = util.RemoveAll(tmpDir)
- }
- }()
+ tmpDir := t.TempDir()
- err = git.InitRepository(git.DefaultContext, tmpDir, true)
+ err := git.InitRepository(git.DefaultContext, tmpDir, true)
assert.NoError(t, err)
gitRepo, err := git.OpenRepository(git.DefaultContext, tmpDir)
diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml
index 2defba7be3..437bda87aa 100644
--- a/snap/snapcraft.yaml
+++ b/snap/snapcraft.yaml
@@ -44,8 +44,8 @@ parts:
plugin: make
source: .
stage-packages: [ git, sqlite3, openssh-client ]
- build-packages: [ git, libpam0g-dev, libsqlite3-dev]
- build-snaps: [ go, node/14/stable ]
+ build-packages: [ git, libpam0g-dev, libsqlite3-dev, build-essential]
+ build-snaps: [ go, node/18/stable ]
build-environment:
- LDFLAGS: ""
override-pull: |
diff --git a/templates/admin/applications/list.tmpl b/templates/admin/applications/list.tmpl
new file mode 100644
index 0000000000..6d627129df
--- /dev/null
+++ b/templates/admin/applications/list.tmpl
@@ -0,0 +1,14 @@
+{{template "base/head" .}}
+
+ {{template "admin/navbar" .}}
+
+
+ {{template "base/alert" .}}
+
+ {{template "user/settings/applications_oauth2_list" .}}
+
+
+
+{{template "base/footer" .}}
diff --git a/templates/admin/applications/oauth2_edit.tmpl b/templates/admin/applications/oauth2_edit.tmpl
new file mode 100644
index 0000000000..84d821ecca
--- /dev/null
+++ b/templates/admin/applications/oauth2_edit.tmpl
@@ -0,0 +1,7 @@
+{{template "base/head" .}}
+
+ {{template "admin/navbar" .}}
+
+ {{template "user/settings/applications_oauth2_edit_form" .}}
+
+{{template "base/footer" .}}
diff --git a/templates/admin/auth/edit.tmpl b/templates/admin/auth/edit.tmpl
index af3f381c8e..bf9d53152c 100644
--- a/templates/admin/auth/edit.tmpl
+++ b/templates/admin/auth/edit.tmpl
@@ -23,7 +23,7 @@
{{if or .Source.IsLDAP .Source.IsDLDAP}}
- {{ $cfg:=.Source.Cfg }}
+ {{$cfg:=.Source.Cfg}}
@@ -180,7 +180,7 @@
{{if .Source.IsSMTP}}
- {{ $cfg:=.Source.Cfg }}
+ {{$cfg:=.Source.Cfg}}
@@ -242,7 +242,7 @@
{{if .Source.IsPAM}}
- {{ $cfg:=.Source.Cfg }}
+ {{$cfg:=.Source.Cfg}}
@@ -262,7 +262,7 @@
{{if .Source.IsOAuth2}}
- {{ $cfg:=.Source.Cfg }}
+ {{$cfg:=.Source.Cfg}}
@@ -337,16 +337,16 @@
-
+
@@ -365,7 +365,7 @@
{{if .Source.IsSSPI}}
- {{ $cfg:=.Source.Cfg }}
+ {{$cfg:=.Source.Cfg}}
diff --git a/templates/admin/auth/list.tmpl b/templates/admin/auth/list.tmpl
index 3ce138449d..c43287ee1a 100644
--- a/templates/admin/auth/list.tmpl
+++ b/templates/admin/auth/list.tmpl
@@ -29,8 +29,8 @@
{{.Name}} |
{{.TypeName}} |
{{if .IsActive}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}} |
-
{{.UpdatedUnix.FormatShort}} |
-
{{.CreatedUnix.FormatShort}} |
+
|
+
|
{{svg "octicon-pencil"}} |
{{end}}
diff --git a/templates/admin/auth/new.tmpl b/templates/admin/auth/new.tmpl
index 6bfda9fcd3..213c621b42 100644
--- a/templates/admin/auth/new.tmpl
+++ b/templates/admin/auth/new.tmpl
@@ -30,10 +30,10 @@
- {{ template "admin/auth/source/ldap" . }}
+ {{template "admin/auth/source/ldap" .}}
- {{ template "admin/auth/source/smtp" . }}
+ {{template "admin/auth/source/smtp" .}}
@@ -51,10 +51,10 @@
- {{ template "admin/auth/source/oauth" . }}
+ {{template "admin/auth/source/oauth" .}}
- {{ template "admin/auth/source/sspi" . }}
+ {{template "admin/auth/source/sspi" .}}
diff --git a/templates/admin/config.tmpl b/templates/admin/config.tmpl
index a55a797262..982cfb2800 100644
--- a/templates/admin/config.tmpl
+++ b/templates/admin/config.tmpl
@@ -50,7 +50,7 @@
{{.locale.Tr "admin.config.reverse_auth_user"}}
{{.ReverseProxyAuthUser}}
- {{if .EnvVars }}
+ {{if .EnvVars}}
{{range .EnvVars}}
{{.Name}}
@@ -218,12 +218,7 @@
{{if .MailerEnabled}}
{{.locale.Tr "admin.config.mailer_name"}}
{{.Mailer.Name}}
- {{if eq .Mailer.MailerType "smtp"}}
-
{{.locale.Tr "admin.config.mailer_disable_helo"}}
-
{{if .DisableHelo}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}
-
{{.locale.Tr "admin.config.mailer_host"}}
-
{{.Mailer.Host}}
- {{else if eq .Mailer.MailerType "sendmail"}}
+ {{if eq .Mailer.Protocol "sendmail"}}
{{.locale.Tr "admin.config.mailer_use_sendmail"}}
{{svg "octicon-check"}}
{{.locale.Tr "admin.config.mailer_sendmail_path"}}
@@ -232,6 +227,18 @@
{{.Mailer.SendmailArgs}}
{{.locale.Tr "admin.config.mailer_sendmail_timeout"}}
{{.Mailer.SendmailTimeout}} {{.locale.Tr "tool.raw_seconds"}}
+ {{else if eq .Mailer.Protocol "dummy"}}
+
{{.locale.Tr "admin.config.mailer_use_dummy"}}
+
{{svg "octicon-check"}}
+ {{else}}{{/* SMTP family */}}
+
{{.locale.Tr "admin.config.mailer_protocol"}}
+
{{.Mailer.Protocol}}
+
{{.locale.Tr "admin.config.mailer_enable_helo"}}
+
{{if .Mailer.EnableHelo}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}
+
{{.locale.Tr "admin.config.mailer_smtp_addr"}}
+
{{.Mailer.SMTPAddr}}
+
{{.locale.Tr "admin.config.mailer_smtp_port"}}
+
{{.Mailer.SMTPPort}}
{{end}}
{{.locale.Tr "admin.config.mailer_user"}}
{{if .Mailer.User}}{{.Mailer.User}}{{else}}(empty){{end}}
@@ -294,10 +301,18 @@
diff --git a/templates/admin/cron.tmpl b/templates/admin/cron.tmpl
index b5db356bd8..d34999e6eb 100644
--- a/templates/admin/cron.tmpl
+++ b/templates/admin/cron.tmpl
@@ -22,9 +22,9 @@
{{$.locale.Tr (printf "admin.dashboard.%s" .Name)}} |
{{.Spec}} |
{{DateFmtLong .Next}} |
-
{{if gt .Prev.Year 1 }}{{DateFmtLong .Prev}}{{else}}N/A{{end}} |
+
{{if gt .Prev.Year 1}}{{DateFmtLong .Prev}}{{else}}N/A{{end}} |
{{.ExecTimes}} |
-
{{if eq .Status "" }}—{{else if eq .Status "finished"}}{{svg "octicon-check" 16}}{{else}}{{svg "octicon-x" 16}}{{end}} |
+
{{if eq .Status ""}}—{{else if eq .Status "finished"}}{{svg "octicon-check" 16}}{{else}}{{svg "octicon-x" 16}}{{end}} |
{{end}}
diff --git a/templates/admin/navbar.tmpl b/templates/admin/navbar.tmpl
index 0db1aab079..b138eb79ba 100644
--- a/templates/admin/navbar.tmpl
+++ b/templates/admin/navbar.tmpl
@@ -26,6 +26,11 @@
{{.locale.Tr "admin.emails"}}
+ {{if .EnableOAuth2}}
+
+ {{.locale.Tr "settings.applications"}}
+
+ {{end}}
{{.locale.Tr "admin.config"}}
diff --git a/templates/admin/notice.tmpl b/templates/admin/notice.tmpl
index 7d0fa0019a..2777741efb 100644
--- a/templates/admin/notice.tmpl
+++ b/templates/admin/notice.tmpl
@@ -29,12 +29,12 @@
{{.ID}} |
{{$.locale.Tr .TrStr}} |
{{.Description}} |
-
{{.CreatedUnix.FormatShort}} |
+
|
{{svg "octicon-note" 16 "view-detail"}} |
{{end}}
- {{ if .Notices }}
+ {{if .Notices}}
|
@@ -65,11 +65,11 @@
- {{ end }}
+ {{end}}
- {{ template "base/paginate" . }}
+ {{template "base/paginate" .}}
diff --git a/templates/admin/org/list.tmpl b/templates/admin/org/list.tmpl
index aec3f2c841..11dc23c60e 100644
--- a/templates/admin/org/list.tmpl
+++ b/templates/admin/org/list.tmpl
@@ -44,7 +44,7 @@
{{.NumTeams}} |
{{.NumMembers}} |
{{.NumRepos}} |
-
{{.CreatedUnix.FormatShort}} |
+
|
{{svg "octicon-pencil"}} |
{{end}}
diff --git a/templates/admin/packages/list.tmpl b/templates/admin/packages/list.tmpl
index 18e8c5fed8..b62e788799 100644
--- a/templates/admin/packages/list.tmpl
+++ b/templates/admin/packages/list.tmpl
@@ -21,8 +21,10 @@
+
+
@@ -73,7 +75,7 @@
{{end}}
{{FileSize .CalculateBlobSize}} |
-
{{.Version.CreatedUnix.FormatShort}} |
+
|
{{svg "octicon-trash"}} |
{{end}}
diff --git a/templates/admin/process-row.tmpl b/templates/admin/process-row.tmpl
index b97d0fc8dc..4ab190af46 100644
--- a/templates/admin/process-row.tmpl
+++ b/templates/admin/process-row.tmpl
@@ -1,6 +1,6 @@
-
{{if eq .Process.Type "request"}}{{svg "octicon-globe" 16 }}{{else if eq .Process.Type "system"}}{{svg "octicon-cpu" 16 }}{{else}}{{svg "octicon-terminal" 16 }}{{end}}
+
{{if eq .Process.Type "request"}}{{svg "octicon-globe" 16}}{{else if eq .Process.Type "system"}}{{svg "octicon-cpu" 16}}{{else}}{{svg "octicon-terminal" 16}}{{end}}
{{TimeSince .Process.Start .root.locale}}
diff --git a/templates/admin/queue.tmpl b/templates/admin/queue.tmpl
index a7ea4c602a..cd50798f80 100644
--- a/templates/admin/queue.tmpl
+++ b/templates/admin/queue.tmpl
@@ -30,12 +30,12 @@
- {{if lt $sum 0 }}
+ {{if lt $sum 0}}
- {{if eq .Queue.Type "wrapped" }}
+ {{if eq .Queue.Type "wrapped"}}
{{.locale.Tr "admin.monitor.queue.wrapped.desc"}}
{{else if eq .Queue.Type "persistable-channel"}}
{{.locale.Tr "admin.monitor.queue.persistable-channel.desc"}}
@@ -58,7 +58,7 @@
-
+
@@ -157,7 +157,7 @@
{{range .Queue.Workers}}
- {{.Workers}}{{if .IsFlusher}}{{svg "octicon-sync"}}{{end}} |
+ {{.Workers}}{{if .IsFlusher}}{{svg "octicon-sync"}}{{end}} |
{{DateFmtLong .Start}} |
{{if .HasTimeout}}{{DateFmtLong .Timeout}}{{else}}-{{end}} |
@@ -166,7 +166,7 @@
|
{{else}}
- {{.locale.Tr "admin.monitor.queue.pool.workers.none" }}
+ | {{.locale.Tr "admin.monitor.queue.pool.workers.none"}}
|
{{end}}
diff --git a/templates/admin/repo/list.tmpl b/templates/admin/repo/list.tmpl
index b26ec2eb78..837802f0d0 100644
--- a/templates/admin/repo/list.tmpl
+++ b/templates/admin/repo/list.tmpl
@@ -83,7 +83,7 @@
{{.NumForks}} |
{{.NumIssues}} |
{{FileSize .Size}} |
-
{{.CreatedUnix.FormatShort}} |
+
|
{{svg "octicon-trash"}} |
{{end}}
diff --git a/templates/admin/stacktrace-row.tmpl b/templates/admin/stacktrace-row.tmpl
index d8b337fc09..a8ae486bcb 100644
--- a/templates/admin/stacktrace-row.tmpl
+++ b/templates/admin/stacktrace-row.tmpl
@@ -2,13 +2,13 @@
{{if eq .Process.Type "request"}}
- {{svg "octicon-globe" 16 }}
+ {{svg "octicon-globe" 16}}
{{else if eq .Process.Type "system"}}
- {{svg "octicon-cpu" 16 }}
+ {{svg "octicon-cpu" 16}}
{{else if eq .Process.Type "normal"}}
- {{svg "octicon-terminal" 16 }}
+ {{svg "octicon-terminal" 16}}
{{else}}
- {{svg "octicon-code" 16 }}
+ {{svg "octicon-code" 16}}
{{end}}
@@ -16,7 +16,7 @@
{{if ne .Process.Type "none"}}{{TimeSince .Process.Start .root.locale}}{{end}}
@@ -29,7 +29,7 @@
{{range .Labels}}
@@ -41,7 +41,7 @@
{{range .Entry}}
-
{{svg "octicon-dot-fill" 16 }}
+
{{svg "octicon-dot-fill" 16}}
{{template "base/footer" .}}
diff --git a/templates/admin/user/list.tmpl b/templates/admin/user/list.tmpl
index 061e663850..56f6eaa3ad 100644
--- a/templates/admin/user/list.tmpl
+++ b/templates/admin/user/list.tmpl
@@ -94,9 +94,9 @@
{{if .IsRestricted}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}} |
{{if index $.UsersTwoFaStatus .ID}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}} |
{{.NumRepos}} |
-
{{.CreatedUnix.FormatShort}} |
+
|
{{if .LastLoginUnix}}
-
{{.LastLoginUnix.FormatShort}} |
+
|
{{else}}
{{$.locale.Tr "admin.users.never_login"}} |
{{end}}
diff --git a/templates/admin/user/new.tmpl b/templates/admin/user/new.tmpl
index b8fd8c0ad1..9fdf0dce93 100644
--- a/templates/admin/user/new.tmpl
+++ b/templates/admin/user/new.tmpl
@@ -69,7 +69,7 @@
diff --git a/templates/base/alert.tmpl b/templates/base/alert.tmpl
index cf886f529c..b8a04b89a9 100644
--- a/templates/base/alert.tmpl
+++ b/templates/base/alert.tmpl
@@ -13,3 +13,8 @@
{{.Flash.InfoMsg | Str2html}}
{{end}}
+{{if .Flash.WarningMsg}}
+
+
{{.Flash.WarningMsg | Str2html}}
+
+{{end}}
diff --git a/templates/base/footer.tmpl b/templates/base/footer.tmpl
index 9bf16f8aa5..3fa0f8e7ac 100644
--- a/templates/base/footer.tmpl
+++ b/templates/base/footer.tmpl
@@ -16,13 +16,13 @@
{{if .EnableCaptcha}}
{{if eq .CaptchaType "recaptcha"}}
-
+
{{end}}
{{if eq .CaptchaType "hcaptcha"}}
{{end}}
{{end}}
-
+
{{template "custom/footer" .}}
diff --git a/templates/base/footer_content.tmpl b/templates/base/footer_content.tmpl
index dfc0646610..89be609225 100644
--- a/templates/base/footer_content.tmpl
+++ b/templates/base/footer_content.tmpl
@@ -1,7 +1,7 @@
diff --git a/templates/base/head.tmpl b/templates/base/head.tmpl
index e0d2b26f2c..70a88ff755 100644
--- a/templates/base/head.tmpl
+++ b/templates/base/head.tmpl
@@ -1,9 +1,9 @@
-
+
-
{{if .Title}}{{.Title | RenderEmojiPlain}} - {{end}} {{if .Repository.Name}}{{.Repository.Name}} - {{end}}{{AppName}}
+
{{if .Title}}{{.Title | RenderEmojiPlain}} - {{end}}{{if .Repository.Name}}{{.Repository.Name}} - {{end}}{{AppName}}
@@ -21,7 +21,7 @@
{{end}}
-
+
{{template "base/head_script" .}}