mirror of
https://github.com/go-gitea/gitea.git
synced 2025-11-13 02:02:53 +09:00
Compare commits
55 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
877040e652 | ||
|
|
91f5be889a | ||
|
|
a818a48c76 | ||
|
|
76e1c130fb | ||
|
|
148a417774 | ||
|
|
6081948ef0 | ||
|
|
48bd54286c | ||
|
|
c69b3b65f3 | ||
|
|
fe91d9617b | ||
|
|
711ca52f1f | ||
|
|
a15f0cb010 | ||
|
|
2051f850ef | ||
|
|
3ae4c4898b | ||
|
|
3a77465e4e | ||
|
|
fc8c23edb7 | ||
|
|
31df892059 | ||
|
|
9879e23c57 | ||
|
|
56a3b50136 | ||
|
|
9a8532d928 | ||
|
|
d29a0fc3be | ||
|
|
04517e17d6 | ||
|
|
3a222ee416 | ||
|
|
add85f5a85 | ||
|
|
76ad83f05e | ||
|
|
714ecd9f1e | ||
|
|
a08856606e | ||
|
|
7be2d7b136 | ||
|
|
6f3596e33c | ||
|
|
0305a73633 | ||
|
|
6cd1ccef3d | ||
|
|
ea0fe83888 | ||
|
|
1cec7f5ab5 | ||
|
|
1cb1101d44 | ||
|
|
653dff4e57 | ||
|
|
b661bbaed7 | ||
|
|
20ae184967 | ||
|
|
15b44496ec | ||
|
|
0d0ff5e32a | ||
|
|
f25f7c592f | ||
|
|
e8cf04bad7 | ||
|
|
251fdaaf41 | ||
|
|
f572fb906f | ||
|
|
9340269d84 | ||
|
|
34650b925b | ||
|
|
718e0db12e | ||
|
|
6110ddc280 | ||
|
|
c7d8181a70 | ||
|
|
548ae3eb98 | ||
|
|
2c383d812d | ||
|
|
ef12b8de80 | ||
|
|
dd1ba34ee5 | ||
|
|
1fbdf96c34 | ||
|
|
5159055278 | ||
|
|
06da10b9a1 | ||
|
|
175ebc6f88 |
@@ -9,7 +9,6 @@ linters:
|
|||||||
- unused
|
- unused
|
||||||
- structcheck
|
- structcheck
|
||||||
- varcheck
|
- varcheck
|
||||||
- golint
|
|
||||||
- dupl
|
- dupl
|
||||||
#- gocyclo # The cyclomatic complexety of a lot of functions is too high, we should refactor those another time.
|
#- gocyclo # The cyclomatic complexety of a lot of functions is too high, we should refactor those another time.
|
||||||
- gofmt
|
- gofmt
|
||||||
|
|||||||
61
CHANGELOG.md
61
CHANGELOG.md
@@ -4,6 +4,67 @@ This changelog goes through all the changes that have been made in each release
|
|||||||
without substantial changes to our git log; to see the highlights of what has
|
without substantial changes to our git log; to see the highlights of what has
|
||||||
been added to each release, please refer to the [blog](https://blog.gitea.io).
|
been added to each release, please refer to the [blog](https://blog.gitea.io).
|
||||||
|
|
||||||
|
## [1.15.8](https://github.com/go-gitea/gitea/releases/tag/v1.15.8) - 2021-12-20
|
||||||
|
|
||||||
|
* BUGFIXES
|
||||||
|
* Move POST /{username}/action/{action} to simply POST /{username} (#18045) (#18046)
|
||||||
|
* Fix delete u2f keys bug (#18040) (#18042)
|
||||||
|
* Reset Session ID on login (#18018) (#18041)
|
||||||
|
* Prevent off-by-one error on comments on newly appended lines (#18029) (#18035)
|
||||||
|
* Stop printing 03d after escaped characters in logs (#18030) (#18034)
|
||||||
|
* Reset locale on login (#18023) (#18025)
|
||||||
|
* Fix reset password email template (#17025) (#18022)
|
||||||
|
* Fix outType on gitea dump (#18000) (#18016)
|
||||||
|
* Ensure complexity, minlength and isPwned are checked on password setting (#18005) (#18015)
|
||||||
|
* Fix rename notification bug (#18011)
|
||||||
|
* Prevent double decoding of % in url params (#17997) (#18001)
|
||||||
|
* Prevent hang in git cat-file if the repository is not a valid repository (Partial #17991) (#17992)
|
||||||
|
* Prevent deadlock in create issue (#17970) (#17982)
|
||||||
|
* TESTING
|
||||||
|
* Use non-expiring key. (#17984) (#17985)
|
||||||
|
|
||||||
|
## [1.15.7](https://github.com/go-gitea/gitea/releases/tag/v1.15.7) - 2021-12-01
|
||||||
|
|
||||||
|
* ENHANCEMENTS
|
||||||
|
* Only allow webhook to send requests to allowed hosts (#17482) (#17510)
|
||||||
|
* Fix login redirection links (#17451) (#17473)
|
||||||
|
* BUGFIXES
|
||||||
|
* Fix database inconsistent when admin change user email (#17549) (#17840)
|
||||||
|
* Use correct user on releases (#17806) (#17818)
|
||||||
|
* Fix commit count in tag view (#17698) (#17790)
|
||||||
|
* Fix close issue but time watcher still running (#17643) (#17761)
|
||||||
|
* Fix Migrate Description (#17692) (#17727)
|
||||||
|
* Fix bug when project board get open issue number (#17703) (#17726)
|
||||||
|
* Return 400 but not 500 when request archive with wrong format (#17691) (#17700)
|
||||||
|
* Fix bug when read mysql database max lifetime (#17682) (#17690)
|
||||||
|
* Fix database deadlock when update issue labels (#17649) (#17665)
|
||||||
|
* Fix bug on detect issue/comment writer (#17592)
|
||||||
|
* Remove appSubUrl from pasted images (#17572) (#17588)
|
||||||
|
* Make `ParsePatch` more robust (#17573) (#17580)
|
||||||
|
* Fix stats upon searching issues (#17566) (#17578)
|
||||||
|
* Escape issue titles in comments list (#17555) (#17556)
|
||||||
|
* Fix zero created time bug on commit api (#17546) (#17547)
|
||||||
|
* Fix database keyword quote problem on migration v161 (#17522) (#17523)
|
||||||
|
* Fix email with + when active (#17518) (#17520)
|
||||||
|
* Stop double encoding blame commit messages (#17498) (#17500)
|
||||||
|
* Quote the table name in CountOrphanedObjects (#17487) (#17488)
|
||||||
|
* Run Migrate in Install rather than just SyncTables (#17475) (#17486)
|
||||||
|
* BUILD
|
||||||
|
* Fix golangci-lint warnings (#17598 et al) (#17668)
|
||||||
|
* MISC
|
||||||
|
* Preserve color when inverting emojis (#17797) (#17799)
|
||||||
|
|
||||||
|
## [1.15.6](https://github.com/go-gitea/gitea/releases/tag/v1.15.6) - 2021-10-28
|
||||||
|
|
||||||
|
* BUGFIXES
|
||||||
|
* Prevent panic in serv.go with Deploy Keys (#17434) (#17435)
|
||||||
|
* Fix CSV render error (#17406) (#17431)
|
||||||
|
* Read expected buffer size (#17409) (#17430)
|
||||||
|
* Ensure that restricted users can access repos for which they are members (#17460) (#17464)
|
||||||
|
* Make commit-statuses popup show correctly (#17447) (#17466)
|
||||||
|
* TESTING
|
||||||
|
* Add integration tests for private.NoServCommand and private.ServCommand (#17456) (#17463)
|
||||||
|
|
||||||
## [1.15.5](https://github.com/go-gitea/gitea/releases/tag/v1.15.5) - 2021-10-21
|
## [1.15.5](https://github.com/go-gitea/gitea/releases/tag/v1.15.5) - 2021-10-21
|
||||||
|
|
||||||
* SECURITY
|
* SECURITY
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
###################################
|
###################################
|
||||||
#Build stage
|
#Build stage
|
||||||
FROM golang:1.16-alpine3.13 AS build-env
|
FROM techknowlogick/go:1.16-alpine3.13 AS build-env
|
||||||
|
|
||||||
ARG GOPROXY
|
ARG GOPROXY
|
||||||
ENV GOPROXY ${GOPROXY:-direct}
|
ENV GOPROXY ${GOPROXY:-direct}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
###################################
|
###################################
|
||||||
#Build stage
|
#Build stage
|
||||||
FROM golang:1.16-alpine3.13 AS build-env
|
FROM techknowlogick/go:1.16-alpine3.13 AS build-env
|
||||||
|
|
||||||
ARG GOPROXY
|
ARG GOPROXY
|
||||||
ENV GOPROXY ${GOPROXY:-direct}
|
ENV GOPROXY ${GOPROXY:-direct}
|
||||||
|
|||||||
1
build.go
1
build.go
@@ -2,6 +2,7 @@
|
|||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build vendor
|
||||||
// +build vendor
|
// +build vendor
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build ignore
|
||||||
// +build ignore
|
// +build ignore
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build ignore
|
||||||
// +build ignore
|
// +build ignore
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
//go:build ignore
|
||||||
// +build ignore
|
// +build ignore
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
//go:build ignore
|
||||||
// +build ignore
|
// +build ignore
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
// gocovmerge takes the results from multiple `go test -coverprofile` runs and
|
// gocovmerge takes the results from multiple `go test -coverprofile` runs and
|
||||||
// merges them into one profile
|
// merges them into one profile
|
||||||
|
|
||||||
|
//go:build ignore
|
||||||
// +build ignore
|
// +build ignore
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|||||||
@@ -335,6 +335,10 @@ func runChangePassword(c *cli.Context) error {
|
|||||||
if err := initDB(); err != nil {
|
if err := initDB(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if len(c.String("password")) < setting.MinPasswordLength {
|
||||||
|
return fmt.Errorf("Password is not long enough. Needs to be at least %d", setting.MinPasswordLength)
|
||||||
|
}
|
||||||
|
|
||||||
if !pwd.IsComplexEnough(c.String("password")) {
|
if !pwd.IsComplexEnough(c.String("password")) {
|
||||||
return errors.New("Password does not meet complexity requirements")
|
return errors.New("Password does not meet complexity requirements")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,7 +43,11 @@ func runDocs(ctx *cli.Context) error {
|
|||||||
// Clean up markdown. The following bug was fixed in v2, but is present in v1.
|
// Clean up markdown. The following bug was fixed in v2, but is present in v1.
|
||||||
// It affects markdown output (even though the issue is referring to man pages)
|
// It affects markdown output (even though the issue is referring to man pages)
|
||||||
// https://github.com/urfave/cli/issues/1040
|
// https://github.com/urfave/cli/issues/1040
|
||||||
docs = docs[strings.Index(docs, "#"):]
|
firstHashtagIndex := strings.Index(docs, "#")
|
||||||
|
|
||||||
|
if firstHashtagIndex > 0 {
|
||||||
|
docs = docs[firstHashtagIndex:]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
out := os.Stdout
|
out := os.Stdout
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ func (o outputType) String() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var outputTypeEnum = &outputType{
|
var outputTypeEnum = &outputType{
|
||||||
Enum: []string{"zip", "tar", "tar.gz", "tar.xz", "tar.bz2"},
|
Enum: []string{"zip", "rar", "tar", "sz", "tar.gz", "tar.xz", "tar.bz2", "tar.br", "tar.lz4"},
|
||||||
Default: "zip",
|
Default: "zip",
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,12 +153,16 @@ func fatal(format string, args ...interface{}) {
|
|||||||
func runDump(ctx *cli.Context) error {
|
func runDump(ctx *cli.Context) error {
|
||||||
var file *os.File
|
var file *os.File
|
||||||
fileName := ctx.String("file")
|
fileName := ctx.String("file")
|
||||||
|
outType := ctx.String("type")
|
||||||
if fileName == "-" {
|
if fileName == "-" {
|
||||||
file = os.Stdout
|
file = os.Stdout
|
||||||
err := log.DelLogger("console")
|
err := log.DelLogger("console")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fatal("Deleting default logger failed. Can not write to stdout: %v", err)
|
fatal("Deleting default logger failed. Can not write to stdout: %v", err)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
fileName = strings.TrimSuffix(fileName, path.Ext(fileName))
|
||||||
|
fileName += "." + outType
|
||||||
}
|
}
|
||||||
setting.NewContext()
|
setting.NewContext()
|
||||||
// make sure we are logging to the console no matter what the configuration tells us do to
|
// make sure we are logging to the console no matter what the configuration tells us do to
|
||||||
@@ -197,7 +201,6 @@ func runDump(ctx *cli.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
verbose := ctx.Bool("verbose")
|
verbose := ctx.Bool("verbose")
|
||||||
outType := ctx.String("type")
|
|
||||||
var iface interface{}
|
var iface interface{}
|
||||||
if fileName == "-" {
|
if fileName == "-" {
|
||||||
iface, err = archiver.ByExtension(fmt.Sprintf(".%s", outType))
|
iface, err = archiver.ByExtension(fmt.Sprintf(".%s", outType))
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build bindata
|
||||||
// +build bindata
|
// +build bindata
|
||||||
|
|
||||||
package cmd
|
package cmd
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build !bindata
|
||||||
// +build !bindata
|
// +build !bindata
|
||||||
|
|
||||||
package cmd
|
package cmd
|
||||||
|
|||||||
@@ -194,6 +194,10 @@ func listen(m http.Handler, handleRedirector bool) error {
|
|||||||
listenAddr = net.JoinHostPort(listenAddr, setting.HTTPPort)
|
listenAddr = net.JoinHostPort(listenAddr, setting.HTTPPort)
|
||||||
}
|
}
|
||||||
log.Info("Listen: %v://%s%s", setting.Protocol, listenAddr, setting.AppSubURL)
|
log.Info("Listen: %v://%s%s", setting.Protocol, listenAddr, setting.AppSubURL)
|
||||||
|
// This can be useful for users, many users do wrong to their config and get strange behaviors behind a reverse-proxy.
|
||||||
|
// A user may fix the configuration mistake when he sees this log.
|
||||||
|
// And this is also very helpful to maintainers to provide help to users to resolve their configuration problems.
|
||||||
|
log.Info("AppURL(ROOT_URL): %s", setting.AppURL)
|
||||||
|
|
||||||
if setting.LFS.StartServer {
|
if setting.LFS.StartServer {
|
||||||
log.Info("LFS server enabled")
|
log.Info("LFS server enabled")
|
||||||
|
|||||||
@@ -1388,6 +1388,13 @@ PATH =
|
|||||||
;; Deliver timeout in seconds
|
;; Deliver timeout in seconds
|
||||||
;DELIVER_TIMEOUT = 5
|
;DELIVER_TIMEOUT = 5
|
||||||
;;
|
;;
|
||||||
|
;; Webhook can only call allowed hosts for security reasons. Comma separated list, eg: external, 192.168.1.0/24, *.mydomain.com
|
||||||
|
;; Built-in: loopback (for localhost), private (for LAN/intranet), external (for public hosts on internet), * (for all hosts)
|
||||||
|
;; CIDR list: 1.2.3.0/8, 2001:db8::/32
|
||||||
|
;; Wildcard hosts: *.mydomain.com, 192.168.100.*
|
||||||
|
;; Default to * for 1.15.x, external for 1.16 and later
|
||||||
|
;ALLOWED_HOST_LIST = *
|
||||||
|
;;
|
||||||
;; Allow insecure certification
|
;; Allow insecure certification
|
||||||
;SKIP_TLS_VERIFY = false
|
;SKIP_TLS_VERIFY = false
|
||||||
;;
|
;;
|
||||||
|
|||||||
@@ -18,9 +18,9 @@ params:
|
|||||||
description: Git with a cup of tea
|
description: Git with a cup of tea
|
||||||
author: The Gitea Authors
|
author: The Gitea Authors
|
||||||
website: https://docs.gitea.io
|
website: https://docs.gitea.io
|
||||||
version: 1.14.6
|
version: 1.15.8
|
||||||
minGoVersion: 1.16
|
minGoVersion: 1.16
|
||||||
goVersion: 1.16
|
goVersion: 1.17
|
||||||
minNodeVersion: 12.17
|
minNodeVersion: 12.17
|
||||||
|
|
||||||
outputs:
|
outputs:
|
||||||
|
|||||||
@@ -545,6 +545,14 @@ Define allowed algorithms and their minimum key length (use -1 to disable a type
|
|||||||
|
|
||||||
- `QUEUE_LENGTH`: **1000**: Hook task queue length. Use caution when editing this value.
|
- `QUEUE_LENGTH`: **1000**: Hook task queue length. Use caution when editing this value.
|
||||||
- `DELIVER_TIMEOUT`: **5**: Delivery timeout (sec) for shooting webhooks.
|
- `DELIVER_TIMEOUT`: **5**: Delivery timeout (sec) for shooting webhooks.
|
||||||
|
- `ALLOWED_HOST_LIST`: `*`: Default to `*` for 1.15.x, `external` for 1.16 and later. Webhook can only call allowed hosts for security reasons. Comma separated list.
|
||||||
|
- Built-in networks:
|
||||||
|
- `loopback`: 127.0.0.0/8 for IPv4 and ::1/128 for IPv6, localhost is included.
|
||||||
|
- `private`: RFC 1918 (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16) and RFC 4193 (FC00::/7). Also called LAN/Intranet.
|
||||||
|
- `external`: A valid non-private unicast IP, you can access all hosts on public internet.
|
||||||
|
- `*`: All hosts are allowed.
|
||||||
|
- CIDR list: `1.2.3.0/8` for IPv4 and `2001:db8::/32` for IPv6
|
||||||
|
- Wildcard hosts: `*.mydomain.com`, `192.168.100.*`
|
||||||
- `SKIP_TLS_VERIFY`: **false**: Allow insecure certification.
|
- `SKIP_TLS_VERIFY`: **false**: Allow insecure certification.
|
||||||
- `PAGING_NUM`: **10**: Number of webhook history events that are shown in one page.
|
- `PAGING_NUM`: **10**: Number of webhook history events that are shown in one page.
|
||||||
- `PROXY_URL`: ****: Proxy server URL, support http://, https//, socks://, blank will follow environment http_proxy/https_proxy
|
- `PROXY_URL`: ****: Proxy server URL, support http://, https//, socks://, blank will follow environment http_proxy/https_proxy
|
||||||
|
|||||||
2
go.mod
2
go.mod
@@ -9,7 +9,7 @@ require (
|
|||||||
gitea.com/go-chi/binding v0.0.0-20210301195521-1fe1c9a555e7
|
gitea.com/go-chi/binding v0.0.0-20210301195521-1fe1c9a555e7
|
||||||
gitea.com/go-chi/cache v0.0.0-20210110083709-82c4c9ce2d5e
|
gitea.com/go-chi/cache v0.0.0-20210110083709-82c4c9ce2d5e
|
||||||
gitea.com/go-chi/captcha v0.0.0-20210110083842-e7696c336a1e
|
gitea.com/go-chi/captcha v0.0.0-20210110083842-e7696c336a1e
|
||||||
gitea.com/go-chi/session v0.0.0-20210108030337-0cb48c5ba8ee
|
gitea.com/go-chi/session v0.0.0-20211218221615-e3605d8b28b8
|
||||||
gitea.com/lunny/levelqueue v0.4.1
|
gitea.com/lunny/levelqueue v0.4.1
|
||||||
github.com/Microsoft/go-winio v0.5.0 // indirect
|
github.com/Microsoft/go-winio v0.5.0 // indirect
|
||||||
github.com/NYTimes/gziphandler v1.1.1
|
github.com/NYTimes/gziphandler v1.1.1
|
||||||
|
|||||||
7
go.sum
7
go.sum
@@ -47,8 +47,8 @@ gitea.com/go-chi/cache v0.0.0-20210110083709-82c4c9ce2d5e h1:zgPGaf3kXP0cVm9J0l8
|
|||||||
gitea.com/go-chi/cache v0.0.0-20210110083709-82c4c9ce2d5e/go.mod h1:k2V/gPDEtXGjjMGuBJiapffAXTv76H4snSmlJRLUhH0=
|
gitea.com/go-chi/cache v0.0.0-20210110083709-82c4c9ce2d5e/go.mod h1:k2V/gPDEtXGjjMGuBJiapffAXTv76H4snSmlJRLUhH0=
|
||||||
gitea.com/go-chi/captcha v0.0.0-20210110083842-e7696c336a1e h1:YjaQU6XFicdhPN+MlGolcXO8seYY2+EY5g7vZPB17CQ=
|
gitea.com/go-chi/captcha v0.0.0-20210110083842-e7696c336a1e h1:YjaQU6XFicdhPN+MlGolcXO8seYY2+EY5g7vZPB17CQ=
|
||||||
gitea.com/go-chi/captcha v0.0.0-20210110083842-e7696c336a1e/go.mod h1:nfA7JaGv3hbGQ1ktdhAsZhdS84qKffI8NMlHr+Opsog=
|
gitea.com/go-chi/captcha v0.0.0-20210110083842-e7696c336a1e/go.mod h1:nfA7JaGv3hbGQ1ktdhAsZhdS84qKffI8NMlHr+Opsog=
|
||||||
gitea.com/go-chi/session v0.0.0-20210108030337-0cb48c5ba8ee h1:9U6HuKUBt/cGK6T/64dEuz0r7Yp97WAAEJvXHDlY3ws=
|
gitea.com/go-chi/session v0.0.0-20211218221615-e3605d8b28b8 h1:tJQRXgZigkLeeW9LPlps9G9aMoE6LAmqigLA+wxmd1Q=
|
||||||
gitea.com/go-chi/session v0.0.0-20210108030337-0cb48c5ba8ee/go.mod h1:Ozg8IchVNb/Udg+ui39iHRYqVHSvf3C99ixdpLR8Vu0=
|
gitea.com/go-chi/session v0.0.0-20211218221615-e3605d8b28b8/go.mod h1:fc/pjt5EqNKgqQXYzcas1Z5L5whkZHyOvTA7OzWVJck=
|
||||||
gitea.com/lunny/levelqueue v0.4.1 h1:RZ+AFx5gBsZuyqCvofhAkPQ9uaVDPJnsULoJZIYaJNw=
|
gitea.com/lunny/levelqueue v0.4.1 h1:RZ+AFx5gBsZuyqCvofhAkPQ9uaVDPJnsULoJZIYaJNw=
|
||||||
gitea.com/lunny/levelqueue v0.4.1/go.mod h1:HBqmLbz56JWpfEGG0prskAV97ATNRoj5LDmPicD22hU=
|
gitea.com/lunny/levelqueue v0.4.1/go.mod h1:HBqmLbz56JWpfEGG0prskAV97ATNRoj5LDmPicD22hU=
|
||||||
gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:lSA0F4e9A2NcQSqGqTOXqu2aRi/XEQxDCBwM8yJtE6s=
|
gitea.com/xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:lSA0F4e9A2NcQSqGqTOXqu2aRi/XEQxDCBwM8yJtE6s=
|
||||||
@@ -325,8 +325,9 @@ github.com/go-asn1-ber/asn1-ber v1.5.3/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkPro
|
|||||||
github.com/go-chi/chi v1.5.1/go.mod h1:REp24E+25iKvxgeTfHmdUoL5x15kBiDBlnIl5bCwe2k=
|
github.com/go-chi/chi v1.5.1/go.mod h1:REp24E+25iKvxgeTfHmdUoL5x15kBiDBlnIl5bCwe2k=
|
||||||
github.com/go-chi/chi v1.5.4 h1:QHdzF2szwjqVV4wmByUnTcsbIg7UGaQ0tPF2t5GcAIs=
|
github.com/go-chi/chi v1.5.4 h1:QHdzF2szwjqVV4wmByUnTcsbIg7UGaQ0tPF2t5GcAIs=
|
||||||
github.com/go-chi/chi v1.5.4/go.mod h1:uaf8YgoFazUOkPBG7fxPftUylNumIev9awIWOENIuEg=
|
github.com/go-chi/chi v1.5.4/go.mod h1:uaf8YgoFazUOkPBG7fxPftUylNumIev9awIWOENIuEg=
|
||||||
github.com/go-chi/chi/v5 v5.0.1 h1:ALxjCrTf1aflOlkhMnCUP86MubbWFrzB3gkRPReLpTo=
|
|
||||||
github.com/go-chi/chi/v5 v5.0.1/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
github.com/go-chi/chi/v5 v5.0.1/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||||
|
github.com/go-chi/chi/v5 v5.0.4 h1:5e494iHzsYBiyXQAHHuI4tyJS9M3V84OuX3ufIIGHFo=
|
||||||
|
github.com/go-chi/chi/v5 v5.0.4/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||||
github.com/go-chi/cors v1.2.0 h1:tV1g1XENQ8ku4Bq3K9ub2AtgG+p16SmzeMSGTwrOKdE=
|
github.com/go-chi/cors v1.2.0 h1:tV1g1XENQ8ku4Bq3K9ub2AtgG+p16SmzeMSGTwrOKdE=
|
||||||
github.com/go-chi/cors v1.2.0/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=
|
github.com/go-chi/cors v1.2.0/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=
|
||||||
github.com/go-enry/go-enry/v2 v2.7.1 h1:WCqtfyteIz61GYk9lRVy8HblvIv4cP9GIiwm/6txCbU=
|
github.com/go-enry/go-enry/v2 v2.7.1 h1:WCqtfyteIz61GYk9lRVy8HblvIv4cP9GIiwm/6txCbU=
|
||||||
|
|||||||
154
integrations/api_private_serv_test.go
Normal file
154
integrations/api_private_serv_test.go
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
// Copyright 2021 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package integrations
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/url"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models"
|
||||||
|
"code.gitea.io/gitea/modules/private"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAPIPrivateNoServ(t *testing.T) {
|
||||||
|
onGiteaRun(t, func(*testing.T, *url.URL) {
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
key, user, err := private.ServNoCommand(ctx, 1)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, int64(2), user.ID)
|
||||||
|
assert.Equal(t, "user2", user.Name)
|
||||||
|
assert.Equal(t, int64(1), key.ID)
|
||||||
|
assert.Equal(t, "user2@localhost", key.Name)
|
||||||
|
|
||||||
|
deployKey, err := models.AddDeployKey(1, "test-deploy", "sk-ecdsa-sha2-nistp256@openssh.com AAAAInNrLWVjZHNhLXNoYTItbmlzdHAyNTZAb3BlbnNzaC5jb20AAAAIbmlzdHAyNTYAAABBBGXEEzWmm1dxb+57RoK5KVCL0w2eNv9cqJX2AGGVlkFsVDhOXHzsadS3LTK4VlEbbrDMJdoti9yM8vclA8IeRacAAAAEc3NoOg== nocomment", false)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
key, user, err = private.ServNoCommand(ctx, deployKey.KeyID)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Empty(t, user)
|
||||||
|
assert.Equal(t, deployKey.KeyID, key.ID)
|
||||||
|
assert.Equal(t, "test-deploy", key.Name)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAPIPrivateServ(t *testing.T) {
|
||||||
|
onGiteaRun(t, func(*testing.T, *url.URL) {
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
// Can push to a repo we own
|
||||||
|
results, err := private.ServCommand(ctx, 1, "user2", "repo1", models.AccessModeWrite, "git-upload-pack", "")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.False(t, results.IsWiki)
|
||||||
|
assert.False(t, results.IsDeployKey)
|
||||||
|
assert.Equal(t, int64(1), results.KeyID)
|
||||||
|
assert.Equal(t, "user2@localhost", results.KeyName)
|
||||||
|
assert.Equal(t, "user2", results.UserName)
|
||||||
|
assert.Equal(t, int64(2), results.UserID)
|
||||||
|
assert.Equal(t, "user2", results.OwnerName)
|
||||||
|
assert.Equal(t, "repo1", results.RepoName)
|
||||||
|
assert.Equal(t, int64(1), results.RepoID)
|
||||||
|
|
||||||
|
// Cannot push to a private repo we're not associated with
|
||||||
|
results, err = private.ServCommand(ctx, 1, "user15", "big_test_private_1", models.AccessModeWrite, "git-upload-pack", "")
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Empty(t, results)
|
||||||
|
|
||||||
|
// Cannot pull from a private repo we're not associated with
|
||||||
|
results, err = private.ServCommand(ctx, 1, "user15", "big_test_private_1", models.AccessModeRead, "git-upload-pack", "")
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Empty(t, results)
|
||||||
|
|
||||||
|
// Can pull from a public repo we're not associated with
|
||||||
|
results, err = private.ServCommand(ctx, 1, "user15", "big_test_public_1", models.AccessModeRead, "git-upload-pack", "")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.False(t, results.IsWiki)
|
||||||
|
assert.False(t, results.IsDeployKey)
|
||||||
|
assert.Equal(t, int64(1), results.KeyID)
|
||||||
|
assert.Equal(t, "user2@localhost", results.KeyName)
|
||||||
|
assert.Equal(t, "user2", results.UserName)
|
||||||
|
assert.Equal(t, int64(2), results.UserID)
|
||||||
|
assert.Equal(t, "user15", results.OwnerName)
|
||||||
|
assert.Equal(t, "big_test_public_1", results.RepoName)
|
||||||
|
assert.Equal(t, int64(17), results.RepoID)
|
||||||
|
|
||||||
|
// Cannot push to a public repo we're not associated with
|
||||||
|
results, err = private.ServCommand(ctx, 1, "user15", "big_test_public_1", models.AccessModeWrite, "git-upload-pack", "")
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Empty(t, results)
|
||||||
|
|
||||||
|
// Add reading deploy key
|
||||||
|
deployKey, err := models.AddDeployKey(19, "test-deploy", "sk-ecdsa-sha2-nistp256@openssh.com AAAAInNrLWVjZHNhLXNoYTItbmlzdHAyNTZAb3BlbnNzaC5jb20AAAAIbmlzdHAyNTYAAABBBGXEEzWmm1dxb+57RoK5KVCL0w2eNv9cqJX2AGGVlkFsVDhOXHzsadS3LTK4VlEbbrDMJdoti9yM8vclA8IeRacAAAAEc3NoOg== nocomment", true)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Can pull from repo we're a deploy key for
|
||||||
|
results, err = private.ServCommand(ctx, deployKey.KeyID, "user15", "big_test_private_1", models.AccessModeRead, "git-upload-pack", "")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.False(t, results.IsWiki)
|
||||||
|
assert.True(t, results.IsDeployKey)
|
||||||
|
assert.Equal(t, deployKey.KeyID, results.KeyID)
|
||||||
|
assert.Equal(t, "test-deploy", results.KeyName)
|
||||||
|
assert.Equal(t, "user15", results.UserName)
|
||||||
|
assert.Equal(t, int64(15), results.UserID)
|
||||||
|
assert.Equal(t, "user15", results.OwnerName)
|
||||||
|
assert.Equal(t, "big_test_private_1", results.RepoName)
|
||||||
|
assert.Equal(t, int64(19), results.RepoID)
|
||||||
|
|
||||||
|
// Cannot push to a private repo with reading key
|
||||||
|
results, err = private.ServCommand(ctx, deployKey.KeyID, "user15", "big_test_private_1", models.AccessModeWrite, "git-upload-pack", "")
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Empty(t, results)
|
||||||
|
|
||||||
|
// Cannot pull from a private repo we're not associated with
|
||||||
|
results, err = private.ServCommand(ctx, deployKey.ID, "user15", "big_test_private_2", models.AccessModeRead, "git-upload-pack", "")
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Empty(t, results)
|
||||||
|
|
||||||
|
// Cannot pull from a public repo we're not associated with
|
||||||
|
results, err = private.ServCommand(ctx, deployKey.ID, "user15", "big_test_public_1", models.AccessModeRead, "git-upload-pack", "")
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Empty(t, results)
|
||||||
|
|
||||||
|
// Add writing deploy key
|
||||||
|
deployKey, err = models.AddDeployKey(20, "test-deploy", "sk-ecdsa-sha2-nistp256@openssh.com AAAAInNrLWVjZHNhLXNoYTItbmlzdHAyNTZAb3BlbnNzaC5jb20AAAAIbmlzdHAyNTYAAABBBGXEEzWmm1dxb+57RoK5KVCL0w2eNv9cqJX2AGGVlkFsVDhOXHzsadS3LTK4VlEbbrDMJdoti9yM8vclA8IeRacAAAAEc3NoOg== nocomment", false)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
// Cannot push to a private repo with reading key
|
||||||
|
results, err = private.ServCommand(ctx, deployKey.KeyID, "user15", "big_test_private_1", models.AccessModeWrite, "git-upload-pack", "")
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Empty(t, results)
|
||||||
|
|
||||||
|
// Can pull from repo we're a writing deploy key for
|
||||||
|
results, err = private.ServCommand(ctx, deployKey.KeyID, "user15", "big_test_private_2", models.AccessModeRead, "git-upload-pack", "")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.False(t, results.IsWiki)
|
||||||
|
assert.True(t, results.IsDeployKey)
|
||||||
|
assert.Equal(t, deployKey.KeyID, results.KeyID)
|
||||||
|
assert.Equal(t, "test-deploy", results.KeyName)
|
||||||
|
assert.Equal(t, "user15", results.UserName)
|
||||||
|
assert.Equal(t, int64(15), results.UserID)
|
||||||
|
assert.Equal(t, "user15", results.OwnerName)
|
||||||
|
assert.Equal(t, "big_test_private_2", results.RepoName)
|
||||||
|
assert.Equal(t, int64(20), results.RepoID)
|
||||||
|
|
||||||
|
// Can push to repo we're a writing deploy key for
|
||||||
|
results, err = private.ServCommand(ctx, deployKey.KeyID, "user15", "big_test_private_2", models.AccessModeWrite, "git-upload-pack", "")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.False(t, results.IsWiki)
|
||||||
|
assert.True(t, results.IsDeployKey)
|
||||||
|
assert.Equal(t, deployKey.KeyID, results.KeyID)
|
||||||
|
assert.Equal(t, "test-deploy", results.KeyName)
|
||||||
|
assert.Equal(t, "user15", results.UserName)
|
||||||
|
assert.Equal(t, int64(15), results.UserID)
|
||||||
|
assert.Equal(t, "user15", results.OwnerName)
|
||||||
|
assert.Equal(t, "big_test_private_2", results.RepoName)
|
||||||
|
assert.Equal(t, int64(20), results.RepoID)
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
44
integrations/api_repo_archive_test.go
Normal file
44
integrations/api_repo_archive_test.go
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
// Copyright 2021 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package integrations
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAPIDownloadArchive(t *testing.T) {
|
||||||
|
defer prepareTestEnv(t)()
|
||||||
|
|
||||||
|
repo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
|
||||||
|
user2 := models.AssertExistsAndLoadBean(t, &models.User{ID: 2}).(*models.User)
|
||||||
|
session := loginUser(t, user2.LowerName)
|
||||||
|
token := getTokenForLoggedInUser(t, session)
|
||||||
|
|
||||||
|
link, _ := url.Parse(fmt.Sprintf("/api/v1/repos/%s/%s/archive/master.zip", user2.Name, repo.Name))
|
||||||
|
link.RawQuery = url.Values{"token": {token}}.Encode()
|
||||||
|
resp := MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK)
|
||||||
|
bs, err := io.ReadAll(resp.Body)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.EqualValues(t, 320, len(bs))
|
||||||
|
|
||||||
|
link, _ = url.Parse(fmt.Sprintf("/api/v1/repos/%s/%s/archive/master.tar.gz", user2.Name, repo.Name))
|
||||||
|
link.RawQuery = url.Values{"token": {token}}.Encode()
|
||||||
|
resp = MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK)
|
||||||
|
bs, err = io.ReadAll(resp.Body)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.EqualValues(t, 266, len(bs))
|
||||||
|
|
||||||
|
link, _ = url.Parse(fmt.Sprintf("/api/v1/repos/%s/%s/archive/master", user2.Name, repo.Name))
|
||||||
|
link.RawQuery = url.Values{"token": {token}}.Encode()
|
||||||
|
MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusBadRequest)
|
||||||
|
}
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1 +1 @@
|
|||||||
3a810dbf6b96afaa8c5f69a8b6ec1dabfca7368b
|
59e2c41e8f5140bb0182acebec17c8ad9831cc62
|
||||||
|
|||||||
@@ -251,6 +251,26 @@ func prepareTestEnv(t testing.TB, skip ...int) func() {
|
|||||||
assert.NoError(t, util.RemoveAll(setting.RepoRootPath))
|
assert.NoError(t, util.RemoveAll(setting.RepoRootPath))
|
||||||
|
|
||||||
assert.NoError(t, util.CopyDir(path.Join(filepath.Dir(setting.AppPath), "integrations/gitea-repositories-meta"), setting.RepoRootPath))
|
assert.NoError(t, util.CopyDir(path.Join(filepath.Dir(setting.AppPath), "integrations/gitea-repositories-meta"), setting.RepoRootPath))
|
||||||
|
ownerDirs, err := os.ReadDir(setting.RepoRootPath)
|
||||||
|
if err != nil {
|
||||||
|
assert.NoError(t, err, "unable to read the new repo root: %v\n", err)
|
||||||
|
}
|
||||||
|
for _, ownerDir := range ownerDirs {
|
||||||
|
if !ownerDir.Type().IsDir() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
repoDirs, err := os.ReadDir(filepath.Join(setting.RepoRootPath, ownerDir.Name()))
|
||||||
|
if err != nil {
|
||||||
|
assert.NoError(t, err, "unable to read the new repo root: %v\n", err)
|
||||||
|
}
|
||||||
|
for _, repoDir := range repoDirs {
|
||||||
|
_ = os.MkdirAll(filepath.Join(setting.RepoRootPath, ownerDir.Name(), repoDir.Name(), "objects", "pack"), 0755)
|
||||||
|
_ = os.MkdirAll(filepath.Join(setting.RepoRootPath, ownerDir.Name(), repoDir.Name(), "objects", "info"), 0755)
|
||||||
|
_ = os.MkdirAll(filepath.Join(setting.RepoRootPath, ownerDir.Name(), repoDir.Name(), "refs", "heads"), 0755)
|
||||||
|
_ = os.MkdirAll(filepath.Join(setting.RepoRootPath, ownerDir.Name(), repoDir.Name(), "refs", "tag"), 0755)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return deferFn
|
return deferFn
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -529,4 +549,23 @@ func resetFixtures(t *testing.T) {
|
|||||||
assert.NoError(t, models.LoadFixtures())
|
assert.NoError(t, models.LoadFixtures())
|
||||||
assert.NoError(t, util.RemoveAll(setting.RepoRootPath))
|
assert.NoError(t, util.RemoveAll(setting.RepoRootPath))
|
||||||
assert.NoError(t, util.CopyDir(path.Join(filepath.Dir(setting.AppPath), "integrations/gitea-repositories-meta"), setting.RepoRootPath))
|
assert.NoError(t, util.CopyDir(path.Join(filepath.Dir(setting.AppPath), "integrations/gitea-repositories-meta"), setting.RepoRootPath))
|
||||||
|
ownerDirs, err := os.ReadDir(setting.RepoRootPath)
|
||||||
|
if err != nil {
|
||||||
|
assert.NoError(t, err, "unable to read the new repo root: %v\n", err)
|
||||||
|
}
|
||||||
|
for _, ownerDir := range ownerDirs {
|
||||||
|
if !ownerDir.Type().IsDir() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
repoDirs, err := os.ReadDir(filepath.Join(setting.RepoRootPath, ownerDir.Name()))
|
||||||
|
if err != nil {
|
||||||
|
assert.NoError(t, err, "unable to read the new repo root: %v\n", err)
|
||||||
|
}
|
||||||
|
for _, repoDir := range repoDirs {
|
||||||
|
_ = os.MkdirAll(filepath.Join(setting.RepoRootPath, ownerDir.Name(), repoDir.Name(), "objects", "pack"), 0755)
|
||||||
|
_ = os.MkdirAll(filepath.Join(setting.RepoRootPath, ownerDir.Name(), repoDir.Name(), "objects", "info"), 0755)
|
||||||
|
_ = os.MkdirAll(filepath.Join(setting.RepoRootPath, ownerDir.Name(), repoDir.Name(), "refs", "heads"), 0755)
|
||||||
|
_ = os.MkdirAll(filepath.Join(setting.RepoRootPath, ownerDir.Name(), repoDir.Name(), "refs", "tag"), 0755)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,6 +61,25 @@ func initMigrationTest(t *testing.T) func() {
|
|||||||
assert.True(t, len(setting.RepoRootPath) != 0)
|
assert.True(t, len(setting.RepoRootPath) != 0)
|
||||||
assert.NoError(t, util.RemoveAll(setting.RepoRootPath))
|
assert.NoError(t, util.RemoveAll(setting.RepoRootPath))
|
||||||
assert.NoError(t, util.CopyDir(path.Join(filepath.Dir(setting.AppPath), "integrations/gitea-repositories-meta"), setting.RepoRootPath))
|
assert.NoError(t, util.CopyDir(path.Join(filepath.Dir(setting.AppPath), "integrations/gitea-repositories-meta"), setting.RepoRootPath))
|
||||||
|
ownerDirs, err := os.ReadDir(setting.RepoRootPath)
|
||||||
|
if err != nil {
|
||||||
|
assert.NoError(t, err, "unable to read the new repo root: %v\n", err)
|
||||||
|
}
|
||||||
|
for _, ownerDir := range ownerDirs {
|
||||||
|
if !ownerDir.Type().IsDir() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
repoDirs, err := os.ReadDir(filepath.Join(setting.RepoRootPath, ownerDir.Name()))
|
||||||
|
if err != nil {
|
||||||
|
assert.NoError(t, err, "unable to read the new repo root: %v\n", err)
|
||||||
|
}
|
||||||
|
for _, repoDir := range repoDirs {
|
||||||
|
_ = os.MkdirAll(filepath.Join(setting.RepoRootPath, ownerDir.Name(), repoDir.Name(), "objects", "pack"), 0755)
|
||||||
|
_ = os.MkdirAll(filepath.Join(setting.RepoRootPath, ownerDir.Name(), repoDir.Name(), "objects", "info"), 0755)
|
||||||
|
_ = os.MkdirAll(filepath.Join(setting.RepoRootPath, ownerDir.Name(), repoDir.Name(), "refs", "heads"), 0755)
|
||||||
|
_ = os.MkdirAll(filepath.Join(setting.RepoRootPath, ownerDir.Name(), repoDir.Name(), "refs", "tag"), 0755)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
git.CheckLFSVersion()
|
git.CheckLFSVersion()
|
||||||
setting.InitDBConfig()
|
setting.InitDBConfig()
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ package integrations
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"path"
|
"path"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@@ -83,7 +84,7 @@ func TestNonasciiBranches(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
from: "Plus+Is+Not+Space/Файл.md",
|
from: "Plus+Is+Not+Space/Файл.md",
|
||||||
to: "branch/Plus+Is+Not+Space/%d0%a4%d0%b0%d0%b9%d0%bb.md",
|
to: "branch/Plus+Is+Not+Space/%D0%A4%D0%B0%D0%B9%D0%BB.md",
|
||||||
status: http.StatusOK,
|
status: http.StatusOK,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -114,7 +115,7 @@ func TestNonasciiBranches(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
from: "タグ/ファイル.md",
|
from: "タグ/ファイル.md",
|
||||||
to: "tag/%e3%82%bf%e3%82%b0/%e3%83%95%e3%82%a1%e3%82%a4%e3%83%ab.md",
|
to: "tag/%e3%82%bf%e3%82%b0/%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB.md",
|
||||||
status: http.StatusOK,
|
status: http.StatusOK,
|
||||||
},
|
},
|
||||||
// Files
|
// Files
|
||||||
@@ -125,12 +126,12 @@ func TestNonasciiBranches(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
from: "Файл.md",
|
from: "Файл.md",
|
||||||
to: "branch/Plus+Is+Not+Space/%d0%a4%d0%b0%d0%b9%d0%bb.md",
|
to: "branch/Plus+Is+Not+Space/%D0%A4%D0%B0%D0%B9%D0%BB.md",
|
||||||
status: http.StatusOK,
|
status: http.StatusOK,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
from: "ファイル.md",
|
from: "ファイル.md",
|
||||||
to: "branch/Plus+Is+Not+Space/%e3%83%95%e3%82%a1%e3%82%a4%e3%83%ab.md",
|
to: "branch/Plus+Is+Not+Space/%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB.md",
|
||||||
status: http.StatusNotFound, // it's not on default branch
|
status: http.StatusNotFound, // it's not on default branch
|
||||||
},
|
},
|
||||||
// Same but url-encoded (few tests)
|
// Same but url-encoded (few tests)
|
||||||
@@ -146,7 +147,7 @@ func TestNonasciiBranches(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
from: "%D0%A4%D0%B0%D0%B9%D0%BB.md",
|
from: "%D0%A4%D0%B0%D0%B9%D0%BB.md",
|
||||||
to: "branch/Plus+Is+Not+Space/%d0%a4%d0%b0%d0%b9%d0%bb.md",
|
to: "branch/Plus+Is+Not+Space/%D0%A4%D0%B0%D0%B9%D0%BB.md",
|
||||||
status: http.StatusOK,
|
status: http.StatusOK,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -159,6 +160,41 @@ func TestNonasciiBranches(t *testing.T) {
|
|||||||
to: "tag/%d0%81/%e4%ba%ba",
|
to: "tag/%d0%81/%e4%ba%ba",
|
||||||
status: http.StatusOK,
|
status: http.StatusOK,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
from: "Plus+Is+Not+Space/%25%252525mightnotplaywell",
|
||||||
|
to: "branch/Plus+Is+Not+Space/%25%252525mightnotplaywell",
|
||||||
|
status: http.StatusOK,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
from: "Plus+Is+Not+Space/%25253Fisnotaquestion%25253F",
|
||||||
|
to: "branch/Plus+Is+Not+Space/%25253Fisnotaquestion%25253F",
|
||||||
|
status: http.StatusOK,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
from: "Plus+Is+Not+Space/" + url.PathEscape("%3Fis?and#afile"),
|
||||||
|
to: "branch/Plus+Is+Not+Space/" + url.PathEscape("%3Fis?and#afile"),
|
||||||
|
status: http.StatusOK,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
from: "Plus+Is+Not+Space/10%25.md",
|
||||||
|
to: "branch/Plus+Is+Not+Space/10%25.md",
|
||||||
|
status: http.StatusOK,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
from: "Plus+Is+Not+Space/" + url.PathEscape("This+file%20has 1space"),
|
||||||
|
to: "branch/Plus+Is+Not+Space/" + url.PathEscape("This+file%20has 1space"),
|
||||||
|
status: http.StatusOK,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
from: "Plus+Is+Not+Space/" + url.PathEscape("This+file%2520has 2 spaces"),
|
||||||
|
to: "branch/Plus+Is+Not+Space/" + url.PathEscape("This+file%2520has 2 spaces"),
|
||||||
|
status: http.StatusOK,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
from: "Plus+Is+Not+Space/" + url.PathEscape("£15&$6.txt"),
|
||||||
|
to: "branch/Plus+Is+Not+Space/" + url.PathEscape("£15&$6.txt"),
|
||||||
|
status: http.StatusOK,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
defer prepareTestEnv(t)()
|
defer prepareTestEnv(t)()
|
||||||
|
|||||||
@@ -5,10 +5,12 @@
|
|||||||
package integrations
|
package integrations
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
api "code.gitea.io/gitea/modules/structs"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -110,3 +112,64 @@ func TestPrivateOrg(t *testing.T) {
|
|||||||
req = NewRequest(t, "GET", "/privated_org/private_repo_on_private_org")
|
req = NewRequest(t, "GET", "/privated_org/private_repo_on_private_org")
|
||||||
session.MakeRequest(t, req, http.StatusOK)
|
session.MakeRequest(t, req, http.StatusOK)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestOrgRestrictedUser(t *testing.T) {
|
||||||
|
defer prepareTestEnv(t)()
|
||||||
|
|
||||||
|
// privated_org is a private org who has id 23
|
||||||
|
orgName := "privated_org"
|
||||||
|
|
||||||
|
// public_repo_on_private_org is a public repo on privated_org
|
||||||
|
repoName := "public_repo_on_private_org"
|
||||||
|
|
||||||
|
// user29 is a restricted user who is not a member of the organization
|
||||||
|
restrictedUser := "user29"
|
||||||
|
|
||||||
|
// #17003 reports a bug whereby adding a restricted user to a read-only team doesn't work
|
||||||
|
|
||||||
|
// assert restrictedUser cannot see the org or the public repo
|
||||||
|
restrictedSession := loginUser(t, restrictedUser)
|
||||||
|
req := NewRequest(t, "GET", fmt.Sprintf("/%s", orgName))
|
||||||
|
restrictedSession.MakeRequest(t, req, http.StatusNotFound)
|
||||||
|
|
||||||
|
req = NewRequest(t, "GET", fmt.Sprintf("/%s/%s", orgName, repoName))
|
||||||
|
restrictedSession.MakeRequest(t, req, http.StatusNotFound)
|
||||||
|
|
||||||
|
// Therefore create a read-only team
|
||||||
|
adminSession := loginUser(t, "user1")
|
||||||
|
token := getTokenForLoggedInUser(t, adminSession)
|
||||||
|
|
||||||
|
teamToCreate := &api.CreateTeamOption{
|
||||||
|
Name: "codereader",
|
||||||
|
Description: "Code Reader",
|
||||||
|
IncludesAllRepositories: true,
|
||||||
|
Permission: "read",
|
||||||
|
Units: []string{"repo.code"},
|
||||||
|
}
|
||||||
|
|
||||||
|
req = NewRequestWithJSON(t, "POST",
|
||||||
|
fmt.Sprintf("/api/v1/orgs/%s/teams?token=%s", orgName, token), teamToCreate)
|
||||||
|
|
||||||
|
var apiTeam api.Team
|
||||||
|
|
||||||
|
resp := adminSession.MakeRequest(t, req, http.StatusCreated)
|
||||||
|
DecodeJSON(t, resp, &apiTeam)
|
||||||
|
checkTeamResponse(t, &apiTeam, teamToCreate.Name, teamToCreate.Description, teamToCreate.IncludesAllRepositories,
|
||||||
|
teamToCreate.Permission, teamToCreate.Units)
|
||||||
|
checkTeamBean(t, apiTeam.ID, teamToCreate.Name, teamToCreate.Description, teamToCreate.IncludesAllRepositories,
|
||||||
|
teamToCreate.Permission, teamToCreate.Units)
|
||||||
|
//teamID := apiTeam.ID
|
||||||
|
|
||||||
|
// Now we need to add the restricted user to the team
|
||||||
|
req = NewRequest(t, "PUT",
|
||||||
|
fmt.Sprintf("/api/v1/teams/%d/members/%s?token=%s", apiTeam.ID, restrictedUser, token))
|
||||||
|
_ = adminSession.MakeRequest(t, req, http.StatusNoContent)
|
||||||
|
|
||||||
|
// Now we need to check if the restrictedUser can access the repo
|
||||||
|
req = NewRequest(t, "GET", fmt.Sprintf("/%s", orgName))
|
||||||
|
restrictedSession.MakeRequest(t, req, http.StatusOK)
|
||||||
|
|
||||||
|
req = NewRequest(t, "GET", fmt.Sprintf("/%s/%s", orgName, repoName))
|
||||||
|
restrictedSession.MakeRequest(t, req, http.StatusOK)
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,128 +1,81 @@
|
|||||||
-----BEGIN PGP PRIVATE KEY BLOCK-----
|
-----BEGIN PGP PRIVATE KEY BLOCK-----
|
||||||
|
|
||||||
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
|
ZMtt+nu5oJo/m1bvhWiYLhW6WX8TcmRKD3s=
|
||||||
AMYlmZ47NqBMBeaN0o/ahYMe8eIMaroWkufMfC9VRBSMAkpbDl34oNp0cflmnMYo
|
=V9rS
|
||||||
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
|
|
||||||
=511T
|
|
||||||
-----END PGP PRIVATE KEY BLOCK-----
|
-----END PGP PRIVATE KEY BLOCK-----
|
||||||
|
|||||||
@@ -302,7 +302,7 @@ func DeleteOrphanedIssues() error {
|
|||||||
// CountOrphanedObjects count subjects with have no existing refobject anymore
|
// CountOrphanedObjects count subjects with have no existing refobject anymore
|
||||||
func CountOrphanedObjects(subject, refobject, joinCond string) (int64, error) {
|
func CountOrphanedObjects(subject, refobject, joinCond string) (int64, error) {
|
||||||
return x.Table("`"+subject+"`").
|
return x.Table("`"+subject+"`").
|
||||||
Join("LEFT", refobject, joinCond).
|
Join("LEFT", "`"+refobject+"`", joinCond).
|
||||||
Where(builder.IsNull{"`" + refobject + "`.id"}).
|
Where(builder.IsNull{"`" + refobject + "`.id"}).
|
||||||
Count("id")
|
Count("id")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -568,7 +568,7 @@
|
|||||||
-
|
-
|
||||||
id: 40
|
id: 40
|
||||||
owner_id: 23
|
owner_id: 23
|
||||||
owner_name: limited_org
|
owner_name: privated_org
|
||||||
lower_name: public_repo_on_private_org
|
lower_name: public_repo_on_private_org
|
||||||
name: public_repo_on_private_org
|
name: public_repo_on_private_org
|
||||||
is_private: false
|
is_private: false
|
||||||
@@ -581,7 +581,7 @@
|
|||||||
-
|
-
|
||||||
id: 41
|
id: 41
|
||||||
owner_id: 23
|
owner_id: 23
|
||||||
owner_name: limited_org
|
owner_name: privated_org
|
||||||
lower_name: private_repo_on_private_org
|
lower_name: private_repo_on_private_org
|
||||||
name: private_repo_on_private_org
|
name: private_repo_on_private_org
|
||||||
is_private: true
|
is_private: true
|
||||||
|
|||||||
@@ -1517,12 +1517,12 @@ func GetIssueStats(opts *IssueStatsOptions) (*IssueStats, error) {
|
|||||||
func getIssueStatsChunk(opts *IssueStatsOptions, issueIDs []int64) (*IssueStats, error) {
|
func getIssueStatsChunk(opts *IssueStatsOptions, issueIDs []int64) (*IssueStats, error) {
|
||||||
stats := &IssueStats{}
|
stats := &IssueStats{}
|
||||||
|
|
||||||
countSession := func(opts *IssueStatsOptions) *xorm.Session {
|
countSession := func(opts *IssueStatsOptions, issueIDs []int64) *xorm.Session {
|
||||||
sess := x.
|
sess := x.
|
||||||
Where("issue.repo_id = ?", opts.RepoID)
|
Where("issue.repo_id = ?", opts.RepoID)
|
||||||
|
|
||||||
if len(opts.IssueIDs) > 0 {
|
if len(issueIDs) > 0 {
|
||||||
sess.In("issue.id", opts.IssueIDs)
|
sess.In("issue.id", issueIDs)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(opts.Labels) > 0 && opts.Labels != "0" {
|
if len(opts.Labels) > 0 && opts.Labels != "0" {
|
||||||
@@ -1572,13 +1572,13 @@ func getIssueStatsChunk(opts *IssueStatsOptions, issueIDs []int64) (*IssueStats,
|
|||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
stats.OpenCount, err = countSession(opts).
|
stats.OpenCount, err = countSession(opts, issueIDs).
|
||||||
And("issue.is_closed = ?", false).
|
And("issue.is_closed = ?", false).
|
||||||
Count(new(Issue))
|
Count(new(Issue))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return stats, err
|
return stats, err
|
||||||
}
|
}
|
||||||
stats.ClosedCount, err = countSession(opts).
|
stats.ClosedCount, err = countSession(opts, issueIDs).
|
||||||
And("issue.is_closed = ?", true).
|
And("issue.is_closed = ?", true).
|
||||||
Count(new(Issue))
|
Count(new(Issue))
|
||||||
return stats, err
|
return stats, err
|
||||||
|
|||||||
@@ -13,6 +13,26 @@ import (
|
|||||||
"xorm.io/xorm"
|
"xorm.io/xorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ErrIssueStopwatchNotExist represents an error that stopwatch is not exist
|
||||||
|
type ErrIssueStopwatchNotExist struct {
|
||||||
|
UserID int64
|
||||||
|
IssueID int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err ErrIssueStopwatchNotExist) Error() string {
|
||||||
|
return fmt.Sprintf("issue stopwatch doesn't exist[uid: %d, issue_id: %d", err.UserID, err.IssueID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrIssueStopwatchAlreadyExist represents an error that stopwatch is already exist
|
||||||
|
type ErrIssueStopwatchAlreadyExist struct {
|
||||||
|
UserID int64
|
||||||
|
IssueID int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err ErrIssueStopwatchAlreadyExist) Error() string {
|
||||||
|
return fmt.Sprintf("issue stopwatch already exists[uid: %d, issue_id: %d", err.UserID, err.IssueID)
|
||||||
|
}
|
||||||
|
|
||||||
// Stopwatch represents a stopwatch for time tracking.
|
// Stopwatch represents a stopwatch for time tracking.
|
||||||
type Stopwatch struct {
|
type Stopwatch struct {
|
||||||
ID int64 `xorm:"pk autoincr"`
|
ID int64 `xorm:"pk autoincr"`
|
||||||
@@ -74,29 +94,55 @@ func hasUserStopwatch(e Engine, userID int64) (exists bool, sw *Stopwatch, err e
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FinishIssueStopwatchIfPossible if stopwatch exist then finish it otherwise ignore
|
||||||
|
func FinishIssueStopwatchIfPossible(user *User, issue *Issue) error {
|
||||||
|
_, exists, err := getStopwatch(x, user.ID, issue.ID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !exists {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return FinishIssueStopwatch(user, issue)
|
||||||
|
}
|
||||||
|
|
||||||
// CreateOrStopIssueStopwatch will create or remove a stopwatch and will log it into issue's timeline.
|
// CreateOrStopIssueStopwatch will create or remove a stopwatch and will log it into issue's timeline.
|
||||||
func CreateOrStopIssueStopwatch(user *User, issue *Issue) error {
|
func CreateOrStopIssueStopwatch(user *User, issue *Issue) error {
|
||||||
|
_, exists, err := getStopwatch(x, user.ID, issue.ID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if exists {
|
||||||
|
return FinishIssueStopwatch(user, issue)
|
||||||
|
}
|
||||||
|
return CreateIssueStopwatch(user, issue)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FinishIssueStopwatch if stopwatch exist then finish it otherwise return an error
|
||||||
|
func FinishIssueStopwatch(user *User, issue *Issue) error {
|
||||||
sess := x.NewSession()
|
sess := x.NewSession()
|
||||||
defer sess.Close()
|
defer sess.Close()
|
||||||
if err := sess.Begin(); err != nil {
|
if err := sess.Begin(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := createOrStopIssueStopwatch(sess, user, issue); err != nil {
|
if err := finishIssueStopwatch(sess, user, issue); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return sess.Commit()
|
return sess.Commit()
|
||||||
}
|
}
|
||||||
|
|
||||||
func createOrStopIssueStopwatch(e *xorm.Session, user *User, issue *Issue) error {
|
func finishIssueStopwatch(e *xorm.Session, user *User, issue *Issue) error {
|
||||||
sw, exists, err := getStopwatch(e, user.ID, issue.ID)
|
sw, exists, err := getStopwatch(e, user.ID, issue.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := issue.loadRepo(e); err != nil {
|
if !exists {
|
||||||
return err
|
return ErrIssueStopwatchNotExist{
|
||||||
|
UserID: user.ID,
|
||||||
|
IssueID: issue.ID,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if exists {
|
|
||||||
// Create tracked time out of the time difference between start date and actual date
|
// Create tracked time out of the time difference between start date and actual date
|
||||||
timediff := time.Now().Unix() - int64(sw.CreatedUnix)
|
timediff := time.Now().Unix() - int64(sw.CreatedUnix)
|
||||||
|
|
||||||
@@ -112,6 +158,9 @@ func createOrStopIssueStopwatch(e *xorm.Session, user *User, issue *Issue) error
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := issue.loadRepo(e); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
if _, err := createComment(e, &CreateCommentOptions{
|
if _, err := createComment(e, &CreateCommentOptions{
|
||||||
Doer: user,
|
Doer: user,
|
||||||
Issue: issue,
|
Issue: issue,
|
||||||
@@ -122,10 +171,28 @@ func createOrStopIssueStopwatch(e *xorm.Session, user *User, issue *Issue) error
|
|||||||
}); err != nil {
|
}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if _, err := e.Delete(sw); err != nil {
|
_, err = e.Delete(sw)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
|
// CreateIssueStopwatch creates a stopwatch if not exist, otherwise return an error
|
||||||
|
func CreateIssueStopwatch(user *User, issue *Issue) error {
|
||||||
|
sess := x.NewSession()
|
||||||
|
defer sess.Close()
|
||||||
|
if err := sess.Begin(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := createIssueStopwatch(sess, user, issue); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return sess.Commit()
|
||||||
|
}
|
||||||
|
|
||||||
|
func createIssueStopwatch(e *xorm.Session, user *User, issue *Issue) error {
|
||||||
|
if err := issue.loadRepo(e); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// if another stopwatch is running: stop it
|
// if another stopwatch is running: stop it
|
||||||
exists, sw, err := hasUserStopwatch(e, user.ID)
|
exists, sw, err := hasUserStopwatch(e, user.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -136,7 +203,7 @@ func createOrStopIssueStopwatch(e *xorm.Session, user *User, issue *Issue) error
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := createOrStopIssueStopwatch(e, user, issue); err != nil {
|
if err := finishIssueStopwatch(e, user, issue); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -151,6 +218,10 @@ func createOrStopIssueStopwatch(e *xorm.Session, user *User, issue *Issue) error
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := issue.loadRepo(e); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if _, err := createComment(e, &CreateCommentOptions{
|
if _, err := createComment(e, &CreateCommentOptions{
|
||||||
Doer: user,
|
Doer: user,
|
||||||
Issue: issue,
|
Issue: issue,
|
||||||
@@ -159,7 +230,6 @@ func createOrStopIssueStopwatch(e *xorm.Session, user *User, issue *Issue) error
|
|||||||
}); err != nil {
|
}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,9 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"sort"
|
"sort"
|
||||||
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -417,3 +419,43 @@ func TestIssue_ResolveMentions(t *testing.T) {
|
|||||||
// Private repo, whole team
|
// Private repo, whole team
|
||||||
testSuccess("user17", "big_test_private_4", "user15", []string{"user17/owners"}, []int64{18})
|
testSuccess("user17", "big_test_private_4", "user15", []string{"user17/owners"}, []int64{18})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCorrectIssueStats(t *testing.T) {
|
||||||
|
assert.NoError(t, PrepareTestDatabase())
|
||||||
|
|
||||||
|
// Because the condition is to have chunked database look-ups,
|
||||||
|
// We have to more issues than `maxQueryParameters`, we will insert.
|
||||||
|
// maxQueryParameters + 10 issues into the testDatabase.
|
||||||
|
// Each new issues will have a constant description "Bugs are nasty"
|
||||||
|
// Which will be used later on.
|
||||||
|
|
||||||
|
issueAmount := maxQueryParameters + 10
|
||||||
|
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
for i := 0; i < issueAmount; i++ {
|
||||||
|
wg.Add(1)
|
||||||
|
go func(i int) {
|
||||||
|
testInsertIssue(t, fmt.Sprintf("Issue %d", i+1), "Bugs are nasty", 0)
|
||||||
|
wg.Done()
|
||||||
|
}(i)
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
// Now we will get all issueID's that match the "Bugs are nasty" query.
|
||||||
|
total, ids, err := SearchIssueIDsByKeyword("Bugs are nasty", []int64{1}, issueAmount, 0)
|
||||||
|
|
||||||
|
// Just to be sure.
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.EqualValues(t, issueAmount, total)
|
||||||
|
|
||||||
|
// Now we will call the GetIssueStats with these IDs and if working,
|
||||||
|
// get the correct stats back.
|
||||||
|
issueStats, err := GetIssueStats(&IssueStatsOptions{
|
||||||
|
RepoID: 1,
|
||||||
|
IssueIDs: ids,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Now check the values.
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.EqualValues(t, issueStats.OpenCount, issueAmount)
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ package migrations
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
@@ -762,8 +763,14 @@ func dropTableColumns(sess *xorm.Session, tableName string, columnNames ...strin
|
|||||||
}
|
}
|
||||||
tableSQL := string(res[0]["sql"])
|
tableSQL := string(res[0]["sql"])
|
||||||
|
|
||||||
|
// Get the string offset for column definitions: `CREATE TABLE ( column-definitions... )`
|
||||||
|
columnDefinitionsIndex := strings.Index(tableSQL, "(")
|
||||||
|
if columnDefinitionsIndex < 0 {
|
||||||
|
return errors.New("couldn't find column definitions")
|
||||||
|
}
|
||||||
|
|
||||||
// Separate out the column definitions
|
// Separate out the column definitions
|
||||||
tableSQL = tableSQL[strings.Index(tableSQL, "("):]
|
tableSQL = tableSQL[columnDefinitionsIndex:]
|
||||||
|
|
||||||
// Remove the required columnNames
|
// Remove the required columnNames
|
||||||
for _, name := range columnNames {
|
for _, name := range columnNames {
|
||||||
|
|||||||
@@ -205,6 +205,25 @@ func prepareTestEnv(t *testing.T, skip int, syncModels ...interface{}) (*xorm.En
|
|||||||
|
|
||||||
assert.NoError(t, com.CopyDir(path.Join(filepath.Dir(setting.AppPath), "integrations/gitea-repositories-meta"),
|
assert.NoError(t, com.CopyDir(path.Join(filepath.Dir(setting.AppPath), "integrations/gitea-repositories-meta"),
|
||||||
setting.RepoRootPath))
|
setting.RepoRootPath))
|
||||||
|
ownerDirs, err := os.ReadDir(setting.RepoRootPath)
|
||||||
|
if err != nil {
|
||||||
|
assert.NoError(t, err, "unable to read the new repo root: %v\n", err)
|
||||||
|
}
|
||||||
|
for _, ownerDir := range ownerDirs {
|
||||||
|
if !ownerDir.Type().IsDir() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
repoDirs, err := os.ReadDir(filepath.Join(setting.RepoRootPath, ownerDir.Name()))
|
||||||
|
if err != nil {
|
||||||
|
assert.NoError(t, err, "unable to read the new repo root: %v\n", err)
|
||||||
|
}
|
||||||
|
for _, repoDir := range repoDirs {
|
||||||
|
_ = os.MkdirAll(filepath.Join(setting.RepoRootPath, ownerDir.Name(), repoDir.Name(), "objects", "pack"), 0755)
|
||||||
|
_ = os.MkdirAll(filepath.Join(setting.RepoRootPath, ownerDir.Name(), repoDir.Name(), "objects", "info"), 0755)
|
||||||
|
_ = os.MkdirAll(filepath.Join(setting.RepoRootPath, ownerDir.Name(), repoDir.Name(), "refs", "heads"), 0755)
|
||||||
|
_ = os.MkdirAll(filepath.Join(setting.RepoRootPath, ownerDir.Name(), repoDir.Name(), "refs", "tag"), 0755)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if err := deleteDB(); err != nil {
|
if err := deleteDB(); err != nil {
|
||||||
t.Errorf("unable to reset database: %v", err)
|
t.Errorf("unable to reset database: %v", err)
|
||||||
|
|||||||
@@ -5,6 +5,8 @@
|
|||||||
package migrations
|
package migrations
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
"xorm.io/xorm"
|
"xorm.io/xorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -40,8 +42,17 @@ func convertTaskTypeToString(x *xorm.Engine) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// to keep the migration could be rerun
|
||||||
|
exist, err := x.Dialect().IsColumnExist(x.DB(), context.Background(), "hook_task", "type")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !exist {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
for i, s := range hookTaskTypes {
|
for i, s := range hookTaskTypes {
|
||||||
if _, err := x.Exec("UPDATE hook_task set typ = ? where type=?", s, i); err != nil {
|
if _, err := x.Exec("UPDATE hook_task set typ = ? where `type`=?", s, i); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
package migrations
|
package migrations
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
@@ -19,6 +20,22 @@ func renameTaskErrorsToMessage(x *xorm.Engine) error {
|
|||||||
Status int `xorm:"index"`
|
Status int `xorm:"index"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This migration maybe rerun so that we should check if it has been run
|
||||||
|
messageExist, err := x.Dialect().IsColumnExist(x.DB(), context.Background(), "task", "message")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if messageExist {
|
||||||
|
errorsExist, err := x.Dialect().IsColumnExist(x.DB(), context.Background(), "task", "errors")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !errorsExist {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
sess := x.NewSession()
|
sess := x.NewSession()
|
||||||
defer sess.Close()
|
defer sess.Close()
|
||||||
if err := sess.Begin(); err != nil {
|
if err := sess.Begin(); err != nil {
|
||||||
@@ -29,6 +46,13 @@ func renameTaskErrorsToMessage(x *xorm.Engine) error {
|
|||||||
return fmt.Errorf("error on Sync2: %v", err)
|
return fmt.Errorf("error on Sync2: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if messageExist {
|
||||||
|
// if both errors and message exist, drop message at first
|
||||||
|
if err := dropTableColumns(sess, "task", "message"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case setting.Database.UseMySQL:
|
case setting.Database.UseMySQL:
|
||||||
if _, err := sess.Exec("ALTER TABLE `task` CHANGE errors message text"); err != nil {
|
if _, err := sess.Exec("ALTER TABLE `task` CHANGE errors message text"); err != nil {
|
||||||
|
|||||||
@@ -179,16 +179,35 @@ func syncTables() error {
|
|||||||
return x.StoreEngine("InnoDB").Sync2(tables...)
|
return x.StoreEngine("InnoDB").Sync2(tables...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewTestEngine sets a new test xorm.Engine
|
// NewInstallTestEngine creates a new xorm.Engine for testing during install
|
||||||
func NewTestEngine() (err error) {
|
//
|
||||||
|
// This function will cause the basic database schema to be created
|
||||||
|
func NewInstallTestEngine(ctx context.Context, migrateFunc func(*xorm.Engine) error) (err error) {
|
||||||
x, err = GetNewEngine()
|
x, err = GetNewEngine()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("Connect to database: %v", err)
|
return fmt.Errorf("failed to connect to database: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
x.SetMapper(names.GonicMapper{})
|
x.SetMapper(names.GonicMapper{})
|
||||||
x.SetLogger(NewXORMLogger(!setting.IsProd()))
|
x.SetLogger(NewXORMLogger(!setting.IsProd()))
|
||||||
x.ShowSQL(!setting.IsProd())
|
x.ShowSQL(!setting.IsProd())
|
||||||
|
|
||||||
|
x.SetDefaultContext(ctx)
|
||||||
|
|
||||||
|
if err = x.Ping(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// We have to run migrateFunc here in case the user is re-running installation on a previously created DB.
|
||||||
|
// If we do not then table schemas will be changed and there will be conflicts when the migrations run properly.
|
||||||
|
//
|
||||||
|
// Installation should only be being re-run if users want to recover an old database.
|
||||||
|
// However, we should think carefully about should we support re-install on an installed instance,
|
||||||
|
// as there may be other problems due to secret reinitialization.
|
||||||
|
if err = migrateFunc(x); err != nil {
|
||||||
|
return fmt.Errorf("migrate: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
return syncTables()
|
return syncTables()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -115,7 +115,9 @@ func (p *Project) NumClosedIssues() int {
|
|||||||
func (p *Project) NumOpenIssues() int {
|
func (p *Project) NumOpenIssues() int {
|
||||||
c, err := x.Table("project_issue").
|
c, err := x.Table("project_issue").
|
||||||
Join("INNER", "issue", "project_issue.issue_id=issue.id").
|
Join("INNER", "issue", "project_issue.issue_id=issue.id").
|
||||||
Where("project_issue.project_id=? AND issue.is_closed=?", p.ID, false).Count("issue.id")
|
Where("project_issue.project_id=? AND issue.is_closed=?", p.ID, false).
|
||||||
|
Cols("issue_id").
|
||||||
|
Count()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,13 +21,18 @@ func (repo *Repository) CanEnableTimetracker() bool {
|
|||||||
|
|
||||||
// IsTimetrackerEnabled returns whether or not the timetracker is enabled. It returns the default value from config if an error occurs.
|
// IsTimetrackerEnabled returns whether or not the timetracker is enabled. It returns the default value from config if an error occurs.
|
||||||
func (repo *Repository) IsTimetrackerEnabled() bool {
|
func (repo *Repository) IsTimetrackerEnabled() bool {
|
||||||
|
return repo.isTimetrackerEnabled(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsTimetrackerEnabled returns whether or not the timetracker is enabled. It returns the default value from config if an error occurs.
|
||||||
|
func (repo *Repository) isTimetrackerEnabled(e Engine) bool {
|
||||||
if !setting.Service.EnableTimetracking {
|
if !setting.Service.EnableTimetracking {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
var u *RepoUnit
|
var u *RepoUnit
|
||||||
var err error
|
var err error
|
||||||
if u, err = repo.GetUnit(UnitTypeIssues); err != nil {
|
if u, err = repo.getUnit(e, UnitTypeIssues); err != nil {
|
||||||
return setting.Service.DefaultEnableTimetracking
|
return setting.Service.DefaultEnableTimetracking
|
||||||
}
|
}
|
||||||
return u.IssuesConfig().EnableTimetracker
|
return u.IssuesConfig().EnableTimetracker
|
||||||
|
|||||||
@@ -87,6 +87,26 @@ func MainTest(m *testing.M, pathToGiteaRoot string) {
|
|||||||
fatalTestError("util.CopyDir: %v\n", err)
|
fatalTestError("util.CopyDir: %v\n", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ownerDirs, err := os.ReadDir(setting.RepoRootPath)
|
||||||
|
if err != nil {
|
||||||
|
fatalTestError("unable to read the new repo root: %v\n", err)
|
||||||
|
}
|
||||||
|
for _, ownerDir := range ownerDirs {
|
||||||
|
if !ownerDir.Type().IsDir() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
repoDirs, err := os.ReadDir(filepath.Join(setting.RepoRootPath, ownerDir.Name()))
|
||||||
|
if err != nil {
|
||||||
|
fatalTestError("unable to read the new repo root: %v\n", err)
|
||||||
|
}
|
||||||
|
for _, repoDir := range repoDirs {
|
||||||
|
_ = os.MkdirAll(filepath.Join(setting.RepoRootPath, ownerDir.Name(), repoDir.Name(), "objects", "pack"), 0755)
|
||||||
|
_ = os.MkdirAll(filepath.Join(setting.RepoRootPath, ownerDir.Name(), repoDir.Name(), "objects", "info"), 0755)
|
||||||
|
_ = os.MkdirAll(filepath.Join(setting.RepoRootPath, ownerDir.Name(), repoDir.Name(), "refs", "heads"), 0755)
|
||||||
|
_ = os.MkdirAll(filepath.Join(setting.RepoRootPath, ownerDir.Name(), repoDir.Name(), "refs", "tag"), 0755)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
exitStatus := m.Run()
|
exitStatus := m.Run()
|
||||||
if err = util.RemoveAll(setting.RepoRootPath); err != nil {
|
if err = util.RemoveAll(setting.RepoRootPath); err != nil {
|
||||||
fatalTestError("util.RemoveAll: %v\n", err)
|
fatalTestError("util.RemoveAll: %v\n", err)
|
||||||
@@ -128,6 +148,23 @@ func PrepareTestEnv(t testing.TB) {
|
|||||||
assert.NoError(t, util.RemoveAll(setting.RepoRootPath))
|
assert.NoError(t, util.RemoveAll(setting.RepoRootPath))
|
||||||
metaPath := filepath.Join(giteaRoot, "integrations", "gitea-repositories-meta")
|
metaPath := filepath.Join(giteaRoot, "integrations", "gitea-repositories-meta")
|
||||||
assert.NoError(t, util.CopyDir(metaPath, setting.RepoRootPath))
|
assert.NoError(t, util.CopyDir(metaPath, setting.RepoRootPath))
|
||||||
|
|
||||||
|
ownerDirs, err := os.ReadDir(setting.RepoRootPath)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
for _, ownerDir := range ownerDirs {
|
||||||
|
if !ownerDir.Type().IsDir() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
repoDirs, err := os.ReadDir(filepath.Join(setting.RepoRootPath, ownerDir.Name()))
|
||||||
|
assert.NoError(t, err)
|
||||||
|
for _, repoDir := range repoDirs {
|
||||||
|
_ = os.MkdirAll(filepath.Join(setting.RepoRootPath, ownerDir.Name(), repoDir.Name(), "objects", "pack"), 0755)
|
||||||
|
_ = os.MkdirAll(filepath.Join(setting.RepoRootPath, ownerDir.Name(), repoDir.Name(), "objects", "info"), 0755)
|
||||||
|
_ = os.MkdirAll(filepath.Join(setting.RepoRootPath, ownerDir.Name(), repoDir.Name(), "refs", "heads"), 0755)
|
||||||
|
_ = os.MkdirAll(filepath.Join(setting.RepoRootPath, ownerDir.Name(), repoDir.Name(), "refs", "tag"), 0755)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
base.SetupGiteaRoot() // Makes sure GITEA_ROOT is set
|
base.SetupGiteaRoot() // Makes sure GITEA_ROOT is set
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -77,9 +77,6 @@ var (
|
|||||||
// ErrEmailNotActivated e-mail address has not been activated error
|
// ErrEmailNotActivated e-mail address has not been activated error
|
||||||
ErrEmailNotActivated = errors.New("E-mail address has not been activated")
|
ErrEmailNotActivated = errors.New("E-mail address has not been activated")
|
||||||
|
|
||||||
// ErrUserNameIllegal user name contains illegal characters error
|
|
||||||
ErrUserNameIllegal = errors.New("User name contains illegal characters")
|
|
||||||
|
|
||||||
// ErrLoginSourceNotActived login source is not actived error
|
// ErrLoginSourceNotActived login source is not actived error
|
||||||
ErrLoginSourceNotActived = errors.New("Login source is not actived")
|
ErrLoginSourceNotActived = errors.New("Login source is not actived")
|
||||||
|
|
||||||
@@ -1072,18 +1069,46 @@ func validateUser(u *User) error {
|
|||||||
return ValidateEmail(u.Email)
|
return ValidateEmail(u.Email)
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateUser(e Engine, u *User) error {
|
func updateUser(e Engine, u *User, changePrimaryEmail bool) error {
|
||||||
if err := validateUser(u); err != nil {
|
if err := validateUser(u); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if changePrimaryEmail {
|
||||||
|
var emailAddress EmailAddress
|
||||||
|
has, err := e.Where("lower_email=?", strings.ToLower(u.Email)).Get(&emailAddress)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !has {
|
||||||
|
// 1. Update old primary email
|
||||||
|
if _, err = e.Where("uid=? AND is_primary=?", u.ID, true).Cols("is_primary").Update(&EmailAddress{
|
||||||
|
IsPrimary: false,
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
emailAddress.Email = u.Email
|
||||||
|
emailAddress.UID = u.ID
|
||||||
|
emailAddress.IsActivated = true
|
||||||
|
emailAddress.IsPrimary = true
|
||||||
|
if _, err := e.Insert(&emailAddress); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else if _, err := e.ID(emailAddress).Cols("is_primary").Update(&EmailAddress{
|
||||||
|
IsPrimary: true,
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_, err := e.ID(u.ID).AllCols().Update(u)
|
_, err := e.ID(u.ID).AllCols().Update(u)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateUser updates user's information.
|
// UpdateUser updates user's information.
|
||||||
func UpdateUser(u *User) error {
|
func UpdateUser(u *User, changePrimaryEmail bool) error {
|
||||||
return updateUser(x, u)
|
return updateUser(x, u, changePrimaryEmail)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateUserCols update user according special columns
|
// UpdateUserCols update user according special columns
|
||||||
@@ -1112,7 +1137,7 @@ func UpdateUserSetting(u *User) (err error) {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err = updateUser(sess, u); err != nil {
|
if err = updateUser(sess, u, false); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return sess.Commit()
|
return sess.Commit()
|
||||||
|
|||||||
@@ -475,17 +475,17 @@ func TestUpdateUser(t *testing.T) {
|
|||||||
user := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User)
|
user := AssertExistsAndLoadBean(t, &User{ID: 2}).(*User)
|
||||||
|
|
||||||
user.KeepActivityPrivate = true
|
user.KeepActivityPrivate = true
|
||||||
assert.NoError(t, UpdateUser(user))
|
assert.NoError(t, UpdateUser(user, false))
|
||||||
user = AssertExistsAndLoadBean(t, &User{ID: 2}).(*User)
|
user = AssertExistsAndLoadBean(t, &User{ID: 2}).(*User)
|
||||||
assert.True(t, user.KeepActivityPrivate)
|
assert.True(t, user.KeepActivityPrivate)
|
||||||
|
|
||||||
setting.Service.AllowedUserVisibilityModesSlice = []bool{true, false, false}
|
setting.Service.AllowedUserVisibilityModesSlice = []bool{true, false, false}
|
||||||
user.KeepActivityPrivate = false
|
user.KeepActivityPrivate = false
|
||||||
user.Visibility = structs.VisibleTypePrivate
|
user.Visibility = structs.VisibleTypePrivate
|
||||||
assert.Error(t, UpdateUser(user))
|
assert.Error(t, UpdateUser(user, false))
|
||||||
user = AssertExistsAndLoadBean(t, &User{ID: 2}).(*User)
|
user = AssertExistsAndLoadBean(t, &User{ID: 2}).(*User)
|
||||||
assert.True(t, user.KeepActivityPrivate)
|
assert.True(t, user.KeepActivityPrivate)
|
||||||
|
|
||||||
user.Email = "no mail@mail.org"
|
user.Email = "no mail@mail.org"
|
||||||
assert.Error(t, UpdateUser(user))
|
assert.Error(t, UpdateUser(user, true))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
//go:build pam
|
||||||
// +build pam
|
// +build pam
|
||||||
|
|
||||||
// Copyright 2014 The Gogs Authors. All rights reserved.
|
// Copyright 2014 The Gogs Authors. All rights reserved.
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
// +build !pam
|
|
||||||
|
|
||||||
// Copyright 2014 The Gogs Authors. All rights reserved.
|
// Copyright 2014 The Gogs Authors. All rights reserved.
|
||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build !pam
|
||||||
|
// +build !pam
|
||||||
|
|
||||||
package pam
|
package pam
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
//go:build pam
|
||||||
// +build pam
|
// +build pam
|
||||||
|
|
||||||
// Copyright 2021 The Gitea Authors. All rights reserved.
|
// Copyright 2021 The Gitea Authors. All rights reserved.
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import (
|
|||||||
|
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/setting"
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
"code.gitea.io/gitea/modules/util"
|
||||||
|
|
||||||
"github.com/gogs/chardet"
|
"github.com/gogs/chardet"
|
||||||
"golang.org/x/net/html/charset"
|
"golang.org/x/net/html/charset"
|
||||||
@@ -26,9 +27,9 @@ var UTF8BOM = []byte{'\xef', '\xbb', '\xbf'}
|
|||||||
// ToUTF8WithFallbackReader detects the encoding of content and coverts to UTF-8 reader if possible
|
// ToUTF8WithFallbackReader detects the encoding of content and coverts to UTF-8 reader if possible
|
||||||
func ToUTF8WithFallbackReader(rd io.Reader) io.Reader {
|
func ToUTF8WithFallbackReader(rd io.Reader) io.Reader {
|
||||||
var buf = make([]byte, 2048)
|
var buf = make([]byte, 2048)
|
||||||
n, err := rd.Read(buf)
|
n, err := util.ReadAtMost(rd, buf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return rd
|
return io.MultiReader(bytes.NewReader(RemoveBOMIfPresent(buf[:n])), rd)
|
||||||
}
|
}
|
||||||
|
|
||||||
charsetLabel, err := DetectEncoding(buf[:n])
|
charsetLabel, err := DetectEncoding(buf[:n])
|
||||||
|
|||||||
@@ -669,6 +669,10 @@ func Contexter() func(next http.Handler) http.Handler {
|
|||||||
var locale = middleware.Locale(resp, req)
|
var locale = middleware.Locale(resp, req)
|
||||||
var startTime = time.Now()
|
var startTime = time.Now()
|
||||||
var link = setting.AppSubURL + strings.TrimSuffix(req.URL.EscapedPath(), "/")
|
var link = setting.AppSubURL + strings.TrimSuffix(req.URL.EscapedPath(), "/")
|
||||||
|
|
||||||
|
chiCtx := chi.RouteContext(req.Context())
|
||||||
|
chiCtx.RoutePath = req.URL.EscapedPath()
|
||||||
|
|
||||||
var ctx = Context{
|
var ctx = Context{
|
||||||
Resp: NewResponse(resp),
|
Resp: NewResponse(resp),
|
||||||
Cache: mc.GetCache(),
|
Cache: mc.GetCache(),
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ type Repository struct {
|
|||||||
Commit *git.Commit
|
Commit *git.Commit
|
||||||
Tag *git.Tag
|
Tag *git.Tag
|
||||||
GitRepo *git.Repository
|
GitRepo *git.Repository
|
||||||
|
RefName string
|
||||||
BranchName string
|
BranchName string
|
||||||
TagName string
|
TagName string
|
||||||
TreePath string
|
TreePath string
|
||||||
@@ -190,9 +191,9 @@ func (r *Repository) BranchNameSubURL() string {
|
|||||||
case r.IsViewBranch:
|
case r.IsViewBranch:
|
||||||
return "branch/" + r.BranchName
|
return "branch/" + r.BranchName
|
||||||
case r.IsViewTag:
|
case r.IsViewTag:
|
||||||
return "tag/" + r.BranchName
|
return "tag/" + r.TagName
|
||||||
case r.IsViewCommit:
|
case r.IsViewCommit:
|
||||||
return "commit/" + r.BranchName
|
return "commit/" + r.CommitID
|
||||||
}
|
}
|
||||||
log.Error("Unknown view type for repo: %v", r)
|
log.Error("Unknown view type for repo: %v", r)
|
||||||
return ""
|
return ""
|
||||||
@@ -345,7 +346,7 @@ func repoAssignment(ctx *Context, repo *models.Repository) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check access.
|
// Check access.
|
||||||
if ctx.Repo.Permission.AccessMode == models.AccessModeNone {
|
if !ctx.Repo.Permission.HasAccess() {
|
||||||
if ctx.Query("go-get") == "1" {
|
if ctx.Query("go-get") == "1" {
|
||||||
EarlyResponseForGoGetMeta(ctx)
|
EarlyResponseForGoGetMeta(ctx)
|
||||||
return
|
return
|
||||||
@@ -562,8 +563,6 @@ func RepoAssignment(ctx *Context) (cancel context.CancelFunc) {
|
|||||||
ctx.Data["Branches"] = brs
|
ctx.Data["Branches"] = brs
|
||||||
ctx.Data["BranchesCount"] = len(brs)
|
ctx.Data["BranchesCount"] = len(brs)
|
||||||
|
|
||||||
ctx.Data["TagName"] = ctx.Repo.TagName
|
|
||||||
|
|
||||||
// If not branch selected, try default one.
|
// If not branch selected, try default one.
|
||||||
// If default branch doesn't exists, fall back to some other branch.
|
// If default branch doesn't exists, fall back to some other branch.
|
||||||
if len(ctx.Repo.BranchName) == 0 {
|
if len(ctx.Repo.BranchName) == 0 {
|
||||||
@@ -572,9 +571,9 @@ func RepoAssignment(ctx *Context) (cancel context.CancelFunc) {
|
|||||||
} else if len(brs) > 0 {
|
} else if len(brs) > 0 {
|
||||||
ctx.Repo.BranchName = brs[0]
|
ctx.Repo.BranchName = brs[0]
|
||||||
}
|
}
|
||||||
|
ctx.Repo.RefName = ctx.Repo.BranchName
|
||||||
}
|
}
|
||||||
ctx.Data["BranchName"] = ctx.Repo.BranchName
|
ctx.Data["BranchName"] = ctx.Repo.BranchName
|
||||||
ctx.Data["CommitID"] = ctx.Repo.CommitID
|
|
||||||
|
|
||||||
// People who have push access or have forked repository can propose a new pull request.
|
// People who have push access or have forked repository can propose a new pull request.
|
||||||
canPush := ctx.Repo.CanWrite(models.UnitTypeCode) || (ctx.IsSigned && ctx.User.HasForkedRepo(ctx.Repo.Repository.ID))
|
canPush := ctx.Repo.CanWrite(models.UnitTypeCode) || (ctx.IsSigned && ctx.User.HasForkedRepo(ctx.Repo.Repository.ID))
|
||||||
@@ -759,7 +758,6 @@ func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context
|
|||||||
// Get default branch.
|
// Get default branch.
|
||||||
if len(ctx.Params("*")) == 0 {
|
if len(ctx.Params("*")) == 0 {
|
||||||
refName = ctx.Repo.Repository.DefaultBranch
|
refName = ctx.Repo.Repository.DefaultBranch
|
||||||
ctx.Repo.BranchName = refName
|
|
||||||
if !ctx.Repo.GitRepo.IsBranchExist(refName) {
|
if !ctx.Repo.GitRepo.IsBranchExist(refName) {
|
||||||
brs, _, err := ctx.Repo.GitRepo.GetBranches(0, 0)
|
brs, _, err := ctx.Repo.GitRepo.GetBranches(0, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -773,6 +771,8 @@ func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context
|
|||||||
}
|
}
|
||||||
refName = brs[0]
|
refName = brs[0]
|
||||||
}
|
}
|
||||||
|
ctx.Repo.RefName = refName
|
||||||
|
ctx.Repo.BranchName = refName
|
||||||
ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(refName)
|
ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(refName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.ServerError("GetBranchCommit", err)
|
ctx.ServerError("GetBranchCommit", err)
|
||||||
@@ -783,9 +783,10 @@ func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context
|
|||||||
|
|
||||||
} else {
|
} else {
|
||||||
refName = getRefName(ctx, refType)
|
refName = getRefName(ctx, refType)
|
||||||
ctx.Repo.BranchName = refName
|
ctx.Repo.RefName = refName
|
||||||
if refType.RefTypeIncludesBranches() && ctx.Repo.GitRepo.IsBranchExist(refName) {
|
if refType.RefTypeIncludesBranches() && ctx.Repo.GitRepo.IsBranchExist(refName) {
|
||||||
ctx.Repo.IsViewBranch = true
|
ctx.Repo.IsViewBranch = true
|
||||||
|
ctx.Repo.BranchName = refName
|
||||||
|
|
||||||
ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(refName)
|
ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(refName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -796,6 +797,8 @@ func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context
|
|||||||
|
|
||||||
} else if refType.RefTypeIncludesTags() && ctx.Repo.GitRepo.IsTagExist(refName) {
|
} else if refType.RefTypeIncludesTags() && ctx.Repo.GitRepo.IsTagExist(refName) {
|
||||||
ctx.Repo.IsViewTag = true
|
ctx.Repo.IsViewTag = true
|
||||||
|
ctx.Repo.TagName = refName
|
||||||
|
|
||||||
ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetTagCommit(refName)
|
ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetTagCommit(refName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.ServerError("GetTagCommit", err)
|
ctx.ServerError("GetTagCommit", err)
|
||||||
@@ -830,13 +833,14 @@ func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context
|
|||||||
setting.AppSubURL,
|
setting.AppSubURL,
|
||||||
strings.TrimSuffix(ctx.Req.URL.Path, ctx.Params("*")),
|
strings.TrimSuffix(ctx.Req.URL.Path, ctx.Params("*")),
|
||||||
ctx.Repo.BranchNameSubURL(),
|
ctx.Repo.BranchNameSubURL(),
|
||||||
ctx.Repo.TreePath))
|
util.PathEscapeSegments(ctx.Repo.TreePath)))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.Data["BranchName"] = ctx.Repo.BranchName
|
ctx.Data["BranchName"] = ctx.Repo.BranchName
|
||||||
ctx.Data["BranchNameSubURL"] = ctx.Repo.BranchNameSubURL()
|
ctx.Data["BranchNameSubURL"] = ctx.Repo.BranchNameSubURL()
|
||||||
|
ctx.Data["TagName"] = ctx.Repo.TagName
|
||||||
ctx.Data["CommitID"] = ctx.Repo.CommitID
|
ctx.Data["CommitID"] = ctx.Repo.CommitID
|
||||||
ctx.Data["TreePath"] = ctx.Repo.TreePath
|
ctx.Data["TreePath"] = ctx.Repo.TreePath
|
||||||
ctx.Data["IsViewBranch"] = ctx.Repo.IsViewBranch
|
ctx.Data["IsViewBranch"] = ctx.Repo.IsViewBranch
|
||||||
|
|||||||
@@ -149,6 +149,7 @@ func ToCommit(repo *models.Repository, commit *git.Commit, userCache map[string]
|
|||||||
CommitMeta: &api.CommitMeta{
|
CommitMeta: &api.CommitMeta{
|
||||||
URL: repo.APIURL() + "/git/commits/" + commit.ID.String(),
|
URL: repo.APIURL() + "/git/commits/" + commit.ID.String(),
|
||||||
SHA: commit.ID.String(),
|
SHA: commit.ID.String(),
|
||||||
|
Created: commit.Committer.When,
|
||||||
},
|
},
|
||||||
HTMLURL: repo.HTMLURL() + "/commit/" + commit.ID.String(),
|
HTMLURL: repo.HTMLURL() + "/commit/" + commit.ID.String(),
|
||||||
RepoCommit: &api.RepoCommit{
|
RepoCommit: &api.RepoCommit{
|
||||||
@@ -171,6 +172,7 @@ func ToCommit(repo *models.Repository, commit *git.Commit, userCache map[string]
|
|||||||
Tree: &api.CommitMeta{
|
Tree: &api.CommitMeta{
|
||||||
URL: repo.APIURL() + "/git/trees/" + commit.ID.String(),
|
URL: repo.APIURL() + "/git/trees/" + commit.ID.String(),
|
||||||
SHA: commit.ID.String(),
|
SHA: commit.ID.String(),
|
||||||
|
Created: commit.Committer.When,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Author: apiAuthor,
|
Author: apiAuthor,
|
||||||
|
|||||||
@@ -28,35 +28,24 @@ func CreateReader(input io.Reader, delimiter rune) *stdcsv.Reader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CreateReaderAndGuessDelimiter tries to guess the field delimiter from the content and creates a csv.Reader.
|
// CreateReaderAndGuessDelimiter tries to guess the field delimiter from the content and creates a csv.Reader.
|
||||||
|
// Reads at most 10k bytes.
|
||||||
func CreateReaderAndGuessDelimiter(rd io.Reader) (*stdcsv.Reader, error) {
|
func CreateReaderAndGuessDelimiter(rd io.Reader) (*stdcsv.Reader, error) {
|
||||||
var data = make([]byte, 1e4)
|
var data = make([]byte, 1e4)
|
||||||
size, err := rd.Read(data)
|
size, err := util.ReadAtMost(rd, data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == io.EOF {
|
|
||||||
return CreateReader(bytes.NewReader([]byte{}), rune(',')), nil
|
|
||||||
}
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
delimiter := guessDelimiter(data[:size])
|
return CreateReader(
|
||||||
|
io.MultiReader(bytes.NewReader(data[:size]), rd),
|
||||||
var newInput io.Reader
|
guessDelimiter(data[:size]),
|
||||||
if size < 1e4 {
|
), nil
|
||||||
newInput = bytes.NewReader(data[:size])
|
|
||||||
} else {
|
|
||||||
newInput = io.MultiReader(bytes.NewReader(data), rd)
|
|
||||||
}
|
|
||||||
|
|
||||||
return CreateReader(newInput, delimiter), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// guessDelimiter scores the input CSV data against delimiters, and returns the best match.
|
// guessDelimiter scores the input CSV data against delimiters, and returns the best match.
|
||||||
// Reads at most 10k bytes & 10 lines.
|
|
||||||
func guessDelimiter(data []byte) rune {
|
func guessDelimiter(data []byte) rune {
|
||||||
maxLines := 10
|
maxLines := 10
|
||||||
maxBytes := util.Min(len(data), 1e4)
|
text := quoteRegexp.ReplaceAllLiteralString(string(data), "")
|
||||||
text := string(data[:maxBytes])
|
|
||||||
text = quoteRegexp.ReplaceAllLiteralString(text, "")
|
|
||||||
lines := strings.SplitN(text, "\n", maxLines+1)
|
lines := strings.SplitN(text, "\n", maxLines+1)
|
||||||
lines = lines[:util.Min(maxLines, len(lines))]
|
lines = lines[:util.Min(maxLines, len(lines))]
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,20 @@ type WriteCloserError interface {
|
|||||||
CloseWithError(err error) error
|
CloseWithError(err error) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EnsureValidGitRepository runs git rev-parse in the repository path - thus ensuring that the repository is a valid repository.
|
||||||
|
// Run before opening git cat-file.
|
||||||
|
// This is needed otherwise the git cat-file will hang for invalid repositories.
|
||||||
|
func EnsureValidGitRepository(ctx context.Context, repoPath string) error {
|
||||||
|
stderr := strings.Builder{}
|
||||||
|
err := NewCommandContext(ctx, "rev-parse").
|
||||||
|
SetDescription(fmt.Sprintf("%s rev-parse [repo_path: %s]", GitExecutable, repoPath)).
|
||||||
|
RunInDirFullPipeline(repoPath, nil, &stderr, nil)
|
||||||
|
if err != nil {
|
||||||
|
return ConcatenateError(err, (&stderr).String())
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// CatFileBatchCheck opens git cat-file --batch-check in the provided repo and returns a stdin pipe, a stdout reader and cancel function
|
// CatFileBatchCheck opens git cat-file --batch-check in the provided repo and returns a stdin pipe, a stdout reader and cancel function
|
||||||
func CatFileBatchCheck(repoPath string) (WriteCloserError, *bufio.Reader, func()) {
|
func CatFileBatchCheck(repoPath string) (WriteCloserError, *bufio.Reader, func()) {
|
||||||
batchStdinReader, batchStdinWriter := io.Pipe()
|
batchStdinReader, batchStdinWriter := io.Pipe()
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/typesniffer"
|
"code.gitea.io/gitea/modules/typesniffer"
|
||||||
|
"code.gitea.io/gitea/modules/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// This file contains common functions between the gogit and !gogit variants for git Blobs
|
// This file contains common functions between the gogit and !gogit variants for git Blobs
|
||||||
@@ -29,7 +30,7 @@ func (b *Blob) GetBlobContent() (string, error) {
|
|||||||
}
|
}
|
||||||
defer dataRc.Close()
|
defer dataRc.Close()
|
||||||
buf := make([]byte, 1024)
|
buf := make([]byte, 1024)
|
||||||
n, _ := dataRc.Read(buf)
|
n, _ := util.ReadAtMost(dataRc, buf)
|
||||||
buf = buf[:n]
|
buf = buf[:n]
|
||||||
return string(buf), nil
|
return string(buf), nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build gogit
|
||||||
// +build gogit
|
// +build gogit
|
||||||
|
|
||||||
package git
|
package git
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build !gogit
|
||||||
// +build !gogit
|
// +build !gogit
|
||||||
|
|
||||||
package git
|
package git
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build race
|
||||||
// +build race
|
// +build race
|
||||||
|
|
||||||
package git
|
package git
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build gogit
|
||||||
// +build gogit
|
// +build gogit
|
||||||
|
|
||||||
package git
|
package git
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build gogit
|
||||||
// +build gogit
|
// +build gogit
|
||||||
|
|
||||||
package git
|
package git
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build !gogit
|
||||||
// +build !gogit
|
// +build !gogit
|
||||||
|
|
||||||
package git
|
package git
|
||||||
|
|||||||
@@ -218,6 +218,8 @@ func CutDiffAroundLine(originalDiff io.Reader, line int64, old bool, numbersOfLi
|
|||||||
} else {
|
} else {
|
||||||
otherLine++
|
otherLine++
|
||||||
}
|
}
|
||||||
|
case '\\':
|
||||||
|
// FIXME: handle `\ No newline at end of file`
|
||||||
default:
|
default:
|
||||||
currentLine++
|
currentLine++
|
||||||
otherLine++
|
otherLine++
|
||||||
|
|||||||
@@ -42,6 +42,57 @@ index d8e4c92..19dc8ad 100644
|
|||||||
/
|
/
|
||||||
`
|
`
|
||||||
|
|
||||||
|
var issue17875Diff = `diff --git a/Geschäftsordnung.md b/Geschäftsordnung.md
|
||||||
|
index d46c152..a7d2d55 100644
|
||||||
|
--- a/Geschäftsordnung.md
|
||||||
|
+++ b/Geschäftsordnung.md
|
||||||
|
@@ -1,5 +1,5 @@
|
||||||
|
---
|
||||||
|
-date: "23.01.2021"
|
||||||
|
+date: "30.11.2021"
|
||||||
|
...
|
||||||
|
` + `
|
||||||
|
# Geschäftsordnung
|
||||||
|
@@ -16,4 +16,22 @@ Diese Geschäftsordnung regelt alle Prozesse des Vereins, solange diese nicht du
|
||||||
|
` + `
|
||||||
|
## § 3 Datenschutzverantwortlichkeit
|
||||||
|
` + `
|
||||||
|
-1. Der Verein bestellt eine datenschutzverantwortliche Person mit den Aufgaben nach Artikel 39 DSGVO.
|
||||||
|
\ No newline at end of file
|
||||||
|
+1. Der Verein bestellt eine datenschutzverantwortliche Person mit den Aufgaben nach Artikel 39 DSGVO.
|
||||||
|
+
|
||||||
|
+## §4 Umgang mit der SARS-Cov-2-Pandemie
|
||||||
|
+
|
||||||
|
+1. Der Vorstand hat die Befugnis, in Rücksprache mit den Vereinsmitgliedern, verschiedene Hygienemaßnahmen für Präsenzveranstaltungen zu beschließen.
|
||||||
|
+
|
||||||
|
+2. Die Einführung, Änderung und Abschaffung dieser Maßnahmen sind nur zum Zweck der Eindämmung der SARS-Cov-2-Pandemie zulässig.
|
||||||
|
+
|
||||||
|
+3. Die Einführung, Änderung und Abschaffung von Maßnahmen nach Abs. 2 bedarf einer wissenschaftlichen Grundlage.
|
||||||
|
+
|
||||||
|
+4. Die Maßnahmen nach Abs. 2 setzen sich aus den folgenden Bausteinen inklusive einer ihrer Ausprägungen zusammen.
|
||||||
|
+
|
||||||
|
+ 1. Maskenpflicht: Keine; Maskenpflicht, außer am Platz, oder wo Abstände nicht eingehalten werden können; Maskenpflicht, wenn Abstände nicht eingehalten werden können; Maskenpflicht
|
||||||
|
+
|
||||||
|
+ 2. Geimpft-, Genesen- oder Testnachweis: Kein Nachweis notwendig; Nachweis, dass Person geimpft, genesen oder tagesaktuell getestet ist (3G); Nachweis, dass Person geimpft oder genesen ist (2G); Nachweis, dass Person geimpft bzw. genesen und tagesaktuell getestet ist (2G+)
|
||||||
|
+
|
||||||
|
+ 3. Online-Veranstaltung: Keine, parallele Online-Veranstaltung, ausschließlich Online-Veranstaltung
|
||||||
|
+
|
||||||
|
+5. Bei Präsenzveranstungen gelten außerdem die Hygienevorschriften des Veranstaltungsorts. Bei Regelkollision greift die restriktivere Regel.
|
||||||
|
\ No newline at end of file`
|
||||||
|
|
||||||
|
func TestCutDiffAroundLineIssue17875(t *testing.T) {
|
||||||
|
result, err := CutDiffAroundLine(strings.NewReader(issue17875Diff), 23, false, 3)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
expected := `diff --git a/Geschäftsordnung.md b/Geschäftsordnung.md
|
||||||
|
--- a/Geschäftsordnung.md
|
||||||
|
+++ b/Geschäftsordnung.md
|
||||||
|
@@ -20,0 +21,3 @@
|
||||||
|
+## §4 Umgang mit der SARS-Cov-2-Pandemie
|
||||||
|
+
|
||||||
|
+1. Der Vorstand hat die Befugnis, in Rücksprache mit den Vereinsmitgliedern, verschiedene Hygienemaßnahmen für Präsenzveranstaltungen zu beschließen.`
|
||||||
|
assert.Equal(t, expected, result)
|
||||||
|
}
|
||||||
|
|
||||||
func TestCutDiffAroundLine(t *testing.T) {
|
func TestCutDiffAroundLine(t *testing.T) {
|
||||||
result, err := CutDiffAroundLine(strings.NewReader(exampleDiff), 4, false, 3)
|
result, err := CutDiffAroundLine(strings.NewReader(exampleDiff), 4, false, 3)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build gogit
|
||||||
// +build gogit
|
// +build gogit
|
||||||
|
|
||||||
package git
|
package git
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build !gogit
|
||||||
// +build !gogit
|
// +build !gogit
|
||||||
|
|
||||||
package git
|
package git
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build gogit
|
||||||
// +build gogit
|
// +build gogit
|
||||||
|
|
||||||
package git
|
package git
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build !gogit
|
||||||
// +build !gogit
|
// +build !gogit
|
||||||
|
|
||||||
package git
|
package git
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build gogit
|
||||||
// +build gogit
|
// +build gogit
|
||||||
|
|
||||||
package git
|
package git
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build gogit
|
||||||
// +build gogit
|
// +build gogit
|
||||||
|
|
||||||
package git
|
package git
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build !gogit
|
||||||
// +build !gogit
|
// +build !gogit
|
||||||
|
|
||||||
package git
|
package git
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build !gogit
|
||||||
// +build !gogit
|
// +build !gogit
|
||||||
|
|
||||||
package git
|
package git
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build gogit
|
||||||
// +build gogit
|
// +build gogit
|
||||||
|
|
||||||
package pipeline
|
package pipeline
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build !gogit
|
||||||
// +build !gogit
|
// +build !gogit
|
||||||
|
|
||||||
package pipeline
|
package pipeline
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build gogit
|
||||||
// +build gogit
|
// +build gogit
|
||||||
|
|
||||||
package git
|
package git
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build !gogit
|
||||||
// +build !gogit
|
// +build !gogit
|
||||||
|
|
||||||
package git
|
package git
|
||||||
@@ -42,6 +43,11 @@ func OpenRepository(repoPath string) (*Repository, error) {
|
|||||||
return nil, errors.New("no such file or directory")
|
return nil, errors.New("no such file or directory")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Now because of some insanity with git cat-file not immediately failing if not run in a valid git directory we need to run git rev-parse first!
|
||||||
|
if err := EnsureValidGitRepository(DefaultContext, repoPath); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
repo := &Repository{
|
repo := &Repository{
|
||||||
Path: repoPath,
|
Path: repoPath,
|
||||||
tagCache: newObjectCache(),
|
tagCache: newObjectCache(),
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build gogit
|
||||||
// +build gogit
|
// +build gogit
|
||||||
|
|
||||||
package git
|
package git
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build !gogit
|
||||||
// +build !gogit
|
// +build !gogit
|
||||||
|
|
||||||
package git
|
package git
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build gogit
|
||||||
// +build gogit
|
// +build gogit
|
||||||
|
|
||||||
package git
|
package git
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build !gogit
|
||||||
// +build !gogit
|
// +build !gogit
|
||||||
|
|
||||||
package git
|
package git
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build gogit
|
||||||
// +build gogit
|
// +build gogit
|
||||||
|
|
||||||
package git
|
package git
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build !gogit
|
||||||
// +build !gogit
|
// +build !gogit
|
||||||
|
|
||||||
package git
|
package git
|
||||||
@@ -37,7 +38,10 @@ func (repo *Repository) ResolveReference(name string) (string, error) {
|
|||||||
func (repo *Repository) GetRefCommitID(name string) (string, error) {
|
func (repo *Repository) GetRefCommitID(name string) (string, error) {
|
||||||
wr, rd, cancel := repo.CatFileBatchCheck()
|
wr, rd, cancel := repo.CatFileBatchCheck()
|
||||||
defer cancel()
|
defer cancel()
|
||||||
_, _ = wr.Write([]byte(name + "\n"))
|
_, err := wr.Write([]byte(name + "\n"))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
shaBs, _, _, err := ReadBatchLine(rd)
|
shaBs, _, _, err := ReadBatchLine(rd)
|
||||||
if IsErrNotExist(err) {
|
if IsErrNotExist(err) {
|
||||||
return "", ErrNotExist{name, ""}
|
return "", ErrNotExist{name, ""}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build gogit
|
||||||
// +build gogit
|
// +build gogit
|
||||||
|
|
||||||
package git
|
package git
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build gogit
|
||||||
// +build gogit
|
// +build gogit
|
||||||
|
|
||||||
package git
|
package git
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build !gogit
|
||||||
// +build !gogit
|
// +build !gogit
|
||||||
|
|
||||||
package git
|
package git
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build gogit
|
||||||
// +build gogit
|
// +build gogit
|
||||||
|
|
||||||
package git
|
package git
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build !gogit
|
||||||
// +build !gogit
|
// +build !gogit
|
||||||
|
|
||||||
package git
|
package git
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build gogit
|
||||||
// +build gogit
|
// +build gogit
|
||||||
|
|
||||||
package git
|
package git
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build !gogit
|
||||||
// +build !gogit
|
// +build !gogit
|
||||||
|
|
||||||
package git
|
package git
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build gogit
|
||||||
// +build gogit
|
// +build gogit
|
||||||
|
|
||||||
package git
|
package git
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build !gogit
|
||||||
// +build !gogit
|
// +build !gogit
|
||||||
|
|
||||||
package git
|
package git
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build gogit
|
||||||
// +build gogit
|
// +build gogit
|
||||||
|
|
||||||
package git
|
package git
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
// Use of this source code is governed by a MIT-style
|
// Use of this source code is governed by a MIT-style
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build !gogit
|
||||||
// +build !gogit
|
// +build !gogit
|
||||||
|
|
||||||
package git
|
package git
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user