1
0
mirror of https://github.com/go-gitea/gitea.git synced 2025-01-04 09:17:43 +03:00

Compare commits

...

33 Commits

Author SHA1 Message Date
Bence Sántha
8f93cb9ef6
Merge 1969e6b4ad into d45456b1b5 2024-12-30 12:04:48 +01:00
Lunny Xiao
d45456b1b5
Move SetMerged to service layer (#33045)
No code change.
Extract from #32178
2024-12-30 07:04:03 +00:00
Lunny Xiao
8eecca3478
Remove aws go sdk package dependency (#33029)
This PR removed the dependency of `github.com/aws/aws-sdk-go/aws`

Patially fix for #33023
2024-12-30 06:30:28 +00:00
cassio zareck
1e2c8eb494
Fix settings not being loaded at CLI (#26402)
Closes #25898
The problem was that the default settings weren't being loaded

---------

Signed-off-by: cassiozareck <cassiomilczareck@gmail.com>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-12-30 05:54:20 +00:00
wxiaoguang
dafadf0028
Refactor fixture loading for testing (#33024)
To help binary size and testing performance
2024-12-30 04:06:57 +00:00
Lunny Xiao
f4ccbd38dc
Use gitrepo.GetTreePathLatestCommit to get file lastest commit instead from latest commit cache (#32987)
The latest commit cache is currently used only for listing tree files.
However, a cold start may take longer than directly invoking the Git
command. This PR addresses the issue of slow response times when
accessing raw files, improving performance in such scenarios.

```log
gitea.log:105521:2024/12/23 08:22:18 ...eb/routing/logger.go:68:func1() [W] router: slow      GET /xxxx/xxxxxx/raw/commit/xxxxxxxxxxxxxxxxxxxxxxxxxxx/.editorconfig for 172.18.0.5:53252, elapsed 3526.8ms @ repo/download.go:117(repo.SingleDownload)
```
2024-12-30 03:30:01 +00:00
Lunny Xiao
344c89ea34
Fix bug automerge cannot be chosed when there is only 1 merge style (#33040)
This is a quick bug fix. Even if there is only 1 merge style, the
dropdown menu will still be displayed to allow users to choose
auto-merge.

Fix #32448
2024-12-30 03:04:22 +00:00
techknowlogick
232867cff6
use -s -w ldflags for release artifacts (#33041)
fix #33030
2024-12-30 02:37:59 +00:00
wxiaoguang
cd1b5488a3
Refactor pagination (#33037)
I am sure the simple approach should work, let's try it in 1.24

Follow #29834 and #29841
2024-12-30 01:57:38 +00:00
wxiaoguang
1dbf0d7f08
Test webhook email (#33033)
Close #27918
2024-12-30 01:25:49 +08:00
Henry Goodman
a96776b3cb
Fix review code comment avatar alignment (#33031)
Fixes #33017

Avatar should only have offset if the `Comment` has `Content` or
`Attachment` to align with the speech bubble.

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-12-29 11:04:13 +00:00
TheFox0x7
ff96873e3e
Fix templating in pull request comparison (#33025)
Adds test for expected behavior

Closes: https://github.com/go-gitea/gitea/issues/33013
2024-12-29 01:32:19 +00:00
wxiaoguang
0ed160ffea
Refactor tests (#33021)
1. fix incorrect tests, for example: BeanExists doesn't do assert and
shouldn't be used
2. remove unnecessary test functions
3. introduce DumpQueryResult to help to see the database rows during
test (at least I need it)

```
====== DumpQueryResult: SELECT * FROM action_runner_token ======
- # row[0]
  id: 1
  token: xeiWBL5kuTYxGPynHCqQdoeYmJAeG3IzGXCYTrDX
  owner_id: 0
...
```
2024-12-29 01:05:56 +00:00
GiteaBot
e95b946f6d [skip ci] Updated translations via Crowdin 2024-12-29 00:36:47 +00:00
metiftikci
64bebc9402
always show assignees on right (#33006)
### Before

![old_issue_list](https://github.com/user-attachments/assets/c7a6631d-9330-4e29-9e01-c1bcb2a0387f)

### After

![new_issue_list](https://github.com/user-attachments/assets/5a13c413-b58e-40bb-888b-9edfe3c94e0c)
2024-12-29 00:30:06 +00:00
metiftikci
94048f3035
fix toggle commit body button ui when latest commit message is long (#32997)
#### Before


![before](https://github.com/user-attachments/assets/fe36bdb3-10e8-4fe7-9106-0897f49bedb3)

#### After


![after](https://github.com/user-attachments/assets/745bd164-5f25-41ca-b340-36cb695551db)


## Edit:

I found an issue on mobile view and changed the code as using flex gap


![small](https://github.com/user-attachments/assets/dd7c2093-6860-4800-a2bc-676a03e764c8)


![large](https://github.com/user-attachments/assets/5c933779-8281-4d48-9fd0-4d7b245bf4ac)

---------

Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
2024-12-29 08:04:56 +08:00
Bence Santha
1969e6b4ad Refactor workflow dispatch
Signed-off-by: Bence Santha <git@santha.eu>
2024-10-12 10:20:32 +02:00
Bence Santha
0357003ddf Updated permissions
Signed-off-by: Bence Santha <git@santha.eu>
2024-10-03 08:46:34 +02:00
Bence Sántha
a65d8f3031
Merge branch 'go-gitea:main' into main 2024-10-03 08:43:51 +02:00
Bence Sántha
18c6b9b174
Merge branch 'main' into main 2024-10-02 10:56:10 +02:00
Bence Santha
8883fadb9a refactor
Signed-off-by: Bence Santha <git@santha.eu>
2024-10-02 10:55:31 +02:00
Bence Santha
a983e12185 updated swagger spec
Signed-off-by: Bence Santha <git@santha.eu>
2024-09-24 09:34:17 +02:00
Bence Sántha
e67b371352
Merge branch 'main' into main 2024-09-24 09:17:25 +02:00
Bence Santha
27d1906283 refactor
Signed-off-by: Bence Santha <git@santha.eu>
2024-09-24 09:16:50 +02:00
Bence Santha
35bb7e7ed5 Merge remote-tracking branch 'upstream/main' 2024-09-20 14:15:56 +02:00
Bence Santha
6f1b19976f adding more fields to the workflow list output
Signed-off-by: Bence Santha <git@santha.eu>
2024-09-20 14:10:50 +02:00
Bence Santha
b525269c8d removed unused imports
Signed-off-by: Bence Santha <git@santha.eu>
2024-09-19 20:53:42 +02:00
Bence Santha
ea12e16ddd Resolve conflict
Signed-off-by: Bence Santha <git@santha.eu>
2024-09-19 20:41:08 +02:00
Bence Santha
a4dfa0b85a Feature: Support workflow event dispatch via API
Signed-off-by: Bence Santha <git@santha.eu>
2024-09-19 20:37:53 +02:00
techknowlogick
1fae9c8d09 make fmt 2024-09-18 10:49:31 -04:00
techknowlogick
ccfbdaf36f
remove extra empty line 2024-09-18 10:43:00 -04:00
techknowlogick
554de04fb2
re-arrange imports 2024-09-18 10:42:31 -04:00
Bence Santha
8814e9f14c Feature: Support workflow event dispatch via API 2024-09-17 18:00:01 +02:00
84 changed files with 1960 additions and 618 deletions

View File

@ -806,22 +806,22 @@ $(DIST_DIRS):
.PHONY: release-windows .PHONY: release-windows
release-windows: | $(DIST_DIRS) release-windows: | $(DIST_DIRS)
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -buildmode exe -dest $(DIST)/binaries -tags 'osusergo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'windows/*' -out gitea-$(VERSION) . CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -buildmode exe -dest $(DIST)/binaries -tags 'osusergo $(TAGS)' -ldflags '-s -w -linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'windows/*' -out gitea-$(VERSION) .
ifeq (,$(findstring gogit,$(TAGS))) ifeq (,$(findstring gogit,$(TAGS)))
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -buildmode exe -dest $(DIST)/binaries -tags 'osusergo gogit $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'windows/*' -out gitea-$(VERSION)-gogit . CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -buildmode exe -dest $(DIST)/binaries -tags 'osusergo gogit $(TAGS)' -ldflags '-s -w -linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'windows/*' -out gitea-$(VERSION)-gogit .
endif endif
.PHONY: release-linux .PHONY: release-linux
release-linux: | $(DIST_DIRS) release-linux: | $(DIST_DIRS)
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets '$(LINUX_ARCHS)' -out gitea-$(VERSION) . CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-s -w -linkmode external -extldflags "-static" $(LDFLAGS)' -targets '$(LINUX_ARCHS)' -out gitea-$(VERSION) .
.PHONY: release-darwin .PHONY: release-darwin
release-darwin: | $(DIST_DIRS) release-darwin: | $(DIST_DIRS)
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '$(LDFLAGS)' -targets 'darwin-10.12/amd64,darwin-10.12/arm64' -out gitea-$(VERSION) . CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-s -w $(LDFLAGS)' -targets 'darwin-10.12/amd64,darwin-10.12/arm64' -out gitea-$(VERSION) .
.PHONY: release-freebsd .PHONY: release-freebsd
release-freebsd: | $(DIST_DIRS) release-freebsd: | $(DIST_DIRS)
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '$(LDFLAGS)' -targets 'freebsd/amd64' -out gitea-$(VERSION) . CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-s -w $(LDFLAGS)' -targets 'freebsd/amd64' -out gitea-$(VERSION) .
.PHONY: release-copy .PHONY: release-copy
release-copy: | $(DIST_DIRS) release-copy: | $(DIST_DIRS)

File diff suppressed because one or more lines are too long

View File

@ -69,6 +69,10 @@ var microcmdUserCreate = &cli.Command{
} }
func runCreateUser(c *cli.Context) error { func runCreateUser(c *cli.Context) error {
// this command highly depends on the many setting options (create org, visibility, etc.), so it must have a full setting load first
// duplicate setting loading should be safe at the moment, but it should be refactored & improved in the future.
setting.LoadSettings()
if err := argsSet(c, "email"); err != nil { if err := argsSet(c, "email"); err != nil {
return err return err
} }

View File

@ -12,6 +12,7 @@ import (
"path/filepath" "path/filepath"
"strconv" "strconv"
"strings" "strings"
"time"
_ "net/http/pprof" // Used for debugging if enabled and a web server is running _ "net/http/pprof" // Used for debugging if enabled and a web server is running
@ -115,6 +116,16 @@ func showWebStartupMessage(msg string) {
log.Info("* CustomPath: %s", setting.CustomPath) log.Info("* CustomPath: %s", setting.CustomPath)
log.Info("* ConfigFile: %s", setting.CustomConf) log.Info("* ConfigFile: %s", setting.CustomConf)
log.Info("%s", msg) // show startup message log.Info("%s", msg) // show startup message
if setting.CORSConfig.Enabled {
log.Info("CORS Service Enabled")
}
if setting.DefaultUILocation != time.Local {
log.Info("Default UI Location is %v", setting.DefaultUILocation.String())
}
if setting.MailService != nil {
log.Info("Mail Service Enabled: RegisterEmailConfirm=%v, Service.EnableNotifyMail=%v", setting.Service.RegisterEmailConfirm, setting.Service.EnableNotifyMail)
}
} }
func serveInstall(ctx *cli.Context) error { func serveInstall(ctx *cli.Context) error {

10
go.mod
View File

@ -28,7 +28,6 @@ require (
github.com/PuerkitoBio/goquery v1.10.0 github.com/PuerkitoBio/goquery v1.10.0
github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.7.3 github.com/SaveTheRbtz/zstd-seekable-format-go/pkg v0.7.3
github.com/alecthomas/chroma/v2 v2.14.0 github.com/alecthomas/chroma/v2 v2.14.0
github.com/aws/aws-sdk-go v1.55.5
github.com/aws/aws-sdk-go-v2/credentials v1.17.42 github.com/aws/aws-sdk-go-v2/credentials v1.17.42
github.com/aws/aws-sdk-go-v2/service/codecommit v1.27.3 github.com/aws/aws-sdk-go-v2/service/codecommit v1.27.3
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb
@ -61,7 +60,6 @@ require (
github.com/go-redsync/redsync/v4 v4.13.0 github.com/go-redsync/redsync/v4 v4.13.0
github.com/go-sql-driver/mysql v1.8.1 github.com/go-sql-driver/mysql v1.8.1
github.com/go-swagger/go-swagger v0.31.0 github.com/go-swagger/go-swagger v0.31.0
github.com/go-testfixtures/testfixtures/v3 v3.11.0
github.com/go-webauthn/webauthn v0.11.2 github.com/go-webauthn/webauthn v0.11.2
github.com/gobwas/glob v0.2.3 github.com/gobwas/glob v0.2.3
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f
@ -145,8 +143,6 @@ require (
filippo.io/edwards25519 v1.1.0 // indirect filippo.io/edwards25519 v1.1.0 // indirect
git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078 // indirect git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect
github.com/ClickHouse/ch-go v0.63.1 // indirect
github.com/ClickHouse/clickhouse-go/v2 v2.24.0 // indirect
github.com/DataDog/zstd v1.5.6 // indirect github.com/DataDog/zstd v1.5.6 // indirect
github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/semver/v3 v3.3.0 // indirect github.com/Masterminds/semver/v3 v3.3.0 // indirect
@ -204,8 +200,6 @@ require (
github.com/go-ap/errors v0.0.0-20240910140019-1e9d33cc1568 // indirect github.com/go-ap/errors v0.0.0-20240910140019-1e9d33cc1568 // indirect
github.com/go-asn1-ber/asn1-ber v1.5.7 // indirect github.com/go-asn1-ber/asn1-ber v1.5.7 // indirect
github.com/go-enry/go-oniguruma v1.2.1 // indirect github.com/go-enry/go-oniguruma v1.2.1 // indirect
github.com/go-faster/city v1.0.1 // indirect
github.com/go-faster/errors v0.7.1 // indirect
github.com/go-fed/httpsig v1.1.1-0.20201223112313-55836744818e // indirect github.com/go-fed/httpsig v1.1.1-0.20201223112313-55836744818e // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-ini/ini v1.67.0 // indirect github.com/go-ini/ini v1.67.0 // indirect
@ -270,7 +264,6 @@ require (
github.com/oklog/ulid v1.3.1 // indirect github.com/oklog/ulid v1.3.1 // indirect
github.com/olekukonko/tablewriter v0.0.5 // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect
github.com/onsi/ginkgo v1.16.5 // indirect github.com/onsi/ginkgo v1.16.5 // indirect
github.com/paulmach/orb v0.11.1 // indirect
github.com/pelletier/go-toml/v2 v2.2.3 // indirect github.com/pelletier/go-toml/v2 v2.2.3 // indirect
github.com/pierrec/lz4/v4 v4.1.21 // indirect github.com/pierrec/lz4/v4 v4.1.21 // indirect
github.com/pjbgf/sha1cd v0.3.0 // indirect github.com/pjbgf/sha1cd v0.3.0 // indirect
@ -285,7 +278,6 @@ require (
github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/sagikazarmark/locafero v0.6.0 // indirect github.com/sagikazarmark/locafero v0.6.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/segmentio/asm v1.2.0 // indirect
github.com/shopspring/decimal v1.4.0 // indirect github.com/shopspring/decimal v1.4.0 // indirect
github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c // indirect github.com/shurcooL/httpfs v0.0.0-20230704072500-f1e31cf0ba5c // indirect
github.com/sirupsen/logrus v1.9.3 // indirect github.com/sirupsen/logrus v1.9.3 // indirect
@ -310,8 +302,6 @@ require (
github.com/zeebo/blake3 v0.2.4 // indirect github.com/zeebo/blake3 v0.2.4 // indirect
go.etcd.io/bbolt v1.3.11 // indirect go.etcd.io/bbolt v1.3.11 // indirect
go.mongodb.org/mongo-driver v1.17.1 // indirect go.mongodb.org/mongo-driver v1.17.1 // indirect
go.opentelemetry.io/otel v1.31.0 // indirect
go.opentelemetry.io/otel/trace v1.31.0 // indirect
go.uber.org/atomic v1.11.0 // indirect go.uber.org/atomic v1.11.0 // indirect
go.uber.org/multierr v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect go.uber.org/zap v1.27.0 // indirect

62
go.sum
View File

@ -59,10 +59,6 @@ github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzS
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 h1:XHOnouVk1mxXfQidrMEnLlPk9UMeRtyBTnEFtxkV0kU= github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 h1:XHOnouVk1mxXfQidrMEnLlPk9UMeRtyBTnEFtxkV0kU=
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/ClickHouse/ch-go v0.63.1 h1:s2JyZvWLTCSAGdtjMBBmAgQQHMco6pawLJMOXi0FODM=
github.com/ClickHouse/ch-go v0.63.1/go.mod h1:I1kJJCL3WJcBMGe1m+HVK0+nREaG+JOYYBWjrDrF3R0=
github.com/ClickHouse/clickhouse-go/v2 v2.24.0 h1:L/n/pVVpk95KtkHOiKuSnO7cu2ckeW4gICbbOh5qs74=
github.com/ClickHouse/clickhouse-go/v2 v2.24.0/go.mod h1:iDTViXk2Fgvf1jn2dbJd1ys+fBkdD1UMRnXlwmhijhQ=
github.com/DataDog/zstd v1.5.6 h1:LbEglqepa/ipmmQJUDnSsfvA8e8IStVcGaFWDuxvGOY= github.com/DataDog/zstd v1.5.6 h1:LbEglqepa/ipmmQJUDnSsfvA8e8IStVcGaFWDuxvGOY=
github.com/DataDog/zstd v1.5.6/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= github.com/DataDog/zstd v1.5.6/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw=
github.com/Julusian/godocdown v0.0.0-20170816220326-6d19f8ff2df8/go.mod h1:INZr5t32rG59/5xeltqoCJoNY7e5x/3xoY9WSWVWg74= github.com/Julusian/godocdown v0.0.0-20170816220326-6d19f8ff2df8/go.mod h1:INZr5t32rG59/5xeltqoCJoNY7e5x/3xoY9WSWVWg74=
@ -109,8 +105,6 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPd
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU=
github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
github.com/aws/aws-sdk-go-v2 v1.32.3 h1:T0dRlFBKcdaUPGNtkBSwHZxrtis8CQU17UpNBZYd0wk= github.com/aws/aws-sdk-go-v2 v1.32.3 h1:T0dRlFBKcdaUPGNtkBSwHZxrtis8CQU17UpNBZYd0wk=
github.com/aws/aws-sdk-go-v2 v1.32.3/go.mod h1:2SK5n0a2karNTv5tbP1SjsX0uhttou00v/HpXKM1ZUo= github.com/aws/aws-sdk-go-v2 v1.32.3/go.mod h1:2SK5n0a2karNTv5tbP1SjsX0uhttou00v/HpXKM1ZUo=
github.com/aws/aws-sdk-go-v2/credentials v1.17.42 h1:sBP0RPjBU4neGpIYyx8mkU2QqLPl5u9cmdTWVzIpHkM= github.com/aws/aws-sdk-go-v2/credentials v1.17.42 h1:sBP0RPjBU4neGpIYyx8mkU2QqLPl5u9cmdTWVzIpHkM=
@ -237,8 +231,6 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davidmz/go-pageant v1.0.2 h1:bPblRCh5jGU+Uptpz6LgMZGD5hJoOt7otgT454WvHn0= github.com/davidmz/go-pageant v1.0.2 h1:bPblRCh5jGU+Uptpz6LgMZGD5hJoOt7otgT454WvHn0=
github.com/davidmz/go-pageant v1.0.2/go.mod h1:P2EDDnMqIwG5Rrp05dTRITj9z2zpGcD9efWSkTNKLIE= github.com/davidmz/go-pageant v1.0.2/go.mod h1:P2EDDnMqIwG5Rrp05dTRITj9z2zpGcD9efWSkTNKLIE=
github.com/denisenkom/go-mssqldb v0.12.3 h1:pBSGx9Tq67pBOTLmxNuirNTeB8Vjmf886Kx+8Y+8shw=
github.com/denisenkom/go-mssqldb v0.12.3/go.mod h1:k0mtMFOnU+AihqFxPMiF05rtiDrorD1Vrm1KEz5hxDo=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/dimiro1/reply v0.0.0-20200315094148-d0136a4c9e21 h1:PdsjTl0Cg+ZJgOx/CFV5NNgO1ThTreqdgKYiDCMHJwA= github.com/dimiro1/reply v0.0.0-20200315094148-d0136a4c9e21 h1:PdsjTl0Cg+ZJgOx/CFV5NNgO1ThTreqdgKYiDCMHJwA=
@ -317,10 +309,6 @@ github.com/go-enry/go-enry/v2 v2.9.1 h1:G9iDteJ/Mc0F4Di5NeQknf83R2OkRbwY9cAYmcqV
github.com/go-enry/go-enry/v2 v2.9.1/go.mod h1:9yrj4ES1YrbNb1Wb7/PWYr2bpaCXUGRt0uafN0ISyG8= github.com/go-enry/go-enry/v2 v2.9.1/go.mod h1:9yrj4ES1YrbNb1Wb7/PWYr2bpaCXUGRt0uafN0ISyG8=
github.com/go-enry/go-oniguruma v1.2.1 h1:k8aAMuJfMrqm/56SG2lV9Cfti6tC4x8673aHCcBk+eo= 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-enry/go-oniguruma v1.2.1/go.mod h1:bWDhYP+S6xZQgiRL7wlTScFYBe023B6ilRZbCAD5Hf4=
github.com/go-faster/city v1.0.1 h1:4WAxSZ3V2Ws4QRDrscLEDcibJY8uf41H6AhXDrNDcGw=
github.com/go-faster/city v1.0.1/go.mod h1:jKcUJId49qdW3L1qKHH/3wPeUstCVpVSXTM6vO3VcTw=
github.com/go-faster/errors v0.7.1 h1:MkJTnDoEdi9pDabt1dpWf7AA8/BaSYZqibYyhZ20AYg=
github.com/go-faster/errors v0.7.1/go.mod h1:5ySTjWFiphBs07IKuiL69nxdfd5+fzh1u7FPGZP2quo=
github.com/go-fed/httpsig v1.1.1-0.20201223112313-55836744818e h1:oRq/fiirun5HqlEWMLIcDmLpIELlG4iGbd0s8iqgPi8= 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-fed/httpsig v1.1.1-0.20201223112313-55836744818e/go.mod h1:RCMrTZvN1bJYtofsG4rd5NaO5obxQ5xBkdiS7xsT7bM=
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
@ -372,8 +360,6 @@ github.com/go-swagger/go-swagger v0.31.0/go.mod h1:WSigRRWEig8zV6t6Sm8Y+EmUjlzA/
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/go-test/deep v1.1.0 h1:WOcxcdHcvdgThNXjw0t76K42FXTU7HpNQWHpA2HHNlg= github.com/go-test/deep v1.1.0 h1:WOcxcdHcvdgThNXjw0t76K42FXTU7HpNQWHpA2HHNlg=
github.com/go-test/deep v1.1.0/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/go-test/deep v1.1.0/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
github.com/go-testfixtures/testfixtures/v3 v3.11.0 h1:XxQr8AnPORcZkyNd7go5UNLPD3dULN8ixYISlzrlfEQ=
github.com/go-testfixtures/testfixtures/v3 v3.11.0/go.mod h1:THmudHF1Ixq++J2/UodcJpxUphfyEd77m83TvDtryqE=
github.com/go-webauthn/webauthn v0.11.2 h1:Fgx0/wlmkClTKlnOsdOQ+K5HcHDsDcYIvtYmfhEOSUc= github.com/go-webauthn/webauthn v0.11.2 h1:Fgx0/wlmkClTKlnOsdOQ+K5HcHDsDcYIvtYmfhEOSUc=
github.com/go-webauthn/webauthn v0.11.2/go.mod h1:aOtudaF94pM71g3jRwTYYwQTG1KyTILTcZqN1srkmD0= github.com/go-webauthn/webauthn v0.11.2/go.mod h1:aOtudaF94pM71g3jRwTYYwQTG1KyTILTcZqN1srkmD0=
github.com/go-webauthn/x v0.1.15 h1:eG1OhggBJTkDE8gUeOlGRbRe8E/PSVG26YG4AyFbwkU= github.com/go-webauthn/x v0.1.15 h1:eG1OhggBJTkDE8gUeOlGRbRe8E/PSVG26YG4AyFbwkU=
@ -385,7 +371,6 @@ github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6Wezm
github.com/gobwas/ws v1.2.1/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY= github.com/gobwas/ws v1.2.1/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY=
github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA= github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f h1:3BSP1Tbs2djlpprl7wCLuiqMaUh5SJkkzI2gDs+FgLs= github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f h1:3BSP1Tbs2djlpprl7wCLuiqMaUh5SJkkzI2gDs+FgLs=
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f/go.mod h1:Pcatq5tYkCW2Q6yrR2VRHlbHpZ/R4/7qyL1TCF7vl14= github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f/go.mod h1:Pcatq5tYkCW2Q6yrR2VRHlbHpZ/R4/7qyL1TCF7vl14=
github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85 h1:UjoPNDAQ5JPCjlxoJd6K8ALZqSDDhk2ymieAZOVaDg0= github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85 h1:UjoPNDAQ5JPCjlxoJd6K8ALZqSDDhk2ymieAZOVaDg0=
@ -410,7 +395,6 @@ github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrU
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
@ -497,22 +481,6 @@ github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI
github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/ianlancetaylor/demangle v0.0.0-20230524184225-eabc099b10ab/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw= github.com/ianlancetaylor/demangle v0.0.0-20230524184225-eabc099b10ab/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8=
github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
github.com/jackc/pgconn v1.14.3 h1:bVoTr12EGANZz66nZPkMInAV/KHD2TxH9npjXXgiB3w=
github.com/jackc/pgconn v1.14.3/go.mod h1:RZbme4uasqzybK2RK5c65VsHxoyaml09lx3tXOcO/VM=
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/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
github.com/jackc/pgproto3/v2 v2.3.3 h1:1HLSx5H+tXR9pW3in3zaztoEwQYRC9SQaYUHjTSUOag=
github.com/jackc/pgproto3/v2 v2.3.3/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
github.com/jackc/pgtype v1.14.0 h1:y+xUdabmyMkJLyApYuPj38mW+aAIqCe5uuBB51rH3Vw=
github.com/jackc/pgtype v1.14.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
github.com/jackc/pgx/v4 v4.18.3 h1:dE2/TrEsGX3RBprb3qryqSV9Y60iZN1C6i8IrmW9/BA=
github.com/jackc/pgx/v4 v4.18.3/go.mod h1:Ey4Oru5tH5sB6tV7hDmfWFahwF15Eb7DNXlRKx2CkVw=
github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056 h1:iCHtR9CQyktQ5+f3dMVZfwD2KWJUgm7M0gdL9NGr8KA= github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056 h1:iCHtR9CQyktQ5+f3dMVZfwD2KWJUgm7M0gdL9NGr8KA=
github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk= github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
@ -533,10 +501,6 @@ github.com/jessevdk/go-flags v1.6.1 h1:Cvu5U8UGrLay1rZfv/zP7iLpSHGUZ/Ou68T0iX1bB
github.com/jessevdk/go-flags v1.6.1/go.mod h1:Mk8T1hIAWpOiJiHa9rJASDK2UGWji0EuPGBnNLMooyc= github.com/jessevdk/go-flags v1.6.1/go.mod h1:Mk8T1hIAWpOiJiHa9rJASDK2UGWji0EuPGBnNLMooyc=
github.com/jhillyerd/enmime v1.3.0 h1:LV5kzfLidiOr8qRGIpYYmUZCnhrPbcFAnAFUnWn99rw= github.com/jhillyerd/enmime v1.3.0 h1:LV5kzfLidiOr8qRGIpYYmUZCnhrPbcFAnAFUnWn99rw=
github.com/jhillyerd/enmime v1.3.0/go.mod h1:6c6jg5HdRRV2FtvVL69LjiX1M8oE0xDX9VEhV3oy4gs= github.com/jhillyerd/enmime v1.3.0/go.mod h1:6c6jg5HdRRV2FtvVL69LjiX1M8oE0xDX9VEhV3oy4gs=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
@ -550,11 +514,8 @@ github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4
github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
github.com/keybase/go-crypto v0.0.0-20200123153347-de78d2cb44f4 h1:cTxwSmnaqLoo+4tLukHoB9iqHOu3LmLhRmgUxZo6Vp4= github.com/keybase/go-crypto v0.0.0-20200123153347-de78d2cb44f4 h1:cTxwSmnaqLoo+4tLukHoB9iqHOu3LmLhRmgUxZo6Vp4=
github.com/keybase/go-crypto v0.0.0-20200123153347-de78d2cb44f4/go.mod h1:ghbZscTyKdM07+Fw3KSi0hcJm+AlEUWj8QLlPtijN/M= github.com/keybase/go-crypto v0.0.0-20200123153347-de78d2cb44f4/go.mod h1:ghbZscTyKdM07+Fw3KSi0hcJm+AlEUWj8QLlPtijN/M=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/compress v1.11.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.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.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
@ -629,7 +590,6 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
github.com/mrjones/oauth v0.0.0-20190623134757-126b35219450 h1:j2kD3MT1z4PXCiUllUJF9mWUESr9TWKS7iEKsQ/IipM= github.com/mrjones/oauth v0.0.0-20190623134757-126b35219450 h1:j2kD3MT1z4PXCiUllUJF9mWUESr9TWKS7iEKsQ/IipM=
github.com/mrjones/oauth v0.0.0-20190623134757-126b35219450/go.mod h1:skjdDftzkFALcuGzYSklqYd8gvat6F1gZJ4YPVbkZpM= github.com/mrjones/oauth v0.0.0-20190623134757-126b35219450/go.mod h1:skjdDftzkFALcuGzYSklqYd8gvat6F1gZJ4YPVbkZpM=
github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg= github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg=
@ -670,9 +630,6 @@ github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3I
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0= github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0=
github.com/paulmach/orb v0.11.1 h1:3koVegMC4X/WeiXYz9iswopaTwMem53NzTJuTF20JzU=
github.com/paulmach/orb v0.11.1/go.mod h1:5mULz1xQfs3bmQm63QEJA6lNGujuRafwA5S/EnuLaLU=
github.com/paulmach/protoscan v0.2.1/go.mod h1:SpcSwydNLrxUGSDvXvO0P7g7AuhJ7lcKfDlhJCDw2gY=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
@ -735,8 +692,6 @@ github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 h1:lZUw3E0/J3roVtGQ+SCrUrg3ON6Ng
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1/go.mod h1:uToXkOrWAZ6/Oc07xWQrPOhJotwFIyu2bBVN41fcDUY= github.com/santhosh-tekuri/jsonschema/v5 v5.3.1/go.mod h1:uToXkOrWAZ6/Oc07xWQrPOhJotwFIyu2bBVN41fcDUY=
github.com/sassoftware/go-rpmutils v0.4.0 h1:ojND82NYBxgwrV+mX1CWsd5QJvvEZTKddtCdFLPWhpg= github.com/sassoftware/go-rpmutils v0.4.0 h1:ojND82NYBxgwrV+mX1CWsd5QJvvEZTKddtCdFLPWhpg=
github.com/sassoftware/go-rpmutils v0.4.0/go.mod h1:3goNWi7PGAT3/dlql2lv3+MSN5jNYPjT5mVcQcIsYzI= github.com/sassoftware/go-rpmutils v0.4.0/go.mod h1:3goNWi7PGAT3/dlql2lv3+MSN5jNYPjT5mVcQcIsYzI=
github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys=
github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs=
github.com/serenize/snaker v0.0.0-20171204205717-a683aaf2d516/go.mod h1:Yow6lPLSAXx2ifx470yD/nUe22Dv5vBvxK/UK9UUTVs= github.com/serenize/snaker v0.0.0-20171204205717-a683aaf2d516/go.mod h1:Yow6lPLSAXx2ifx470yD/nUe22Dv5vBvxK/UK9UUTVs=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8= github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
@ -783,7 +738,6 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 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.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
@ -797,7 +751,6 @@ github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE= github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE=
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ= github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/tinylib/msgp v1.1.0/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE= github.com/tinylib/msgp v1.1.0/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
github.com/toqueteos/webbrowser v1.2.0 h1:tVP/gpK69Fx+qMJKsLE7TD8LuGWPnEV71wBN9rrstGQ= github.com/toqueteos/webbrowser v1.2.0 h1:tVP/gpK69Fx+qMJKsLE7TD8LuGWPnEV71wBN9rrstGQ=
github.com/toqueteos/webbrowser v1.2.0/go.mod h1:XWoZq4cyp9WeUeak7w7LXRUQf1F1ATJMir8RTqb4ayM= github.com/toqueteos/webbrowser v1.2.0/go.mod h1:XWoZq4cyp9WeUeak7w7LXRUQf1F1ATJMir8RTqb4ayM=
@ -823,9 +776,6 @@ github.com/xanzy/go-gitlab v0.112.0 h1:6Z0cqEooCvBMfBIHw+CgO4AKGRV8na/9781xOb0+D
github.com/xanzy/go-gitlab v0.112.0/go.mod h1:wKNKh3GkYDMOsGmnfuX+ITCmDuSDWFO0G+C4AygL9RY= github.com/xanzy/go-gitlab v0.112.0/go.mod h1:wKNKh3GkYDMOsGmnfuX+ITCmDuSDWFO0G+C4AygL9RY=
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g=
github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
@ -842,9 +792,7 @@ github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZ
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
github.com/yohcop/openid-go v1.0.1 h1:DPRd3iPO5F6O5zX2e62XpVAbPT6wV51cuucH0z9g3js= github.com/yohcop/openid-go v1.0.1 h1:DPRd3iPO5F6O5zX2e62XpVAbPT6wV51cuucH0z9g3js=
github.com/yohcop/openid-go v1.0.1/go.mod h1:b/AvD03P0KHj4yuihb+VtLD6bYYgsy0zqBzPCRjkCNs= github.com/yohcop/openid-go v1.0.1/go.mod h1:b/AvD03P0KHj4yuihb+VtLD6bYYgsy0zqBzPCRjkCNs=
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/goldmark v1.4.15/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark v1.4.15/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
@ -863,13 +811,8 @@ github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l
go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
go.etcd.io/bbolt v1.3.11 h1:yGEzV1wPz2yVCLsD8ZAiGHhHVlczyC9d1rP43/VCRJ0= go.etcd.io/bbolt v1.3.11 h1:yGEzV1wPz2yVCLsD8ZAiGHhHVlczyC9d1rP43/VCRJ0=
go.etcd.io/bbolt v1.3.11/go.mod h1:dksAq7YMXoljX0xu6VF5DMZGbhYYoLUalEiSySYAS4I= go.etcd.io/bbolt v1.3.11/go.mod h1:dksAq7YMXoljX0xu6VF5DMZGbhYYoLUalEiSySYAS4I=
go.mongodb.org/mongo-driver v1.11.4/go.mod h1:PTSz5yu21bkT/wXpkS7WR5f0ddqw5quethTUn9WM+2g=
go.mongodb.org/mongo-driver v1.17.1 h1:Wic5cJIwJgSpBhe3lx3+/RybR5PiYRMpVFgO7cOHyIM= go.mongodb.org/mongo-driver v1.17.1 h1:Wic5cJIwJgSpBhe3lx3+/RybR5PiYRMpVFgO7cOHyIM=
go.mongodb.org/mongo-driver v1.17.1/go.mod h1:wwWm/+BuOddhcq3n68LKRmgk2wXzmF6s0SFOa0GINL4= go.mongodb.org/mongo-driver v1.17.1/go.mod h1:wwWm/+BuOddhcq3n68LKRmgk2wXzmF6s0SFOa0GINL4=
go.opentelemetry.io/otel v1.31.0 h1:NsJcKPIW0D0H3NgzPDHmo0WW6SptzPdqg/L1zsIm2hY=
go.opentelemetry.io/otel v1.31.0/go.mod h1:O0C14Yl9FgkjqcCZAsE053C13OaddMYr/hz6clDkEJE=
go.opentelemetry.io/otel/trace v1.31.0 h1:ffjsj1aRouKewfr85U2aGagJ46+MvodynlQ1HYdmJys=
go.opentelemetry.io/otel/trace v1.31.0/go.mod h1:TXZkRk7SM2ZQLtR6eoAWQFIHPvzQ06FJAsO1tJg480A=
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
@ -941,7 +884,6 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 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-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
@ -1022,10 +964,8 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200325010219-a49f79bcc224/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= golang.org/x/tools v0.0.0-20200325010219-a49f79bcc224/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200928182047-19e03678916f/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= golang.org/x/tools v0.0.0-20200928182047-19e03678916f/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
@ -1046,8 +986,6 @@ google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQ
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

View File

@ -64,11 +64,9 @@ func TestGetUserHeatmapDataByUser(t *testing.T) {
for _, tc := range testCases { for _, tc := range testCases {
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: tc.userID}) user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: tc.userID})
doer := &user_model.User{ID: tc.doerID} var doer *user_model.User
_, err := unittest.LoadBeanIfExists(doer) if tc.doerID != 0 {
assert.NoError(t, err) doer = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: tc.doerID})
if tc.doerID == 0 {
doer = nil
} }
// get the action for comparison // get the action for comparison

View File

@ -44,7 +44,7 @@ func TestWebAuthnCredential_UpdateSignCount(t *testing.T) {
cred := unittest.AssertExistsAndLoadBean(t, &auth_model.WebAuthnCredential{ID: 1}) cred := unittest.AssertExistsAndLoadBean(t, &auth_model.WebAuthnCredential{ID: 1})
cred.SignCount = 1 cred.SignCount = 1
assert.NoError(t, cred.UpdateSignCount(db.DefaultContext)) assert.NoError(t, cred.UpdateSignCount(db.DefaultContext))
unittest.AssertExistsIf(t, true, &auth_model.WebAuthnCredential{ID: 1, SignCount: 1}) unittest.AssertExistsAndLoadBean(t, &auth_model.WebAuthnCredential{ID: 1, SignCount: 1})
} }
func TestWebAuthnCredential_UpdateLargeCounter(t *testing.T) { func TestWebAuthnCredential_UpdateLargeCounter(t *testing.T) {
@ -52,7 +52,7 @@ func TestWebAuthnCredential_UpdateLargeCounter(t *testing.T) {
cred := unittest.AssertExistsAndLoadBean(t, &auth_model.WebAuthnCredential{ID: 1}) cred := unittest.AssertExistsAndLoadBean(t, &auth_model.WebAuthnCredential{ID: 1})
cred.SignCount = 0xffffffff cred.SignCount = 0xffffffff
assert.NoError(t, cred.UpdateSignCount(db.DefaultContext)) assert.NoError(t, cred.UpdateSignCount(db.DefaultContext))
unittest.AssertExistsIf(t, true, &auth_model.WebAuthnCredential{ID: 1, SignCount: 0xffffffff}) unittest.AssertExistsAndLoadBean(t, &auth_model.WebAuthnCredential{ID: 1, SignCount: 0xffffffff})
} }
func TestCreateCredential(t *testing.T) { func TestCreateCredential(t *testing.T) {
@ -63,5 +63,5 @@ func TestCreateCredential(t *testing.T) {
assert.Equal(t, "WebAuthn Created Credential", res.Name) assert.Equal(t, "WebAuthn Created Credential", res.Name)
assert.Equal(t, []byte("Test"), res.CredentialID) assert.Equal(t, []byte("Test"), res.CredentialID)
unittest.AssertExistsIf(t, true, &auth_model.WebAuthnCredential{Name: "WebAuthn Created Credential", UserID: 1}) unittest.AssertExistsAndLoadBean(t, &auth_model.WebAuthnCredential{Name: "WebAuthn Created Credential", UserID: 1})
} }

View File

@ -64,7 +64,7 @@
name: job2 name: job2
attempt: 1 attempt: 1
job_id: job2 job_id: job2
needs: [job1] needs: '["job1"]'
task_id: 51 task_id: 51
status: 5 status: 5
started: 1683636528 started: 1683636528

View File

@ -2,23 +2,23 @@
id: 1 id: 1
repo_id: 4 repo_id: 4
name_pattern: /v.+/ name_pattern: /v.+/
allowlist_user_i_ds: [] allowlist_user_i_ds: "[]"
allowlist_team_i_ds: [] allowlist_team_i_ds: "[]"
created_unix: 1715596037 created_unix: 1715596037
updated_unix: 1715596037 updated_unix: 1715596037
- -
id: 2 id: 2
repo_id: 1 repo_id: 1
name_pattern: v-* name_pattern: v-*
allowlist_user_i_ds: [] allowlist_user_i_ds: "[]"
allowlist_team_i_ds: [] allowlist_team_i_ds: "[]"
created_unix: 1715596037 created_unix: 1715596037
updated_unix: 1715596037 updated_unix: 1715596037
- -
id: 3 id: 3
repo_id: 1 repo_id: 1
name_pattern: v-1.1 name_pattern: v-1.1
allowlist_user_i_ds: [2] allowlist_user_i_ds: "[2]"
allowlist_team_i_ds: [] allowlist_team_i_ds: "[]"
created_unix: 1715596037 created_unix: 1715596037
updated_unix: 1715596037 updated_unix: 1715596037

View File

@ -34,7 +34,7 @@ func UpdateIssueCols(ctx context.Context, issue *Issue, cols ...string) error {
return nil return nil
} }
func changeIssueStatus(ctx context.Context, issue *Issue, doer *user_model.User, isClosed, isMergePull bool) (*Comment, error) { func ChangeIssueStatus(ctx context.Context, issue *Issue, doer *user_model.User, isClosed, isMergePull bool) (*Comment, error) {
// Reload the issue // Reload the issue
currentIssue, err := GetIssueByID(ctx, issue.ID) currentIssue, err := GetIssueByID(ctx, issue.ID)
if err != nil { if err != nil {
@ -134,7 +134,7 @@ func CloseIssue(ctx context.Context, issue *Issue, doer *user_model.User) (*Comm
} }
defer committer.Close() defer committer.Close()
comment, err := changeIssueStatus(ctx, issue, doer, true, false) comment, err := ChangeIssueStatus(ctx, issue, doer, true, false)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -159,7 +159,7 @@ func ReopenIssue(ctx context.Context, issue *Issue, doer *user_model.User) (*Com
} }
defer committer.Close() defer committer.Close()
comment, err := changeIssueStatus(ctx, issue, doer, false, false) comment, err := ChangeIssueStatus(ctx, issue, doer, false, false)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -12,6 +12,7 @@ import (
"code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/models/unittest"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
) )
func Test_NewIssueUsers(t *testing.T) { func Test_NewIssueUsers(t *testing.T) {
@ -27,9 +28,8 @@ func Test_NewIssueUsers(t *testing.T) {
} }
// artificially insert new issue // artificially insert new issue
unittest.AssertSuccessfulInsert(t, newIssue) require.NoError(t, db.Insert(db.DefaultContext, newIssue))
require.NoError(t, issues_model.NewIssueUsers(db.DefaultContext, repo, newIssue))
assert.NoError(t, issues_model.NewIssueUsers(db.DefaultContext, repo, newIssue))
// issue_user table should now have entries for new issue // issue_user table should now have entries for new issue
unittest.AssertExistsAndLoadBean(t, &issues_model.IssueUser{IssueID: newIssue.ID, UID: newIssue.PosterID}) unittest.AssertExistsAndLoadBean(t, &issues_model.IssueUser{IssueID: newIssue.ID, UID: newIssue.PosterID})

View File

@ -387,7 +387,7 @@ func TestDeleteIssueLabel(t *testing.T) {
expectedNumIssues := label.NumIssues expectedNumIssues := label.NumIssues
expectedNumClosedIssues := label.NumClosedIssues expectedNumClosedIssues := label.NumClosedIssues
if unittest.BeanExists(t, &issues_model.IssueLabel{IssueID: issueID, LabelID: labelID}) { if unittest.GetBean(t, &issues_model.IssueLabel{IssueID: issueID, LabelID: labelID}) != nil {
expectedNumIssues-- expectedNumIssues--
if issue.IsClosed { if issue.IsClosed {
expectedNumClosedIssues-- expectedNumClosedIssues--

View File

@ -499,65 +499,6 @@ func (pr *PullRequest) IsFromFork() bool {
return pr.HeadRepoID != pr.BaseRepoID return pr.HeadRepoID != pr.BaseRepoID
} }
// SetMerged sets a pull request to merged and closes the corresponding issue
func (pr *PullRequest) SetMerged(ctx context.Context) (bool, error) {
if pr.HasMerged {
return false, fmt.Errorf("PullRequest[%d] already merged", pr.Index)
}
if pr.MergedCommitID == "" || pr.MergedUnix == 0 || pr.Merger == nil {
return false, fmt.Errorf("Unable to merge PullRequest[%d], some required fields are empty", pr.Index)
}
pr.HasMerged = true
sess := db.GetEngine(ctx)
if _, err := sess.Exec("UPDATE `issue` SET `repo_id` = `repo_id` WHERE `id` = ?", pr.IssueID); err != nil {
return false, err
}
if _, err := sess.Exec("UPDATE `pull_request` SET `issue_id` = `issue_id` WHERE `id` = ?", pr.ID); err != nil {
return false, err
}
pr.Issue = nil
if err := pr.LoadIssue(ctx); err != nil {
return false, err
}
if tmpPr, err := GetPullRequestByID(ctx, pr.ID); err != nil {
return false, err
} else if tmpPr.HasMerged {
if pr.Issue.IsClosed {
return false, nil
}
return false, fmt.Errorf("PullRequest[%d] already merged but it's associated issue [%d] is not closed", pr.Index, pr.IssueID)
} else if pr.Issue.IsClosed {
return false, fmt.Errorf("PullRequest[%d] already closed", pr.Index)
}
if err := pr.Issue.LoadRepo(ctx); err != nil {
return false, err
}
if err := pr.Issue.Repo.LoadOwner(ctx); err != nil {
return false, err
}
if _, err := changeIssueStatus(ctx, pr.Issue, pr.Merger, true, true); err != nil {
return false, fmt.Errorf("Issue.changeStatus: %w", err)
}
// reset the conflicted files as there cannot be any if we're merged
pr.ConflictedFiles = []string{}
// We need to save all of the data used to compute this merge as it may have already been changed by TestPatch. FIXME: need to set some state to prevent TestPatch from running whilst we are merging.
if _, err := sess.Where("id = ?", pr.ID).Cols("has_merged, status, merge_base, merged_commit_id, merger_id, merged_unix, conflicted_files").Update(pr); err != nil {
return false, fmt.Errorf("Failed to update pr[%d]: %w", pr.ID, err)
}
return true, nil
}
// NewPullRequest creates new pull request with labels for repository. // NewPullRequest creates new pull request with labels for repository.
func NewPullRequest(ctx context.Context, repo *repo_model.Repository, issue *Issue, labelIDs []int64, uuids []string, pr *PullRequest) (err error) { func NewPullRequest(ctx context.Context, repo *repo_model.Repository, issue *Issue, labelIDs []int64, uuids []string, pr *PullRequest) (err error) {
ctx, committer, err := db.TxContext(ctx) ctx, committer, err := db.TxContext(ctx)

View File

@ -76,7 +76,7 @@ func PrepareTestEnv(t *testing.T, skip int, syncModels ...any) (*xorm.Engine, fu
t.Errorf("error whilst initializing fixtures from %s: %v", fixturesDir, err) t.Errorf("error whilst initializing fixtures from %s: %v", fixturesDir, err)
return x, deferFn return x, deferFn
} }
if err := unittest.LoadFixtures(x); err != nil { if err := unittest.LoadFixtures(); err != nil {
t.Errorf("error whilst loading fixtures from %s: %v", fixturesDir, err) t.Errorf("error whilst loading fixtures from %s: %v", fixturesDir, err)
return x, deferFn return x, deferFn
} }

View File

@ -131,7 +131,7 @@ func TestAddOrgUser(t *testing.T) {
testSuccess := func(orgID, userID int64, isPublic bool) { testSuccess := func(orgID, userID int64, isPublic bool) {
org := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: orgID}) org := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: orgID})
expectedNumMembers := org.NumMembers expectedNumMembers := org.NumMembers
if !unittest.BeanExists(t, &organization.OrgUser{OrgID: orgID, UID: userID}) { if unittest.GetBean(t, &organization.OrgUser{OrgID: orgID, UID: userID}) == nil {
expectedNumMembers++ expectedNumMembers++
} }
assert.NoError(t, organization.AddOrgUser(db.DefaultContext, orgID, userID)) assert.NoError(t, organization.AddOrgUser(db.DefaultContext, orgID, userID))

View File

@ -1,97 +1,33 @@
// Copyright 2021 The Gitea Authors. All rights reserved. // Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
//nolint:forbidigo
package unittest package unittest
import ( import (
"fmt" "fmt"
"os"
"time"
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/auth/password/hash" "code.gitea.io/gitea/modules/auth/password/hash"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/util"
"github.com/go-testfixtures/testfixtures/v3"
"xorm.io/xorm" "xorm.io/xorm"
"xorm.io/xorm/schemas" "xorm.io/xorm/schemas"
) )
var fixturesLoader *testfixtures.Loader type FixturesLoader interface {
Load() error
}
var fixturesLoader FixturesLoader
// GetXORMEngine gets the XORM engine // GetXORMEngine gets the XORM engine
func GetXORMEngine(engine ...*xorm.Engine) (x *xorm.Engine) { func GetXORMEngine() (x *xorm.Engine) {
if len(engine) == 1 {
return engine[0]
}
return db.GetEngine(db.DefaultContext).(*xorm.Engine) return db.GetEngine(db.DefaultContext).(*xorm.Engine)
} }
// InitFixtures initialize test fixtures for a test database func loadFixtureResetSeqPgsql(e *xorm.Engine) error {
func InitFixtures(opts FixturesOptions, engine ...*xorm.Engine) (err error) { results, err := e.QueryString(`SELECT 'SELECT SETVAL(' ||
e := GetXORMEngine(engine...)
var fixtureOptionFiles func(*testfixtures.Loader) error
if opts.Dir != "" {
fixtureOptionFiles = testfixtures.Directory(opts.Dir)
} else {
fixtureOptionFiles = testfixtures.Files(opts.Files...)
}
dialect := "unknown"
switch e.Dialect().URI().DBType {
case schemas.POSTGRES:
dialect = "postgres"
case schemas.MYSQL:
dialect = "mysql"
case schemas.MSSQL:
dialect = "mssql"
case schemas.SQLITE:
dialect = "sqlite3"
default:
fmt.Println("Unsupported RDBMS for integration tests")
os.Exit(1)
}
loaderOptions := []func(loader *testfixtures.Loader) error{
testfixtures.Database(e.DB().DB),
testfixtures.Dialect(dialect),
testfixtures.DangerousSkipTestDatabaseCheck(),
fixtureOptionFiles,
}
if e.Dialect().URI().DBType == schemas.POSTGRES {
loaderOptions = append(loaderOptions, testfixtures.SkipResetSequences())
}
fixturesLoader, err = testfixtures.New(loaderOptions...)
if err != nil {
return err
}
// register the dummy hash algorithm function used in the test fixtures
_ = hash.Register("dummy", hash.NewDummyHasher)
setting.PasswordHashAlgo, _ = hash.SetDefaultPasswordHashAlgorithm("dummy")
return err
}
// LoadFixtures load fixtures for a test database
func LoadFixtures(engine ...*xorm.Engine) error {
e := GetXORMEngine(engine...)
var err error
// (doubt) database transaction conflicts could occur and result in ROLLBACK? just try for a few times.
for i := 0; i < 5; i++ {
if err = fixturesLoader.Load(); err == nil {
break
}
time.Sleep(200 * time.Millisecond)
}
if err != nil {
fmt.Printf("LoadFixtures failed after retries: %v\n", err)
}
// Now if we're running postgres we need to tell it to update the sequences
if e.Dialect().URI().DBType == schemas.POSTGRES {
results, err := e.QueryString(`SELECT 'SELECT SETVAL(' ||
quote_literal(quote_ident(PGT.schemaname) || '.' || quote_ident(S.relname)) || quote_literal(quote_ident(PGT.schemaname) || '.' || quote_ident(S.relname)) ||
', COALESCE(MAX(' ||quote_ident(C.attname)|| '), 1) ) FROM ' || ', COALESCE(MAX(' ||quote_ident(C.attname)|| '), 1) ) FROM ' ||
quote_ident(PGT.schemaname)|| '.'||quote_ident(T.relname)|| ';' quote_ident(PGT.schemaname)|| '.'||quote_ident(T.relname)|| ';'
@ -107,22 +43,42 @@ func LoadFixtures(engine ...*xorm.Engine) error {
AND D.refobjsubid = C.attnum AND D.refobjsubid = C.attnum
AND T.relname = PGT.tablename AND T.relname = PGT.tablename
ORDER BY S.relname;`) ORDER BY S.relname;`)
if err != nil { if err != nil {
fmt.Printf("Failed to generate sequence update: %v\n", err) return fmt.Errorf("failed to generate sequence update: %w", err)
return err }
} for _, r := range results {
for _, r := range results { for _, value := range r {
for _, value := range r { _, err = e.Exec(value)
_, err = e.Exec(value) if err != nil {
if err != nil { return fmt.Errorf("failed to update sequence: %s, error: %w", value, err)
fmt.Printf("Failed to update sequence: %s Error: %v\n", value, err)
return err
}
} }
} }
} }
return nil
}
// InitFixtures initialize test fixtures for a test database
func InitFixtures(opts FixturesOptions, engine ...*xorm.Engine) (err error) {
xormEngine := util.IfZero(util.OptionalArg(engine), GetXORMEngine())
fixturesLoader, err = NewFixturesLoader(xormEngine, opts)
// fixturesLoader = NewFixturesLoaderVendor(xormEngine, opts)
// register the dummy hash algorithm function used in the test fixtures
_ = hash.Register("dummy", hash.NewDummyHasher) _ = hash.Register("dummy", hash.NewDummyHasher)
setting.PasswordHashAlgo, _ = hash.SetDefaultPasswordHashAlgorithm("dummy") setting.PasswordHashAlgo, _ = hash.SetDefaultPasswordHashAlgorithm("dummy")
return err return err
} }
// LoadFixtures load fixtures for a test database
func LoadFixtures() error {
if err := fixturesLoader.Load(); err != nil {
return err
}
// Now if we're running postgres we need to tell it to update the sequences
if GetXORMEngine().Dialect().URI().DBType == schemas.POSTGRES {
if err := loadFixtureResetSeqPgsql(GetXORMEngine()); err != nil {
return err
}
}
return nil
}

View File

@ -0,0 +1,201 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package unittest
import (
"database/sql"
"encoding/hex"
"fmt"
"os"
"path/filepath"
"slices"
"strings"
"gopkg.in/yaml.v3"
"xorm.io/xorm"
"xorm.io/xorm/schemas"
)
type fixtureItem struct {
tableName string
tableNameQuoted string
sqlInserts []string
sqlInsertArgs [][]any
mssqlHasIdentityColumn bool
}
type fixturesLoaderInternal struct {
db *sql.DB
dbType schemas.DBType
files []string
fixtures map[string]*fixtureItem
quoteObject func(string) string
paramPlaceholder func(idx int) string
}
func (f *fixturesLoaderInternal) mssqlTableHasIdentityColumn(db *sql.DB, tableName string) (bool, error) {
row := db.QueryRow(`SELECT COUNT(*) FROM sys.identity_columns WHERE OBJECT_ID = OBJECT_ID(?)`, tableName)
var count int
if err := row.Scan(&count); err != nil {
return false, err
}
return count > 0, nil
}
func (f *fixturesLoaderInternal) preprocessFixtureRow(row []map[string]any) (err error) {
for _, m := range row {
for k, v := range m {
if s, ok := v.(string); ok {
if strings.HasPrefix(s, "0x") {
if m[k], err = hex.DecodeString(s[2:]); err != nil {
return err
}
}
}
}
}
return nil
}
func (f *fixturesLoaderInternal) prepareFixtureItem(file string) (_ *fixtureItem, err error) {
fixture := &fixtureItem{}
fixture.tableName, _, _ = strings.Cut(filepath.Base(file), ".")
fixture.tableNameQuoted = f.quoteObject(fixture.tableName)
if f.dbType == schemas.MSSQL {
fixture.mssqlHasIdentityColumn, err = f.mssqlTableHasIdentityColumn(f.db, fixture.tableName)
if err != nil {
return nil, err
}
}
data, err := os.ReadFile(file)
if err != nil {
return nil, fmt.Errorf("failed to read file %q: %w", file, err)
}
var rows []map[string]any
if err = yaml.Unmarshal(data, &rows); err != nil {
return nil, fmt.Errorf("failed to unmarshal yaml data from %q: %w", file, err)
}
if err = f.preprocessFixtureRow(rows); err != nil {
return nil, fmt.Errorf("failed to preprocess fixture rows from %q: %w", file, err)
}
var sqlBuf []byte
var sqlArguments []any
for _, row := range rows {
sqlBuf = append(sqlBuf, fmt.Sprintf("INSERT INTO %s (", fixture.tableNameQuoted)...)
for k, v := range row {
sqlBuf = append(sqlBuf, f.quoteObject(k)...)
sqlBuf = append(sqlBuf, ","...)
sqlArguments = append(sqlArguments, v)
}
sqlBuf = sqlBuf[:len(sqlBuf)-1]
sqlBuf = append(sqlBuf, ") VALUES ("...)
paramIdx := 1
for range row {
sqlBuf = append(sqlBuf, f.paramPlaceholder(paramIdx)...)
sqlBuf = append(sqlBuf, ',')
paramIdx++
}
sqlBuf[len(sqlBuf)-1] = ')'
fixture.sqlInserts = append(fixture.sqlInserts, string(sqlBuf))
fixture.sqlInsertArgs = append(fixture.sqlInsertArgs, slices.Clone(sqlArguments))
sqlBuf = sqlBuf[:0]
sqlArguments = sqlArguments[:0]
}
return fixture, nil
}
func (f *fixturesLoaderInternal) loadFixtures(tx *sql.Tx, file string) (err error) {
fixture := f.fixtures[file]
if fixture == nil {
if fixture, err = f.prepareFixtureItem(file); err != nil {
return err
}
f.fixtures[file] = fixture
}
_, err = tx.Exec(fmt.Sprintf("DELETE FROM %s", fixture.tableNameQuoted)) // sqlite3 doesn't support truncate
if err != nil {
return err
}
if fixture.mssqlHasIdentityColumn {
_, err = tx.Exec(fmt.Sprintf("SET IDENTITY_INSERT %s ON", fixture.tableNameQuoted))
if err != nil {
return err
}
defer func() { _, err = tx.Exec(fmt.Sprintf("SET IDENTITY_INSERT %s OFF", fixture.tableNameQuoted)) }()
}
for i := range fixture.sqlInserts {
_, err = tx.Exec(fixture.sqlInserts[i], fixture.sqlInsertArgs[i]...)
}
if err != nil {
return err
}
return nil
}
func (f *fixturesLoaderInternal) Load() error {
tx, err := f.db.Begin()
if err != nil {
return err
}
defer func() { _ = tx.Rollback() }()
for _, file := range f.files {
if err := f.loadFixtures(tx, file); err != nil {
return fmt.Errorf("failed to load fixtures from %s: %w", file, err)
}
}
return tx.Commit()
}
func FixturesFileFullPaths(dir string, files []string) ([]string, error) {
if files != nil && len(files) == 0 {
return nil, nil // load nothing
}
files = slices.Clone(files)
if len(files) == 0 {
entries, err := os.ReadDir(dir)
if err != nil {
return nil, err
}
for _, e := range entries {
files = append(files, e.Name())
}
}
for i, file := range files {
if !filepath.IsAbs(file) {
files[i] = filepath.Join(dir, file)
}
}
return files, nil
}
func NewFixturesLoader(x *xorm.Engine, opts FixturesOptions) (FixturesLoader, error) {
files, err := FixturesFileFullPaths(opts.Dir, opts.Files)
if err != nil {
return nil, fmt.Errorf("failed to get fixtures files: %w", err)
}
f := &fixturesLoaderInternal{db: x.DB().DB, dbType: x.Dialect().URI().DBType, files: files, fixtures: map[string]*fixtureItem{}}
switch f.dbType {
case schemas.SQLITE:
f.quoteObject = func(s string) string { return fmt.Sprintf(`"%s"`, s) }
f.paramPlaceholder = func(idx int) string { return "?" }
case schemas.POSTGRES:
f.quoteObject = func(s string) string { return fmt.Sprintf(`"%s"`, s) }
f.paramPlaceholder = func(idx int) string { return fmt.Sprintf(`$%d`, idx) }
case schemas.MYSQL:
f.quoteObject = func(s string) string { return fmt.Sprintf("`%s`", s) }
f.paramPlaceholder = func(idx int) string { return "?" }
case schemas.MSSQL:
f.quoteObject = func(s string) string { return fmt.Sprintf("[%s]", s) }
f.paramPlaceholder = func(idx int) string { return "?" }
}
return f, nil
}

View File

@ -0,0 +1,114 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package unittest_test
import (
"path/filepath"
"testing"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/test"
"github.com/stretchr/testify/require"
"xorm.io/xorm"
)
var NewFixturesLoaderVendor = func(e *xorm.Engine, opts unittest.FixturesOptions) (unittest.FixturesLoader, error) {
return nil, nil
}
/*
// the old code is kept here in case we are still interested in benchmarking the two implementations
func init() {
NewFixturesLoaderVendor = func(e *xorm.Engine, opts unittest.FixturesOptions) (unittest.FixturesLoader, error) {
return NewFixturesLoaderVendorGoTestfixtures(e, opts)
}
}
func NewFixturesLoaderVendorGoTestfixtures(e *xorm.Engine, opts unittest.FixturesOptions) (*testfixtures.Loader, error) {
files, err := unittest.FixturesFileFullPaths(opts.Dir, opts.Files)
if err != nil {
return nil, fmt.Errorf("failed to get fixtures files: %w", err)
}
var dialect string
switch e.Dialect().URI().DBType {
case schemas.POSTGRES:
dialect = "postgres"
case schemas.MYSQL:
dialect = "mysql"
case schemas.MSSQL:
dialect = "mssql"
case schemas.SQLITE:
dialect = "sqlite3"
default:
return nil, fmt.Errorf("unsupported RDBMS for integration tests: %q", e.Dialect().URI().DBType)
}
loaderOptions := []func(loader *testfixtures.Loader) error{
testfixtures.Database(e.DB().DB),
testfixtures.Dialect(dialect),
testfixtures.DangerousSkipTestDatabaseCheck(),
testfixtures.Files(files...),
}
if e.Dialect().URI().DBType == schemas.POSTGRES {
loaderOptions = append(loaderOptions, testfixtures.SkipResetSequences())
}
return testfixtures.New(loaderOptions...)
}
*/
func prepareTestFixturesLoaders(t testing.TB) unittest.FixturesOptions {
_ = user_model.User{}
opts := unittest.FixturesOptions{Dir: filepath.Join(test.SetupGiteaRoot(), "models", "fixtures"), Files: []string{
"user.yml",
}}
require.NoError(t, unittest.CreateTestEngine(opts))
return opts
}
func TestFixturesLoader(t *testing.T) {
opts := prepareTestFixturesLoaders(t)
loaderInternal, err := unittest.NewFixturesLoader(unittest.GetXORMEngine(), opts)
require.NoError(t, err)
loaderVendor, err := NewFixturesLoaderVendor(unittest.GetXORMEngine(), opts)
require.NoError(t, err)
t.Run("Internal", func(t *testing.T) {
require.NoError(t, loaderInternal.Load())
require.NoError(t, loaderInternal.Load())
})
t.Run("Vendor", func(t *testing.T) {
if loaderVendor == nil {
t.Skip()
}
require.NoError(t, loaderVendor.Load())
require.NoError(t, loaderVendor.Load())
})
}
func BenchmarkFixturesLoader(b *testing.B) {
opts := prepareTestFixturesLoaders(b)
require.NoError(b, unittest.CreateTestEngine(opts))
loaderInternal, err := unittest.NewFixturesLoader(unittest.GetXORMEngine(), opts)
require.NoError(b, err)
loaderVendor, err := NewFixturesLoaderVendor(unittest.GetXORMEngine(), opts)
require.NoError(b, err)
// BenchmarkFixturesLoader/Vendor
// BenchmarkFixturesLoader/Vendor-12 1696 719416 ns/op
// BenchmarkFixturesLoader/Internal
// BenchmarkFixturesLoader/Internal-12 1746 670457 ns/op
b.Run("Internal", func(b *testing.B) {
for i := 0; i < b.N; i++ {
require.NoError(b, loaderInternal.Load())
}
})
b.Run("Vendor", func(b *testing.B) {
if loaderVendor == nil {
b.Skip()
}
for i := 0; i < b.N; i++ {
require.NoError(b, loaderVendor.Load())
}
})
}

View File

@ -4,7 +4,7 @@
package unittest package unittest
import ( import (
"log" "fmt"
"reflect" "reflect"
) )
@ -14,7 +14,7 @@ func fieldByName(v reflect.Value, field string) reflect.Value {
} }
f := v.FieldByName(field) f := v.FieldByName(field)
if !f.IsValid() { if !f.IsValid() {
log.Panicf("can not read %s for %v", field, v) panic(fmt.Errorf("can not read %s for %v", field, v))
} }
return f return f
} }

View File

@ -28,16 +28,7 @@ import (
"xorm.io/xorm/names" "xorm.io/xorm/names"
) )
// giteaRoot a path to the gitea root var giteaRoot string
var (
giteaRoot string
fixturesDir string
)
// FixturesDir returns the fixture directory
func FixturesDir() string {
return fixturesDir
}
func fatalTestError(fmtStr string, args ...any) { func fatalTestError(fmtStr string, args ...any) {
_, _ = fmt.Fprintf(os.Stderr, fmtStr, args...) _, _ = fmt.Fprintf(os.Stderr, fmtStr, args...)
@ -79,39 +70,14 @@ type TestOptions struct {
// MainTest a reusable TestMain(..) function for unit tests that need to use a // MainTest a reusable TestMain(..) function for unit tests that need to use a
// test database. Creates the test database, and sets necessary settings. // test database. Creates the test database, and sets necessary settings.
func MainTest(m *testing.M, testOpts ...*TestOptions) { func MainTest(m *testing.M, testOptsArg ...*TestOptions) {
searchDir, _ := os.Getwd() testOpts := util.OptionalArg(testOptsArg, &TestOptions{})
for searchDir != "" { giteaRoot = test.SetupGiteaRoot()
if _, err := os.Stat(filepath.Join(searchDir, "go.mod")); err == nil {
break // The "go.mod" should be the one for Gitea repository
}
if dir := filepath.Dir(searchDir); dir == searchDir {
searchDir = "" // reaches the root of filesystem
} else {
searchDir = dir
}
}
if searchDir == "" {
panic("The tests should run in a Gitea repository, there should be a 'go.mod' in the root")
}
giteaRoot = searchDir
setting.CustomPath = filepath.Join(giteaRoot, "custom") setting.CustomPath = filepath.Join(giteaRoot, "custom")
InitSettings() InitSettings()
fixturesDir = filepath.Join(giteaRoot, "models", "fixtures") fixturesOpts := FixturesOptions{Dir: filepath.Join(giteaRoot, "models", "fixtures"), Files: testOpts.FixtureFiles}
var opts FixturesOptions if err := CreateTestEngine(fixturesOpts); err != nil {
if len(testOpts) == 0 || len(testOpts[0].FixtureFiles) == 0 {
opts.Dir = fixturesDir
} else {
for _, f := range testOpts[0].FixtureFiles {
if len(f) != 0 {
opts.Files = append(opts.Files, filepath.Join(fixturesDir, f))
}
}
}
if err := CreateTestEngine(opts); err != nil {
fatalTestError("Error creating test engine: %v\n", err) fatalTestError("Error creating test engine: %v\n", err)
} }
@ -172,16 +138,16 @@ func MainTest(m *testing.M, testOpts ...*TestOptions) {
fatalTestError("git.Init: %v\n", err) fatalTestError("git.Init: %v\n", err)
} }
if len(testOpts) > 0 && testOpts[0].SetUp != nil { if testOpts.SetUp != nil {
if err := testOpts[0].SetUp(); err != nil { if err := testOpts.SetUp(); err != nil {
fatalTestError("set up failed: %v\n", err) fatalTestError("set up failed: %v\n", err)
} }
} }
exitStatus := m.Run() exitStatus := m.Run()
if len(testOpts) > 0 && testOpts[0].TearDown != nil { if testOpts.TearDown != nil {
if err := testOpts[0].TearDown(); err != nil { if err := testOpts.TearDown(); err != nil {
fatalTestError("tear down failed: %v\n", err) fatalTestError("tear down failed: %v\n", err)
} }
} }

View File

@ -4,13 +4,17 @@
package unittest package unittest
import ( import (
"fmt"
"math" "math"
"os"
"strings"
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"xorm.io/builder" "xorm.io/builder"
"xorm.io/xorm"
) )
// Code in this file is mainly used by unittest.CheckConsistencyFor, which is not in the unit test for various reasons. // Code in this file is mainly used by unittest.CheckConsistencyFor, which is not in the unit test for various reasons.
@ -51,22 +55,23 @@ func whereOrderConditions(e db.Engine, conditions []any) db.Engine {
return e.OrderBy(orderBy) return e.OrderBy(orderBy)
} }
// LoadBeanIfExists loads beans from fixture database if exist func getBeanIfExists(bean any, conditions ...any) (bool, error) {
func LoadBeanIfExists(bean any, conditions ...any) (bool, error) {
e := db.GetEngine(db.DefaultContext) e := db.GetEngine(db.DefaultContext)
return whereOrderConditions(e, conditions).Get(bean) return whereOrderConditions(e, conditions).Get(bean)
} }
// BeanExists for testing, check if a bean exists func GetBean[T any](t require.TestingT, bean T, conditions ...any) (ret T) {
func BeanExists(t assert.TestingT, bean any, conditions ...any) bool { exists, err := getBeanIfExists(bean, conditions...)
exists, err := LoadBeanIfExists(bean, conditions...) require.NoError(t, err)
assert.NoError(t, err) if exists {
return exists return bean
}
return ret
} }
// AssertExistsAndLoadBean assert that a bean exists and load it from the test database // AssertExistsAndLoadBean assert that a bean exists and load it from the test database
func AssertExistsAndLoadBean[T any](t require.TestingT, bean T, conditions ...any) T { func AssertExistsAndLoadBean[T any](t require.TestingT, bean T, conditions ...any) T {
exists, err := LoadBeanIfExists(bean, conditions...) exists, err := getBeanIfExists(bean, conditions...)
require.NoError(t, err) require.NoError(t, err)
require.True(t, exists, require.True(t, exists,
"Expected to find %+v (of type %T, with conditions %+v), but did not", "Expected to find %+v (of type %T, with conditions %+v), but did not",
@ -112,25 +117,11 @@ func GetCount(t assert.TestingT, bean any, conditions ...any) int {
// AssertNotExistsBean assert that a bean does not exist in the test database // AssertNotExistsBean assert that a bean does not exist in the test database
func AssertNotExistsBean(t assert.TestingT, bean any, conditions ...any) { func AssertNotExistsBean(t assert.TestingT, bean any, conditions ...any) {
exists, err := LoadBeanIfExists(bean, conditions...) exists, err := getBeanIfExists(bean, conditions...)
assert.NoError(t, err) assert.NoError(t, err)
assert.False(t, exists) assert.False(t, exists)
} }
// AssertExistsIf asserts that a bean exists or does not exist, depending on
// what is expected.
func AssertExistsIf(t assert.TestingT, expected bool, bean any, conditions ...any) {
exists, err := LoadBeanIfExists(bean, conditions...)
assert.NoError(t, err)
assert.Equal(t, expected, exists)
}
// AssertSuccessfulInsert assert that beans is successfully inserted
func AssertSuccessfulInsert(t assert.TestingT, beans ...any) {
err := db.Insert(db.DefaultContext, beans...)
assert.NoError(t, err)
}
// AssertCount assert the count of a bean // AssertCount assert the count of a bean
func AssertCount(t assert.TestingT, bean, expected any) bool { func AssertCount(t assert.TestingT, bean, expected any) bool {
return assert.EqualValues(t, expected, GetCount(t, bean)) return assert.EqualValues(t, expected, GetCount(t, bean))
@ -155,3 +146,39 @@ func AssertCountByCond(t assert.TestingT, tableName string, cond builder.Cond, e
return assert.EqualValues(t, expected, GetCountByCond(t, tableName, cond), return assert.EqualValues(t, expected, GetCountByCond(t, tableName, cond),
"Failed consistency test, the counted bean (of table %s) was %+v", tableName, cond) "Failed consistency test, the counted bean (of table %s) was %+v", tableName, cond)
} }
// DumpQueryResult dumps the result of a query for debugging purpose
func DumpQueryResult(t require.TestingT, sqlOrBean any, sqlArgs ...any) {
x := db.GetEngine(db.DefaultContext).(*xorm.Engine)
goDB := x.DB().DB
sql, ok := sqlOrBean.(string)
if !ok {
sql = fmt.Sprintf("SELECT * FROM %s", db.TableName(sqlOrBean))
} else if !strings.Contains(sql, " ") {
sql = fmt.Sprintf("SELECT * FROM %s", sql)
}
rows, err := goDB.Query(sql, sqlArgs...)
require.NoError(t, err)
defer rows.Close()
columns, err := rows.Columns()
require.NoError(t, err)
_, _ = fmt.Fprintf(os.Stdout, "====== DumpQueryResult: %s ======\n", sql)
idx := 0
for rows.Next() {
row := make([]any, len(columns))
rowPointers := make([]any, len(columns))
for i := range row {
rowPointers[i] = &row[i]
}
require.NoError(t, rows.Scan(rowPointers...))
_, _ = fmt.Fprintf(os.Stdout, "- # row[%d]\n", idx)
for i, col := range columns {
_, _ = fmt.Fprintf(os.Stdout, " %s: %v\n", col, row[i])
}
idx++
}
if idx == 0 {
_, _ = fmt.Fprintf(os.Stdout, "(no result, columns: %s)\n", strings.Join(columns, ", "))
}
}

View File

@ -39,8 +39,6 @@ type SearchUserOptions struct {
IsTwoFactorEnabled optional.Option[bool] IsTwoFactorEnabled optional.Option[bool]
IsProhibitLogin optional.Option[bool] IsProhibitLogin optional.Option[bool]
IncludeReserved bool IncludeReserved bool
ExtraParamStrings map[string]string
} }
func (opts *SearchUserOptions) toSearchQueryBase(ctx context.Context) *xorm.Session { func (opts *SearchUserOptions) toSearchQueryBase(ctx context.Context) *xorm.Session {

View File

@ -62,3 +62,14 @@ func (repo *Repository) LsTree(ref string, filenames ...string) ([]string, error
return filelist, err return filelist, err
} }
// GetTreePathLatestCommitID returns the latest commit of a tree path
func (repo *Repository) GetTreePathLatestCommit(refName, treePath string) (*Commit, error) {
stdout, _, err := NewCommand(repo.Ctx, "rev-list", "-1").
AddDynamicArguments(refName).AddDashesAndList(treePath).
RunStdString(&RunOpts{Dir: repo.Path})
if err != nil {
return nil, err
}
return repo.GetCommit(strings.TrimSpace(stdout))
}

View File

@ -25,3 +25,18 @@ func TestSubTree_Issue29101(t *testing.T) {
assert.True(t, IsErrNotExist(err)) assert.True(t, IsErrNotExist(err))
} }
} }
func Test_GetTreePathLatestCommit(t *testing.T) {
repo, err := openRepositoryWithDefaultContext(filepath.Join(testReposDir, "repo6_blame"))
assert.NoError(t, err)
defer repo.Close()
commitID, err := repo.GetBranchCommitID("master")
assert.NoError(t, err)
assert.EqualValues(t, "544d8f7a3b15927cddf2299b4b562d6ebd71b6a7", commitID)
commit, err := repo.GetTreePathLatestCommit("master", "blame.txt")
assert.NoError(t, err)
assert.NotNil(t, commit)
assert.EqualValues(t, "45fb6cbc12f970b04eacd5cd4165edd11c8d7376", commit.ID.String())
}

View File

@ -5,8 +5,6 @@ package setting
import ( import (
"time" "time"
"code.gitea.io/gitea/modules/log"
) )
// CORSConfig defines CORS settings // CORSConfig defines CORS settings
@ -28,7 +26,4 @@ var CORSConfig = struct {
func loadCorsFrom(rootCfg ConfigProvider) { func loadCorsFrom(rootCfg ConfigProvider) {
mustMapSetting(rootCfg, "cors", &CORSConfig) mustMapSetting(rootCfg, "cors", &CORSConfig)
if CORSConfig.Enabled {
log.Info("CORS Service Enabled")
}
} }

View File

@ -97,7 +97,7 @@ func IndexerGlobFromString(globstr string) []*GlobMatcher {
expr = strings.TrimSpace(expr) expr = strings.TrimSpace(expr)
if expr != "" { if expr != "" {
if g, err := GlobMatcherCompile(expr, '.', '/'); err != nil { if g, err := GlobMatcherCompile(expr, '.', '/'); err != nil {
log.Info("Invalid glob expression '%s' (skipped): %v", expr, err) log.Warn("Invalid glob expression '%s' (skipped): %v", expr, err)
} else { } else {
extarr = append(extarr, g) extarr = append(extarr, g)
} }

View File

@ -255,8 +255,6 @@ func loadMailerFrom(rootCfg ConfigProvider) {
MailService.OverrideEnvelopeFrom = true MailService.OverrideEnvelopeFrom = true
MailService.EnvelopeFrom = parsed.Address MailService.EnvelopeFrom = parsed.Address
} }
log.Info("Mail Service Enabled")
} }
func loadRegisterMailFrom(rootCfg ConfigProvider) { func loadRegisterMailFrom(rootCfg ConfigProvider) {
@ -267,7 +265,6 @@ func loadRegisterMailFrom(rootCfg ConfigProvider) {
return return
} }
Service.RegisterEmailConfirm = true Service.RegisterEmailConfirm = true
log.Info("Register Mail Service Enabled")
} }
func loadNotifyMailFrom(rootCfg ConfigProvider) { func loadNotifyMailFrom(rootCfg ConfigProvider) {
@ -278,7 +275,6 @@ func loadNotifyMailFrom(rootCfg ConfigProvider) {
return return
} }
Service.EnableNotifyMail = true Service.EnableNotifyMail = true
log.Info("Notify Mail Service Enabled")
} }
func tryResolveAddr(addr string) []net.IPAddr { func tryResolveAddr(addr string) []net.IPAddr {

View File

@ -73,6 +73,4 @@ func loadSessionFrom(rootCfg ConfigProvider) {
SessionConfig.ProviderConfig = string(shadowConfig) SessionConfig.ProviderConfig = string(shadowConfig)
SessionConfig.OriginalProvider = SessionConfig.Provider SessionConfig.OriginalProvider = SessionConfig.Provider
SessionConfig.Provider = "VirtualSession" SessionConfig.Provider = "VirtualSession"
log.Info("Session Service Enabled")
} }

View File

@ -20,7 +20,6 @@ func loadTimeFrom(rootCfg ConfigProvider) {
if err != nil { if err != nil {
log.Fatal("Load time zone failed: %v", err) log.Fatal("Load time zone failed: %v", err)
} }
log.Info("Default UI Location is %v", zone)
} }
if DefaultUILocation == nil { if DefaultUILocation == nil {
DefaultUILocation = time.Local DefaultUILocation = time.Local

View File

@ -32,3 +32,37 @@ type ActionTaskResponse struct {
Entries []*ActionTask `json:"workflow_runs"` Entries []*ActionTask `json:"workflow_runs"`
TotalCount int64 `json:"total_count"` TotalCount int64 `json:"total_count"`
} }
// CreateActionWorkflowDispatch represents the payload for triggering a workflow dispatch event
// swagger:model
type CreateActionWorkflowDispatch struct {
// required: true
// example: refs/heads/main
Ref string `json:"ref" binding:"Required"`
// required: false
Inputs map[string]any `json:"inputs,omitempty"`
}
// ActionWorkflow represents a ActionWorkflow
type ActionWorkflow struct {
ID string `json:"id"`
NodeID string `json:"node_id"`
Name string `json:"name"`
Path string `json:"path"`
State string `json:"state"`
// swagger:strfmt date-time
CreatedAt time.Time `json:"created_at"`
// swagger:strfmt date-time
UpdatedAt time.Time `json:"updated_at"`
URL string `json:"url"`
HTMLURL string `json:"html_url"`
BadgeURL string `json:"badge_url"`
// swagger:strfmt date-time
DeletedAt time.Time `json:"deleted_at"`
}
// ActionWorkflowResponse returns a ActionWorkflow
type ActionWorkflowResponse struct {
Workflows []*ActionWorkflow `json:"workflows"`
TotalCount int64 `json:"total_count"`
}

View File

@ -13,9 +13,7 @@ import (
) )
func TestMain(m *testing.M) { func TestMain(m *testing.M) {
unittest.MainTest(m, &unittest.TestOptions{ unittest.MainTest(m, &unittest.TestOptions{FixtureFiles: []string{ /* load nothing */ }})
FixtureFiles: []string{""}, // load nothing
})
} }
type testItem1 struct { type testItem1 struct {
@ -36,8 +34,6 @@ func (*testItem2) Name() string {
} }
func TestAppStateDB(t *testing.T) { func TestAppStateDB(t *testing.T) {
assert.NoError(t, unittest.PrepareTestDatabase())
as := &DBStore{} as := &DBStore{}
item1 := new(testItem1) item1 := new(testItem1)

View File

@ -93,6 +93,7 @@ remove_all=Alle entfernen
remove_label_str=Element "%s " entfernen remove_label_str=Element "%s " entfernen
edit=Bearbeiten edit=Bearbeiten
view=Anzeigen view=Anzeigen
test=Test
enabled=Aktiviert enabled=Aktiviert
disabled=Deaktiviert disabled=Deaktiviert
@ -103,6 +104,7 @@ copy_url=URL kopieren
copy_hash=Hash kopieren copy_hash=Hash kopieren
copy_content=Inhalt kopieren copy_content=Inhalt kopieren
copy_branch=Branchnamen kopieren copy_branch=Branchnamen kopieren
copy_path=Pfad kopieren
copy_success=Kopiert! copy_success=Kopiert!
copy_error=Kopieren fehlgeschlagen copy_error=Kopieren fehlgeschlagen
copy_type_unsupported=Dieser Dateityp kann nicht kopiert werden copy_type_unsupported=Dieser Dateityp kann nicht kopiert werden
@ -143,6 +145,7 @@ confirm_delete_selected=Alle ausgewählten Elemente löschen?
name=Name name=Name
value=Wert value=Wert
readme=Readme
filter=Filter filter=Filter
filter.clear=Filter leeren filter.clear=Filter leeren
@ -158,12 +161,15 @@ filter.public=Öffentlich
filter.private=Privat filter.private=Privat
no_results_found=Es wurden keine Ergebnisse gefunden. no_results_found=Es wurden keine Ergebnisse gefunden.
internal_error_skipped=Ein interner Fehler ist aufgetreten, wurde aber übersprungen: %s
[search] [search]
search=Suche ... search=Suche ...
type_tooltip=Suchmodus type_tooltip=Suchmodus
fuzzy=Ähnlich fuzzy=Ähnlich
fuzzy_tooltip=Ergebnisse einbeziehen, die dem Suchbegriff ähnlich sind fuzzy_tooltip=Ergebnisse einbeziehen, die dem Suchbegriff ähnlich sind
exact=Exakt
exact_tooltip=Nur Suchbegriffe einbeziehen, die dem exakten Suchbegriff entsprechen
repo_kind=Repositories durchsuchen ... repo_kind=Repositories durchsuchen ...
user_kind=Benutzer durchsuchen ... user_kind=Benutzer durchsuchen ...
org_kind=Organisationen durchsuchen ... org_kind=Organisationen durchsuchen ...
@ -174,9 +180,13 @@ code_search_by_git_grep=Aktuelle Code-Suchergebnisse werden von "git grep" berei
package_kind=Pakete durchsuchen ... package_kind=Pakete durchsuchen ...
project_kind=Projekte durchsuchen ... project_kind=Projekte durchsuchen ...
branch_kind=Branches durchsuchen ... branch_kind=Branches durchsuchen ...
tag_kind=Tags durchsuchen...
tag_tooltip=Suche nach passenden Tags. Benutze '%', um jede Sequenz von Zahlen zu treffen.
commit_kind=Commits durchsuchen ... commit_kind=Commits durchsuchen ...
runner_kind=Runner durchsuchen ... runner_kind=Runner durchsuchen ...
no_results=Es wurden keine passenden Ergebnisse gefunden. no_results=Es wurden keine passenden Ergebnisse gefunden.
issue_kind=Issues durchsuchen ...
pull_kind=Pull-Requests durchsuchen...
keyword_search_unavailable=Zurzeit ist die Stichwort-Suche nicht verfügbar. Bitte wende dich an den Website-Administrator. keyword_search_unavailable=Zurzeit ist die Stichwort-Suche nicht verfügbar. Bitte wende dich an den Website-Administrator.
[aria] [aria]
@ -201,7 +211,10 @@ buttons.link.tooltip=Link hinzufügen
buttons.list.unordered.tooltip=Liste hinzufügen buttons.list.unordered.tooltip=Liste hinzufügen
buttons.list.ordered.tooltip=Nummerierte Liste hinzufügen buttons.list.ordered.tooltip=Nummerierte Liste hinzufügen
buttons.list.task.tooltip=Aufgabenliste hinzufügen buttons.list.task.tooltip=Aufgabenliste hinzufügen
buttons.table.add.tooltip=Tabelle hinzufügen
buttons.table.add.insert=Hinzufügen buttons.table.add.insert=Hinzufügen
buttons.table.rows=Zeilen
buttons.table.cols=Spalten
buttons.mention.tooltip=Benutzer oder Team erwähnen buttons.mention.tooltip=Benutzer oder Team erwähnen
buttons.ref.tooltip=Issue oder Pull-Request referenzieren buttons.ref.tooltip=Issue oder Pull-Request referenzieren
buttons.switch_to_legacy.tooltip=Legacy-Editor verwenden buttons.switch_to_legacy.tooltip=Legacy-Editor verwenden
@ -214,16 +227,20 @@ string.desc=ZA
[error] [error]
occurred=Ein Fehler ist aufgetreten occurred=Ein Fehler ist aufgetreten
report_message=Wenn du glaubst, dass dies ein Fehler von Gitea ist, suche bitte auf <a href="%s" target="_blank">GitHub</a> nach diesem Fehler und erstelle gegebenenfalls einen neuen Bugreport.
not_found=Das Ziel konnte nicht gefunden werden. not_found=Das Ziel konnte nicht gefunden werden.
network_error=Netzwerkfehler network_error=Netzwerkfehler
[startpage] [startpage]
app_desc=Ein einfacher, selbst gehosteter Git-Service app_desc=Ein einfacher, selbst gehosteter Git-Service
install=Einfach zu installieren install=Einfach zu installieren
install_desc=Starte einfach <a target="_blank" rel="noopener noreferrer" href="%[1]s">die Anwendung</a> für deine Plattform oder nutze <a target="_blank" rel="noopener noreferrer" href="%[2]s">Docker</a>. Es existieren auch <a target="_blank" rel="noopener noreferrer" href="%[3]s">paketierte Versionen</a>.
platform=Plattformübergreifend platform=Plattformübergreifend
platform_desc=Gitea läuft überall, wo <a target="_blank" rel="noopener noreferrer" href="%s">Go</a> kompiliert: Windows, macOS, Linux, ARM, etc. Wähle das System, das dir am meisten gefällt!
lightweight=Leichtgewicht lightweight=Leichtgewicht
lightweight_desc=Gitea hat minimale Systemanforderungen und kann selbst auf einem günstigen und stromsparenden Raspberry Pi betrieben werden! lightweight_desc=Gitea hat minimale Systemanforderungen und kann selbst auf einem günstigen und stromsparenden Raspberry Pi betrieben werden!
license=Quelloffen license=Quelloffen
license_desc=Hol dir den Code unter <a target="_blank" rel="noopener noreferrer" href="%[1]s">%[2]s</a>! Leiste deinen <a target="_blank" rel="noopener noreferrer" href="%[3]s">Beitrag</a> bei der Verbesserung dieses Projekts. Trau dich!
[install] [install]
install=Installation install=Installation
@ -337,6 +354,7 @@ enable_update_checker=Aktualisierungsprüfung aktivieren
enable_update_checker_helper=Stellt regelmäßig eine Verbindung zu gitea.io her, um nach neuen Versionen zu prüfen. enable_update_checker_helper=Stellt regelmäßig eine Verbindung zu gitea.io her, um nach neuen Versionen zu prüfen.
env_config_keys=Umgebungskonfiguration env_config_keys=Umgebungskonfiguration
env_config_keys_prompt=Die folgenden Umgebungsvariablen werden auch auf Ihre Konfigurationsdatei angewendet: env_config_keys_prompt=Die folgenden Umgebungsvariablen werden auch auf Ihre Konfigurationsdatei angewendet:
config_write_file_prompt=Diese Konfigurationsoptionen werden in %s geschrieben
[home] [home]
nav_menu=Navigationsmenü nav_menu=Navigationsmenü
@ -377,6 +395,8 @@ relevant_repositories=Es werden nur relevante Repositories angezeigt, <a href="%
[auth] [auth]
create_new_account=Konto anlegen create_new_account=Konto anlegen
already_have_account=Du hast bereits ein Konto?
sign_in_now=Jetzt anmelden!
disable_register_prompt=Die Registrierung ist deaktiviert. Bitte wende dich an den Administrator. disable_register_prompt=Die Registrierung ist deaktiviert. Bitte wende dich an den Administrator.
disable_register_mail=E-Mail-Bestätigung bei der Registrierung ist deaktiviert. disable_register_mail=E-Mail-Bestätigung bei der Registrierung ist deaktiviert.
manual_activation_only=Kontaktiere den Website-Administrator, um die Aktivierung abzuschließen. manual_activation_only=Kontaktiere den Website-Administrator, um die Aktivierung abzuschließen.
@ -384,6 +404,8 @@ remember_me=Dieses Gerät speichern
remember_me.compromised=Das Login-Token ist nicht mehr gültig, was auf ein kompromittiertes Konto hindeuten kann. Bitte überprüfe dein Konto auf ungewöhnliche Aktivitäten. remember_me.compromised=Das Login-Token ist nicht mehr gültig, was auf ein kompromittiertes Konto hindeuten kann. Bitte überprüfe dein Konto auf ungewöhnliche Aktivitäten.
forgot_password_title=Passwort vergessen forgot_password_title=Passwort vergessen
forgot_password=Passwort vergessen? forgot_password=Passwort vergessen?
need_account=Noch kein Konto?
sign_up_now=Jetzt registrieren.
sign_up_successful=Konto wurde erfolgreich erstellt. Willkommen! sign_up_successful=Konto wurde erfolgreich erstellt. Willkommen!
confirmation_mail_sent_prompt_ex=Eine neue Bestätigungs-E-Mail wurde an <b>%s</b>gesendet. Bitte überprüfe deinen Posteingang innerhalb der nächsten %s, um den Registrierungsprozess abzuschließen. Wenn deine Registrierungs-E-Mail-Adresse falsch ist, kannst du dich erneut anmelden und diese ändern. confirmation_mail_sent_prompt_ex=Eine neue Bestätigungs-E-Mail wurde an <b>%s</b>gesendet. Bitte überprüfe deinen Posteingang innerhalb der nächsten %s, um den Registrierungsprozess abzuschließen. Wenn deine Registrierungs-E-Mail-Adresse falsch ist, kannst du dich erneut anmelden und diese ändern.
must_change_password=Aktualisiere dein Passwort must_change_password=Aktualisiere dein Passwort
@ -424,6 +446,7 @@ oauth_signin_submit=Konto verbinden
oauth.signin.error=Beim Verarbeiten der Autorisierungsanfrage ist ein Fehler aufgetreten. Wenn dieser Fehler weiterhin besteht, wende dich bitte an deinen Administrator. oauth.signin.error=Beim Verarbeiten der Autorisierungsanfrage ist ein Fehler aufgetreten. Wenn dieser Fehler weiterhin besteht, wende dich bitte an deinen Administrator.
oauth.signin.error.access_denied=Die Autorisierungsanfrage wurde abgelehnt. oauth.signin.error.access_denied=Die Autorisierungsanfrage wurde abgelehnt.
oauth.signin.error.temporarily_unavailable=Autorisierung fehlgeschlagen, da der Authentifizierungsserver vorübergehend nicht verfügbar ist. Bitte versuch es später erneut. oauth.signin.error.temporarily_unavailable=Autorisierung fehlgeschlagen, da der Authentifizierungsserver vorübergehend nicht verfügbar ist. Bitte versuch es später erneut.
oauth_callback_unable_auto_reg=Automatische Registrierung ist aktiviert, aber der OAuth2-Provider %[1]s hat fehlende Felder zurückgegeben: %[2]s, kann den Account nicht automatisch erstellen. Bitte erstelle oder verbinde einen Account oder kontaktieren den Administrator.
openid_connect_submit=Verbinden openid_connect_submit=Verbinden
openid_connect_title=Mit bestehendem Konto verbinden openid_connect_title=Mit bestehendem Konto verbinden
openid_connect_desc=Die gewählte OpenID-URI ist unbekannt. Ordne sie hier einem neuen Account zu. openid_connect_desc=Die gewählte OpenID-URI ist unbekannt. Ordne sie hier einem neuen Account zu.
@ -437,12 +460,16 @@ authorize_application=Anwendung autorisieren
authorize_redirect_notice=Du wirst zu %s weitergeleitet, wenn du diese Anwendung autorisierst. 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_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 Repositories und Organisationen.
authorize_application_with_scopes=Mit Bereichen: %s
authorize_title=`"%s" den Zugriff auf deinen Account gestatten?` authorize_title=`"%s" den Zugriff auf deinen Account gestatten?`
authorization_failed=Autorisierung fehlgeschlagen authorization_failed=Autorisierung fehlgeschlagen
authorization_failed_desc=Die Autorisierung ist fehlgeschlagen, da wir eine ungültige Anfrage erkannt haben. Bitte kontaktiere den Betreuer der App, die du zu autorisieren versucht hast. authorization_failed_desc=Die Autorisierung ist fehlgeschlagen, da wir eine ungültige Anfrage erkannt haben. Bitte kontaktiere den Betreuer der App, die du zu autorisieren versucht hast.
sspi_auth_failed=SSPI-Authentifizierung fehlgeschlagen sspi_auth_failed=SSPI-Authentifizierung fehlgeschlagen
password_pwned=Das von dir gewählte Passwort befindet sich auf einer <a target="_blank" rel="noopener noreferrer" href="%s">Liste gestohlener Passwörter</a>, die öffentlich verfügbar sind. Bitte versuche es erneut mit einem anderen Passwort und ziehe in Erwägung, auch anderswo deine Passwörter zu ändern.
password_pwned_err=Anfrage an HaveIBeenPwned konnte nicht abgeschlossen werden password_pwned_err=Anfrage an HaveIBeenPwned konnte nicht abgeschlossen werden
last_admin=Du kannst den letzten Admin nicht entfernen. Es muss mindestens einen Administrator geben. last_admin=Du kannst den letzten Admin nicht entfernen. Es muss mindestens einen Administrator geben.
signin_passkey=Mit einem Passkey anmelden
back_to_sign_in=Zurück zum Anmelden
[mail] [mail]
view_it_on=Auf %s ansehen view_it_on=Auf %s ansehen
@ -459,6 +486,7 @@ activate_email=Bestätige deine E-Mail-Adresse
activate_email.title=%s, bitte verifiziere deine E-Mail-Adresse activate_email.title=%s, bitte verifiziere deine E-Mail-Adresse
activate_email.text=Bitte klicke innerhalb von <b>%s</b> auf folgenden Link, um dein Konto zu aktivieren: activate_email.text=Bitte klicke innerhalb von <b>%s</b> auf folgenden Link, um dein Konto zu aktivieren:
register_notify=Willkommen bei %s
register_notify.title=%[1]s, willkommen bei %[2]s register_notify.title=%[1]s, willkommen bei %[2]s
register_notify.text_1=dies ist deine Bestätigungs-E-Mail für %s! register_notify.text_1=dies ist deine Bestätigungs-E-Mail für %s!
register_notify.text_2=Du kannst dich jetzt mit dem Benutzernamen "%s" anmelden. register_notify.text_2=Du kannst dich jetzt mit dem Benutzernamen "%s" anmelden.
@ -560,6 +588,8 @@ lang_select_error=Wähle eine Sprache aus der Liste aus.
username_been_taken=Der Benutzername ist bereits vergeben. username_been_taken=Der Benutzername ist bereits vergeben.
username_change_not_local_user=Nicht-lokale Benutzer dürfen ihren Nutzernamen nicht ändern. username_change_not_local_user=Nicht-lokale Benutzer dürfen ihren Nutzernamen nicht ändern.
change_username_disabled=Ändern des Benutzernamens ist deaktiviert.
change_full_name_disabled=Ändern des vollständigen Namens ist deaktiviert.
username_has_not_been_changed=Benutzername wurde nicht geändert username_has_not_been_changed=Benutzername wurde nicht geändert
repo_name_been_taken=Der Repository-Name wird schon verwendet. 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 Repositories können nicht veröffentlicht werden.
@ -609,6 +639,7 @@ org_still_own_repo=Diese Organisation besitzt noch ein oder mehrere Repositories
org_still_own_packages=Diese Organisation besitzt noch ein oder mehrere Pakete, lösche diese zuerst. org_still_own_packages=Diese Organisation besitzt noch ein oder mehrere Pakete, lösche diese zuerst.
target_branch_not_exist=Der Ziel-Branch existiert nicht. target_branch_not_exist=Der Ziel-Branch existiert nicht.
target_ref_not_exist=Zielreferenz existiert nicht %s
admin_cannot_delete_self=Du kannst dich nicht selbst löschen, wenn du ein Administrator bist. Bitte entferne zuerst deine Administratorrechte. admin_cannot_delete_self=Du kannst dich nicht selbst löschen, wenn du ein Administrator bist. Bitte entferne zuerst deine Administratorrechte.
@ -685,6 +716,8 @@ public_profile=Öffentliches Profil
biography_placeholder=Erzähle uns ein wenig über Dich selbst! (Du kannst Markdown verwenden) biography_placeholder=Erzähle uns ein wenig über Dich selbst! (Du kannst Markdown verwenden)
location_placeholder=Teile Deinen ungefähren Standort mit anderen location_placeholder=Teile Deinen ungefähren Standort mit anderen
profile_desc=Lege fest, wie dein Profil anderen Benutzern angezeigt wird. Deine primäre E-Mail-Adresse wird für Benachrichtigungen, Passwort-Wiederherstellung und webbasierte Git-Operationen verwendet. profile_desc=Lege fest, wie dein Profil anderen Benutzern angezeigt wird. Deine primäre E-Mail-Adresse wird für Benachrichtigungen, Passwort-Wiederherstellung und webbasierte Git-Operationen verwendet.
password_username_disabled=Du bist nicht berechtigt, den Benutzernamen zu ändern. Bitte kontaktiere Deinen Seitenadministrator für weitere Details.
password_full_name_disabled=Du bist nicht berechtigt, den vollständigen Namen zu ändern. Bitte kontaktiere Deinen Seitenadministrator für weitere Details.
full_name=Vollständiger Name full_name=Vollständiger Name
website=Webseite website=Webseite
location=Standort location=Standort
@ -702,6 +735,7 @@ cancel=Abbrechen
language=Sprache language=Sprache
ui=Theme ui=Theme
hidden_comment_types=Ausgeblendeter Kommentartypen hidden_comment_types=Ausgeblendeter Kommentartypen
hidden_comment_types_description=Die hier markierten Kommentartypen werden nicht innerhalb der Issue-Seiten angezeigt. Beispielsweise entfernt das Markieren von "Label" alle "{user} hat {label} hinzugefügt/entfernt"-Kommentare.
hidden_comment_types.ref_tooltip=Kommentare, in denen dieses Issue von einem anderen Issue/Commit referenziert wurde hidden_comment_types.ref_tooltip=Kommentare, in denen dieses Issue von einem anderen Issue/Commit referenziert wurde
hidden_comment_types.issue_ref_tooltip=Kommentare, bei denen der Benutzer den Branch/Tag des Issues ändert hidden_comment_types.issue_ref_tooltip=Kommentare, bei denen der Benutzer den Branch/Tag des Issues ändert
comment_type_group_reference=Verweis auf Mitglieder comment_type_group_reference=Verweis auf Mitglieder
@ -733,6 +767,7 @@ uploaded_avatar_not_a_image=Die hochgeladene Datei ist kein Bild.
uploaded_avatar_is_too_big=Die hochgeladene Dateigröße (%d KiB) überschreitet die maximale Größe (%d KiB). uploaded_avatar_is_too_big=Die hochgeladene Dateigröße (%d KiB) überschreitet die maximale Größe (%d KiB).
update_avatar_success=Dein Profilbild wurde geändert. update_avatar_success=Dein Profilbild wurde geändert.
update_user_avatar_success=Der Avatar des Benutzers wurde aktualisiert. update_user_avatar_success=Der Avatar des Benutzers wurde aktualisiert.
cropper_prompt=Sie können das Bild vor dem Speichern bearbeiten. Das bearbeitete Bild wird als PNG-Datei gespeichert.
change_password=Passwort aktualisieren change_password=Passwort aktualisieren
old_password=Aktuelles Passwort old_password=Aktuelles Passwort
@ -748,6 +783,8 @@ manage_themes=Standard-Theme auswählen
manage_openid=OpenID-Adressen verwalten manage_openid=OpenID-Adressen verwalten
email_desc=Deine primäre E-Mail-Adresse wird für Benachrichtigungen, Passwort-Wiederherstellung und, sofern sie nicht versteckt ist, web-basierte Git-Operationen verwendet. email_desc=Deine primäre E-Mail-Adresse wird für Benachrichtigungen, Passwort-Wiederherstellung und, sofern sie nicht versteckt ist, web-basierte Git-Operationen verwendet.
theme_desc=Dies wird dein Standard-Theme auf der Seite sein. theme_desc=Dies wird dein Standard-Theme auf der Seite sein.
theme_colorblindness_help=Hilfe zum Theme für Farbenblinde
theme_colorblindness_prompt=Gitea erhält aktuell einfache Unterstützung für Farbenblinde durch einige Themes, die nur wenige Farben definiert haben. Die Arbeit ist noch im Gange. Weitere Verbesserungen können durch die Definition von mehr Farben in den CSS-Theme-Dateien vorgenommen werden.
primary=Primär primary=Primär
activated=Aktiviert activated=Aktiviert
requires_activation=Erfordert Aktivierung requires_activation=Erfordert Aktivierung
@ -872,8 +909,9 @@ repo_and_org_access=Repository- und Organisationszugriff
permissions_public_only=Nur öffentlich permissions_public_only=Nur öffentlich
permissions_access_all=Alle (öffentlich, privat und begrenzt) permissions_access_all=Alle (öffentlich, privat und begrenzt)
select_permissions=Berechtigungen auswählen select_permissions=Berechtigungen auswählen
permission_not_set=Nicht festgelegt
permission_no_access=Kein Zugriff permission_no_access=Kein Zugriff
permission_read=Gelesen permission_read=Lesen
permission_write=Lesen und Schreiben permission_write=Lesen und Schreiben
access_token_desc=Ausgewählte Token-Berechtigungen beschränken die Authentifizierung auf die entsprechenden <a %s>API</a>-Routen. Lies die <a %s>Dokumentation</a> für mehr Informationen. access_token_desc=Ausgewählte Token-Berechtigungen beschränken die Authentifizierung auf die entsprechenden <a %s>API</a>-Routen. Lies die <a %s>Dokumentation</a> für mehr Informationen.
at_least_one_permission=Du musst mindestens eine Berechtigung auswählen, um ein Token zu erstellen at_least_one_permission=Du musst mindestens eine Berechtigung auswählen, um ein Token zu erstellen
@ -891,6 +929,7 @@ create_oauth2_application_success=Du hast erfolgreich eine neue OAuth2-Anwendung
update_oauth2_application_success=Du hast die OAuth2-Anwendung erfolgreich aktualisiert. update_oauth2_application_success=Du hast die OAuth2-Anwendung erfolgreich aktualisiert.
oauth2_application_name=Name der Anwendung oauth2_application_name=Name der Anwendung
oauth2_confidential_client=Vertraulicher Client. Für Anwendungen aktivieren, die das Geheimnis sicher speichern, z. B. Webanwendungen. Wähle diese Option nicht für native Anwendungen für PCs und Mobilgeräte. oauth2_confidential_client=Vertraulicher Client. Für Anwendungen aktivieren, die das Geheimnis sicher speichern, z. B. Webanwendungen. Wähle diese Option nicht für native Anwendungen für PCs und Mobilgeräte.
oauth2_skip_secondary_authorization=Autorisierung für öffentliche Clients nach einmaliger Gewährung des Zugriffs überspringen. <strong>Dies kann ein Sicherheitsrisiko darstellen.</strong>
oauth2_redirect_uris=URIs für die Weiterleitung. Bitte verwende eine neue Zeile für jede URI. oauth2_redirect_uris=URIs für die Weiterleitung. Bitte verwende eine neue Zeile für jede URI.
save_application=Speichern save_application=Speichern
oauth2_client_id=Client-ID oauth2_client_id=Client-ID
@ -901,6 +940,7 @@ oauth2_client_secret_hint=Das Secret wird nach dem Verlassen oder Aktualisieren
oauth2_application_edit=Bearbeiten oauth2_application_edit=Bearbeiten
oauth2_application_create_description=OAuth2 Anwendungen geben deiner Drittanwendung Zugriff auf Benutzeraccounts dieser Gitea-Instanz. oauth2_application_create_description=OAuth2 Anwendungen geben deiner Drittanwendung Zugriff auf Benutzeraccounts dieser Gitea-Instanz.
oauth2_application_remove_description=Das Entfernen einer OAuth2-Anwendung hat zur Folge, dass diese nicht mehr auf autorisierte Benutzeraccounts auf dieser Instanz zugreifen kann. Möchtest Du fortfahren? oauth2_application_remove_description=Das Entfernen einer OAuth2-Anwendung hat zur Folge, dass diese nicht mehr auf autorisierte Benutzeraccounts auf dieser Instanz zugreifen kann. Möchtest Du fortfahren?
oauth2_application_locked=Wenn es in der Konfiguration aktiviert ist, registriert Gitea einige OAuth2-Anwendungen beim Starten vor. Um unerwartetes Verhalten zu verhindern, können diese weder bearbeitet noch entfernt werden. Weitere Informationen findest Du in der OAuth2-Dokumentation.
authorized_oauth2_applications=Autorisierte OAuth2-Anwendungen authorized_oauth2_applications=Autorisierte OAuth2-Anwendungen
authorized_oauth2_applications_description=Den folgenden Drittanbieter-Apps hast Du Zugriff auf Deinen persönlichen Gitea-Account gewährt. Bitte widerrufe die Autorisierung für Apps, die Du nicht mehr nutzt. authorized_oauth2_applications_description=Den folgenden Drittanbieter-Apps hast Du Zugriff auf Deinen persönlichen Gitea-Account gewährt. Bitte widerrufe die Autorisierung für Apps, die Du nicht mehr nutzt.
@ -909,20 +949,26 @@ revoke_oauth2_grant=Autorisierung widerrufen
revoke_oauth2_grant_description=Wenn du die Autorisierung widerrufst, kann die Anwendung nicht mehr auf deine Daten zugreifen. Bist du dir sicher? revoke_oauth2_grant_description=Wenn du die Autorisierung widerrufst, kann die Anwendung nicht mehr auf deine Daten zugreifen. Bist du dir sicher?
revoke_oauth2_grant_success=Zugriff erfolgreich widerrufen. revoke_oauth2_grant_success=Zugriff erfolgreich widerrufen.
twofa_desc=Um dein Konto vor Passwortdiebstahl zu schützen, kannst du ein Smartphone oder ein anderes Gerät verwenden, um zeitbasierte Einmalpasswörter ("TOTP") zu erhalten.
twofa_recovery_tip=Wenn du dein Gerät verlierst, kannst du einen einmalig verwendbaren Wiederherstellungsschlüssel nutzen, um den Zugriff auf dein Konto wiederherzustellen. twofa_recovery_tip=Wenn du dein Gerät verlierst, kannst du einen einmalig verwendbaren Wiederherstellungsschlüssel nutzen, um den Zugriff auf dein Konto wiederherzustellen.
twofa_is_enrolled=Für dein Konto ist die Zwei-Faktor-Authentifizierung <strong>eingeschaltet</strong>. twofa_is_enrolled=Für dein Konto ist die Zwei-Faktor-Authentifizierung <strong>eingeschaltet</strong>.
twofa_not_enrolled=Für dein Konto ist die Zwei-Faktor-Authentifizierung momentan nicht eingeschaltet. twofa_not_enrolled=Für dein Konto ist die Zwei-Faktor-Authentifizierung momentan nicht eingeschaltet.
twofa_disable=Zwei-Faktor-Authentifizierung deaktivieren twofa_disable=Zwei-Faktor-Authentifizierung deaktivieren
twofa_scratch_token_regenerate=Einweg-Wiederherstellungsschlüssel neu generieren
twofa_scratch_token_regenerated=Dein Einweg-Wiederherstellungsschlüssel ist jetzt %s. Speichere ihn an einem sicheren Ort, er wird nie wieder angezeigt.
twofa_enroll=Zwei-Faktor-Authentifizierung aktivieren twofa_enroll=Zwei-Faktor-Authentifizierung aktivieren
twofa_disable_note=Du kannst die Zwei-Faktor-Authentifizierung auch wieder deaktivieren. twofa_disable_note=Du kannst die Zwei-Faktor-Authentifizierung auch wieder deaktivieren.
twofa_disable_desc=Wenn du die Zwei-Faktor-Authentifizierung deaktivierst, wird die Sicherheit deines Kontos verringert. Fortfahren? twofa_disable_desc=Wenn du die Zwei-Faktor-Authentifizierung deaktivierst, wird die Sicherheit deines Kontos verringert. Fortfahren?
regenerate_scratch_token_desc=Wenn du deinen Wiederherstellungsschlüssel verlegt oder bereits benutzt hast, kannst du ihn hier zurücksetzen.
twofa_disabled=Zwei-Faktor-Authentifizierung wurde deaktiviert. twofa_disabled=Zwei-Faktor-Authentifizierung wurde deaktiviert.
scan_this_image=Scanne diese Grafik mit deiner Authentifizierungs-App: scan_this_image=Scanne diese Grafik mit deiner Authentifizierungs-App:
or_enter_secret=Oder gib das Secret ein: %s or_enter_secret=Oder gib das Secret ein: %s
then_enter_passcode=Und gebe dann die angezeigte PIN der Anwendung ein: then_enter_passcode=Und gebe dann die angezeigte PIN der Anwendung ein:
passcode_invalid=Die PIN ist falsch. Probiere es erneut. passcode_invalid=Die PIN ist falsch. Probiere es erneut.
twofa_enrolled=Die Zwei-Faktor-Authentifizierung wurde für dein Konto aktiviert. Bewahre deinen Einweg-Wiederherstellungsschlüssel (%s) an einem sicheren Ort auf, da er nicht wieder angezeigt werden wird.
twofa_failed_get_secret=Fehler beim Abrufen des Secrets. twofa_failed_get_secret=Fehler beim Abrufen des Secrets.
webauthn_desc=Sicherheitsschlüssel sind Geräte, die kryptografische Schlüssel beeinhalten. Diese können für die Zwei-Faktor-Authentifizierung verwendet werden. Der Sicherheitsschlüssel muss den Standard "<a rel="noreferrer" target="_blank" href="%s">WebAuthn</a>" unterstützen.
webauthn_register_key=Sicherheitsschlüssel hinzufügen webauthn_register_key=Sicherheitsschlüssel hinzufügen
webauthn_nickname=Nickname webauthn_nickname=Nickname
webauthn_delete_key=Sicherheitsschlüssel entfernen webauthn_delete_key=Sicherheitsschlüssel entfernen
@ -988,6 +1034,8 @@ fork_to_different_account=Fork in ein anderes Konto erstellen
fork_visibility_helper=Die Sichtbarkeit eines geforkten Repositories kann nicht geändert werden. fork_visibility_helper=Die Sichtbarkeit eines geforkten Repositories kann nicht geändert werden.
fork_branch=Branch, der zum Fork geklont werden soll fork_branch=Branch, der zum Fork geklont werden soll
all_branches=Alle Branches all_branches=Alle Branches
view_all_branches=Alle Branches anzeigen
view_all_tags=Alle Tags anzeigen
fork_no_valid_owners=Dieses Repository kann nicht geforkt werden, da keine gültigen Besitzer vorhanden sind. fork_no_valid_owners=Dieses Repository kann nicht geforkt werden, da keine gültigen Besitzer vorhanden sind.
fork.blocked_user=Das Repository kann nicht geforkt werden, da du vom Repository-Eigentümer blockiert wurdest. fork.blocked_user=Das Repository kann nicht geforkt werden, da du vom Repository-Eigentümer blockiert wurdest.
use_template=Dieses Template verwenden use_template=Dieses Template verwenden
@ -999,6 +1047,8 @@ generate_repo=Repository erstellen
generate_from=Erstelle aus generate_from=Erstelle aus
repo_desc=Beschreibung repo_desc=Beschreibung
repo_desc_helper=Gib eine kurze Beschreibung an (optional) repo_desc_helper=Gib eine kurze Beschreibung an (optional)
repo_no_desc=Keine Beschreibung vorhanden
repo_lang=Sprachen
repo_gitignore_helper=Wähle eine .gitignore-Vorlage aus. repo_gitignore_helper=Wähle eine .gitignore-Vorlage aus.
repo_gitignore_helper_desc=Wähle aus einer Liste an Vorlagen für bekannte Sprachen, welche Dateien ignoriert werden sollen. Typische Artefakte, die durch die Build Tools der gewählten Sprache generiert werden, sind standardmäßig Bestandteil der .gitignore. repo_gitignore_helper_desc=Wähle aus einer Liste an Vorlagen für bekannte Sprachen, welche Dateien ignoriert werden sollen. Typische Artefakte, die durch die Build Tools der gewählten Sprache generiert werden, sind standardmäßig Bestandteil der .gitignore.
issue_labels=Issue Label issue_labels=Issue Label
@ -1006,6 +1056,7 @@ issue_labels_helper=Wähle ein Issue-Label-Set.
license=Lizenz license=Lizenz
license_helper=Wähle eine Lizenz aus. license_helper=Wähle eine Lizenz aus.
license_helper_desc=Eine Lizenz regelt, was Andere mit deinem Code (nicht) tun können. Unsicher, welches für dein Projekt die Richtige ist? Siehe <a target="_blank" rel="noopener noreferrer" href="%s">eine Lizenz wählen</a>. license_helper_desc=Eine Lizenz regelt, was Andere mit deinem Code (nicht) tun können. Unsicher, welches für dein Projekt die Richtige ist? Siehe <a target="_blank" rel="noopener noreferrer" href="%s">eine Lizenz wählen</a>.
multiple_licenses=Mehrere Lizenzen
object_format=Objektformat object_format=Objektformat
object_format_helper=Objektformat des Repositories. Es kann später nicht geändert werden. SHA1 ist am meisten kompatibel. object_format_helper=Objektformat des Repositories. Es kann später nicht geändert werden. SHA1 ist am meisten kompatibel.
readme=README readme=README
@ -1059,13 +1110,16 @@ delete_preexisting_success=Nicht übernommene Dateien in %s gelöscht
blame_prior=Blame vor dieser Änderung anzeigen blame_prior=Blame vor dieser Änderung anzeigen
blame.ignore_revs=Revisionen in <a href="%s">.git-blame-ignore-revs</a> werden ignoriert. Klicke <a href="%s">hier, um das zu umgehen</a> und die normale Blame-Ansicht zu sehen. blame.ignore_revs=Revisionen in <a href="%s">.git-blame-ignore-revs</a> werden ignoriert. Klicke <a href="%s">hier, um das zu umgehen</a> und die normale Blame-Ansicht zu sehen.
blame.ignore_revs.failed=Fehler beim Ignorieren der Revisionen in <a href="%s">.git-blame-ignore-revs</a>. blame.ignore_revs.failed=Fehler beim Ignorieren der Revisionen in <a href="%s">.git-blame-ignore-revs</a>.
user_search_tooltip=Zeigt maximal 30 Benutzer
tree_path_not_found_commit=Pfad %[1]s existiert nicht in Commit%[2]s tree_path_not_found_commit=Pfad %[1]s existiert nicht in Commit%[2]s
tree_path_not_found_branch=Pfad %[1]s existiert nicht in Branch %[2]s tree_path_not_found_branch=Pfad %[1]s existiert nicht in Branch %[2]s
tree_path_not_found_tag=Pfad %[1]s existiert nicht in Tag %[2]s tree_path_not_found_tag=Pfad %[1]s existiert nicht in Tag %[2]s
transfer.accept=Übertragung Akzeptieren transfer.accept=Übertragung Akzeptieren
transfer.accept_desc=`Übertragung nach "%s"`
transfer.reject=Übertragung Ablehnen transfer.reject=Übertragung Ablehnen
transfer.reject_desc=Übertragung nach "%s " abbrechen
transfer.no_permission_to_accept=Du hast keine Berechtigung, diesen Transfer anzunehmen. transfer.no_permission_to_accept=Du hast keine Berechtigung, diesen Transfer anzunehmen.
transfer.no_permission_to_reject=Du hast keine Berechtigung, diesen Transfer abzulehnen. transfer.no_permission_to_reject=Du hast keine Berechtigung, diesen Transfer abzulehnen.
@ -1140,6 +1194,11 @@ 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.onedev.description=Daten von code.onedev.io oder anderen OneDev Instanzen migrieren.
migrate.codebase.description=Daten von codebasehq.com migrieren. migrate.codebase.description=Daten von codebasehq.com migrieren.
migrate.gitbucket.description=Daten von GitBucket Instanzen migrieren. migrate.gitbucket.description=Daten von GitBucket Instanzen migrieren.
migrate.codecommit.description=Daten von AWS CodeCommit migrieren.
migrate.codecommit.aws_access_key_id=AWS Access Key ID
migrate.codecommit.aws_secret_access_key=AWS Secret Access Key
migrate.codecommit.https_git_credentials_username=HTTPS-Git-Nutzername
migrate.codecommit.https_git_credentials_password=HTTPS-Git-Passwort
migrate.migrating_git=Git-Daten werden migriert migrate.migrating_git=Git-Daten werden migriert
migrate.migrating_topics=Themen werden migriert migrate.migrating_topics=Themen werden migriert
migrate.migrating_milestones=Meilensteine werden migriert migrate.migrating_milestones=Meilensteine werden migriert
@ -1200,6 +1259,7 @@ releases=Releases
tag=Tag tag=Tag
released_this=hat released released_this=hat released
tagged_this=hat getaggt tagged_this=hat getaggt
file.title=%s in %s
file_raw=Originalformat file_raw=Originalformat
file_history=Verlauf file_history=Verlauf
file_view_source=Quelltext anzeigen file_view_source=Quelltext anzeigen
@ -1207,12 +1267,16 @@ file_view_rendered=Ansicht rendern
file_view_raw=Originalformat anzeigen file_view_raw=Originalformat anzeigen
file_permalink=Permalink file_permalink=Permalink
file_too_large=Die Datei ist zu groß zum Anzeigen. file_too_large=Die Datei ist zu groß zum Anzeigen.
file_is_empty=Die Datei ist leer.
code_preview_line_from_to=Zeilen %[1]d bis %[2]d in %[3]s
code_preview_line_in=Zeile %[1]d in %[2]s
invisible_runes_header=`Diese Datei enthält unsichtbare Unicode-Zeichen` invisible_runes_header=`Diese Datei enthält unsichtbare Unicode-Zeichen`
invisible_runes_description=`Diese Datei enthält unsichtbare Unicode-Zeichen, die für Menschen nicht unterscheidbar sind, aber von einem Computer unterschiedlich verarbeitet werden können. Wenn du glaubst, dass das absichtlich so ist, kannst du diese Warnung ignorieren. Benutze den „Escape“-Button, um versteckte Zeichen anzuzeigen.` invisible_runes_description=`Diese Datei enthält unsichtbare Unicode-Zeichen, die für Menschen nicht unterscheidbar sind, aber von einem Computer unterschiedlich verarbeitet werden können. Wenn du glaubst, dass das absichtlich so ist, kannst du diese Warnung ignorieren. Benutze den „Escape“-Button, um versteckte Zeichen anzuzeigen.`
ambiguous_runes_header=`Diese Datei enthält mehrdeutige Unicode-Zeichen` ambiguous_runes_header=`Diese Datei enthält mehrdeutige Unicode-Zeichen`
ambiguous_runes_description=`Diese Datei enthält Unicode-Zeichen, die mit anderen Zeichen verwechselt werden können. Wenn du glaubst, dass das absichtlich so ist, kannst du diese Warnung ignorieren. Benutze den „Escape“-Button, um versteckte Zeichen anzuzeigen.` ambiguous_runes_description=`Diese Datei enthält Unicode-Zeichen, die mit anderen Zeichen verwechselt werden können. Wenn du glaubst, dass das absichtlich so ist, kannst du diese Warnung ignorieren. Benutze den „Escape“-Button, um versteckte Zeichen anzuzeigen.`
invisible_runes_line=`Diese Zeile enthält unsichtbare Unicode-Zeichen` invisible_runes_line=`Diese Zeile enthält unsichtbare Unicode-Zeichen`
ambiguous_runes_line=`Diese Zeile enthält mehrdeutige Unicode-Zeichen` ambiguous_runes_line=`Diese Zeile enthält mehrdeutige Unicode-Zeichen`
ambiguous_character=`%[1]c [U+%04[1]X] kann mit %[2]c [U+%04[2]X] verwechselt werden`
escape_control_characters=Escapen escape_control_characters=Escapen
unescape_control_characters=Unescapen unescape_control_characters=Unescapen
@ -1260,6 +1324,7 @@ editor.or=oder
editor.cancel_lower=Abbrechen editor.cancel_lower=Abbrechen
editor.commit_signed_changes=Committe signierte Änderungen editor.commit_signed_changes=Committe signierte Änderungen
editor.commit_changes=Änderungen committen editor.commit_changes=Änderungen committen
editor.add_tmpl='{filename}' hinzufügen
editor.add=%s hinzugefügt editor.add=%s hinzugefügt
editor.update=%s aktualisiert editor.update=%s aktualisiert
editor.delete=%s gelöscht editor.delete=%s gelöscht
@ -1343,6 +1408,7 @@ commitstatus.success=Erfolg
ext_issues=Zugriff auf Externe Issues ext_issues=Zugriff auf Externe Issues
ext_issues.desc=Link zu externem Issuetracker. ext_issues.desc=Link zu externem Issuetracker.
projects.desc=Verwalte Issues und Pull-Requests in Projekten.
projects.description=Beschreibung (optional) projects.description=Beschreibung (optional)
projects.description_placeholder=Beschreibung projects.description_placeholder=Beschreibung
projects.create=Projekt erstellen projects.create=Projekt erstellen
@ -1402,7 +1468,9 @@ issues.new.clear_milestone=Meilenstein entfernen
issues.new.assignees=Zuständig issues.new.assignees=Zuständig
issues.new.clear_assignees=Zuständige entfernen issues.new.clear_assignees=Zuständige entfernen
issues.new.no_assignees=Niemand zuständig issues.new.no_assignees=Niemand zuständig
issues.new.no_reviewers=Keine Reviewer
issues.new.blocked_user=Das Issue kann nicht erstellt werden, da du vom Repository-Eigentümer blockiert wurdest. issues.new.blocked_user=Das Issue kann nicht erstellt werden, da du vom Repository-Eigentümer blockiert wurdest.
issues.edit.already_changed=Änderungen zum Issue konnten nicht gespeichert werden. Es scheint, dass der Inhalt bereits von einem anderen Benutzer geändert wurde. Bitte aktualisiere die Seite und bearbeite diese erneut, um zu verhindern, dass die Änderungen des anderen Benutzers überschrieben werden
issues.edit.blocked_user=Der Inhalt kann nicht bearbeitet werden, da du vom Repository-Eigentümer blockiert wurdest. issues.edit.blocked_user=Der Inhalt kann nicht bearbeitet werden, da du vom Repository-Eigentümer blockiert wurdest.
issues.choose.get_started=Los geht's issues.choose.get_started=Los geht's
issues.choose.open_external_link=Öffnen issues.choose.open_external_link=Öffnen
@ -1429,6 +1497,7 @@ issues.remove_labels=hat die Labels %s %s entfernt
issues.add_remove_labels=hat %s hinzugefügt, und %s %s entfernt issues.add_remove_labels=hat %s hinzugefügt, und %s %s entfernt
issues.add_milestone_at=`hat diesen Issue %[2]s zum <b>%[1]s</b> Meilenstein hinzugefügt` issues.add_milestone_at=`hat diesen Issue %[2]s zum <b>%[1]s</b> Meilenstein hinzugefügt`
issues.add_project_at=`hat dieses zum <b>%s</b> projekt %s hinzugefügt` issues.add_project_at=`hat dieses zum <b>%s</b> projekt %s hinzugefügt`
issues.move_to_column_of_project=`hat dies zu %s in %s %s verschoben`
issues.change_milestone_at=`hat den Meilenstein %[3]s von <b>%[1]s</b> zu <b>%[2]s</b> geändert` issues.change_milestone_at=`hat den Meilenstein %[3]s von <b>%[1]s</b> zu <b>%[2]s</b> geändert`
issues.change_project_at=`hat das Projekt %[3]s von <b>%[1]s</b> zu <b>%[2]s</b> geändert` issues.change_project_at=`hat das Projekt %[3]s von <b>%[1]s</b> zu <b>%[2]s</b> geändert`
issues.remove_milestone_at=`hat dieses Issue %[2]s vom <b>%[1]s</b> Meilenstein entfernt` issues.remove_milestone_at=`hat dieses Issue %[2]s vom <b>%[1]s</b> Meilenstein entfernt`
@ -1460,6 +1529,8 @@ issues.filter_assignee=Zuständig
issues.filter_assginee_no_select=Alle Zuständigen issues.filter_assginee_no_select=Alle Zuständigen
issues.filter_assginee_no_assignee=Niemand zuständig issues.filter_assginee_no_assignee=Niemand zuständig
issues.filter_poster=Autor issues.filter_poster=Autor
issues.filter_user_placeholder=Benutzer suchen
issues.filter_user_no_select=Alle Benutzer
issues.filter_type=Typ issues.filter_type=Typ
issues.filter_type.all_issues=Alle Issues issues.filter_type.all_issues=Alle Issues
issues.filter_type.assigned_to_you=Dir zugewiesen issues.filter_type.assigned_to_you=Dir zugewiesen
@ -1513,7 +1584,9 @@ issues.no_content=Keine Beschreibung angegeben.
issues.close=Issue schließen issues.close=Issue schließen
issues.comment_pull_merged_at=hat Commit %[1]s in %[2]s %[3]s gemerged issues.comment_pull_merged_at=hat Commit %[1]s in %[2]s %[3]s gemerged
issues.comment_manually_pull_merged_at=hat Commit %[1]s in %[2]s %[3]s manuell gemerged issues.comment_manually_pull_merged_at=hat Commit %[1]s in %[2]s %[3]s manuell gemerged
issues.close_comment_issue=Kommentieren und schließen
issues.reopen_issue=Wieder öffnen issues.reopen_issue=Wieder öffnen
issues.reopen_comment_issue=Kommentieren und wieder öffnen
issues.create_comment=Kommentieren issues.create_comment=Kommentieren
issues.comment.blocked_user=Der Kommentar kann nicht erstellt oder bearbeitet werden, da du vom Repository-Eigentümer blockiert wurdest. issues.comment.blocked_user=Der Kommentar kann nicht erstellt oder bearbeitet werden, da du vom Repository-Eigentümer blockiert wurdest.
issues.closed_at=`hat diesen Issue <a id="%[1]s" href="#%[1]s">%[2]s</a> geschlossen` issues.closed_at=`hat diesen Issue <a id="%[1]s" href="#%[1]s">%[2]s</a> geschlossen`
@ -1602,12 +1675,25 @@ issues.delete.title=Dieses Issue löschen?
issues.delete.text=Möchtest du dieses Issue wirklich löschen? (Dadurch wird der Inhalt dauerhaft gelöscht. Denke daran, es stattdessen zu schließen, wenn du es archivieren willst) issues.delete.text=Möchtest du dieses Issue wirklich löschen? (Dadurch wird der Inhalt dauerhaft gelöscht. Denke daran, es stattdessen zu schließen, wenn du es archivieren willst)
issues.tracker=Zeiterfassung issues.tracker=Zeiterfassung
issues.timetracker_timer_start=Timer starten
issues.timetracker_timer_stop=Timer stoppen
issues.timetracker_timer_discard=Timer verwerfen
issues.timetracker_timer_manually_add=Zeit hinzufügen
issues.time_estimate_set=Geschätzte Zeit festlegen
issues.time_estimate_display=Schätzung: %s
issues.change_time_estimate_at=Zeitschätzung geändert zu <b>%s</b> %s
issues.remove_time_estimate_at=Zeitschätzung %s entfernt
issues.time_estimate_invalid=Format der Zeitschätzung ist ungültig
issues.start_tracking_history=hat die Zeiterfassung %s gestartet
issues.tracker_auto_close=Der Timer wird automatisch gestoppt, wenn dieser Issue geschlossen wird issues.tracker_auto_close=Der Timer wird automatisch gestoppt, wenn dieser Issue geschlossen wird
issues.tracking_already_started=`Du hast die Zeiterfassung bereits in <a href="%s">diesem Issue</a> gestartet!` issues.tracking_already_started=`Du hast die Zeiterfassung bereits in <a href="%s">diesem Issue</a> gestartet!`
issues.stop_tracking_history=hat für <b>%s</b> gearbeitet %s
issues.cancel_tracking_history=`hat die Zeiterfassung %s abgebrochen` issues.cancel_tracking_history=`hat die Zeiterfassung %s abgebrochen`
issues.del_time=Diese Zeiterfassung löschen issues.del_time=Diese Zeiterfassung löschen
issues.add_time_history=hat <b>%s</b> gearbeitete Zeit hinzugefügt %s
issues.del_time_history=`hat %s gearbeitete Zeit gelöscht` issues.del_time_history=`hat %s gearbeitete Zeit gelöscht`
issues.add_time_manually=Zeit manuell hinzufügen
issues.add_time_hours=Stunden issues.add_time_hours=Stunden
issues.add_time_minutes=Minuten issues.add_time_minutes=Minuten
issues.add_time_sum_to_small=Es wurde keine Zeit eingegeben. issues.add_time_sum_to_small=Es wurde keine Zeit eingegeben.
@ -1667,6 +1753,7 @@ issues.dependency.add_error_dep_not_same_repo=Beide Issues müssen sich im selbe
issues.review.self.approval=Du kannst nicht dein eigenen Pull-Request genehmigen. issues.review.self.approval=Du kannst nicht dein eigenen Pull-Request genehmigen.
issues.review.self.rejection=Du kannst keine Änderungen an deinem eigenen Pull-Request anfragen. issues.review.self.rejection=Du kannst keine Änderungen an deinem eigenen Pull-Request anfragen.
issues.review.approve=hat die Änderungen %s genehmigt issues.review.approve=hat die Änderungen %s genehmigt
issues.review.comment=hat %s überprüft
issues.review.dismissed=verwarf %ss Review %s issues.review.dismissed=verwarf %ss Review %s
issues.review.dismissed_label=Verworfen issues.review.dismissed_label=Verworfen
issues.review.left_comment=hat einen Kommentar hinterlassen issues.review.left_comment=hat einen Kommentar hinterlassen
@ -1692,6 +1779,11 @@ issues.review.resolve_conversation=Diskussion als "erledigt" markieren
issues.review.un_resolve_conversation=Diskussion als "nicht-erledigt" markieren issues.review.un_resolve_conversation=Diskussion als "nicht-erledigt" markieren
issues.review.resolved_by=markierte diese Unterhaltung als gelöst issues.review.resolved_by=markierte diese Unterhaltung als gelöst
issues.review.commented=Kommentieren issues.review.commented=Kommentieren
issues.review.official=Genehmigt
issues.review.requested=Prüfung ausstehend
issues.review.rejected=Änderungen angefordert
issues.review.stale=Aktualisiert seit der Genehmigung
issues.review.unofficial=Ungezählte Genehmigung
issues.assignee.error=Aufgrund eines unerwarteten Fehlers konnten nicht alle Beauftragten hinzugefügt werden. issues.assignee.error=Aufgrund eines unerwarteten Fehlers konnten nicht alle Beauftragten hinzugefügt werden.
issues.reference_issue.body=Beschreibung issues.reference_issue.body=Beschreibung
issues.content_history.deleted=gelöscht issues.content_history.deleted=gelöscht
@ -1708,6 +1800,8 @@ compare.compare_head=vergleichen
pulls.desc=Pull-Requests und Code-Reviews aktivieren. pulls.desc=Pull-Requests und Code-Reviews aktivieren.
pulls.new=Neuer Pull-Request pulls.new=Neuer Pull-Request
pulls.new.blocked_user=Der Pull Request kann nicht erstellt werden, da du vom Repository-Eigentümer blockiert wurdest. pulls.new.blocked_user=Der Pull Request kann nicht erstellt werden, da du vom Repository-Eigentümer blockiert wurdest.
pulls.new.must_collaborator=Du musst Mitarbeiter sein, um Pull-Requests zu erstellen.
pulls.edit.already_changed=Änderungen zum Pull-Request konnten nicht gespeichert werden. Es scheint, dass der Inhalt bereits von einem anderen Benutzer geändert wurde. Bitte aktualisieren die Seite und bearbeite diesen erneut, um zu verhindern, dass die Änderungen des anderen Benutzers überschrieben werden
pulls.view=Pull-Request ansehen pulls.view=Pull-Request ansehen
pulls.compare_changes=Neuer Pull-Request pulls.compare_changes=Neuer Pull-Request
pulls.allow_edits_from_maintainers=Änderungen von Maintainern erlauben pulls.allow_edits_from_maintainers=Änderungen von Maintainern erlauben
@ -1763,6 +1857,8 @@ pulls.is_empty=Die Änderungen an diesem Branch sind bereits auf dem Zielbranch.
pulls.required_status_check_failed=Einige erforderliche Prüfungen waren nicht erfolgreich. 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_missing=Einige erforderliche Prüfungen fehlen.
pulls.required_status_check_administrator=Als Administrator kannst du diesen Pull-Request weiterhin mergen. pulls.required_status_check_administrator=Als Administrator kannst du diesen Pull-Request weiterhin mergen.
pulls.blocked_by_approvals=Dieser Pull-Request hat noch nicht genügend Genehmigungen. %d von %d Genehmigungen erteilt.
pulls.blocked_by_approvals_whitelisted=Dieser Pull-Request hat noch nicht genug erforderliche Genehmigungen. %d von %d Genehmigungen von Benutzern oder Teams auf der Berechtigungsliste.
pulls.blocked_by_rejection=Dieser Pull-Request hat Änderungen, die von einem offiziellen Reviewer angefragt wurden. pulls.blocked_by_rejection=Dieser Pull-Request hat Änderungen, die von einem offiziellen Reviewer angefragt wurden.
pulls.blocked_by_official_review_requests=Dieser Pull Request hat offizielle Review-Anfragen. pulls.blocked_by_official_review_requests=Dieser Pull Request hat offizielle Review-Anfragen.
pulls.blocked_by_outdated_branch=Dieser Pull Request ist blockiert, da er veraltet ist. pulls.blocked_by_outdated_branch=Dieser Pull Request ist blockiert, da er veraltet ist.
@ -1804,7 +1900,9 @@ pulls.unrelated_histories=Merge fehlgeschlagen: Der Head des Merges und die Basi
pulls.merge_out_of_date=Merge fehlgeschlagen: Während des Mergens wurde die Basis aktualisiert. Hinweis: Versuche es erneut. pulls.merge_out_of_date=Merge fehlgeschlagen: Während des Mergens wurde die Basis aktualisiert. Hinweis: Versuche es erneut.
pulls.head_out_of_date=Mergen fehlgeschlagen: Der Head wurde aktualisiert während der Merge erstellt wurde. Tipp: Versuche es erneut. pulls.head_out_of_date=Mergen fehlgeschlagen: Der Head wurde aktualisiert während der Merge erstellt wurde. Tipp: Versuche es erneut.
pulls.has_merged=Fehler: Der Pull-Request wurde gemerged, du kannst den Zielbranch nicht wieder mergen oder ändern. pulls.has_merged=Fehler: Der Pull-Request wurde gemerged, du kannst den Zielbranch nicht wieder mergen oder ändern.
pulls.push_rejected=Push fehlgeschlagen: Der Push wurde abgelehnt. Überprüfe die Git Hooks für dieses Repository.
pulls.push_rejected_summary=Vollständige Ablehnungsmeldung pulls.push_rejected_summary=Vollständige Ablehnungsmeldung
pulls.push_rejected_no_message=Push fehlgeschlagen: Der Push wurde abgelehnt, aber es gab keine Fehlermeldung. Überprüfe die Git Hooks für dieses Repository
pulls.open_unmerged_pull_exists=`Du kannst diesen Pull-Request nicht erneut öffnen, da noch ein anderer (#%d) mit identischen Eigenschaften offen ist.` pulls.open_unmerged_pull_exists=`Du kannst diesen Pull-Request nicht erneut öffnen, da noch ein anderer (#%d) mit identischen Eigenschaften offen ist.`
pulls.status_checking=Einige Prüfungen sind noch ausstehend pulls.status_checking=Einige Prüfungen sind noch ausstehend
pulls.status_checks_success=Alle Prüfungen waren erfolgreich pulls.status_checks_success=Alle Prüfungen waren erfolgreich
@ -1828,6 +1926,7 @@ pulls.cmd_instruction_checkout_title=Checkout
pulls.cmd_instruction_checkout_desc=Wechsle auf einen neuen Branch in deinem lokalen Repository und teste die Änderungen. pulls.cmd_instruction_checkout_desc=Wechsle auf einen neuen Branch in deinem lokalen Repository und teste die Änderungen.
pulls.cmd_instruction_merge_title=Mergen pulls.cmd_instruction_merge_title=Mergen
pulls.cmd_instruction_merge_desc=Die Änderungen mergen und auf Gitea aktualisieren. pulls.cmd_instruction_merge_desc=Die Änderungen mergen und auf Gitea aktualisieren.
pulls.cmd_instruction_merge_warning=Warnung: Dieser Vorgang kann den Pull-Request nicht mergen, da "manueller Merge" nicht aktiviert wurde
pulls.clear_merge_message=Merge-Nachricht löschen pulls.clear_merge_message=Merge-Nachricht löschen
pulls.clear_merge_message_hint=Das Löschen der Merge-Nachricht wird nur den Inhalt der Commit-Nachricht entfernen und generierte Git-Trailer wie "Co-Authored-By …" erhalten. pulls.clear_merge_message_hint=Das Löschen der Merge-Nachricht wird nur den Inhalt der Commit-Nachricht entfernen und generierte Git-Trailer wie "Co-Authored-By …" erhalten.
@ -1847,9 +1946,15 @@ pulls.delete.title=Diesen Pull-Request löschen?
pulls.delete.text=Willst du diesen Pull-Request wirklich löschen? (Dies wird den Inhalt unwiderruflich löschen. Überlege, ob du ihn nicht lieber schließen willst, um ihn zu archivieren) pulls.delete.text=Willst du diesen Pull-Request wirklich löschen? (Dies wird den Inhalt unwiderruflich löschen. Überlege, ob du ihn nicht lieber schließen willst, um ihn zu archivieren)
pulls.recently_pushed_new_branches=Du hast auf den Branch <strong>%[1]s</strong> %[2]s gepusht pulls.recently_pushed_new_branches=Du hast auf den Branch <strong>%[1]s</strong> %[2]s gepusht
pulls.upstream_diverging_prompt_behind_1=Dieser Branch ist %[1]d Commit hinter %[2]s
pulls.upstream_diverging_prompt_behind_n=Dieser Branch ist %[1]d Commits hinter %[2]s
pulls.upstream_diverging_prompt_base_newer=Der Basis-Branch %s hat neue Änderungen
pulls.upstream_diverging_merge=Fork synchronisieren
pull.deleted_branch=(gelöscht):%s pull.deleted_branch=(gelöscht):%s
pull.agit_documentation=Dokumentation zu AGit durchschauen
comments.edit.already_changed=Änderungen zum Kommentar konnten nicht gespeichert werden. Es scheint, dass der Inhalt bereits von einem anderen Benutzer geändert wurde. Bitte aktualisiere die Seite und bearbeite diesen erneut, um zu verhindern, dass die Änderungen des anderen Benutzers überschrieben werden
milestones.new=Neuer Meilenstein milestones.new=Neuer Meilenstein
milestones.closed=Geschlossen %s milestones.closed=Geschlossen %s
@ -1858,6 +1963,7 @@ milestones.no_due_date=Kein Fälligkeitsdatum
milestones.open=Öffnen milestones.open=Öffnen
milestones.close=Schließen milestones.close=Schließen
milestones.new_subheader=Benutze Meilensteine, um Issues zu organisieren und den Fortschritt darzustellen. milestones.new_subheader=Benutze Meilensteine, um Issues zu organisieren und den Fortschritt darzustellen.
milestones.completeness=<strong>%d%%</strong> abgeschlossen
milestones.create=Meilenstein erstellen milestones.create=Meilenstein erstellen
milestones.title=Titel milestones.title=Titel
milestones.desc=Beschreibung milestones.desc=Beschreibung
@ -2042,12 +2148,14 @@ settings.push_mirror_sync_in_progress=Aktuell werden Änderungen auf %s gepusht.
settings.site=Webseite settings.site=Webseite
settings.update_settings=Einstellungen speichern settings.update_settings=Einstellungen speichern
settings.update_mirror_settings=Mirror-Einstellungen aktualisieren settings.update_mirror_settings=Mirror-Einstellungen aktualisieren
settings.branches.switch_default_branch=Standardbranch wechseln
settings.branches.update_default_branch=Standardbranch aktualisieren settings.branches.update_default_branch=Standardbranch aktualisieren
settings.branches.add_new_rule=Neue Regel hinzufügen settings.branches.add_new_rule=Neue Regel hinzufügen
settings.advanced_settings=Erweiterte Einstellungen settings.advanced_settings=Erweiterte Einstellungen
settings.wiki_desc=Repository-Wiki aktivieren settings.wiki_desc=Repository-Wiki aktivieren
settings.use_internal_wiki=Eingebautes Wiki verwenden settings.use_internal_wiki=Eingebautes Wiki verwenden
settings.default_wiki_branch_name=Standardbezeichnung für Wiki-Branch settings.default_wiki_branch_name=Standardbezeichnung für Wiki-Branch
settings.default_wiki_everyone_access=Standard-Zugriffsberechtigung für angemeldete Benutzer:
settings.failed_to_change_default_wiki_branch=Das Ändern des Standard-Wiki-Branches ist fehlgeschlagen. settings.failed_to_change_default_wiki_branch=Das Ändern des Standard-Wiki-Branches ist fehlgeschlagen.
settings.use_external_wiki=Externes Wiki verwenden settings.use_external_wiki=Externes Wiki verwenden
settings.external_wiki_url=Externe Wiki-URL settings.external_wiki_url=Externe Wiki-URL
@ -2078,6 +2186,7 @@ settings.pulls.default_delete_branch_after_merge=Standardmäßig bei Pull-Reques
settings.pulls.default_allow_edits_from_maintainers=Änderungen von Maintainern standardmäßig erlauben settings.pulls.default_allow_edits_from_maintainers=Änderungen von Maintainern standardmäßig erlauben
settings.releases_desc=Repository-Releases aktivieren settings.releases_desc=Repository-Releases aktivieren
settings.packages_desc=Repository Packages Registry aktivieren settings.packages_desc=Repository Packages Registry aktivieren
settings.projects_desc=Projekte aktivieren
settings.projects_mode_desc=Projekte-Modus (welche Art Projekte angezeigt werden sollen) settings.projects_mode_desc=Projekte-Modus (welche Art Projekte angezeigt werden sollen)
settings.projects_mode_repo=Nur Repo-Projekte settings.projects_mode_repo=Nur Repo-Projekte
settings.projects_mode_owner=Nur Benutzer- oder Organisations-Projekte settings.projects_mode_owner=Nur Benutzer- oder Organisations-Projekte
@ -2117,6 +2226,7 @@ settings.transfer_in_progress=Es gibt derzeit eine laufende Übertragung. Bitte
settings.transfer_notices_1= Du wirst keinen Zugriff mehr haben, wenn der neue Besitzer ein individueller Benutzer ist. settings.transfer_notices_1= Du wirst keinen Zugriff mehr haben, wenn der neue Besitzer ein individueller Benutzer ist.
settings.transfer_notices_2= Du wirst weiterhin Zugriff haben, wenn der neue Besitzer eine Organisation ist und du einer der Besitzer bist. settings.transfer_notices_2= Du wirst weiterhin Zugriff haben, wenn der neue Besitzer eine Organisation ist und du einer der Besitzer bist.
settings.transfer_notices_3=- Wenn das Repository privat ist und an einen einzelnen Benutzer übertragen wird, wird sichergestellt, dass der Benutzer mindestens Leserechte hat (und die Berechtigungen werden gegebenenfalls ändert). settings.transfer_notices_3=- Wenn das Repository privat ist und an einen einzelnen Benutzer übertragen wird, wird sichergestellt, dass der Benutzer mindestens Leserechte hat (und die Berechtigungen werden gegebenenfalls ändert).
settings.transfer_notices_4=- Wenn das Repository einer Organisation gehört und du es an eine andere Organisation oder eine andere Person überträgst, verlierst du die Verlinkungen zwischen den Issues des Repositorys und dem Projektboard der Organisation.
settings.transfer_owner=Neuer Besitzer settings.transfer_owner=Neuer Besitzer
settings.transfer_perform=Übertragung durchführen settings.transfer_perform=Übertragung durchführen
settings.transfer_started=`Für dieses Repository wurde eine Übertragung eingeleitet und wartet nun auf die Bestätigung von "%s"` settings.transfer_started=`Für dieses Repository wurde eine Übertragung eingeleitet und wartet nun auf die Bestätigung von "%s"`
@ -2216,6 +2326,7 @@ settings.event_wiki_desc=Wiki-Seite erstellt, umbenannt, bearbeitet oder gelösc
settings.event_release=Release settings.event_release=Release
settings.event_release_desc=Release in einem Repository veröffentlicht, aktualisiert oder gelöscht. settings.event_release_desc=Release in einem Repository veröffentlicht, aktualisiert oder gelöscht.
settings.event_push=Push settings.event_push=Push
settings.event_force_push=Force Push
settings.event_push_desc=Git push in ein Repository. settings.event_push_desc=Git push in ein Repository.
settings.event_repository=Repository settings.event_repository=Repository
settings.event_repository_desc=Repository erstellt oder gelöscht. settings.event_repository_desc=Repository erstellt oder gelöscht.
@ -2252,6 +2363,7 @@ settings.event_pull_request_merge=Pull-Request-Merge
settings.event_package=Paket settings.event_package=Paket
settings.event_package_desc=Paket wurde in einem Repository erstellt oder gelöscht. settings.event_package_desc=Paket wurde in einem Repository erstellt oder gelöscht.
settings.branch_filter=Branch-Filter settings.branch_filter=Branch-Filter
settings.branch_filter_desc=Whitelist für Branches für Push-, Erzeugungs- und Löschevents, als Glob-Pattern beschrieben. Es werden Events für alle Branches gemeldet, falls das Pattern <code>*</code> ist, oder falls es leer ist. Siehe die <a href="%[1]s">%[2]s</a> Dokumentation für die Syntax (Englisch). Beispiele: <code>master</code>, <code>{master,release*}</code>.
settings.authorization_header=Authorization-Header settings.authorization_header=Authorization-Header
settings.authorization_header_desc=Wird, falls vorhanden, als Authorization-Header mitgesendet. Beispiele: %s. settings.authorization_header_desc=Wird, falls vorhanden, als Authorization-Header mitgesendet. Beispiele: %s.
settings.active=Aktiv settings.active=Aktiv
@ -2300,22 +2412,50 @@ settings.branches=Branches
settings.protected_branch=Branch-Schutz settings.protected_branch=Branch-Schutz
settings.protected_branch.save_rule=Regel speichern settings.protected_branch.save_rule=Regel speichern
settings.protected_branch.delete_rule=Regel löschen settings.protected_branch.delete_rule=Regel löschen
settings.protected_branch_can_push=Push erlauben?
settings.protected_branch_can_push_yes=Du kannst pushen
settings.protected_branch_can_push_no=Du kannst nicht pushen
settings.branch_protection=Branch-Schutz für Branch '<b>%s</b>'
settings.protect_this_branch=Branch-Schutz aktivieren settings.protect_this_branch=Branch-Schutz aktivieren
settings.protect_this_branch_desc=Verhindert das Löschen und schränkt Git auf Push- und Merge-Änderungen auf dem Branch ein. settings.protect_this_branch_desc=Verhindert das Löschen und schränkt Git auf Push- und Merge-Änderungen auf dem Branch ein.
settings.protect_disable_push=Push deaktivieren settings.protect_disable_push=Push deaktivieren
settings.protect_disable_push_desc=Kein Push auf diesen Branch erlauben. settings.protect_disable_push_desc=Kein Push auf diesen Branch erlauben.
settings.protect_disable_force_push=Force-Push deaktivieren
settings.protect_disable_force_push_desc=Force-Push auf diesen Branch nicht erlauben.
settings.protect_enable_push=Push aktivieren settings.protect_enable_push=Push aktivieren
settings.protect_enable_push_desc=Jeder, der Schreibzugriff hat, darf in diesen Branch Pushen (aber kein Force-Push). settings.protect_enable_push_desc=Jeder, der Schreibzugriff hat, darf in diesen Branch Pushen (aber kein Force-Push).
settings.protect_enable_force_push_all=Force-Push aktivieren
settings.protect_enable_force_push_all_desc=Jeder mit Push-Zugriff wird in diesen Branch force-pushen können.
settings.protect_enable_force_push_allowlist=Force-Push beschränkt auf Genehmigungsliste
settings.protect_enable_force_push_allowlist_desc=Nur Benutzer oder Teams auf der Genehmigungsliste mit Push-Zugriff werden in diesen Branch force-pushen können.
settings.protect_enable_merge=Merge aktivieren settings.protect_enable_merge=Merge aktivieren
settings.protect_enable_merge_desc=Jeder mit Schreibzugriff darf die Pull-Requests in diesen Branch mergen. settings.protect_enable_merge_desc=Jeder mit Schreibzugriff darf die Pull-Requests in diesen Branch mergen.
settings.protect_whitelist_committers=Genehmigungsliste für eingeschränkten Push
settings.protect_whitelist_committers_desc=Jeder, der auf der Genehmigungsliste steht, darf in diesen Branch pushen (aber kein Force-Push).
settings.protect_whitelist_deploy_keys=Genehmigungsliste für Deploy-Schlüssel mit Schreibzugriff zum Pushen.
settings.protect_whitelist_users=Nutzer, die pushen dürfen:
settings.protect_whitelist_teams=Teams, die pushen dürfen:
settings.protect_force_push_allowlist_users=Erlaubte Benutzer für Force-Push:
settings.protect_force_push_allowlist_teams=Erlaubte Teams für Force-Push:
settings.protect_force_push_allowlist_deploy_keys=Genehmigungsliste für Deploy-Schlüssel mit Schreibzugriff zum Force-Push.
settings.protect_merge_whitelist_committers=Merge-Genehmigungsliste aktivieren
settings.protect_merge_whitelist_committers_desc=Erlaube Nutzern oder Teams auf der Genehmigungsliste Pull-Requests in diesen Branch zu mergen.
settings.protect_merge_whitelist_users=Nutzer, die mergen dürfen:
settings.protect_merge_whitelist_teams=Teams, die mergen dürfen:
settings.protect_check_status_contexts=Statusprüfungen aktivieren settings.protect_check_status_contexts=Statusprüfungen aktivieren
settings.protect_status_check_patterns=Statuscheck-Muster: settings.protect_status_check_patterns=Statuscheck-Muster:
settings.protect_status_check_patterns_desc=Gib Muster ein, um festzulegen, welche Statusüberprüfungen durchgeführt werden müssen, bevor Branches in einen Branch, der dieser Regel entspricht, gemerged werden können. Jede Zeile gibt ein Muster an. Muster dürfen nicht leer sein. settings.protect_status_check_patterns_desc=Gib Muster ein, um festzulegen, welche Statusüberprüfungen durchgeführt werden müssen, bevor Branches in einen Branch, der dieser Regel entspricht, gemerged werden können. Jede Zeile gibt ein Muster an. Muster dürfen nicht leer sein.
settings.protect_check_status_contexts_desc=Vor dem Mergen müssen Statusprüfungen bestanden werden. Wähle aus, welche Statusprüfungen erfolgreich durchgeführt werden müssen, bevor Branches in einen anderen gemergt werden können, der dieser Regel entspricht. Wenn aktiviert, müssen Commits zuerst auf einen anderen Branch gepusht werden, dann nach bestandener Statusprüfung gemergt oder direkt auf einen Branch gepusht werden, der dieser Regel entspricht. Wenn kein Kontext ausgewählt ist, muss der letzte Commit unabhängig vom Kontext erfolgreich sein.
settings.protect_check_status_contexts_list=Statusprüfungen, die in der letzten Woche für dieses Repository gefunden wurden settings.protect_check_status_contexts_list=Statusprüfungen, die in der letzten Woche für dieses Repository gefunden wurden
settings.protect_status_check_matched=Übereinstimmung settings.protect_status_check_matched=Übereinstimmung
settings.protect_invalid_status_check_pattern=Ungültiges Muster: "%s". settings.protect_invalid_status_check_pattern=Ungültiges Muster: "%s".
settings.protect_no_valid_status_check_patterns=Keine gültigen Statuscheck-Muster. settings.protect_no_valid_status_check_patterns=Keine gültigen Statuscheck-Muster.
settings.protect_required_approvals=Erforderliche Zustimmungen: settings.protect_required_approvals=Erforderliche Zustimmungen:
settings.protect_required_approvals_desc=Erlaube das Mergen des Pull-Requests nur mit genügend Genehmigungen.
settings.protect_approvals_whitelist_enabled=Genehmigungen auf Benutzer oder Teams auf der Genehmigungsliste beschränken
settings.protect_approvals_whitelist_enabled_desc=Nur Bewertungen von Benutzern auf der Genehmigungsliste oder Teams zählen zu den erforderlichen Genehmigungen. Gibt es keine Genehmigungsliste, so zählen Reviews von jedem mit Schreibzugriff zu den erforderlichen Genehmigungen.
settings.protect_approvals_whitelist_users=Freigeschaltete Reviewer:
settings.protect_approvals_whitelist_teams=Freigeschaltete Teams:
settings.dismiss_stale_approvals=Entferne alte Genehmigungen settings.dismiss_stale_approvals=Entferne alte Genehmigungen
settings.dismiss_stale_approvals_desc=Wenn neue Commits gepusht werden, die den Inhalt des Pull-Requests ändern, werden alte Genehmigungen entfernt. settings.dismiss_stale_approvals_desc=Wenn neue Commits gepusht werden, die den Inhalt des Pull-Requests ändern, werden alte Genehmigungen entfernt.
settings.ignore_stale_approvals=Veraltete Genehmigungen ignorieren settings.ignore_stale_approvals=Veraltete Genehmigungen ignorieren
@ -2323,12 +2463,18 @@ settings.ignore_stale_approvals_desc=Genehmigungen, die für ältere Commits ert
settings.require_signed_commits=Signierte Commits erforderlich settings.require_signed_commits=Signierte Commits erforderlich
settings.require_signed_commits_desc=Pushes auf diesen Branch ablehnen, wenn Commits nicht signiert oder nicht überprüfbar sind. settings.require_signed_commits_desc=Pushes auf diesen Branch ablehnen, wenn Commits nicht signiert oder nicht überprüfbar sind.
settings.protect_branch_name_pattern=Muster für geschützte Branchnamen settings.protect_branch_name_pattern=Muster für geschützte Branchnamen
settings.protect_branch_name_pattern_desc=Geschützte Branch-Namensmuster. Siehe <a href="%s">die Dokumentation</a> für die Pattern-Syntax. Beispiele: main, release/**
settings.protect_patterns=Muster settings.protect_patterns=Muster
settings.protect_protected_file_patterns=Geschützte Dateimuster (durch Semikolon ';' getrennt): settings.protect_protected_file_patterns=Geschützte Dateimuster (durch Semikolon ';' getrennt):
settings.protect_protected_file_patterns_desc=Geschützte Dateien dürfen nicht direkt geändert werden, auch wenn der Benutzer Rechte hat, Dateien in diesem Branch hinzuzufügen, zu bearbeiten oder zu löschen. Mehrere Muster können mit Semikolon (';') getrennt werden. Siehe <a href='%[1]s'>%[2]s</a> Dokumentation zur Pattern-Syntax. Beispiele: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>.
settings.protect_unprotected_file_patterns=Ungeschützte Dateimuster (durch Semikolon ';' getrennt): settings.protect_unprotected_file_patterns=Ungeschützte Dateimuster (durch Semikolon ';' getrennt):
settings.protect_unprotected_file_patterns_desc=Ungeschützte Dateien, die direkt geändert werden dürfen, wenn der Benutzer Schreibzugriff hat, können die Push-Beschränkung umgehen. Mehrere Muster können mit Semikolon (';') getrennt werden. Siehe <a href='%[1]s'>%[2]s</a> Dokumentation zur Mustersyntax. Beispiele: <code>.drone.yml</code>, <code>/docs/**/*.txt</code>.
settings.add_protected_branch=Schutz aktivieren
settings.delete_protected_branch=Schutz deaktivieren
settings.update_protect_branch_success=Branchschutzregel "%s" wurde geändert. settings.update_protect_branch_success=Branchschutzregel "%s" wurde geändert.
settings.remove_protected_branch_success=Branchschutzregel "%s" wurde deaktiviert. settings.remove_protected_branch_success=Branchschutzregel "%s" wurde deaktiviert.
settings.remove_protected_branch_failed=Entfernen der Branchschutzregel "%s" fehlgeschlagen. settings.remove_protected_branch_failed=Entfernen der Branchschutzregel "%s" fehlgeschlagen.
settings.protected_branch_deletion=Branch-Schutz deaktivieren
settings.protected_branch_deletion_desc=Wenn du den Branch-Schutz deaktivierst, können alle Nutzer mit Schreibrechten auf den Branch pushen. Fortfahren? settings.protected_branch_deletion_desc=Wenn du den Branch-Schutz deaktivierst, können alle Nutzer mit Schreibrechten auf den Branch pushen. Fortfahren?
settings.block_rejected_reviews=Merge bei abgelehnten Reviews blockieren settings.block_rejected_reviews=Merge bei abgelehnten Reviews blockieren
settings.block_rejected_reviews_desc=Mergen ist nicht möglich, wenn Änderungen durch offizielle Reviewer angefragt werden, auch wenn es genügend Zustimmungen gibt. settings.block_rejected_reviews_desc=Mergen ist nicht möglich, wenn Änderungen durch offizielle Reviewer angefragt werden, auch wenn es genügend Zustimmungen gibt.
@ -2336,8 +2482,11 @@ settings.block_on_official_review_requests=Mergen bei offiziellen Review-Anfrage
settings.block_on_official_review_requests_desc=Mergen ist nicht möglich wenn offizielle Review-Anfrangen vorliegen, selbst wenn es genügend Zustimmungen gibt. settings.block_on_official_review_requests_desc=Mergen ist nicht möglich wenn offizielle Review-Anfrangen vorliegen, selbst wenn es genügend Zustimmungen gibt.
settings.block_outdated_branch=Merge blockieren, wenn der Pull-Request veraltet ist settings.block_outdated_branch=Merge blockieren, wenn der Pull-Request veraltet ist
settings.block_outdated_branch_desc=Mergen ist nicht möglich, wenn der Head-Branch hinter dem Basis-Branch ist. settings.block_outdated_branch_desc=Mergen ist nicht möglich, wenn der Head-Branch hinter dem Basis-Branch ist.
settings.block_admin_merge_override=Administratoren müssen die Schutzregeln für Branches befolgen
settings.block_admin_merge_override_desc=Administratoren müssen die Schutzregeln für Branches befolgen und können sie nicht umgehen.
settings.default_branch_desc=Wähle einen Standardbranch für Pull-Requests und Code-Commits: settings.default_branch_desc=Wähle einen Standardbranch für Pull-Requests und Code-Commits:
settings.merge_style_desc=Merge-Styles settings.merge_style_desc=Merge-Styles
settings.default_merge_style_desc=Standard-Mergeverhalten für Pull-Requests
settings.choose_branch=Branch wählen… settings.choose_branch=Branch wählen…
settings.no_protected_branch=Es gibt keine geschützten Branches. settings.no_protected_branch=Es gibt keine geschützten Branches.
settings.edit_protected_branch=Bearbeiten settings.edit_protected_branch=Bearbeiten
@ -2353,12 +2502,25 @@ settings.tags.protection.allowed.teams=Erlaubte Teams
settings.tags.protection.allowed.noone=Niemand settings.tags.protection.allowed.noone=Niemand
settings.tags.protection.create=Tag schützen settings.tags.protection.create=Tag schützen
settings.tags.protection.none=Es gibt keine geschützten Tags. settings.tags.protection.none=Es gibt keine geschützten Tags.
settings.tags.protection.pattern.description=Du kannst einen einzigen Namen oder ein globales Schema oder einen regulären Ausdruck verwenden, um mehrere Tags zu schützen. Mehr dazu im <a target="_blank" rel="noopener" href="%s">Guide für geschützte Tags (Englisch)</a>.
settings.bot_token=Bot-Token settings.bot_token=Bot-Token
settings.chat_id=Chat-ID settings.chat_id=Chat-ID
settings.thread_id=Thread-ID settings.thread_id=Thread-ID
settings.matrix.homeserver_url=Homeserver-URL settings.matrix.homeserver_url=Homeserver-URL
settings.matrix.room_id=Raum-ID settings.matrix.room_id=Raum-ID
settings.matrix.message_type=Nachrichtentyp settings.matrix.message_type=Nachrichtentyp
settings.visibility.private.button=Auf privat setzen
settings.visibility.private.text=Das Ändern der Sichtbarkeit auf privat wird das Repository nicht nur für erlaubte Mitglieder sichtbar machen, sondern kann auch die Beziehung zwischen ihm und Forks, Beobachtern und Sternen entfernen.
settings.visibility.private.bullet_title=<strong>Das Ändern der Sichtbarkeit auf privat wird:</strong>
settings.visibility.private.bullet_one=Das Repository nur für zugelassene Mitglieder sichtbar machen.
settings.visibility.private.bullet_two=Kann die Beziehung zwischen ihm und <strong>Forks</strong>, <strong>Beobachtern</strong>und <strong>Sternen</strong> entfernen.
settings.visibility.public.button=Auf öffentlich setzen
settings.visibility.public.text=Das Ändern der Sichtbarkeit auf öffentlich macht das Repository für jeden sichtbar.
settings.visibility.public.bullet_title=<strong>Das Ändern der Sichtbarkeit auf öffentlich wird:</strong>
settings.visibility.public.bullet_one=Das Repository für jeden sichtbar machen.
settings.visibility.success=Die Sichtbarkeit des Repositorys wurde geändert.
settings.visibility.error=Beim Versuch, die Sichtbarkeit des Repositorys zu ändern, ist ein Fehler aufgetreten.
settings.visibility.fork_error=Die Sichtbarkeit von geforkten Repositories ist nicht veränderbar.
settings.archive.button=Repo archivieren settings.archive.button=Repo archivieren
settings.archive.header=Dieses Repo archivieren settings.archive.header=Dieses Repo archivieren
settings.archive.text=Durch das Archivieren wird ein Repo vollständig schreibgeschützt. Es wird vom Dashboard versteckt. Niemand (nicht einmal du!) wird in der Lage sein, neue Commits zu erstellen oder Issues oder Pull-Requests zu öffnen. settings.archive.text=Durch das Archivieren wird ein Repo vollständig schreibgeschützt. Es wird vom Dashboard versteckt. Niemand (nicht einmal du!) wird in der Lage sein, neue Commits zu erstellen oder Issues oder Pull-Requests zu öffnen.
@ -2470,6 +2632,7 @@ release.new_release=Neues Release
release.draft=Entwurf release.draft=Entwurf
release.prerelease=Pre-Release release.prerelease=Pre-Release
release.stable=Stabil release.stable=Stabil
release.latest=Aktuell
release.compare=Vergleichen release.compare=Vergleichen
release.edit=bearbeiten release.edit=bearbeiten
release.ahead.commits=<strong>%d</strong> Commits release.ahead.commits=<strong>%d</strong> Commits
@ -2554,6 +2717,7 @@ tag.create_success=Tag "%s" wurde erstellt.
topic.manage_topics=Themen verwalten topic.manage_topics=Themen verwalten
topic.done=Fertig topic.done=Fertig
topic.count_prompt=Du kannst nicht mehr als 25 Themen auswählen
topic.format_prompt=Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) und Punkte ('.') enthalten und bis zu 35 Zeichen lang sein. Nur Kleinbuchstaben sind zulässig. topic.format_prompt=Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) und Punkte ('.') enthalten und bis zu 35 Zeichen lang sein. Nur Kleinbuchstaben sind zulässig.
find_file.go_to_file=Datei suchen find_file.go_to_file=Datei suchen
@ -2651,6 +2815,7 @@ teams.leave.detail=%s verlassen?
teams.can_create_org_repo=Repositories erstellen teams.can_create_org_repo=Repositories erstellen
teams.can_create_org_repo_helper=Mitglieder können neue Repositories in der Organisation erstellen. Der Ersteller erhält Administrator-Zugriff auf das neue Repository. teams.can_create_org_repo_helper=Mitglieder können neue Repositories in der Organisation erstellen. Der Ersteller erhält Administrator-Zugriff auf das neue Repository.
teams.none_access=Kein Zugriff teams.none_access=Kein Zugriff
teams.none_access_helper=Mitglieder können keine anderen Aktionen für diese Einheit anzeigen oder durchführen. Dies hat keine Wirkung auf öffentliche Repositories.
teams.general_access=Allgemeiner Zugriff teams.general_access=Allgemeiner Zugriff
teams.general_access_helper=Mitgliederberechtigungen werden durch folgende Berechtigungstabelle festgelegt. teams.general_access_helper=Mitgliederberechtigungen werden durch folgende Berechtigungstabelle festgelegt.
teams.read_access=Lesen teams.read_access=Lesen
@ -2697,6 +2862,7 @@ teams.invite.by=Von %s eingeladen
teams.invite.description=Bitte klicke auf die folgende Schaltfläche, um dem Team beizutreten. teams.invite.description=Bitte klicke auf die folgende Schaltfläche, um dem Team beizutreten.
[admin] [admin]
maintenance=Wartung
dashboard=Dashboard dashboard=Dashboard
self_check=Selbstprüfung self_check=Selbstprüfung
identity_access=Identität & Zugriff identity_access=Identität & Zugriff
@ -2718,7 +2884,9 @@ last_page=Letzte
total=Gesamt: %d total=Gesamt: %d
settings=Administratoreinstellungen settings=Administratoreinstellungen
dashboard.new_version_hint=Gitea %s ist jetzt verfügbar, deine derzeitige Version ist %s. Weitere Details findest du im <a target="_blank" rel="noreferrer" href="%s">Blog</a>.
dashboard.statistic=Übersicht dashboard.statistic=Übersicht
dashboard.maintenance_operations=Wartungsoperationen
dashboard.system_status=System-Status dashboard.system_status=System-Status
dashboard.operation_name=Name der Operation dashboard.operation_name=Name der Operation
dashboard.operation_switch=Wechseln dashboard.operation_switch=Wechseln
@ -2759,6 +2927,7 @@ dashboard.reinit_missing_repos=Alle Git-Repositories neu einlesen, für die Eint
dashboard.sync_external_users=Externe Benutzerdaten synchronisieren dashboard.sync_external_users=Externe Benutzerdaten synchronisieren
dashboard.cleanup_hook_task_table=Hook-Task-Tabelle bereinigen dashboard.cleanup_hook_task_table=Hook-Task-Tabelle bereinigen
dashboard.cleanup_packages=Veraltete Pakete löschen dashboard.cleanup_packages=Veraltete Pakete löschen
dashboard.cleanup_actions=Abgelaufene Ressourcen von Actions bereinigen
dashboard.server_uptime=Server-Uptime dashboard.server_uptime=Server-Uptime
dashboard.current_goroutine=Aktuelle Goroutinen dashboard.current_goroutine=Aktuelle Goroutinen
dashboard.current_memory_usage=Aktuelle Speichernutzung dashboard.current_memory_usage=Aktuelle Speichernutzung
@ -2788,12 +2957,19 @@ dashboard.total_gc_time=Gesamte GC-Pause
dashboard.total_gc_pause=Gesamte GC-Pause dashboard.total_gc_pause=Gesamte GC-Pause
dashboard.last_gc_pause=Letzte GC-Pause dashboard.last_gc_pause=Letzte GC-Pause
dashboard.gc_times=Anzahl GC dashboard.gc_times=Anzahl GC
dashboard.delete_old_actions=Alle alten Aktionen aus der Datenbank löschen
dashboard.delete_old_actions.started=Löschen aller alten Aktionen in der Datenbank gestartet.
dashboard.update_checker=Update-Checker dashboard.update_checker=Update-Checker
dashboard.delete_old_system_notices=Alle alten Systemmeldungen aus der Datenbank löschen dashboard.delete_old_system_notices=Alle alten Systemmeldungen aus der Datenbank löschen
dashboard.gc_lfs=Garbage-Collection für LFS Meta-Objekte ausführen dashboard.gc_lfs=Garbage-Collection für LFS Meta-Objekte ausführen
dashboard.stop_zombie_tasks=Zombie-Aufgaben stoppen
dashboard.stop_endless_tasks=Endlose Aktionen stoppen
dashboard.cancel_abandoned_jobs=Aufgegebene Jobs abbrechen
dashboard.start_schedule_tasks=Terminierte Aufgaben starten
dashboard.sync_branch.started=Synchronisierung der Branches gestartet dashboard.sync_branch.started=Synchronisierung der Branches gestartet
dashboard.sync_tag.started=Tag-Synchronisierung gestartet dashboard.sync_tag.started=Tag-Synchronisierung gestartet
dashboard.rebuild_issue_indexer=Issue-Indexer neu bauen dashboard.rebuild_issue_indexer=Issue-Indexer neu bauen
dashboard.sync_repo_licenses=Repo-Lizenzen synchronisieren
users.user_manage_panel=Benutzerkontenverwaltung users.user_manage_panel=Benutzerkontenverwaltung
users.new_account=Benutzerkonto erstellen users.new_account=Benutzerkonto erstellen
@ -2865,6 +3041,10 @@ emails.not_updated=Fehler beim Aktualisieren der angeforderten E-Mail-Adresse: %
emails.duplicate_active=Diese E-Mail-Adresse wird bereits von einem Nutzer verwendet. emails.duplicate_active=Diese E-Mail-Adresse wird bereits von einem Nutzer verwendet.
emails.change_email_header=E-Mail-Eigenschaften aktualisieren emails.change_email_header=E-Mail-Eigenschaften aktualisieren
emails.change_email_text=Bist du dir sicher, dass du diese E-Mail-Adresse aktualisieren möchtest? emails.change_email_text=Bist du dir sicher, dass du diese E-Mail-Adresse aktualisieren möchtest?
emails.delete=E-Mail löschen
emails.delete_desc=Willst du diese E-Mail-Adresse wirklich löschen?
emails.deletion_success=Die E-Mail-Adresse wurde gelöscht.
emails.delete_primary_email_error=Du kannst die primäre E-Mail-Adresse nicht löschen.
orgs.org_manage_panel=Organisationsverwaltung orgs.org_manage_panel=Organisationsverwaltung
orgs.name=Name orgs.name=Name
@ -2897,10 +3077,12 @@ packages.size=Größe
packages.published=Veröffentlicht packages.published=Veröffentlicht
defaulthooks=Standard-Webhooks defaulthooks=Standard-Webhooks
defaulthooks.desc=Webhooks senden automatisch eine HTTP-POST-Anfrage an einen Server, wenn bestimmte Gitea-Events ausgelöst werden. Hier definierte Webhooks sind die Standardwerte, die in alle neuen Repositories kopiert werden. Mehr Infos findest du in der <a target="_blank" rel="noopener" href="%s">Webhooks-Anleitung</a> (auf Englisch).
defaulthooks.add_webhook=Standard-Webhook hinzufügen defaulthooks.add_webhook=Standard-Webhook hinzufügen
defaulthooks.update_webhook=Standard-Webhook aktualisieren defaulthooks.update_webhook=Standard-Webhook aktualisieren
systemhooks=System-Webhooks systemhooks=System-Webhooks
systemhooks.desc=Webhooks senden automatisch HTTP-POST-Anfragen an einen Server, wenn bestimmte Gitea-Events ausgelöst werden. Hier definierte Webhooks werden auf alle Repositories des Systems übertragen, beachte daher mögliche Performance-Einbrüche. Mehr Infos findest du in der <a target="_blank" rel="noopener" href="%s">Webhooks-Anleitung</a> (auf Englisch).
systemhooks.add_webhook=System-Webhook hinzufügen systemhooks.add_webhook=System-Webhook hinzufügen
systemhooks.update_webhook=System-Webhook aktualisieren systemhooks.update_webhook=System-Webhook aktualisieren
@ -2995,7 +3177,18 @@ auths.tips=Tipps
auths.tips.oauth2.general=OAuth2-Authentifizierung auths.tips.oauth2.general=OAuth2-Authentifizierung
auths.tips.oauth2.general.tip=Beim Registrieren einer OAuth2-Anwendung sollte die Callback-URL folgendermaßen lauten: auths.tips.oauth2.general.tip=Beim Registrieren einer OAuth2-Anwendung sollte die Callback-URL folgendermaßen lauten:
auths.tip.oauth2_provider=OAuth2-Anbieter auths.tip.oauth2_provider=OAuth2-Anbieter
auths.tip.bitbucket=Registriere einen neuen OAuth-Consumer unter %s und füge die Berechtigung 'Account' - 'Read' hinzu
auths.tip.nextcloud=Registriere über das "Settings -> Security -> OAuth 2.0 client"-Menü einen neuen "OAuth consumer" auf der Nextcloud-Instanz auths.tip.nextcloud=Registriere über das "Settings -> Security -> OAuth 2.0 client"-Menü einen neuen "OAuth consumer" auf der Nextcloud-Instanz
auths.tip.dropbox=Erstelle eine neue App auf %s
auths.tip.facebook=Erstelle eine neue Anwendung auf %s und füge das Produkt "Facebook Login“ hinzu
auths.tip.github=Erstelle unter %s eine neue OAuth-Anwendung
auths.tip.gitlab_new=Erstelle eine neue Anwendung unter %s
auths.tip.google_plus=Du erhältst die OAuth2-Client-Zugangsdaten in der Google-API-Konsole unter %s
auths.tip.openid_connect=Benutze die OpenID-Connect-Discovery-URL "https://{server}/.well-known/openid-configuration", um die Endpunkte zu spezifizieren
auths.tip.twitter=Gehe zu %s, erstelle eine Anwendung und stelle sicher, dass die Option „Allow this application to be used to Sign in with Twitter“ aktiviert ist
auths.tip.discord=Erstelle unter %s eine neue Anwendung
auths.tip.gitea=Registriere eine neue OAuth2-Anwendung. Eine Anleitung findest du unter %s
auths.tip.yandex=`Erstelle eine neue Anwendung auf %s. Wähle folgende Berechtigungen aus dem "Yandex.Passport API" Bereich: "Zugriff auf E-Mail-Adresse", "Zugriff auf Benutzeravatar" und "Zugriff auf Benutzername, Vor- und Nachname, Geschlecht"`
auths.tip.mastodon=Gebe eine benutzerdefinierte URL für die Mastodon-Instanz ein, mit der du dich authentifizieren möchtest (oder benutze die standardmäßige) auths.tip.mastodon=Gebe eine benutzerdefinierte URL für die Mastodon-Instanz ein, mit der du dich authentifizieren möchtest (oder benutze die standardmäßige)
auths.edit=Authentifikationsquelle bearbeiten auths.edit=Authentifikationsquelle bearbeiten
auths.activated=Diese Authentifikationsquelle ist aktiviert auths.activated=Diese Authentifikationsquelle ist aktiviert
@ -3111,6 +3304,10 @@ config.cache_adapter=Cache-Adapter
config.cache_interval=Cache-Intervall config.cache_interval=Cache-Intervall
config.cache_conn=Cache-Anbindung config.cache_conn=Cache-Anbindung
config.cache_item_ttl=Cache Item-TTL config.cache_item_ttl=Cache Item-TTL
config.cache_test=Cache testen
config.cache_test_failed=Fehler beim Prüfen des Caches: %v.
config.cache_test_slow=Cache-Test erfolgreich, aber die Antwortzeit ist langsam: %s.
config.cache_test_succeeded=Cache-Test erfolgreich, Antwort in %s erhalten.
config.session_config=Session-Konfiguration config.session_config=Session-Konfiguration
config.session_provider=Session-Provider config.session_provider=Session-Provider
@ -3157,6 +3354,7 @@ monitor.next=Nächste Ausführung
monitor.previous=Letzte Ausführung monitor.previous=Letzte Ausführung
monitor.execute_times=Ausführungen monitor.execute_times=Ausführungen
monitor.process=Laufende Prozesse monitor.process=Laufende Prozesse
monitor.stacktrace=Stacktraces
monitor.processes_count=%d Prozesse monitor.processes_count=%d Prozesse
monitor.download_diagnosis_report=Diagnosebericht herunterladen monitor.download_diagnosis_report=Diagnosebericht herunterladen
monitor.desc=Beschreibung monitor.desc=Beschreibung
@ -3164,6 +3362,8 @@ monitor.start=Startzeit
monitor.execute_time=Ausführungszeit monitor.execute_time=Ausführungszeit
monitor.last_execution_result=Ergebnis monitor.last_execution_result=Ergebnis
monitor.process.cancel=Prozess abbrechen monitor.process.cancel=Prozess abbrechen
monitor.process.cancel_desc=Abbrechen eines Prozesses kann Datenverlust verursachen
monitor.process.cancel_notices=Abbrechen: <strong>%s</strong>?
monitor.process.children=Subprozesse monitor.process.children=Subprozesse
monitor.queues=Warteschlangen monitor.queues=Warteschlangen
@ -3202,11 +3402,13 @@ notices.op=Aktion
notices.delete_success=Diese Systemmeldung wurde gelöscht. notices.delete_success=Diese Systemmeldung wurde gelöscht.
self_check.no_problem_found=Bisher wurde kein Problem festgestellt. self_check.no_problem_found=Bisher wurde kein Problem festgestellt.
self_check.startup_warnings=Warnungen beim Start:
self_check.database_collation_mismatch=Erwarte Datenbank-Kollation: %s self_check.database_collation_mismatch=Erwarte Datenbank-Kollation: %s
self_check.database_collation_case_insensitive=Die Datenbank verwendet die Kollation %s, was eine unsensible Kollation ist. Obwohl Gitea damit arbeiten könnte, gibt es vielleicht einige seltene Fälle, die nicht wie erwartet funktionieren. self_check.database_collation_case_insensitive=Die Datenbank verwendet die Kollation %s, was eine unsensible Kollation ist. Obwohl Gitea damit arbeiten könnte, gibt es vielleicht einige seltene Fälle, die nicht wie erwartet funktionieren.
self_check.database_inconsistent_collation_columns=Die Datenbank verwendet die Kollation %s, aber diese Spalten verwenden unzutreffende Kollationen. Dies könnte zu unerwarteten Problemen führen. self_check.database_inconsistent_collation_columns=Die Datenbank verwendet die Kollation %s, aber diese Spalten verwenden unzutreffende Kollationen. Dies könnte zu unerwarteten Problemen führen.
self_check.database_fix_mysql=Für MySQL/MariaDB-Benutzer kann man den Befehl "gitea doctor convert" oder manuell auch "ALTER ... COLLATE ..."-SQLs verwenden, um die Sortierprobleme zu beheben. self_check.database_fix_mysql=Für MySQL/MariaDB-Benutzer kann man den Befehl "gitea doctor convert" oder manuell auch "ALTER ... COLLATE ..."-SQLs verwenden, um die Sortierprobleme zu beheben.
self_check.database_fix_mssql=Für MSSQL-Benutzer kann das Problem im Moment nur durch "ALTER ... COLLATE ..." SQLs manuell behoben werden. self_check.database_fix_mssql=Für MSSQL-Benutzer kann das Problem im Moment nur durch "ALTER ... COLLATE ..." SQLs manuell behoben werden.
self_check.location_origin_mismatch=Aktuelle URL (%[1]s) stimmt nicht mit der URL überein, die Gitea (%[2]s) sieht. Wenn du einen Reverse-Proxy verwendest, stelle bitte sicher, dass die Header "Host" und "X-Forwarded-Proto" korrekt gesetzt sind.
[action] [action]
create_repo=hat das Repository <a href="%s">%s</a> erstellt create_repo=hat das Repository <a href="%s">%s</a> erstellt
@ -3234,6 +3436,7 @@ mirror_sync_create=neue Referenz <a href="%[2]s">%[3]s</a> bei <a href="%[1]s">%
mirror_sync_delete=hat die Referenz des Mirrors <code>%[2]s</code> in <a href="%[1]s">%[3]s</a> synchronisiert und gelöscht mirror_sync_delete=hat die Referenz des Mirrors <code>%[2]s</code> in <a href="%[1]s">%[3]s</a> synchronisiert und gelöscht
approve_pull_request=`hat <a href="%[1]s">%[3]s#%[2]s</a> approved` approve_pull_request=`hat <a href="%[1]s">%[3]s#%[2]s</a> approved`
reject_pull_request=`schlug Änderungen für <a href="%[1]s">%[3]s#%[2]s</a> vor` reject_pull_request=`schlug Änderungen für <a href="%[1]s">%[3]s#%[2]s</a> vor`
publish_release=`veröffentlichte Release <a href="%[2]s"> "%[4]s" </a> in <a href="%[1]s">%[3]s</a>`
review_dismissed=`verwarf das Review von <b>%[4]s</b> in <a href="%[1]s">%[3]s#%[2]s</a>` review_dismissed=`verwarf das Review von <b>%[4]s</b> in <a href="%[1]s">%[3]s#%[2]s</a>`
review_dismissed_reason=Grund: review_dismissed_reason=Grund:
create_branch=legte den Branch <a href="%[2]s">%[3]s</a> in <a href="%[1]s">%[4]s</a> an create_branch=legte den Branch <a href="%[2]s">%[3]s</a> in <a href="%[1]s">%[4]s</a> an
@ -3262,6 +3465,8 @@ raw_minutes=Minuten
[dropzone] [dropzone]
default_message=Zum Hochladen hier klicken oder Datei ablegen. default_message=Zum Hochladen hier klicken oder Datei ablegen.
invalid_input_type=Dateien dieses Dateityps können nicht hochgeladen werden.
file_too_big=Dateigröße ({{filesize}} MB) überschreitet die Maximalgröße ({{maxFilesize}} MB).
remove_file=Datei entfernen remove_file=Datei entfernen
[notification] [notification]
@ -3298,6 +3503,7 @@ error.unit_not_allowed=Du hast keine Berechtigung, um auf diesen Repository-Bere
title=Pakete title=Pakete
desc=Repository-Pakete verwalten. desc=Repository-Pakete verwalten.
empty=Noch keine Pakete vorhanden. empty=Noch keine Pakete vorhanden.
no_metadata=Keine Metadaten.
empty.documentation=Weitere Informationen zur Paket-Registry findest Du in der <a target="_blank" rel="noopener noreferrer" href="%s">Dokumentation</a>. empty.documentation=Weitere Informationen zur Paket-Registry findest Du in der <a target="_blank" rel="noopener noreferrer" href="%s">Dokumentation</a>.
empty.repo=Hast du ein Paket hochgeladen, das hier nicht angezeigt wird? Gehe zu den <a href="%[1]s">Paketeinstellungen</a> und verlinke es mit diesem Repo. empty.repo=Hast du ein Paket hochgeladen, das hier nicht angezeigt wird? Gehe zu den <a href="%[1]s">Paketeinstellungen</a> und verlinke es mit diesem Repo.
registry.documentation=Für weitere Informationen zur %s-Registry, schaue in der <a target="_blank" rel="noopener noreferrer" href="%s">Dokumentation</a> nach. registry.documentation=Für weitere Informationen zur %s-Registry, schaue in der <a target="_blank" rel="noopener noreferrer" href="%s">Dokumentation</a> nach.
@ -3332,6 +3538,8 @@ alpine.repository=Repository-Informationen
alpine.repository.branches=Branches alpine.repository.branches=Branches
alpine.repository.repositories=Repositories alpine.repository.repositories=Repositories
alpine.repository.architectures=Architekturen alpine.repository.architectures=Architekturen
arch.registry=Server mit gebrauchtem Repository und Architektur zu <code>/etc/pacman.conf</code> hinzufügen:
arch.install=Paket mit pacman synchronisieren:
arch.repository=Repository-Informationen arch.repository=Repository-Informationen
arch.repository.repositories=Repositories arch.repository.repositories=Repositories
arch.repository.architectures=Architekturen arch.repository.architectures=Architekturen
@ -3382,6 +3590,7 @@ npm.install=Um das Paket mit npm zu installieren, führe den folgenden Befehl au
npm.install2=oder füge es zur package.json-Datei hinzu: npm.install2=oder füge es zur package.json-Datei hinzu:
npm.dependencies=Abhängigkeiten npm.dependencies=Abhängigkeiten
npm.dependencies.development=Entwicklungsabhängigkeiten npm.dependencies.development=Entwicklungsabhängigkeiten
npm.dependencies.bundle=Gebündelte Abhängigkeiten
npm.dependencies.peer=Peer Abhängigkeiten npm.dependencies.peer=Peer Abhängigkeiten
npm.dependencies.optional=Optionale Abhängigkeiten npm.dependencies.optional=Optionale Abhängigkeiten
npm.details.tag=Tag npm.details.tag=Tag
@ -3513,6 +3722,7 @@ runners.status.active=Aktiv
runners.status.offline=Offline runners.status.offline=Offline
runners.version=Version runners.version=Version
runners.reset_registration_token=Registrierungs-Token zurücksetzen runners.reset_registration_token=Registrierungs-Token zurücksetzen
runners.reset_registration_token_confirm=Möchtest du den aktuellen Token invalidieren und einen neuen generieren?
runners.reset_registration_token_success=Runner-Registrierungstoken erfolgreich zurückgesetzt runners.reset_registration_token_success=Runner-Registrierungstoken erfolgreich zurückgesetzt
runs.all_workflows=Alle Workflows runs.all_workflows=Alle Workflows
@ -3522,6 +3732,7 @@ runs.pushed_by=gepusht von
runs.invalid_workflow_helper=Die Workflow-Konfigurationsdatei ist ungültig. Bitte überprüfe Deine Konfigurationsdatei: %s runs.invalid_workflow_helper=Die Workflow-Konfigurationsdatei ist ungültig. Bitte überprüfe Deine Konfigurationsdatei: %s
runs.no_matching_online_runner_helper=Kein passender Runner online mit Label: %s runs.no_matching_online_runner_helper=Kein passender Runner online mit Label: %s
runs.no_job_without_needs=Der Workflow muss mindestens einen Job ohne Abhängigkeiten enthalten. runs.no_job_without_needs=Der Workflow muss mindestens einen Job ohne Abhängigkeiten enthalten.
runs.no_job=Der Workflow muss mindestens einen Job enthalten
runs.actor=Initiator runs.actor=Initiator
runs.status=Status runs.status=Status
runs.actors_no_select=Alle Initiatoren runs.actors_no_select=Alle Initiatoren
@ -3532,12 +3743,18 @@ runs.no_workflows.quick_start=Du weißt nicht, wie du mit Gitea Actions loslegst
runs.no_workflows.documentation=Weitere Informationen zu Gitea Actions findest du in der <a target="_blank" rel="noopener noreferrer" href="%s"> Dokumentation</a>. runs.no_workflows.documentation=Weitere Informationen zu Gitea Actions findest du in der <a target="_blank" rel="noopener noreferrer" href="%s"> Dokumentation</a>.
runs.no_runs=Der Workflow hat noch keine Ausführungen. runs.no_runs=Der Workflow hat noch keine Ausführungen.
runs.empty_commit_message=(leere Commit-Nachricht) runs.empty_commit_message=(leere Commit-Nachricht)
runs.expire_log_message=Protokolle wurden geleert, weil sie zu alt waren.
workflow.disable=Workflow deaktivieren workflow.disable=Workflow deaktivieren
workflow.disable_success=Workflow '%s' erfolgreich deaktiviert. workflow.disable_success=Workflow '%s' erfolgreich deaktiviert.
workflow.enable=Workflow aktivieren workflow.enable=Workflow aktivieren
workflow.enable_success=Workflow '%s' erfolgreich aktiviert. workflow.enable_success=Workflow '%s' erfolgreich aktiviert.
workflow.disabled=Workflow ist deaktiviert. workflow.disabled=Workflow ist deaktiviert.
workflow.run=Workflow ausführen
workflow.not_found=Workflow '%s' wurde nicht gefunden.
workflow.run_success=Workflow '%s' erfolgreich ausgeführt.
workflow.from_ref=Nutze Workflow von
workflow.has_workflow_dispatch=Dieser Workflow hat einen workflow_dispatch Event-Trigger.
need_approval_desc=Um Workflows für den Pull-Request eines Forks auszuführen, ist eine Genehmigung erforderlich. need_approval_desc=Um Workflows für den Pull-Request eines Forks auszuführen, ist eine Genehmigung erforderlich.
@ -3557,8 +3774,11 @@ variables.creation.success=Die Variable „%s“ wurde hinzugefügt.
variables.update.failed=Fehler beim Bearbeiten der Variable. variables.update.failed=Fehler beim Bearbeiten der Variable.
variables.update.success=Die Variable wurde bearbeitet. variables.update.success=Die Variable wurde bearbeitet.
logs.always_auto_scroll=Autoscroll für Logs immer aktivieren
logs.always_expand_running=Laufende Logs immer erweitern
[projects] [projects]
deleted.display_name=Gelöschtes Projekt
type-1.display_name=Individuelles Projekt type-1.display_name=Individuelles Projekt
type-2.display_name=Repository-Projekt type-2.display_name=Repository-Projekt
type-3.display_name=Organisationsprojekt type-3.display_name=Organisationsprojekt

View File

@ -905,6 +905,22 @@ func Routes() *web.Router {
}) })
} }
addActionsWorkflowRoutes := func(
m *web.Router,
reqChecker func(ctx *context.APIContext),
actw actions.WorkflowAPI,
) {
m.Group("/actions", func() {
m.Group("/workflows", func() {
m.Get("", reqToken(), reqChecker, actw.ListRepositoryWorkflows)
m.Get("/{workflow_id}", reqToken(), reqChecker, actw.GetWorkflow)
m.Put("/{workflow_id}/disable", reqToken(), reqChecker, actw.DisableWorkflow)
m.Post("/{workflow_id}/dispatches", reqToken(), reqChecker, bind(api.CreateActionWorkflowDispatch{}), actw.DispatchWorkflow)
m.Put("/{workflow_id}/enable", reqToken(), reqChecker, actw.EnableWorkflow)
}, context.ReferencesGitRepo(), reqRepoWriter(unit.TypeActions))
})
}
m.Group("", func() { m.Group("", func() {
// Miscellaneous (no scope required) // Miscellaneous (no scope required)
if setting.API.EnableSwagger { if setting.API.EnableSwagger {
@ -1150,6 +1166,11 @@ func Routes() *web.Router {
reqOwner(), reqOwner(),
repo.NewAction(), repo.NewAction(),
) )
addActionsWorkflowRoutes(
m,
reqRepoWriter(unit.TypeActions),
repo.NewActionWorkflow(),
)
m.Group("/hooks/git", func() { m.Group("/hooks/git", func() {
m.Combo("").Get(repo.ListGitHooks) m.Combo("").Get(repo.ListGitHooks)
m.Group("/{id}", func() { m.Group("/{id}", func() {

View File

@ -581,3 +581,270 @@ func ListActionTasks(ctx *context.APIContext) {
ctx.JSON(http.StatusOK, &res) ctx.JSON(http.StatusOK, &res)
} }
// ActionWorkflow implements actions_service.WorkflowAPI
type ActionWorkflow struct{}
// NewActionWorkflow creates a new ActionWorkflow service
func NewActionWorkflow() actions_service.WorkflowAPI {
return ActionWorkflow{}
}
func (a ActionWorkflow) ListRepositoryWorkflows(ctx *context.APIContext) {
// swagger:operation GET /repos/{owner}/{repo}/actions/workflows repository ListRepositoryWorkflows
// ---
// summary: List repository workflows
// 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
// responses:
// "200":
// "$ref": "#/responses/ActionWorkflowList"
// "400":
// "$ref": "#/responses/error"
// "403":
// "$ref": "#/responses/forbidden"
// "404":
// "$ref": "#/responses/notFound"
// "422":
// "$ref": "#/responses/validationError"
// "500":
// "$ref": "#/responses/error"
workflows, err := actions_service.ListActionWorkflows(ctx)
if err != nil {
ctx.Error(http.StatusInternalServerError, "ListActionWorkflows", err)
return
}
if len(workflows) == 0 {
ctx.Error(http.StatusNotFound, "ListActionWorkflows", err)
return
}
ctx.SetTotalCountHeader(int64(len(workflows)))
ctx.JSON(http.StatusOK, workflows)
}
func (a ActionWorkflow) GetWorkflow(ctx *context.APIContext) {
// swagger:operation GET /repos/{owner}/{repo}/actions/workflows/{workflow_id} repository GetWorkflow
// ---
// summary: Get a workflow
// 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: workflow_id
// in: path
// description: id of the workflow
// type: string
// required: true
// responses:
// "200":
// "$ref": "#/responses/ActionWorkflow"
// "400":
// "$ref": "#/responses/error"
// "403":
// "$ref": "#/responses/forbidden"
// "404":
// "$ref": "#/responses/notFound"
// "422":
// "$ref": "#/responses/validationError"
// "500":
// "$ref": "#/responses/error"
workflowID := ctx.PathParam("workflow_id")
if len(workflowID) == 0 {
ctx.Error(http.StatusUnprocessableEntity, "MissingWorkflowParameter", util.NewInvalidArgumentErrorf("workflow_id is required parameter"))
return
}
workflow, err := actions_service.GetActionWorkflow(ctx, workflowID)
if err != nil {
ctx.Error(http.StatusInternalServerError, "GetActionWorkflow", err)
return
}
if workflow == nil {
ctx.Error(http.StatusNotFound, "GetActionWorkflow", err)
return
}
ctx.JSON(http.StatusOK, workflow)
}
func (a ActionWorkflow) DisableWorkflow(ctx *context.APIContext) {
// swagger:operation PUT /repos/{owner}/{repo}/actions/workflows/{workflow_id}/disable repository DisableWorkflow
// ---
// summary: Disable a workflow
// 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: workflow_id
// in: path
// description: id of the workflow
// type: string
// required: true
// responses:
// "204":
// description: No Content
// "400":
// "$ref": "#/responses/error"
// "403":
// "$ref": "#/responses/forbidden"
// "404":
// "$ref": "#/responses/notFound"
// "422":
// "$ref": "#/responses/validationError"
workflowID := ctx.PathParam("workflow_id")
if len(workflowID) == 0 {
ctx.Error(http.StatusUnprocessableEntity, "MissingWorkflowParameter", util.NewInvalidArgumentErrorf("workflow_id is required parameter"))
return
}
err := actions_service.DisableActionWorkflow(ctx, workflowID)
if err != nil {
ctx.Error(http.StatusInternalServerError, "DisableActionWorkflow", err)
return
}
ctx.Status(http.StatusNoContent)
}
func (a ActionWorkflow) DispatchWorkflow(ctx *context.APIContext) {
// swagger:operation POST /repos/{owner}/{repo}/actions/workflows/{workflow_id}/dispatches repository DispatchWorkflow
// ---
// summary: Create a workflow dispatch event
// 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: workflow_id
// in: path
// description: id of the workflow
// type: string
// required: true
// - name: body
// in: body
// schema:
// "$ref": "#/definitions/CreateActionWorkflowDispatch"
// responses:
// "204":
// description: No Content
// "400":
// "$ref": "#/responses/error"
// "403":
// "$ref": "#/responses/forbidden"
// "404":
// "$ref": "#/responses/notFound"
// "422":
// "$ref": "#/responses/validationError"
opt := web.GetForm(ctx).(*api.CreateActionWorkflowDispatch)
workflowID := ctx.PathParam("workflow_id")
if len(workflowID) == 0 {
ctx.Error(http.StatusUnprocessableEntity, "MissingWorkflowParameter", util.NewInvalidArgumentErrorf("workflow_id is required parameter"))
return
}
ref := opt.Ref
if len(ref) == 0 {
ctx.Error(http.StatusUnprocessableEntity, "MissingWorkflowParameter", util.NewInvalidArgumentErrorf("ref is required parameter"))
return
}
actions_service.DispatchActionWorkflow(ctx, workflowID, opt)
ctx.Status(http.StatusNoContent)
}
func (a ActionWorkflow) EnableWorkflow(ctx *context.APIContext) {
// swagger:operation PUT /repos/{owner}/{repo}/actions/workflows/{workflow_id}/enable repository EnableWorkflow
// ---
// summary: Enable a workflow
// 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: workflow_id
// in: path
// description: id of the workflow
// type: string
// required: true
// responses:
// "204":
// description: No Content
// "400":
// "$ref": "#/responses/error"
// "403":
// "$ref": "#/responses/forbidden"
// "404":
// "$ref": "#/responses/notFound"
// "409":
// "$ref": "#/responses/conflict"
// "422":
// "$ref": "#/responses/validationError"
workflowID := ctx.PathParam("workflow_id")
if len(workflowID) == 0 {
ctx.Error(http.StatusUnprocessableEntity, "MissingWorkflowParameter", util.NewInvalidArgumentErrorf("workflow_id is required parameter"))
return
}
err := actions_service.EnableActionWorkflow(ctx, workflowID)
if err != nil {
ctx.Error(http.StatusInternalServerError, "EnableActionWorkflow", err)
return
}
ctx.Status(http.StatusNoContent)
}

View File

@ -11,7 +11,6 @@ import (
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
"path"
"strings" "strings"
"time" "time"
@ -242,19 +241,14 @@ func getBlobForEntry(ctx *context.APIContext) (blob *git.Blob, entry *git.TreeEn
return nil, nil, nil return nil, nil, nil
} }
info, _, err := git.Entries([]*git.TreeEntry{entry}).GetCommitsInfo(ctx, ctx.Repo.Commit, path.Dir("/" + ctx.Repo.TreePath)[1:]) latestCommit, err := ctx.Repo.GitRepo.GetTreePathLatestCommit(ctx.Repo.Commit.ID.String(), ctx.Repo.TreePath)
if err != nil { if err != nil {
ctx.Error(http.StatusInternalServerError, "GetCommitsInfo", err) ctx.Error(http.StatusInternalServerError, "GetTreePathLatestCommit", err)
return nil, nil, nil return nil, nil, nil
} }
when := &latestCommit.Committer.When
if len(info) == 1 { return entry.Blob(), entry, when
// Not Modified
lastModified = &info[0].Commit.Committer.When
}
blob = entry.Blob()
return blob, entry, lastModified
} }
// GetArchive get archive of a repository // GetArchive get archive of a repository

View File

@ -32,3 +32,17 @@ type swaggerResponseVariableList struct {
// in:body // in:body
Body []api.ActionVariable `json:"body"` Body []api.ActionVariable `json:"body"`
} }
// ActionWorkflow
// swagger:response ActionWorkflow
type swaggerResponseActionWorkflow struct {
// in:body
Body api.ActionWorkflow `json:"body"`
}
// ActionWorkflowList
// swagger:response ActionWorkflowList
type swaggerResponseActionWorkflowList struct {
// in:body
Body []api.ActionWorkflow `json:"body"`
}

View File

@ -208,6 +208,9 @@ type swaggerParameterBodies struct {
// in:body // in:body
CreateVariableOption api.CreateVariableOption CreateVariableOption api.CreateVariableOption
// in:body
CreateActionWorkflowDispatch api.CreateActionWorkflowDispatch
// in:body // in:body
UpdateVariableOption api.UpdateVariableOption UpdateVariableOption api.UpdateVariableOption
} }

View File

@ -368,7 +368,7 @@ func handlePullRequestMerging(ctx *gitea_context.PrivateContext, opts *private.H
if err := pull_model.DeleteScheduledAutoMerge(ctx, pr.ID); err != nil && !db.IsErrNotExist(err) { if err := pull_model.DeleteScheduledAutoMerge(ctx, pr.ID); err != nil && !db.IsErrNotExist(err) {
return fmt.Errorf("DeleteScheduledAutoMerge[%d]: %v", opts.PullRequestID, err) return fmt.Errorf("DeleteScheduledAutoMerge[%d]: %v", opts.PullRequestID, err)
} }
if _, err := pr.SetMerged(ctx); err != nil { if _, err := pull_service.SetMerged(ctx, pr); err != nil {
return fmt.Errorf("SetMerged failed: %s/%s Error: %v", ownerName, repoName, err) return fmt.Errorf("SetMerged failed: %s/%s Error: %v", ownerName, repoName, err)
} }
return nil return nil

View File

@ -94,7 +94,7 @@ func Emails(ctx *context.Context) {
ctx.Data["Emails"] = emails ctx.Data["Emails"] = emails
pager := context.NewPagination(int(count), opts.PageSize, opts.Page, 5) pager := context.NewPagination(int(count), opts.PageSize, opts.Page, 5)
pager.SetDefaultParams(ctx) pager.AddParamFromRequest(ctx.Req)
ctx.Data["Page"] = pager ctx.Data["Page"] = pager
ctx.HTML(http.StatusOK, tplEmails) ctx.HTML(http.StatusOK, tplEmails)

View File

@ -77,9 +77,7 @@ func Packages(ctx *context.Context) {
ctx.Data["TotalUnreferencedBlobSize"] = totalUnreferencedBlobSize ctx.Data["TotalUnreferencedBlobSize"] = totalUnreferencedBlobSize
pager := context.NewPagination(int(total), setting.UI.PackagesPagingNum, page, 5) pager := context.NewPagination(int(total), setting.UI.PackagesPagingNum, page, 5)
pager.AddParamString("q", query) pager.AddParamFromRequest(ctx.Req)
pager.AddParamString("type", packageType)
pager.AddParamString("sort", sort)
ctx.Data["Page"] = pager ctx.Data["Page"] = pager
ctx.HTML(http.StatusOK, tplPackagesList) ctx.HTML(http.StatusOK, tplPackagesList)

View File

@ -4,7 +4,6 @@
package admin package admin
import ( import (
"fmt"
"net/http" "net/http"
"net/url" "net/url"
"strings" "strings"
@ -84,8 +83,7 @@ func UnadoptedRepos(ctx *context.Context) {
if !doSearch { if !doSearch {
pager := context.NewPagination(0, opts.PageSize, opts.Page, 5) pager := context.NewPagination(0, opts.PageSize, opts.Page, 5)
pager.SetDefaultParams(ctx) pager.AddParamFromRequest(ctx.Req)
pager.AddParamString("search", fmt.Sprint(doSearch))
ctx.Data["Page"] = pager ctx.Data["Page"] = pager
ctx.HTML(http.StatusOK, tplUnadoptedRepos) ctx.HTML(http.StatusOK, tplUnadoptedRepos)
return return
@ -99,8 +97,7 @@ func UnadoptedRepos(ctx *context.Context) {
} }
ctx.Data["Dirs"] = repoNames ctx.Data["Dirs"] = repoNames
pager := context.NewPagination(count, opts.PageSize, opts.Page, 5) pager := context.NewPagination(count, opts.PageSize, opts.Page, 5)
pager.SetDefaultParams(ctx) pager.AddParamFromRequest(ctx.Req)
pager.AddParamString("search", fmt.Sprint(doSearch))
ctx.Data["Page"] = pager ctx.Data["Page"] = pager
ctx.HTML(http.StatusOK, tplUnadoptedRepos) ctx.HTML(http.StatusOK, tplUnadoptedRepos)
} }

View File

@ -47,16 +47,12 @@ func Users(ctx *context.Context) {
ctx.Data["Title"] = ctx.Tr("admin.users") ctx.Data["Title"] = ctx.Tr("admin.users")
ctx.Data["PageIsAdminUsers"] = true ctx.Data["PageIsAdminUsers"] = true
extraParamStrings := map[string]string{}
statusFilterKeys := []string{"is_active", "is_admin", "is_restricted", "is_2fa_enabled", "is_prohibit_login"} statusFilterKeys := []string{"is_active", "is_admin", "is_restricted", "is_2fa_enabled", "is_prohibit_login"}
statusFilterMap := map[string]string{} statusFilterMap := map[string]string{}
for _, filterKey := range statusFilterKeys { for _, filterKey := range statusFilterKeys {
paramKey := "status_filter[" + filterKey + "]" paramKey := "status_filter[" + filterKey + "]"
paramVal := ctx.FormString(paramKey) paramVal := ctx.FormString(paramKey)
statusFilterMap[filterKey] = paramVal statusFilterMap[filterKey] = paramVal
if paramVal != "" {
extraParamStrings[paramKey] = paramVal
}
} }
sortType := ctx.FormString("sort") sortType := ctx.FormString("sort")
@ -82,7 +78,6 @@ func Users(ctx *context.Context) {
IsTwoFactorEnabled: util.OptionalBoolParse(statusFilterMap["is_2fa_enabled"]), IsTwoFactorEnabled: util.OptionalBoolParse(statusFilterMap["is_2fa_enabled"]),
IsProhibitLogin: util.OptionalBoolParse(statusFilterMap["is_prohibit_login"]), IsProhibitLogin: util.OptionalBoolParse(statusFilterMap["is_prohibit_login"]),
IncludeReserved: true, // administrator needs to list all accounts include reserved, bot, remote ones IncludeReserved: true, // administrator needs to list all accounts include reserved, bot, remote ones
ExtraParamStrings: extraParamStrings,
}, tplUsers) }, tplUsers)
} }

View File

@ -137,8 +137,7 @@ func Code(ctx *context.Context) {
ctx.Data["SearchResultLanguages"] = searchResultLanguages ctx.Data["SearchResultLanguages"] = searchResultLanguages
pager := context.NewPagination(total, setting.UI.RepoSearchPagingNum, page, 5) pager := context.NewPagination(total, setting.UI.RepoSearchPagingNum, page, 5)
pager.SetDefaultParams(ctx) pager.AddParamFromRequest(ctx.Req)
pager.AddParamString("l", language)
ctx.Data["Page"] = pager ctx.Data["Page"] = pager
ctx.HTML(http.StatusOK, tplExploreCode) ctx.HTML(http.StatusOK, tplExploreCode)

View File

@ -4,7 +4,6 @@
package explore package explore
import ( import (
"fmt"
"net/http" "net/http"
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
@ -139,25 +138,7 @@ func RenderRepoSearch(ctx *context.Context, opts *RepoSearchOptions) {
ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
pager := context.NewPagination(int(count), opts.PageSize, page, 5) pager := context.NewPagination(int(count), opts.PageSize, page, 5)
pager.SetDefaultParams(ctx) pager.AddParamFromRequest(ctx.Req)
pager.AddParamString("topic", fmt.Sprint(topicOnly))
pager.AddParamString("language", language)
pager.AddParamString(relevantReposOnlyParam, fmt.Sprint(opts.OnlyShowRelevant))
if archived.Has() {
pager.AddParamString("archived", fmt.Sprint(archived.Value()))
}
if fork.Has() {
pager.AddParamString("fork", fmt.Sprint(fork.Value()))
}
if mirror.Has() {
pager.AddParamString("mirror", fmt.Sprint(mirror.Value()))
}
if template.Has() {
pager.AddParamString("template", fmt.Sprint(template.Value()))
}
if private.Has() {
pager.AddParamString("private", fmt.Sprint(private.Value()))
}
ctx.Data["Page"] = pager ctx.Data["Page"] = pager
ctx.HTML(http.StatusOK, opts.TplName) ctx.HTML(http.StatusOK, opts.TplName)

View File

@ -120,10 +120,7 @@ func RenderUserSearch(ctx *context.Context, opts *user_model.SearchUserOptions,
ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled ctx.Data["IsRepoIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled
pager := context.NewPagination(int(count), opts.PageSize, opts.Page, 5) pager := context.NewPagination(int(count), opts.PageSize, opts.Page, 5)
pager.SetDefaultParams(ctx) pager.AddParamFromRequest(ctx.Req)
for paramKey, paramVal := range opts.ExtraParamStrings {
pager.AddParamString(paramKey, paramVal)
}
ctx.Data["Page"] = pager ctx.Data["Page"] = pager
ctx.HTML(http.StatusOK, tplName) ctx.HTML(http.StatusOK, tplName)

View File

@ -4,7 +4,6 @@
package org package org
import ( import (
"fmt"
"net/http" "net/http"
"path" "path"
"strings" "strings"
@ -146,23 +145,7 @@ func home(ctx *context.Context, viewRepositories bool) {
ctx.Data["Total"] = count ctx.Data["Total"] = count
pager := context.NewPagination(int(count), setting.UI.User.RepoPagingNum, page, 5) pager := context.NewPagination(int(count), setting.UI.User.RepoPagingNum, page, 5)
pager.SetDefaultParams(ctx) pager.AddParamFromRequest(ctx.Req)
pager.AddParamString("language", language)
if archived.Has() {
pager.AddParamString("archived", fmt.Sprint(archived.Value()))
}
if fork.Has() {
pager.AddParamString("fork", fmt.Sprint(fork.Value()))
}
if mirror.Has() {
pager.AddParamString("mirror", fmt.Sprint(mirror.Value()))
}
if template.Has() {
pager.AddParamString("template", fmt.Sprint(template.Value()))
}
if private.Has() {
pager.AddParamString("private", fmt.Sprint(private.Value()))
}
ctx.Data["Page"] = pager ctx.Data["Page"] = pager
ctx.HTML(http.StatusOK, tplOrgHome) ctx.HTML(http.StatusOK, tplOrgHome)

View File

@ -120,7 +120,7 @@ func Projects(ctx *context.Context) {
} }
pager := context.NewPagination(int(total), setting.UI.IssuePagingNum, page, numPages) pager := context.NewPagination(int(total), setting.UI.IssuePagingNum, page, numPages)
pager.AddParamString("state", fmt.Sprint(ctx.Data["State"])) pager.AddParamFromRequest(ctx.Req)
ctx.Data["Page"] = pager ctx.Data["Page"] = pager
ctx.Data["CanWriteProjects"] = canWriteProjects(ctx) ctx.Data["CanWriteProjects"] = canWriteProjects(ctx)

View File

@ -6,7 +6,6 @@ package actions
import ( import (
"bytes" "bytes"
stdCtx "context" stdCtx "context"
"fmt"
"net/http" "net/http"
"slices" "slices"
"strings" "strings"
@ -262,10 +261,7 @@ func List(ctx *context.Context) {
ctx.Data["StatusInfoList"] = actions_model.GetStatusInfoList(ctx) ctx.Data["StatusInfoList"] = actions_model.GetStatusInfoList(ctx)
pager := context.NewPagination(int(total), opts.PageSize, opts.Page, 5) pager := context.NewPagination(int(total), opts.PageSize, opts.Page, 5)
pager.SetDefaultParams(ctx) pager.AddParamFromRequest(ctx.Req)
pager.AddParamString("workflow", workflowID)
pager.AddParamString("actor", fmt.Sprint(actorID))
pager.AddParamString("status", fmt.Sprint(status))
ctx.Data["Page"] = pager ctx.Data["Page"] = pager
ctx.Data["HasWorkflowsOrRuns"] = len(workflows) > 0 || len(runs) > 0 ctx.Data["HasWorkflowsOrRuns"] = len(workflows) > 0 || len(runs) > 0

View File

@ -87,7 +87,7 @@ func Branches(ctx *context.Context) {
ctx.Data["CommitStatuses"] = commitStatuses ctx.Data["CommitStatuses"] = commitStatuses
ctx.Data["DefaultBranchBranch"] = defaultBranch ctx.Data["DefaultBranchBranch"] = defaultBranch
pager := context.NewPagination(int(branchesCount), pageSize, page, 5) pager := context.NewPagination(int(branchesCount), pageSize, page, 5)
pager.SetDefaultParams(ctx) pager.AddParamFromRequest(ctx.Req)
ctx.Data["Page"] = pager ctx.Data["Page"] = pager
ctx.HTML(http.StatusOK, tplBranch) ctx.HTML(http.StatusOK, tplBranch)
} }

View File

@ -101,7 +101,7 @@ func Commits(ctx *context.Context) {
ctx.Data["CommitCount"] = commitsCount ctx.Data["CommitCount"] = commitsCount
pager := context.NewPagination(int(commitsCount), pageSize, page, 5) pager := context.NewPagination(int(commitsCount), pageSize, page, 5)
pager.SetDefaultParams(ctx) pager.AddParamFromRequest(ctx.Req)
ctx.Data["Page"] = pager ctx.Data["Page"] = pager
ctx.HTML(http.StatusOK, tplCommits) ctx.HTML(http.StatusOK, tplCommits)
} }
@ -139,7 +139,6 @@ func Graph(ctx *context.Context) {
if err != nil { if err != nil {
log.Warn("GetCommitGraphsCount error for generate graph exclude prs: %t branches: %s in %-v, Will Ignore branches and try again. Underlying Error: %v", hidePRRefs, branches, ctx.Repo.Repository, err) log.Warn("GetCommitGraphsCount error for generate graph exclude prs: %t branches: %s in %-v, Will Ignore branches and try again. Underlying Error: %v", hidePRRefs, branches, ctx.Repo.Repository, err)
realBranches = []string{} realBranches = []string{}
branches = []string{}
graphCommitsCount, err = ctx.Repo.GetCommitGraphsCount(ctx, hidePRRefs, realBranches, files) graphCommitsCount, err = ctx.Repo.GetCommitGraphsCount(ctx, hidePRRefs, realBranches, files)
if err != nil { if err != nil {
ctx.ServerError("GetCommitGraphsCount", err) ctx.ServerError("GetCommitGraphsCount", err)
@ -175,14 +174,7 @@ func Graph(ctx *context.Context) {
ctx.Data["CommitCount"] = commitsCount ctx.Data["CommitCount"] = commitsCount
paginator := context.NewPagination(int(graphCommitsCount), setting.UI.GraphMaxCommitNum, page, 5) paginator := context.NewPagination(int(graphCommitsCount), setting.UI.GraphMaxCommitNum, page, 5)
paginator.AddParamString("mode", mode) paginator.AddParamFromRequest(ctx.Req)
paginator.AddParamString("hide-pr-refs", fmt.Sprint(hidePRRefs))
for _, branch := range branches {
paginator.AddParamString("branch", branch)
}
for _, file := range files {
paginator.AddParamString("file", file)
}
ctx.Data["Page"] = paginator ctx.Data["Page"] = paginator
if ctx.FormBool("div-only") { if ctx.FormBool("div-only") {
ctx.HTML(http.StatusOK, tplGraphDiv) ctx.HTML(http.StatusOK, tplGraphDiv)
@ -262,7 +254,7 @@ func FileHistory(ctx *context.Context) {
ctx.Data["CommitCount"] = commitsCount ctx.Data["CommitCount"] = commitsCount
pager := context.NewPagination(int(commitsCount), setting.Git.CommitsRangeSize, page, 5) pager := context.NewPagination(int(commitsCount), setting.Git.CommitsRangeSize, page, 5)
pager.SetDefaultParams(ctx) pager.AddParamFromRequest(ctx.Req)
ctx.Data["Page"] = pager ctx.Data["Page"] = pager
ctx.HTML(http.StatusOK, tplCommits) ctx.HTML(http.StatusOK, tplCommits)
} }

View File

@ -5,7 +5,6 @@
package repo package repo
import ( import (
"path"
"time" "time"
git_model "code.gitea.io/gitea/models/git" git_model "code.gitea.io/gitea/models/git"
@ -82,7 +81,7 @@ func ServeBlobOrLFS(ctx *context.Context, blob *git.Blob, lastModified *time.Tim
return common.ServeBlob(ctx.Base, ctx.Repo.TreePath, blob, lastModified) return common.ServeBlob(ctx.Base, ctx.Repo.TreePath, blob, lastModified)
} }
func getBlobForEntry(ctx *context.Context) (blob *git.Blob, lastModified *time.Time) { func getBlobForEntry(ctx *context.Context) (*git.Blob, *time.Time) {
entry, err := ctx.Repo.Commit.GetTreeEntryByPath(ctx.Repo.TreePath) entry, err := ctx.Repo.Commit.GetTreeEntryByPath(ctx.Repo.TreePath)
if err != nil { if err != nil {
if git.IsErrNotExist(err) { if git.IsErrNotExist(err) {
@ -98,19 +97,14 @@ func getBlobForEntry(ctx *context.Context) (blob *git.Blob, lastModified *time.T
return nil, nil return nil, nil
} }
info, _, err := git.Entries([]*git.TreeEntry{entry}).GetCommitsInfo(ctx, ctx.Repo.Commit, path.Dir("/" + ctx.Repo.TreePath)[1:]) latestCommit, err := ctx.Repo.GitRepo.GetTreePathLatestCommit(ctx.Repo.Commit.ID.String(), ctx.Repo.TreePath)
if err != nil { if err != nil {
ctx.ServerError("GetCommitsInfo", err) ctx.ServerError("GetTreePathLatestCommit", err)
return nil, nil return nil, nil
} }
lastModified := &latestCommit.Committer.When
if len(info) == 1 { return entry.Blob(), lastModified
// Not Modified
lastModified = &info[0].Commit.Committer.When
}
blob = entry.Blob()
return blob, lastModified
} }
// SingleDownload download a file by repos path // SingleDownload download a file by repos path

View File

@ -162,10 +162,11 @@ func TestUpdateIssueLabel_Toggle(t *testing.T) {
UpdateIssueLabel(ctx) UpdateIssueLabel(ctx)
assert.EqualValues(t, http.StatusOK, ctx.Resp.Status()) assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
for _, issueID := range testCase.IssueIDs { for _, issueID := range testCase.IssueIDs {
unittest.AssertExistsIf(t, testCase.ExpectedAdd, &issues_model.IssueLabel{ if testCase.ExpectedAdd {
IssueID: issueID, unittest.AssertExistsAndLoadBean(t, &issues_model.IssueLabel{IssueID: issueID, LabelID: testCase.LabelID})
LabelID: testCase.LabelID, } else {
}) unittest.AssertNotExistsBean(t, &issues_model.IssueLabel{IssueID: issueID, LabelID: testCase.LabelID})
}
} }
unittest.CheckConsistencyFor(t, &issues_model.Label{}) unittest.CheckConsistencyFor(t, &issues_model.Label{})
} }

View File

@ -4,7 +4,6 @@
package repo package repo
import ( import (
"fmt"
"net/http" "net/http"
"net/url" "net/url"
@ -93,8 +92,7 @@ func Milestones(ctx *context.Context) {
ctx.Data["IsShowClosed"] = isShowClosed ctx.Data["IsShowClosed"] = isShowClosed
pager := context.NewPagination(int(total), setting.UI.IssuePagingNum, page, 5) pager := context.NewPagination(int(total), setting.UI.IssuePagingNum, page, 5)
pager.AddParamString("state", fmt.Sprint(ctx.Data["State"])) pager.AddParamFromRequest(ctx.Req)
pager.AddParamString("q", keyword)
ctx.Data["Page"] = pager ctx.Data["Page"] = pager
ctx.HTML(http.StatusOK, tplMilestone) ctx.HTML(http.StatusOK, tplMilestone)

View File

@ -70,8 +70,7 @@ func Packages(ctx *context.Context) {
ctx.Data["RepositoryAccessMap"] = map[int64]bool{ctx.Repo.Repository.ID: true} // There is only the current repository ctx.Data["RepositoryAccessMap"] = map[int64]bool{ctx.Repo.Repository.ID: true} // There is only the current repository
pager := context.NewPagination(int(total), setting.UI.PackagesPagingNum, page, 5) pager := context.NewPagination(int(total), setting.UI.PackagesPagingNum, page, 5)
pager.AddParamString("q", query) pager.AddParamFromRequest(ctx.Req)
pager.AddParamString("type", packageType)
ctx.Data["Page"] = pager ctx.Data["Page"] = pager
ctx.HTML(http.StatusOK, tplPackagesList) ctx.HTML(http.StatusOK, tplPackagesList)

View File

@ -115,7 +115,7 @@ func Projects(ctx *context.Context) {
} }
pager := context.NewPagination(total, setting.UI.IssuePagingNum, page, numPages) pager := context.NewPagination(total, setting.UI.IssuePagingNum, page, numPages)
pager.AddParamString("state", fmt.Sprint(ctx.Data["State"])) pager.AddParamFromRequest(ctx.Req)
ctx.Data["Page"] = pager ctx.Data["Page"] = pager
ctx.Data["CanWriteProjects"] = ctx.Repo.Permission.CanWrite(unit.TypeProjects) ctx.Data["CanWriteProjects"] = ctx.Repo.Permission.CanWrite(unit.TypeProjects)

View File

@ -186,7 +186,7 @@ func Releases(ctx *context.Context) {
numReleases := ctx.Data["NumReleases"].(int64) numReleases := ctx.Data["NumReleases"].(int64)
pager := context.NewPagination(int(numReleases), listOptions.PageSize, listOptions.Page, 5) pager := context.NewPagination(int(numReleases), listOptions.PageSize, listOptions.Page, 5)
pager.SetDefaultParams(ctx) pager.AddParamFromRequest(ctx.Req)
ctx.Data["Page"] = pager ctx.Data["Page"] = pager
ctx.HTML(http.StatusOK, tplReleasesList) ctx.HTML(http.StatusOK, tplReleasesList)
} }
@ -240,7 +240,7 @@ func TagsList(ctx *context.Context) {
ctx.Data["TagCount"] = count ctx.Data["TagCount"] = count
pager := context.NewPagination(int(count), opts.PageSize, opts.Page, 5) pager := context.NewPagination(int(count), opts.PageSize, opts.Page, 5)
pager.SetDefaultParams(ctx) pager.AddParamFromRequest(ctx.Req)
ctx.Data["Page"] = pager ctx.Data["Page"] = pager
ctx.Data["PageIsViewCode"] = !ctx.Repo.Repository.UnitEnabled(ctx, unit.TypeReleases) ctx.Data["PageIsViewCode"] = !ctx.Repo.Repository.UnitEnabled(ctx, unit.TypeReleases)
ctx.HTML(http.StatusOK, tplTagsList) ctx.HTML(http.StatusOK, tplTagsList)

View File

@ -108,8 +108,7 @@ func Search(ctx *context.Context) {
ctx.Data["SearchResultLanguages"] = searchResultLanguages ctx.Data["SearchResultLanguages"] = searchResultLanguages
pager := context.NewPagination(total, setting.UI.RepoSearchPagingNum, page, 5) pager := context.NewPagination(total, setting.UI.RepoSearchPagingNum, page, 5)
pager.SetDefaultParams(ctx) pager.AddParamFromRequest(ctx.Req)
pager.AddParamString("l", language)
ctx.Data["Page"] = pager ctx.Data["Page"] = pager
ctx.HTML(http.StatusOK, tplSearch) ctx.HTML(http.StatusOK, tplSearch)

View File

@ -440,8 +440,7 @@ func renderRevisionPage(ctx *context.Context) (*git.Repository, *git.TreeEntry)
ctx.Data["Commits"] = git_model.ConvertFromGitCommit(ctx, commitsHistory, ctx.Repo.Repository) ctx.Data["Commits"] = git_model.ConvertFromGitCommit(ctx, commitsHistory, ctx.Repo.Repository)
pager := context.NewPagination(int(commitsCount), setting.Git.CommitsRangeSize, page, 5) pager := context.NewPagination(int(commitsCount), setting.Git.CommitsRangeSize, page, 5)
pager.SetDefaultParams(ctx) pager.AddParamFromRequest(ctx.Req)
pager.AddParamString("action", "_revision")
ctx.Data["Page"] = pager ctx.Data["Page"] = pager
return wikiRepo, entry return wikiRepo, entry

View File

@ -121,8 +121,7 @@ func CodeSearch(ctx *context.Context) {
ctx.Data["SearchResultLanguages"] = searchResultLanguages ctx.Data["SearchResultLanguages"] = searchResultLanguages
pager := context.NewPagination(total, setting.UI.RepoSearchPagingNum, page, 5) pager := context.NewPagination(total, setting.UI.RepoSearchPagingNum, page, 5)
pager.SetDefaultParams(ctx) pager.AddParamFromRequest(ctx.Req)
pager.AddParamString("l", language)
ctx.Data["Page"] = pager ctx.Data["Page"] = pager
ctx.HTML(http.StatusOK, tplUserCode) ctx.HTML(http.StatusOK, tplUserCode)

View File

@ -139,7 +139,7 @@ func Dashboard(ctx *context.Context) {
ctx.Data["Feeds"] = feeds ctx.Data["Feeds"] = feeds
pager := context.NewPagination(int(count), setting.UI.FeedPagingNum, page, 5) pager := context.NewPagination(int(count), setting.UI.FeedPagingNum, page, 5)
pager.AddParamString("date", date) pager.AddParamFromRequest(ctx.Req)
ctx.Data["Page"] = pager ctx.Data["Page"] = pager
ctx.HTML(http.StatusOK, tplDashboard) ctx.HTML(http.StatusOK, tplDashboard)
@ -330,10 +330,7 @@ func Milestones(ctx *context.Context) {
ctx.Data["IsShowClosed"] = isShowClosed ctx.Data["IsShowClosed"] = isShowClosed
pager := context.NewPagination(pagerCount, setting.UI.IssuePagingNum, page, 5) pager := context.NewPagination(pagerCount, setting.UI.IssuePagingNum, page, 5)
pager.AddParamString("q", keyword) pager.AddParamFromRequest(ctx.Req)
pager.AddParamString("repos", reposQuery)
pager.AddParamString("sort", sortType)
pager.AddParamString("state", fmt.Sprint(ctx.Data["State"]))
ctx.Data["Page"] = pager ctx.Data["Page"] = pager
ctx.HTML(http.StatusOK, tplMilestones) ctx.HTML(http.StatusOK, tplMilestones)

View File

@ -173,7 +173,7 @@ func getNotifications(ctx *context.Context) {
ctx.Data["Status"] = status ctx.Data["Status"] = status
ctx.Data["Notifications"] = notifications ctx.Data["Notifications"] = notifications
pager.SetDefaultParams(ctx) pager.AddParamFromRequest(ctx.Req)
ctx.Data["Page"] = pager ctx.Data["Page"] = pager
} }
@ -357,8 +357,7 @@ func NotificationSubscriptions(ctx *context.Context) {
ctx.Redirect(fmt.Sprintf("/notifications/subscriptions?page=%d", pager.Paginater.Current())) ctx.Redirect(fmt.Sprintf("/notifications/subscriptions?page=%d", pager.Paginater.Current()))
return return
} }
pager.AddParamString("sort", sortType) pager.AddParamFromRequest(ctx.Req)
pager.AddParamString("state", state)
ctx.Data["Page"] = pager ctx.Data["Page"] = pager
ctx.HTML(http.StatusOK, tplNotificationSubscriptions) ctx.HTML(http.StatusOK, tplNotificationSubscriptions)
@ -446,22 +445,7 @@ func NotificationWatching(ctx *context.Context) {
// redirect to last page if request page is more than total pages // redirect to last page if request page is more than total pages
pager := context.NewPagination(total, setting.UI.User.RepoPagingNum, page, 5) pager := context.NewPagination(total, setting.UI.User.RepoPagingNum, page, 5)
pager.SetDefaultParams(ctx) pager.AddParamFromRequest(ctx.Req)
if archived.Has() {
pager.AddParamString("archived", fmt.Sprint(archived.Value()))
}
if fork.Has() {
pager.AddParamString("fork", fmt.Sprint(fork.Value()))
}
if mirror.Has() {
pager.AddParamString("mirror", fmt.Sprint(mirror.Value()))
}
if template.Has() {
pager.AddParamString("template", fmt.Sprint(template.Value()))
}
if private.Has() {
pager.AddParamString("private", fmt.Sprint(private.Value()))
}
ctx.Data["Page"] = pager ctx.Data["Page"] = pager
ctx.Data["Status"] = 2 ctx.Data["Status"] = 2

View File

@ -128,8 +128,7 @@ func ListPackages(ctx *context.Context) {
} }
pager := context.NewPagination(int(total), setting.UI.PackagesPagingNum, page, 5) pager := context.NewPagination(int(total), setting.UI.PackagesPagingNum, page, 5)
pager.AddParamString("q", query) pager.AddParamFromRequest(ctx.Req)
pager.AddParamString("type", packageType)
ctx.Data["Page"] = pager ctx.Data["Page"] = pager
ctx.HTML(http.StatusOK, tplPackagesList) ctx.HTML(http.StatusOK, tplPackagesList)
@ -348,11 +347,6 @@ func ListPackageVersions(ctx *context.Context) {
ctx.Data["Query"] = query ctx.Data["Query"] = query
ctx.Data["Sort"] = sort ctx.Data["Sort"] = sort
pagerParams := map[string]string{
"q": query,
"sort": sort,
}
var ( var (
total int64 total int64
pvs []*packages_model.PackageVersion pvs []*packages_model.PackageVersion
@ -361,7 +355,6 @@ func ListPackageVersions(ctx *context.Context) {
case packages_model.TypeContainer: case packages_model.TypeContainer:
tagged := ctx.FormTrim("tagged") tagged := ctx.FormTrim("tagged")
pagerParams["tagged"] = tagged
ctx.Data["Tagged"] = tagged ctx.Data["Tagged"] = tagged
pvs, total, err = container_model.SearchImageTags(ctx, &container_model.ImageTagsSearchOptions{ pvs, total, err = container_model.SearchImageTags(ctx, &container_model.ImageTagsSearchOptions{
@ -407,9 +400,7 @@ func ListPackageVersions(ctx *context.Context) {
} }
pager := context.NewPagination(int(total), setting.UI.PackagesPagingNum, page, 5) pager := context.NewPagination(int(total), setting.UI.PackagesPagingNum, page, 5)
for k, v := range pagerParams { pager.AddParamFromRequest(ctx.Req)
pager.AddParamString(k, v)
}
ctx.Data["Page"] = pager ctx.Data["Page"] = pager
ctx.HTML(http.StatusOK, tplPackageVersionList) ctx.HTML(http.StatusOK, tplPackageVersionList)

View File

@ -222,7 +222,7 @@ func Organization(ctx *context.Context) {
ctx.Data["Orgs"] = orgs ctx.Data["Orgs"] = orgs
pager := context.NewPagination(int(total), opts.PageSize, opts.Page, 5) pager := context.NewPagination(int(total), opts.PageSize, opts.Page, 5)
pager.SetDefaultParams(ctx) pager.AddParamFromRequest(ctx.Req)
ctx.Data["Page"] = pager ctx.Data["Page"] = pager
ctx.HTML(http.StatusOK, tplSettingsOrganization) ctx.HTML(http.StatusOK, tplSettingsOrganization)
} }
@ -329,7 +329,7 @@ func Repos(ctx *context.Context) {
} }
ctx.Data["ContextUser"] = ctxUser ctx.Data["ContextUser"] = ctxUser
pager := context.NewPagination(count, opts.PageSize, opts.Page, 5) pager := context.NewPagination(count, opts.PageSize, opts.Page, 5)
pager.SetDefaultParams(ctx) pager.AddParamFromRequest(ctx.Req)
ctx.Data["Page"] = pager ctx.Data["Page"] = pager
ctx.HTML(http.StatusOK, tplSettingsRepositories) ctx.HTML(http.StatusOK, tplSettingsRepositories)
} }

View File

@ -0,0 +1,282 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package actions
import (
"fmt"
"net/http"
"os"
"strconv"
"strings"
actions_model "code.gitea.io/gitea/models/actions"
"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"
"code.gitea.io/gitea/models/unit"
"code.gitea.io/gitea/modules/actions"
"code.gitea.io/gitea/modules/git"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/convert"
"github.com/nektos/act/pkg/jobparser"
"github.com/nektos/act/pkg/model"
)
func getActionWorkflowPath(commit *git.Commit) string {
paths := []string{".gitea/workflows", ".github/workflows"}
for _, path := range paths {
if _, err := commit.SubTree(path); err == nil {
return path
}
}
return ""
}
func getActionWorkflowEntry(ctx *context.APIContext, commit *git.Commit, entry *git.TreeEntry) *api.ActionWorkflow {
cfgUnit := ctx.Repo.Repository.MustGetUnit(ctx, unit.TypeActions)
cfg := cfgUnit.ActionsConfig()
defaultBranch, _ := commit.GetBranchName()
URL := fmt.Sprintf("%s/actions/workflows/%s", ctx.Repo.Repository.APIURL(), entry.Name())
HTMLURL := fmt.Sprintf("%s/src/branch/%s/%s/%s", ctx.Repo.Repository.HTMLURL(ctx), defaultBranch, getActionWorkflowPath(commit), entry.Name())
badgeURL := fmt.Sprintf("%s/actions/workflows/%s/badge.svg?branch=%s", ctx.Repo.Repository.HTMLURL(ctx), entry.Name(), ctx.Repo.Repository.DefaultBranch)
// See https://docs.github.com/en/rest/actions/workflows?apiVersion=2022-11-28#get-a-workflow
// State types:
// - active
// - deleted
// - disabled_fork
// - disabled_inactivity
// - disabled_manually
state := "active"
if cfg.IsWorkflowDisabled(entry.Name()) {
state = "disabled_manually"
}
// Currently, the NodeID returns the hostname of the server since, as far as I know, Gitea does not have a parameter
// similar to an instance ID.
hostname, err := os.Hostname()
if err != nil {
hostname = "unknown"
}
// The CreatedAt and UpdatedAt fields currently reflect the timestamp of the latest commit, which can later be refined
// by retrieving the first and last commits for the file history. The first commit would indicate the creation date,
// while the last commit would represent the modification date. The DeletedAt could be determined by identifying
// the last commit where the file existed. However, this implementation has not been done here yet, as it would likely
// cause a significant performance degradation.
createdAt := commit.Author.When
updatedAt := commit.Author.When
return &api.ActionWorkflow{
ID: entry.Name(),
NodeID: hostname,
Name: entry.Name(),
Path: entry.Name(),
State: state,
CreatedAt: createdAt,
UpdatedAt: updatedAt,
URL: URL,
HTMLURL: HTMLURL,
BadgeURL: badgeURL,
}
}
func disableOrEnableWorkflow(ctx *context.APIContext, workflowID string, isEnable bool) error {
workflow, err := GetActionWorkflow(ctx, workflowID)
if err != nil {
return err
}
cfgUnit := ctx.Repo.Repository.MustGetUnit(ctx, unit.TypeActions)
cfg := cfgUnit.ActionsConfig()
if isEnable {
cfg.EnableWorkflow(workflow.ID)
} else {
cfg.DisableWorkflow(workflow.ID)
}
return repo_model.UpdateRepoUnit(ctx, cfgUnit)
}
func ListActionWorkflows(ctx *context.APIContext) ([]*api.ActionWorkflow, error) {
defaultBranchCommit, err := ctx.Repo.GitRepo.GetBranchCommit(ctx.Repo.Repository.DefaultBranch)
if err != nil {
ctx.Error(http.StatusInternalServerError, "WorkflowDefaultBranchError", err.Error())
return nil, err
}
entries, err := actions.ListWorkflows(defaultBranchCommit)
if err != nil {
ctx.Error(http.StatusNotFound, "WorkflowListNotFound", err.Error())
return nil, err
}
workflows := make([]*api.ActionWorkflow, len(entries))
for i, entry := range entries {
workflows[i] = getActionWorkflowEntry(ctx, defaultBranchCommit, entry)
}
return workflows, nil
}
func GetActionWorkflow(ctx *context.APIContext, workflowID string) (*api.ActionWorkflow, error) {
entries, err := ListActionWorkflows(ctx)
if err != nil {
return nil, err
}
for _, entry := range entries {
if entry.Name == workflowID {
return entry, nil
}
}
return nil, fmt.Errorf("workflow '%s' not found", workflowID)
}
func DisableActionWorkflow(ctx *context.APIContext, workflowID string) error {
return disableOrEnableWorkflow(ctx, workflowID, false)
}
func DispatchActionWorkflow(ctx *context.APIContext, workflowID string, opt *api.CreateActionWorkflowDispatch) {
cfgUnit := ctx.Repo.Repository.MustGetUnit(ctx, unit.TypeActions)
cfg := cfgUnit.ActionsConfig()
if cfg.IsWorkflowDisabled(workflowID) {
ctx.Error(http.StatusInternalServerError, "WorkflowDisabled", fmt.Sprintf("workflow '%s' is disabled", workflowID))
return
}
refName := git.RefName(opt.Ref)
var runTargetCommit *git.Commit
var err error
switch {
case refName.IsTag():
runTargetCommit, err = ctx.Repo.GitRepo.GetTagCommit(refName.TagName())
case refName.IsBranch():
runTargetCommit, err = ctx.Repo.GitRepo.GetBranchCommit(refName.BranchName())
default:
ctx.Error(http.StatusInternalServerError, "WorkflowRefNameError", fmt.Sprintf("%s must be a well-formed Git reference name.", opt.Ref))
return
}
if err != nil {
ctx.Error(http.StatusNotFound, "WorkflowRefNotFound", fmt.Sprintf("target ref does not exist %s", opt.Ref))
return
}
defaultBranchCommit, err := ctx.Repo.GitRepo.GetBranchCommit(ctx.Repo.Repository.DefaultBranch)
if err != nil {
ctx.Error(http.StatusInternalServerError, "WorkflowDefaultBranchError", err.Error())
return
}
entries, err := actions.ListWorkflows(defaultBranchCommit)
if err != nil {
ctx.Error(http.StatusNotFound, "WorkflowListNotFound", err.Error())
return
}
var workflow *jobparser.SingleWorkflow
for _, entry := range entries {
if entry.Name() == workflowID {
content, err := actions.GetContentFromEntry(entry)
if err != nil {
ctx.Error(http.StatusInternalServerError, "WorkflowGetContentError", err.Error())
return
}
workflows, err := jobparser.Parse(content)
if err != nil {
ctx.Error(http.StatusInternalServerError, "WorkflowParseError", err.Error())
return
}
if len(workflows) == 0 {
ctx.Error(http.StatusNotFound, "WorkflowNotFound", fmt.Sprintf("workflow '%s' is not found", workflowID))
return
}
workflow = workflows[0]
break
}
}
// Process workflow inputs
inputs := processWorkflowInputs(opt, &model.Workflow{
RawOn: workflow.RawOn,
})
workflowDispatchPayload := &api.WorkflowDispatchPayload{
Workflow: workflowID,
Ref: opt.Ref,
Repository: convert.ToRepo(ctx, ctx.Repo.Repository, access_model.Permission{AccessMode: perm.AccessModeNone}),
Inputs: inputs,
Sender: convert.ToUserWithAccessMode(ctx, ctx.Doer, perm.AccessModeNone),
}
eventPayload, err := workflowDispatchPayload.JSONPayload()
if err != nil {
ctx.Error(http.StatusInternalServerError, "WorkflowDispatchJSONParseError", err.Error())
return
}
run := &actions_model.ActionRun{
Title: strings.SplitN(runTargetCommit.CommitMessage, "\n", 2)[0],
RepoID: ctx.Repo.Repository.ID,
OwnerID: ctx.Repo.Repository.Owner.ID,
WorkflowID: workflowID,
TriggerUserID: ctx.Doer.ID,
Ref: opt.Ref,
CommitSHA: runTargetCommit.ID.String(),
IsForkPullRequest: false,
Event: "workflow_dispatch",
TriggerEvent: "workflow_dispatch",
EventPayload: string(eventPayload),
Status: actions_model.StatusWaiting,
}
if err := actions_model.InsertRun(ctx, run, []*jobparser.SingleWorkflow{workflow}); err != nil {
ctx.Error(http.StatusInternalServerError, "WorkflowInsertRunError", err.Error())
return
}
alljobs, err := db.Find[actions_model.ActionRunJob](ctx, actions_model.FindRunJobOptions{RunID: run.ID})
if err != nil {
ctx.Error(http.StatusInternalServerError, "WorkflowFindRunJobError", err.Error())
return
}
CreateCommitStatus(ctx, alljobs...)
}
func processWorkflowInputs(opt *api.CreateActionWorkflowDispatch, workflow *model.Workflow) map[string]any {
inputs := make(map[string]any)
if workflowDispatch := workflow.WorkflowDispatchConfig(); workflowDispatch != nil {
for name, config := range workflowDispatch.Inputs {
value, exists := opt.Inputs[name]
if !exists {
continue
}
if value == "" {
value = config.Default
}
switch config.Type {
case "boolean":
inputs[name] = strconv.FormatBool(value == "on")
default:
inputs[name] = value
}
}
}
return inputs
}
func EnableActionWorkflow(ctx *context.APIContext, workflowID string) error {
return disableOrEnableWorkflow(ctx, workflowID, true)
}

View File

@ -0,0 +1,20 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package actions
import "code.gitea.io/gitea/services/context"
// WorkflowAPI for action workflow of a repository
type WorkflowAPI interface {
// ListRepositoryWorkflows list repository workflows
ListRepositoryWorkflows(*context.APIContext)
// GetWorkflow get a workflow
GetWorkflow(*context.APIContext)
// DisableWorkflow disable a workflow
DisableWorkflow(*context.APIContext)
// DispatchWorkflow create a workflow dispatch event
DispatchWorkflow(*context.APIContext)
// EnableWorkflow enable a workflow
EnableWorkflow(*context.APIContext)
}

View File

@ -27,19 +27,13 @@ func NewPagination(total, pagingNum, current, numPages int) *Pagination {
return p return p
} }
// AddParamString adds a string parameter directly
func (p *Pagination) AddParamString(key, value string) {
urlParam := fmt.Sprintf("%s=%v", url.QueryEscape(key), url.QueryEscape(value))
p.urlParams = append(p.urlParams, urlParam)
}
func (p *Pagination) AddParamFromRequest(req *http.Request) { func (p *Pagination) AddParamFromRequest(req *http.Request) {
for key, values := range req.URL.Query() { for key, values := range req.URL.Query() {
if key == "page" || len(values) == 0 { if key == "page" || len(values) == 0 || (len(values) == 1 && values[0] == "") {
continue continue
} }
for _, value := range values { for _, value := range values {
urlParam := fmt.Sprintf("%s=%v", key, url.QueryEscape(value)) urlParam := fmt.Sprintf("%s=%v", url.QueryEscape(key), url.QueryEscape(value))
p.urlParams = append(p.urlParams, urlParam) p.urlParams = append(p.urlParams, urlParam)
} }
} }
@ -49,17 +43,3 @@ func (p *Pagination) AddParamFromRequest(req *http.Request) {
func (p *Pagination) GetParams() template.URL { func (p *Pagination) GetParams() template.URL {
return template.URL(strings.Join(p.urlParams, "&")) return template.URL(strings.Join(p.urlParams, "&"))
} }
// SetDefaultParams sets common pagination params that are often used
func (p *Pagination) SetDefaultParams(ctx *Context) {
if v, ok := ctx.Data["SortType"].(string); ok {
p.AddParamString("sort", v)
}
if v, ok := ctx.Data["Keyword"].(string); ok {
p.AddParamString("q", v)
}
if v, ok := ctx.Data["IsFuzzy"].(bool); ok {
p.AddParamString("fuzzy", fmt.Sprint(v))
}
// do not add any more uncommon params here!
}

View File

@ -14,11 +14,11 @@ import (
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
base "code.gitea.io/gitea/modules/migration" base "code.gitea.io/gitea/modules/migration"
"code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
"github.com/aws/aws-sdk-go-v2/credentials" "github.com/aws/aws-sdk-go-v2/credentials"
"github.com/aws/aws-sdk-go-v2/service/codecommit" "github.com/aws/aws-sdk-go-v2/service/codecommit"
"github.com/aws/aws-sdk-go-v2/service/codecommit/types" "github.com/aws/aws-sdk-go-v2/service/codecommit/types"
"github.com/aws/aws-sdk-go/aws"
) )
var ( var (
@ -94,7 +94,7 @@ func (c *CodeCommitDownloader) SetContext(ctx context.Context) {
// GetRepoInfo returns a repository information // GetRepoInfo returns a repository information
func (c *CodeCommitDownloader) GetRepoInfo() (*base.Repository, error) { func (c *CodeCommitDownloader) GetRepoInfo() (*base.Repository, error) {
output, err := c.codeCommitClient.GetRepository(c.ctx, &codecommit.GetRepositoryInput{ output, err := c.codeCommitClient.GetRepository(c.ctx, &codecommit.GetRepositoryInput{
RepositoryName: aws.String(c.repoName), RepositoryName: util.ToPointer(c.repoName),
}) })
if err != nil { if err != nil {
return nil, err return nil, err
@ -126,7 +126,7 @@ func (c *CodeCommitDownloader) GetComments(commentable base.Commentable) ([]*bas
for { for {
resp, err := c.codeCommitClient.GetCommentsForPullRequest(c.ctx, &codecommit.GetCommentsForPullRequestInput{ resp, err := c.codeCommitClient.GetCommentsForPullRequest(c.ctx, &codecommit.GetCommentsForPullRequestInput{
NextToken: nextToken, NextToken: nextToken,
PullRequestId: aws.String(strconv.FormatInt(commentable.GetForeignIndex(), 10)), PullRequestId: util.ToPointer(strconv.FormatInt(commentable.GetForeignIndex(), 10)),
}) })
if err != nil { if err != nil {
return nil, false, err return nil, false, err
@ -171,7 +171,7 @@ func (c *CodeCommitDownloader) GetPullRequests(page, perPage int) ([]*base.PullR
prs := make([]*base.PullRequest, 0, len(batch)) prs := make([]*base.PullRequest, 0, len(batch))
for _, id := range batch { for _, id := range batch {
output, err := c.codeCommitClient.GetPullRequest(c.ctx, &codecommit.GetPullRequestInput{ output, err := c.codeCommitClient.GetPullRequest(c.ctx, &codecommit.GetPullRequestInput{
PullRequestId: aws.String(id), PullRequestId: util.ToPointer(id),
}) })
if err != nil { if err != nil {
return nil, false, err return nil, false, err
@ -243,7 +243,7 @@ func (c *CodeCommitDownloader) getAllPullRequestIDs() ([]string, error) {
for { for {
output, err := c.codeCommitClient.ListPullRequests(c.ctx, &codecommit.ListPullRequestsInput{ output, err := c.codeCommitClient.ListPullRequests(c.ctx, &codecommit.ListPullRequestsInput{
RepositoryName: aws.String(c.repoName), RepositoryName: util.ToPointer(c.repoName),
NextToken: nextToken, NextToken: nextToken,
}) })
if err != nil { if err != nil {

View File

@ -47,7 +47,7 @@ func TestRemoveOrgUser(t *testing.T) {
testSuccess := func(org *organization.Organization, user *user_model.User) { testSuccess := func(org *organization.Organization, user *user_model.User) {
expectedNumMembers := org.NumMembers expectedNumMembers := org.NumMembers
if unittest.BeanExists(t, &organization.OrgUser{OrgID: org.ID, UID: user.ID}) { if unittest.GetBean(t, &organization.OrgUser{OrgID: org.ID, UID: user.ID}) != nil {
expectedNumMembers-- expectedNumMembers--
} }
assert.NoError(t, RemoveOrgUser(db.DefaultContext, org, user)) assert.NoError(t, RemoveOrgUser(db.DefaultContext, org, user))

View File

@ -300,7 +300,7 @@ func manuallyMerged(ctx context.Context, pr *issues_model.PullRequest) bool {
pr.Merger = merger pr.Merger = merger
pr.MergerID = merger.ID pr.MergerID = merger.ID
if merged, err := pr.SetMerged(ctx); err != nil { if merged, err := SetMerged(ctx, pr); err != nil {
log.Error("%-v setMerged : %v", pr, err) log.Error("%-v setMerged : %v", pr, err)
return false return false
} else if !merged { } else if !merged {

View File

@ -639,7 +639,7 @@ func MergedManually(ctx context.Context, pr *issues_model.PullRequest, doer *use
pr.MergerID = doer.ID pr.MergerID = doer.ID
var merged bool var merged bool
if merged, err = pr.SetMerged(ctx); err != nil { if merged, err = SetMerged(ctx, pr); err != nil {
return err return err
} else if !merged { } else if !merged {
return fmt.Errorf("SetMerged failed") return fmt.Errorf("SetMerged failed")
@ -656,3 +656,62 @@ func MergedManually(ctx context.Context, pr *issues_model.PullRequest, doer *use
return handleCloseCrossReferences(ctx, pr, doer) return handleCloseCrossReferences(ctx, pr, doer)
} }
// SetMerged sets a pull request to merged and closes the corresponding issue
func SetMerged(ctx context.Context, pr *issues_model.PullRequest) (bool, error) {
if pr.HasMerged {
return false, fmt.Errorf("PullRequest[%d] already merged", pr.Index)
}
if pr.MergedCommitID == "" || pr.MergedUnix == 0 || pr.Merger == nil {
return false, fmt.Errorf("unable to merge PullRequest[%d], some required fields are empty", pr.Index)
}
pr.HasMerged = true
sess := db.GetEngine(ctx)
if _, err := sess.Exec("UPDATE `issue` SET `repo_id` = `repo_id` WHERE `id` = ?", pr.IssueID); err != nil {
return false, err
}
if _, err := sess.Exec("UPDATE `pull_request` SET `issue_id` = `issue_id` WHERE `id` = ?", pr.ID); err != nil {
return false, err
}
pr.Issue = nil
if err := pr.LoadIssue(ctx); err != nil {
return false, err
}
if tmpPr, err := issues_model.GetPullRequestByID(ctx, pr.ID); err != nil {
return false, err
} else if tmpPr.HasMerged {
if pr.Issue.IsClosed {
return false, nil
}
return false, fmt.Errorf("PullRequest[%d] already merged but it's associated issue [%d] is not closed", pr.Index, pr.IssueID)
} else if pr.Issue.IsClosed {
return false, fmt.Errorf("PullRequest[%d] already closed", pr.Index)
}
if err := pr.Issue.LoadRepo(ctx); err != nil {
return false, err
}
if err := pr.Issue.Repo.LoadOwner(ctx); err != nil {
return false, err
}
if _, err := issues_model.ChangeIssueStatus(ctx, pr.Issue, pr.Merger, true, true); err != nil {
return false, fmt.Errorf("ChangeIssueStatus: %w", err)
}
// reset the conflicted files as there cannot be any if we're merged
pr.ConflictedFiles = []string{}
// We need to save all of the data used to compute this merge as it may have already been changed by TestPatch. FIXME: need to set some state to prevent TestPatch from running whilst we are merging.
if _, err := sess.Where("id = ?", pr.ID).Cols("has_merged, status, merge_base, merged_commit_id, merger_id, merged_unix, conflicted_files").Update(pr); err != nil {
return false, fmt.Errorf("failed to update pr[%d]: %w", pr.ID, err)
}
return true, nil
}

View File

@ -9,11 +9,15 @@ import (
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
repo_model "code.gitea.io/gitea/models/repo" repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
webhook_model "code.gitea.io/gitea/models/webhook" webhook_model "code.gitea.io/gitea/models/webhook"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs" api "code.gitea.io/gitea/modules/structs"
webhook_module "code.gitea.io/gitea/modules/webhook" webhook_module "code.gitea.io/gitea/modules/webhook"
"code.gitea.io/gitea/services/convert"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
) )
func TestWebhook_GetSlackHook(t *testing.T) { func TestWebhook_GetSlackHook(t *testing.T) {
@ -77,3 +81,11 @@ func TestPrepareWebhooksBranchFilterNoMatch(t *testing.T) {
unittest.AssertNotExistsBean(t, hookTask) unittest.AssertNotExistsBean(t, hookTask)
} }
} }
func TestWebhookUserMail(t *testing.T) {
require.NoError(t, unittest.PrepareTestDatabase())
setting.Service.NoReplyAddress = "no-reply.com"
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
assert.Equal(t, user.GetPlaceholderEmail(), convert.ToUser(db.DefaultContext, user, nil).Email)
assert.Equal(t, user.Email, convert.ToUser(db.DefaultContext, user, user).Email)
}

View File

@ -1,7 +1,11 @@
{{.Message}} {{.Message}}
{{if .Details}}
<details> <details>
<summary>{{.Summary}}</summary> <summary>{{.Summary}}</summary>
<code> <code>{{.Details | SanitizeHTML}}</code>
{{.Details | SanitizeHTML}}
</code>
</details> </details>
{{else}}
<div>
{{.Summary}}
</div>
{{end}}

View File

@ -1,10 +1,10 @@
{{if .Statuses}} {{if .Statuses}}
{{if and (eq (len .Statuses) 1) .Status.TargetURL}} {{if and (eq (len .Statuses) 1) .Status.TargetURL}}
<a class="tw-align-middle {{.AdditionalClasses}} tw-no-underline" data-tippy="commit-statuses" href="{{.Status.TargetURL}}"> <a class="flex-text-inline tw-no-underline {{.AdditionalClasses}}" data-tippy="commit-statuses" href="{{.Status.TargetURL}}">
{{template "repo/commit_status" .Status}} {{template "repo/commit_status" .Status}}
</a> </a>
{{else}} {{else}}
<span class="tw-align-middle {{.AdditionalClasses}}" data-tippy="commit-statuses" tabindex="0"> <span class="flex-text-inline {{.AdditionalClasses}}" data-tippy="commit-statuses" tabindex="0">
{{template "repo/commit_status" .Status}} {{template "repo/commit_status" .Status}}
</span> </span>
{{end}} {{end}}

View File

@ -365,8 +365,9 @@
{{if .Review}}{{$reviewType = .Review.Type}}{{end}} {{if .Review}}{{$reviewType = .Review.Type}}{{end}}
{{if not .OriginalAuthor}} {{if not .OriginalAuthor}}
{{/* Some timeline avatars need a offset to correctly align with their speech bubble. {{/* Some timeline avatars need a offset to correctly align with their speech bubble.
The condition depends on whether the comment has contents/attachments or reviews */}} The condition depends on whether the comment has contents/attachments,
<a class="timeline-avatar{{if or .Content .Attachments (and .Review .Review.CodeComments)}} timeline-avatar-offset{{end}}"{{if gt .Poster.ID 0}} href="{{.Poster.HomeLink}}"{{end}}> review's comment is also controlled/rendered by issue comment's Content field */}}
<a class="timeline-avatar{{if or .Content .Attachments}} timeline-avatar-offset{{end}}"{{if gt .Poster.ID 0}} href="{{.Poster.HomeLink}}"{{end}}>
{{ctx.AvatarUtils.Avatar .Poster 40}} {{ctx.AvatarUtils.Avatar .Poster 40}}
</a> </a>
{{end}} {{end}}

View File

@ -3,7 +3,7 @@
{{else}} {{else}}
{{if .LatestCommitUser}} {{if .LatestCommitUser}}
{{ctx.AvatarUtils.Avatar .LatestCommitUser 24 "tw-mr-1"}} {{ctx.AvatarUtils.Avatar .LatestCommitUser 24}}
{{if and .LatestCommitUser.FullName DefaultShowFullName}} {{if and .LatestCommitUser.FullName DefaultShowFullName}}
<a class="muted author-wrapper" title="{{.LatestCommitUser.FullName}}" href="{{.LatestCommitUser.HomeLink}}"><strong>{{.LatestCommitUser.FullName}}</strong></a> <a class="muted author-wrapper" title="{{.LatestCommitUser.FullName}}" href="{{.LatestCommitUser.HomeLink}}"><strong>{{.LatestCommitUser.FullName}}</strong></a>
{{else}} {{else}}
@ -11,7 +11,7 @@
{{end}} {{end}}
{{else}} {{else}}
{{if .LatestCommit.Author}} {{if .LatestCommit.Author}}
{{ctx.AvatarUtils.AvatarByEmail .LatestCommit.Author.Email .LatestCommit.Author.Name 24 "tw-mr-1"}} {{ctx.AvatarUtils.AvatarByEmail .LatestCommit.Author.Email .LatestCommit.Author.Name 24}}
<span class="author-wrapper" title="{{.LatestCommit.Author.Name}}"><strong>{{.LatestCommit.Author.Name}}</strong></span> <span class="author-wrapper" title="{{.LatestCommit.Author.Name}}"><strong>{{.LatestCommit.Author.Name}}</strong></span>
{{end}} {{end}}
{{end}} {{end}}

View File

@ -25,30 +25,10 @@
{{end}} {{end}}
</span> </span>
</div> </div>
{{if or .TotalTrackedTime .Assignees .NumComments}} {{if .TotalTrackedTime}}
<div class="flex-item-trailing"> <div class="text grey flex-text-block">
{{if .TotalTrackedTime}} {{svg "octicon-clock" 16}}
<div class="text grey flex-text-block"> {{.TotalTrackedTime | Sec2Time}}
{{svg "octicon-clock" 16}}
{{.TotalTrackedTime | Sec2Time}}
</div>
{{end}}
{{if .Assignees}}
<div class="text grey">
{{range .Assignees}}
<a class="ui assignee tw-no-underline" href="{{.HomeLink}}" data-tooltip-content="{{.GetDisplayName}}">
{{ctx.AvatarUtils.Avatar . 20}}
</a>
{{end}}
</div>
{{end}}
{{if .NumComments}}
<div class="text grey">
<a class="tw-no-underline muted flex-text-block" href="{{if .Link}}{{.Link}}{{else}}{{$.Link}}/{{.Index}}{{end}}">
{{svg "octicon-comment" 16}}{{.NumComments}}
</a>
</div>
{{end}}
</div> </div>
{{end}} {{end}}
</div> </div>
@ -152,6 +132,26 @@
{{end}} {{end}}
</div> </div>
</div> </div>
{{if or .Assignees .NumComments}}
<div class="flex-item-trailing">
{{if .Assignees}}
<div class="text grey">
{{range .Assignees}}
<a class="ui assignee tw-no-underline" href="{{.HomeLink}}" data-tooltip-content="{{.GetDisplayName}}">
{{ctx.AvatarUtils.Avatar . 20}}
</a>
{{end}}
</div>
{{end}}
{{if .NumComments}}
<div class="text grey">
<a class="tw-no-underline muted flex-text-block" href="{{if .Link}}{{.Link}}{{else}}{{$.Link}}/{{.Index}}{{end}}">
{{svg "octicon-comment" 16}}{{.NumComments}}
</a>
</div>
{{end}}
</div>
{{end}}
</div> </div>
{{end}} {{end}}
{{if .IssueIndexerUnavailable}} {{if .IssueIndexerUnavailable}}

View File

@ -4369,6 +4369,275 @@
} }
} }
}, },
"/repos/{owner}/{repo}/actions/workflows": {
"get": {
"produces": [
"application/json"
],
"tags": [
"repository"
],
"summary": "List repository workflows",
"operationId": "ListRepositoryWorkflows",
"parameters": [
{
"type": "string",
"description": "owner of the repo",
"name": "owner",
"in": "path",
"required": true
},
{
"type": "string",
"description": "name of the repo",
"name": "repo",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"$ref": "#/responses/ActionWorkflowList"
},
"400": {
"$ref": "#/responses/error"
},
"403": {
"$ref": "#/responses/forbidden"
},
"404": {
"$ref": "#/responses/notFound"
},
"422": {
"$ref": "#/responses/validationError"
},
"500": {
"$ref": "#/responses/error"
}
}
}
},
"/repos/{owner}/{repo}/actions/workflows/{workflow_id}": {
"get": {
"produces": [
"application/json"
],
"tags": [
"repository"
],
"summary": "Get a workflow",
"operationId": "GetWorkflow",
"parameters": [
{
"type": "string",
"description": "owner of the repo",
"name": "owner",
"in": "path",
"required": true
},
{
"type": "string",
"description": "name of the repo",
"name": "repo",
"in": "path",
"required": true
},
{
"type": "string",
"description": "id of the workflow",
"name": "workflow_id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"$ref": "#/responses/ActionWorkflow"
},
"400": {
"$ref": "#/responses/error"
},
"403": {
"$ref": "#/responses/forbidden"
},
"404": {
"$ref": "#/responses/notFound"
},
"422": {
"$ref": "#/responses/validationError"
},
"500": {
"$ref": "#/responses/error"
}
}
}
},
"/repos/{owner}/{repo}/actions/workflows/{workflow_id}/disable": {
"put": {
"produces": [
"application/json"
],
"tags": [
"repository"
],
"summary": "Disable a workflow",
"operationId": "DisableWorkflow",
"parameters": [
{
"type": "string",
"description": "owner of the repo",
"name": "owner",
"in": "path",
"required": true
},
{
"type": "string",
"description": "name of the repo",
"name": "repo",
"in": "path",
"required": true
},
{
"type": "string",
"description": "id of the workflow",
"name": "workflow_id",
"in": "path",
"required": true
}
],
"responses": {
"204": {
"description": "No Content"
},
"400": {
"$ref": "#/responses/error"
},
"403": {
"$ref": "#/responses/forbidden"
},
"404": {
"$ref": "#/responses/notFound"
},
"422": {
"$ref": "#/responses/validationError"
}
}
}
},
"/repos/{owner}/{repo}/actions/workflows/{workflow_id}/dispatches": {
"post": {
"produces": [
"application/json"
],
"tags": [
"repository"
],
"summary": "Create a workflow dispatch event",
"operationId": "DispatchWorkflow",
"parameters": [
{
"type": "string",
"description": "owner of the repo",
"name": "owner",
"in": "path",
"required": true
},
{
"type": "string",
"description": "name of the repo",
"name": "repo",
"in": "path",
"required": true
},
{
"type": "string",
"description": "id of the workflow",
"name": "workflow_id",
"in": "path",
"required": true
},
{
"name": "body",
"in": "body",
"schema": {
"$ref": "#/definitions/CreateActionWorkflowDispatch"
}
}
],
"responses": {
"204": {
"description": "No Content"
},
"400": {
"$ref": "#/responses/error"
},
"403": {
"$ref": "#/responses/forbidden"
},
"404": {
"$ref": "#/responses/notFound"
},
"422": {
"$ref": "#/responses/validationError"
}
}
}
},
"/repos/{owner}/{repo}/actions/workflows/{workflow_id}/enable": {
"put": {
"produces": [
"application/json"
],
"tags": [
"repository"
],
"summary": "Enable a workflow",
"operationId": "EnableWorkflow",
"parameters": [
{
"type": "string",
"description": "owner of the repo",
"name": "owner",
"in": "path",
"required": true
},
{
"type": "string",
"description": "name of the repo",
"name": "repo",
"in": "path",
"required": true
},
{
"type": "string",
"description": "id of the workflow",
"name": "workflow_id",
"in": "path",
"required": true
}
],
"responses": {
"204": {
"description": "No Content"
},
"400": {
"$ref": "#/responses/error"
},
"403": {
"$ref": "#/responses/forbidden"
},
"404": {
"$ref": "#/responses/notFound"
},
"409": {
"$ref": "#/responses/conflict"
},
"422": {
"$ref": "#/responses/validationError"
}
}
}
},
"/repos/{owner}/{repo}/activities/feeds": { "/repos/{owner}/{repo}/activities/feeds": {
"get": { "get": {
"produces": [ "produces": [
@ -18567,6 +18836,60 @@
}, },
"x-go-package": "code.gitea.io/gitea/modules/structs" "x-go-package": "code.gitea.io/gitea/modules/structs"
}, },
"ActionWorkflow": {
"description": "ActionWorkflow represents a ActionWorkflow",
"type": "object",
"properties": {
"badge_url": {
"type": "string",
"x-go-name": "BadgeURL"
},
"created_at": {
"type": "string",
"format": "date-time",
"x-go-name": "CreatedAt"
},
"deleted_at": {
"type": "string",
"format": "date-time",
"x-go-name": "DeletedAt"
},
"html_url": {
"type": "string",
"x-go-name": "HTMLURL"
},
"id": {
"type": "string",
"x-go-name": "ID"
},
"name": {
"type": "string",
"x-go-name": "Name"
},
"node_id": {
"type": "string",
"x-go-name": "NodeID"
},
"path": {
"type": "string",
"x-go-name": "Path"
},
"state": {
"type": "string",
"x-go-name": "State"
},
"updated_at": {
"type": "string",
"format": "date-time",
"x-go-name": "UpdatedAt"
},
"url": {
"type": "string",
"x-go-name": "URL"
}
},
"x-go-package": "code.gitea.io/gitea/modules/structs"
},
"Activity": { "Activity": {
"type": "object", "type": "object",
"properties": { "properties": {
@ -19575,6 +19898,26 @@
}, },
"x-go-package": "code.gitea.io/gitea/modules/structs" "x-go-package": "code.gitea.io/gitea/modules/structs"
}, },
"CreateActionWorkflowDispatch": {
"description": "CreateActionWorkflowDispatch represents the payload for triggering a workflow dispatch event",
"type": "object",
"required": [
"ref"
],
"properties": {
"inputs": {
"type": "object",
"additionalProperties": {},
"x-go-name": "Inputs"
},
"ref": {
"type": "string",
"x-go-name": "Ref",
"example": "refs/heads/main"
}
},
"x-go-package": "code.gitea.io/gitea/modules/structs"
},
"CreateBranchProtectionOption": { "CreateBranchProtectionOption": {
"description": "CreateBranchProtectionOption options for creating a branch protection", "description": "CreateBranchProtectionOption options for creating a branch protection",
"type": "object", "type": "object",
@ -25538,6 +25881,21 @@
"$ref": "#/definitions/ActionVariable" "$ref": "#/definitions/ActionVariable"
} }
}, },
"ActionWorkflow": {
"description": "ActionWorkflow",
"schema": {
"$ref": "#/definitions/ActionWorkflow"
}
},
"ActionWorkflowList": {
"description": "ActionWorkflowList",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/ActionWorkflow"
}
}
},
"ActivityFeedsList": { "ActivityFeedsList": {
"description": "ActivityFeedsList", "description": "ActivityFeedsList",
"schema": { "schema": {

View File

@ -332,7 +332,7 @@ func TestLDAPUserSyncWithGroupFilter(t *testing.T) {
// Assert members of LDAP group "cn=git" are added // Assert members of LDAP group "cn=git" are added
for _, gitLDAPUser := range te.gitLDAPUsers { for _, gitLDAPUser := range te.gitLDAPUsers {
unittest.BeanExists(t, &user_model.User{ unittest.AssertExistsAndLoadBean(t, &user_model.User{
Name: gitLDAPUser.UserName, Name: gitLDAPUser.UserName,
}) })
} }

View File

@ -12,6 +12,7 @@ import (
"strings" "strings"
"testing" "testing"
auth_model "code.gitea.io/gitea/models/auth"
"code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/test" "code.gitea.io/gitea/modules/test"
"code.gitea.io/gitea/tests" "code.gitea.io/gitea/tests"
@ -83,6 +84,30 @@ func testPullCreateDirectly(t *testing.T, session *TestSession, baseRepoOwner, b
return resp return resp
} }
func testPullCreateFailure(t *testing.T, session *TestSession, baseRepoOwner, baseRepoName, baseBranch, headRepoOwner, headRepoName, headBranch, title string) *httptest.ResponseRecorder {
headCompare := headBranch
if headRepoOwner != "" {
if headRepoName != "" {
headCompare = fmt.Sprintf("%s/%s:%s", headRepoOwner, headRepoName, headBranch)
} else {
headCompare = fmt.Sprintf("%s:%s", headRepoOwner, headBranch)
}
}
req := NewRequest(t, "GET", fmt.Sprintf("/%s/%s/compare/%s...%s", baseRepoOwner, baseRepoName, baseBranch, headCompare))
resp := session.MakeRequest(t, req, http.StatusOK)
// Submit the form for creating the pull
htmlDoc := NewHTMLParser(t, resp.Body)
link, exists := htmlDoc.doc.Find("form.ui.form").Attr("action")
assert.True(t, exists, "The template has changed")
req = NewRequestWithValues(t, "POST", link, map[string]string{
"_csrf": htmlDoc.GetCSRF(),
"title": title,
})
resp = session.MakeRequest(t, req, http.StatusBadRequest)
return resp
}
func TestPullCreate(t *testing.T) { func TestPullCreate(t *testing.T) {
onGiteaRun(t, func(t *testing.T, u *url.URL) { onGiteaRun(t, func(t *testing.T, u *url.URL) {
session := loginUser(t, "user1") session := loginUser(t, "user1")
@ -245,3 +270,46 @@ func TestCreateAgitPullWithReadPermission(t *testing.T) {
}) })
}) })
} }
/*
Setup: user2 has repository, user1 forks it
---
1. User2 blocks User1
2. User1 adds changes to fork
3. User1 attempts to create a pull request
4. User1 sees alert that the action is not allowed because of the block
*/
func TestCreatePullWhenBlocked(t *testing.T) {
RepoOwner := "user2"
ForkOwner := "user16"
onGiteaRun(t, func(t *testing.T, u *url.URL) {
// Setup
// User1 forks repo1 from User2
sessionFork := loginUser(t, ForkOwner)
testRepoFork(t, sessionFork, RepoOwner, "repo1", ForkOwner, "forkrepo1", "")
// 1. User2 blocks user1
// sessionBase := loginUser(t, "user2")
token := getUserToken(t, RepoOwner, auth_model.AccessTokenScopeWriteUser)
req := NewRequest(t, "GET", fmt.Sprintf("/api/v1/user/blocks/%s", ForkOwner)).
AddTokenAuth(token)
MakeRequest(t, req, http.StatusNotFound)
req = NewRequest(t, "PUT", fmt.Sprintf("/api/v1/user/blocks/%s", ForkOwner)).
AddTokenAuth(token)
MakeRequest(t, req, http.StatusNoContent)
// 2. User1 adds changes to fork
testEditFile(t, sessionFork, ForkOwner, "forkrepo1", "master", "README.md", "Hello, World (Edited)\n")
// 3. User1 attempts to create a pull request
testPullCreateFailure(t, sessionFork, RepoOwner, "repo1", "master", ForkOwner, "forkrepo1", "master", "This is a pull title")
// Teardown
// Unblock user
req = NewRequest(t, "DELETE", fmt.Sprintf("/api/v1/user/blocks/%s", ForkOwner)).
AddTokenAuth(token)
MakeRequest(t, req, http.StatusNoContent)
})
}

View File

@ -92,7 +92,7 @@ func TestPullView_CodeOwner(t *testing.T) {
testPullCreate(t, session, "user2", "test_codeowner", false, repo.DefaultBranch, "codeowner-basebranch", "Test Pull Request") testPullCreate(t, session, "user2", "test_codeowner", false, repo.DefaultBranch, "codeowner-basebranch", "Test Pull Request")
pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{BaseRepoID: repo.ID, HeadRepoID: repo.ID, HeadBranch: "codeowner-basebranch"}) pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{BaseRepoID: repo.ID, HeadRepoID: repo.ID, HeadBranch: "codeowner-basebranch"})
unittest.AssertExistsIf(t, true, &issues_model.Review{IssueID: pr.IssueID, Type: issues_model.ReviewTypeRequest, ReviewerID: 5}) unittest.AssertExistsAndLoadBean(t, &issues_model.Review{IssueID: pr.IssueID, Type: issues_model.ReviewTypeRequest, ReviewerID: 5})
assert.NoError(t, pr.LoadIssue(db.DefaultContext)) assert.NoError(t, pr.LoadIssue(db.DefaultContext))
err := issue_service.ChangeTitle(db.DefaultContext, pr.Issue, user2, "[WIP] Test Pull Request") err := issue_service.ChangeTitle(db.DefaultContext, pr.Issue, user2, "[WIP] Test Pull Request")
@ -139,7 +139,7 @@ func TestPullView_CodeOwner(t *testing.T) {
testPullCreate(t, session, "user2", "test_codeowner", false, repo.DefaultBranch, "codeowner-basebranch2", "Test Pull Request2") testPullCreate(t, session, "user2", "test_codeowner", false, repo.DefaultBranch, "codeowner-basebranch2", "Test Pull Request2")
pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{BaseRepoID: repo.ID, HeadBranch: "codeowner-basebranch2"}) pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{BaseRepoID: repo.ID, HeadBranch: "codeowner-basebranch2"})
unittest.AssertExistsIf(t, true, &issues_model.Review{IssueID: pr.IssueID, Type: issues_model.ReviewTypeRequest, ReviewerID: 8}) unittest.AssertExistsAndLoadBean(t, &issues_model.Review{IssueID: pr.IssueID, Type: issues_model.ReviewTypeRequest, ReviewerID: 8})
}) })
t.Run("Forked Repo Pull Request", func(t *testing.T) { t.Run("Forked Repo Pull Request", func(t *testing.T) {
@ -169,13 +169,13 @@ func TestPullView_CodeOwner(t *testing.T) {
testPullCreateDirectly(t, session, "user5", "test_codeowner", forkedRepo.DefaultBranch, "", "", "codeowner-basebranch-forked", "Test Pull Request on Forked Repository") testPullCreateDirectly(t, session, "user5", "test_codeowner", forkedRepo.DefaultBranch, "", "", "codeowner-basebranch-forked", "Test Pull Request on Forked Repository")
pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{BaseRepoID: forkedRepo.ID, HeadBranch: "codeowner-basebranch-forked"}) pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{BaseRepoID: forkedRepo.ID, HeadBranch: "codeowner-basebranch-forked"})
unittest.AssertExistsIf(t, false, &issues_model.Review{IssueID: pr.IssueID, Type: issues_model.ReviewTypeRequest, ReviewerID: 8}) unittest.AssertNotExistsBean(t, &issues_model.Review{IssueID: pr.IssueID, Type: issues_model.ReviewTypeRequest, ReviewerID: 8})
// create a pull request to base repository, code reviewers should be mentioned // create a pull request to base repository, code reviewers should be mentioned
testPullCreateDirectly(t, session, repo.OwnerName, repo.Name, repo.DefaultBranch, forkedRepo.OwnerName, forkedRepo.Name, "codeowner-basebranch-forked", "Test Pull Request3") testPullCreateDirectly(t, session, repo.OwnerName, repo.Name, repo.DefaultBranch, forkedRepo.OwnerName, forkedRepo.Name, "codeowner-basebranch-forked", "Test Pull Request3")
pr = unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{BaseRepoID: repo.ID, HeadRepoID: forkedRepo.ID, HeadBranch: "codeowner-basebranch-forked"}) pr = unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{BaseRepoID: repo.ID, HeadRepoID: forkedRepo.ID, HeadBranch: "codeowner-basebranch-forked"})
unittest.AssertExistsIf(t, true, &issues_model.Review{IssueID: pr.IssueID, Type: issues_model.ReviewTypeRequest, ReviewerID: 8}) unittest.AssertExistsAndLoadBean(t, &issues_model.Review{IssueID: pr.IssueID, Type: issues_model.ReviewTypeRequest, ReviewerID: 8})
}) })
}) })
} }

View File

@ -9,6 +9,7 @@ import (
"strings" "strings"
"testing" "testing"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
@ -17,6 +18,7 @@ import (
"code.gitea.io/gitea/tests" "code.gitea.io/gitea/tests"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
) )
func testLoginFailed(t *testing.T, username, password, message string) { func testLoginFailed(t *testing.T, username, password, message string) {
@ -42,7 +44,7 @@ func TestSignin(t *testing.T) {
user.Name = "testuser" user.Name = "testuser"
user.LowerName = strings.ToLower(user.Name) user.LowerName = strings.ToLower(user.Name)
user.ID = 0 user.ID = 0
unittest.AssertSuccessfulInsert(t, user) require.NoError(t, db.Insert(db.DefaultContext, user))
samples := []struct { samples := []struct {
username string username string

View File

@ -123,6 +123,12 @@ td .commit-summary {
gap: 0.25em; gap: 0.25em;
} }
@media (max-width: 767.98px) {
.latest-commit .commit-id-short {
display: none;
}
}
.repo-path { .repo-path {
display: flex; display: flex;
overflow-wrap: anywhere; overflow-wrap: anywhere;
@ -1670,6 +1676,10 @@ tbody.commit-list {
white-space: nowrap; white-space: nowrap;
} }
.latest-commit .message-wrapper {
max-width: calc(100% - 2.5rem);
}
/* in the commit list, messages can wrap so we can use inline */ /* in the commit list, messages can wrap so we can use inline */
.commit-list .message-wrapper { .commit-list .message-wrapper {
display: inline; display: inline;

View File

@ -147,7 +147,7 @@ function clearMergeMessage() {
</template> </template>
</span> </span>
</button> </button>
<div class="ui dropdown icon button" @click.stop="showMergeStyleMenu = !showMergeStyleMenu" v-if="mergeStyleAllowedCount>1"> <div class="ui dropdown icon button" @click.stop="showMergeStyleMenu = !showMergeStyleMenu">
<svg-icon name="octicon-triangle-down" :size="14"/> <svg-icon name="octicon-triangle-down" :size="14"/>
<div class="menu" :class="{'show':showMergeStyleMenu}"> <div class="menu" :class="{'show':showMergeStyleMenu}">
<template v-for="msd in mergeForm.mergeStyles"> <template v-for="msd in mergeForm.mergeStyles">