Compare commits
31 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| acd648061d | |||
| c5fe0a096d | |||
| 0c7bf6801f | |||
| 5863f7e048 | |||
| a785c46ca8 | |||
| 6bddfd3086 | |||
| dd8a726b25 | |||
| 08eecba32b | |||
| 9c2212df15 | |||
| 9b4746967c | |||
| 00da1facc4 | |||
| b461993775 | |||
| b885e57762 | |||
| 081449d7a5 | |||
| ee3a21a537 | |||
| 61c7732e12 | |||
| 57c2ca7f26 | |||
| 0704009dd7 | |||
| 14a6aafb50 | |||
| 471a1e8111 | |||
| 123c254b84 | |||
| db43f63c53 | |||
| 3ecd520f8e | |||
| e9935d358c | |||
| 8d653b148b | |||
| b702f2dac3 | |||
| d59b8541f2 | |||
| efd34d0d7d | |||
| 2ec2935f78 | |||
| 540541caa2 | |||
| a13d64bf98 |
+33
-33
@@ -45,7 +45,7 @@ steps:
|
||||
commands:
|
||||
- make lint-backend
|
||||
environment:
|
||||
GOPROXY: https://goproxy.cn # proxy.golang.org is blocked in China, this proxy is not
|
||||
GOPROXY: https://goproxy.io # proxy.golang.org is blocked in China, this proxy is not
|
||||
GOSUMDB: sum.golang.org
|
||||
TAGS: bindata sqlite sqlite_unlock_notify
|
||||
depends_on: [deps-backend]
|
||||
@@ -58,7 +58,7 @@ steps:
|
||||
commands:
|
||||
- make golangci-lint-windows vet
|
||||
environment:
|
||||
GOPROXY: https://goproxy.cn # proxy.golang.org is blocked in China, this proxy is not
|
||||
GOPROXY: https://goproxy.io # proxy.golang.org is blocked in China, this proxy is not
|
||||
GOSUMDB: sum.golang.org
|
||||
TAGS: bindata sqlite sqlite_unlock_notify
|
||||
GOOS: windows
|
||||
@@ -73,7 +73,7 @@ steps:
|
||||
commands:
|
||||
- make lint-backend
|
||||
environment:
|
||||
GOPROXY: https://goproxy.cn # proxy.golang.org is blocked in China, this proxy is not
|
||||
GOPROXY: https://goproxy.io # proxy.golang.org is blocked in China, this proxy is not
|
||||
GOSUMDB: sum.golang.org
|
||||
TAGS: bindata gogit sqlite sqlite_unlock_notify
|
||||
depends_on: [deps-backend]
|
||||
@@ -109,11 +109,11 @@ steps:
|
||||
depends_on: [test-frontend]
|
||||
|
||||
- name: build-backend-no-gcc
|
||||
image: golang:1.17 # this step is kept as the lowest version of golang that we support
|
||||
image: golang:1.16 # this step is kept as the lowest version of golang that we support
|
||||
pull: always
|
||||
environment:
|
||||
GO111MODULE: on
|
||||
GOPROXY: https://goproxy.cn
|
||||
GOPROXY: https://goproxy.io
|
||||
commands:
|
||||
- go build -o gitea_no_gcc # test if build succeeds without the sqlite tag
|
||||
depends_on: [deps-backend, checks-backend]
|
||||
@@ -125,7 +125,7 @@ steps:
|
||||
image: golang:1.18
|
||||
environment:
|
||||
GO111MODULE: on
|
||||
GOPROXY: https://goproxy.cn
|
||||
GOPROXY: https://goproxy.io
|
||||
GOOS: linux
|
||||
GOARCH: arm64
|
||||
TAGS: bindata gogit
|
||||
@@ -141,7 +141,7 @@ steps:
|
||||
image: golang:1.18
|
||||
environment:
|
||||
GO111MODULE: on
|
||||
GOPROXY: https://goproxy.cn
|
||||
GOPROXY: https://goproxy.io
|
||||
GOOS: windows
|
||||
GOARCH: amd64
|
||||
TAGS: bindata gogit
|
||||
@@ -156,7 +156,7 @@ steps:
|
||||
image: golang:1.18
|
||||
environment:
|
||||
GO111MODULE: on
|
||||
GOPROXY: https://goproxy.cn
|
||||
GOPROXY: https://goproxy.io
|
||||
GOOS: linux
|
||||
GOARCH: 386
|
||||
commands:
|
||||
@@ -269,7 +269,7 @@ steps:
|
||||
- ./build/test-env-check.sh
|
||||
- make backend
|
||||
environment:
|
||||
GOPROXY: https://goproxy.cn # proxy.golang.org is blocked in China, this proxy is not
|
||||
GOPROXY: https://goproxy.io # proxy.golang.org is blocked in China, this proxy is not
|
||||
GOSUMDB: sum.golang.org
|
||||
TAGS: bindata sqlite sqlite_unlock_notify
|
||||
depends_on: [deps-backend, prepare-test-env]
|
||||
@@ -283,7 +283,7 @@ steps:
|
||||
commands:
|
||||
- make unit-test-coverage test-check
|
||||
environment:
|
||||
GOPROXY: https://goproxy.cn
|
||||
GOPROXY: https://goproxy.io
|
||||
TAGS: bindata sqlite sqlite_unlock_notify
|
||||
RACE_ENABLED: true
|
||||
GITHUB_READ_TOKEN:
|
||||
@@ -299,7 +299,7 @@ steps:
|
||||
commands:
|
||||
- make unit-test-coverage test-check
|
||||
environment:
|
||||
GOPROXY: https://goproxy.cn
|
||||
GOPROXY: https://goproxy.io
|
||||
TAGS: bindata gogit sqlite sqlite_unlock_notify
|
||||
RACE_ENABLED: true
|
||||
GITHUB_READ_TOKEN:
|
||||
@@ -315,7 +315,7 @@ steps:
|
||||
commands:
|
||||
- make test-mysql-migration integration-test-coverage
|
||||
environment:
|
||||
GOPROXY: https://goproxy.cn
|
||||
GOPROXY: https://goproxy.io
|
||||
TAGS: bindata
|
||||
RACE_ENABLED: true
|
||||
TEST_LDAP: 1
|
||||
@@ -332,7 +332,7 @@ steps:
|
||||
commands:
|
||||
- timeout -s ABRT 40m make test-mysql8-migration test-mysql8
|
||||
environment:
|
||||
GOPROXY: https://goproxy.cn
|
||||
GOPROXY: https://goproxy.io
|
||||
TAGS: bindata
|
||||
RACE_ENABLED: true
|
||||
TEST_LDAP: 1
|
||||
@@ -348,7 +348,7 @@ steps:
|
||||
commands:
|
||||
- make test-mssql-migration test-mssql
|
||||
environment:
|
||||
GOPROXY: https://goproxy.cn
|
||||
GOPROXY: https://goproxy.io
|
||||
TAGS: bindata
|
||||
RACE_ENABLED: true
|
||||
TEST_LDAP: 1
|
||||
@@ -363,7 +363,7 @@ steps:
|
||||
commands:
|
||||
- make coverage
|
||||
environment:
|
||||
GOPROXY: https://goproxy.cn
|
||||
GOPROXY: https://goproxy.io
|
||||
TAGS: bindata
|
||||
depends_on: [unit-test, test-mysql]
|
||||
when:
|
||||
@@ -455,7 +455,7 @@ steps:
|
||||
- ./build/test-env-check.sh
|
||||
- make backend
|
||||
environment:
|
||||
GOPROXY: https://goproxy.cn # proxy.golang.org is blocked in China, this proxy is not
|
||||
GOPROXY: https://goproxy.io # proxy.golang.org is blocked in China, this proxy is not
|
||||
GOSUMDB: sum.golang.org
|
||||
TAGS: bindata gogit sqlite sqlite_unlock_notify
|
||||
depends_on: [deps-backend, prepare-test-env]
|
||||
@@ -469,7 +469,7 @@ steps:
|
||||
commands:
|
||||
- timeout -s ABRT 40m make test-sqlite-migration test-sqlite
|
||||
environment:
|
||||
GOPROXY: https://goproxy.cn
|
||||
GOPROXY: https://goproxy.io
|
||||
TAGS: bindata gogit sqlite sqlite_unlock_notify
|
||||
RACE_ENABLED: true
|
||||
TEST_TAGS: gogit sqlite sqlite_unlock_notify
|
||||
@@ -485,7 +485,7 @@ steps:
|
||||
commands:
|
||||
- timeout -s ABRT 40m make test-pgsql-migration test-pgsql
|
||||
environment:
|
||||
GOPROXY: https://goproxy.cn
|
||||
GOPROXY: https://goproxy.io
|
||||
TAGS: bindata gogit
|
||||
RACE_ENABLED: true
|
||||
TEST_TAGS: gogit
|
||||
@@ -653,7 +653,7 @@ steps:
|
||||
- export PATH=$PATH:$GOPATH/bin
|
||||
- make release
|
||||
environment:
|
||||
GOPROXY: https://goproxy.cn # proxy.golang.org is blocked in China, this proxy is not
|
||||
GOPROXY: https://goproxy.io # proxy.golang.org is blocked in China, this proxy is not
|
||||
TAGS: bindata sqlite sqlite_unlock_notify
|
||||
volumes:
|
||||
- name: deps
|
||||
@@ -771,7 +771,7 @@ steps:
|
||||
- export PATH=$PATH:$GOPATH/bin
|
||||
- make release
|
||||
environment:
|
||||
GOPROXY: https://goproxy.cn # proxy.golang.org is blocked in China, this proxy is not
|
||||
GOPROXY: https://goproxy.io # proxy.golang.org is blocked in China, this proxy is not
|
||||
TAGS: bindata sqlite sqlite_unlock_notify
|
||||
depends_on: [fetch-tags]
|
||||
volumes:
|
||||
@@ -901,7 +901,7 @@ steps:
|
||||
auto_tag_suffix: linux-amd64
|
||||
repo: gitea/gitea
|
||||
build_args:
|
||||
- GOPROXY=https://goproxy.cn
|
||||
- GOPROXY=https://goproxy.io
|
||||
password:
|
||||
from_secret: docker_password
|
||||
username:
|
||||
@@ -919,7 +919,7 @@ steps:
|
||||
auto_tag_suffix: linux-amd64-rootless
|
||||
repo: gitea/gitea
|
||||
build_args:
|
||||
- GOPROXY=https://goproxy.cn
|
||||
- GOPROXY=https://goproxy.io
|
||||
password:
|
||||
from_secret: docker_password
|
||||
username:
|
||||
@@ -964,7 +964,7 @@ steps:
|
||||
tags: dev-linux-amd64
|
||||
repo: gitea/gitea
|
||||
build_args:
|
||||
- GOPROXY=https://goproxy.cn
|
||||
- GOPROXY=https://goproxy.io
|
||||
password:
|
||||
from_secret: docker_password
|
||||
username:
|
||||
@@ -982,7 +982,7 @@ steps:
|
||||
tags: dev-linux-amd64-rootless
|
||||
repo: gitea/gitea
|
||||
build_args:
|
||||
- GOPROXY=https://goproxy.cn
|
||||
- GOPROXY=https://goproxy.io
|
||||
password:
|
||||
from_secret: docker_password
|
||||
username:
|
||||
@@ -1026,7 +1026,7 @@ steps:
|
||||
tags: ${DRONE_BRANCH##release/v}-dev-linux-amd64
|
||||
repo: gitea/gitea
|
||||
build_args:
|
||||
- GOPROXY=https://goproxy.cn
|
||||
- GOPROXY=https://goproxy.io
|
||||
password:
|
||||
from_secret: docker_password
|
||||
username:
|
||||
@@ -1044,7 +1044,7 @@ steps:
|
||||
tags: ${DRONE_BRANCH##release/v}-dev-linux-amd64-rootless
|
||||
repo: gitea/gitea
|
||||
build_args:
|
||||
- GOPROXY=https://goproxy.cn
|
||||
- GOPROXY=https://goproxy.io
|
||||
password:
|
||||
from_secret: docker_password
|
||||
username:
|
||||
@@ -1079,7 +1079,7 @@ steps:
|
||||
repo: gitea/gitea
|
||||
tags: linux-arm64
|
||||
build_args:
|
||||
- GOPROXY=https://goproxy.cn
|
||||
- GOPROXY=https://goproxy.io
|
||||
environment:
|
||||
PLUGIN_MIRROR:
|
||||
from_secret: plugin_mirror
|
||||
@@ -1122,7 +1122,7 @@ steps:
|
||||
auto_tag_suffix: linux-arm64
|
||||
repo: gitea/gitea
|
||||
build_args:
|
||||
- GOPROXY=https://goproxy.cn
|
||||
- GOPROXY=https://goproxy.io
|
||||
password:
|
||||
from_secret: docker_password
|
||||
username:
|
||||
@@ -1140,7 +1140,7 @@ steps:
|
||||
auto_tag_suffix: linux-arm64-rootless
|
||||
repo: gitea/gitea
|
||||
build_args:
|
||||
- GOPROXY=https://goproxy.cn
|
||||
- GOPROXY=https://goproxy.io
|
||||
password:
|
||||
from_secret: docker_password
|
||||
username:
|
||||
@@ -1185,7 +1185,7 @@ steps:
|
||||
tags: dev-linux-arm64
|
||||
repo: gitea/gitea
|
||||
build_args:
|
||||
- GOPROXY=https://goproxy.cn
|
||||
- GOPROXY=https://goproxy.io
|
||||
password:
|
||||
from_secret: docker_password
|
||||
username:
|
||||
@@ -1203,7 +1203,7 @@ steps:
|
||||
tags: dev-linux-arm64-rootless
|
||||
repo: gitea/gitea
|
||||
build_args:
|
||||
- GOPROXY=https://goproxy.cn
|
||||
- GOPROXY=https://goproxy.io
|
||||
password:
|
||||
from_secret: docker_password
|
||||
username:
|
||||
@@ -1247,7 +1247,7 @@ steps:
|
||||
tags: ${DRONE_BRANCH##release/v}-dev-linux-arm64
|
||||
repo: gitea/gitea
|
||||
build_args:
|
||||
- GOPROXY=https://goproxy.cn
|
||||
- GOPROXY=https://goproxy.io
|
||||
password:
|
||||
from_secret: docker_password
|
||||
username:
|
||||
@@ -1265,7 +1265,7 @@ steps:
|
||||
tags: ${DRONE_BRANCH##release/v}-dev-linux-arm64-rootless
|
||||
repo: gitea/gitea
|
||||
build_args:
|
||||
- GOPROXY=https://goproxy.cn
|
||||
- GOPROXY=https://goproxy.io
|
||||
password:
|
||||
from_secret: docker_password
|
||||
username:
|
||||
|
||||
+36
-1
@@ -4,7 +4,42 @@ 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
|
||||
been added to each release, please refer to the [blog](https://blog.gitea.io).
|
||||
|
||||
## [1.16.5](https://github.com/go-gitea/gitea/releases/tag/1.16.5) - 2022-03-23
|
||||
## [1.16.6](https://github.com/go-gitea/gitea/releases/tag/v1.16.6) - 2022-04-20
|
||||
|
||||
* ENHANCEMENTS
|
||||
* Only request write when necessary (#18657) (#19422)
|
||||
* Disable service worker by default (#18914) (#19342)
|
||||
* BUGFIXES
|
||||
* When dumping trim the standard suffices instead of a random suffix (#19440) (#19447)
|
||||
* Fix DELETE request for non-existent public key (#19443) (#19444)
|
||||
* Don't panic on ErrEmailInvalid (#19441) (#19442)
|
||||
* Add uploadpack.allowAnySHA1InWant to allow --filter=blob:none with older git clients (#19430) (#19438)
|
||||
* Warn on SSH connection for incorrect configuration (#19317) (#19437)
|
||||
* Search Issues via API, dont show 500 if filter result in empty list (#19244) (#19436)
|
||||
* When updating mirror repo intervals by API reschedule next update too (#19429) (#19433)
|
||||
* Fix nil error when some pages are rendered outside request context (#19427) (#19428)
|
||||
* Fix double blob-hunk on diff page (#19404) (#19405)
|
||||
* Don't allow merging PR's which are being conflict checked (#19357) (#19358)
|
||||
* Fix middleware function's placements (#19377) (#19378)
|
||||
* Fix invalid CSRF token bug, make sure CSRF tokens can be up-to-date (#19338)
|
||||
* Restore user autoregistration with email addresses (#19261) (#19312)
|
||||
* Move checks for pulls before merge into own function (#19271) (#19277)
|
||||
* Granular webhook events in editHook (#19251) (#19257)
|
||||
* Only send webhook events to active system webhooks and only deliver to active hooks (#19234) (#19248)
|
||||
* Use full output of git show-ref --tags to get tags for PushUpdateAddTag (#19235) (#19236)
|
||||
* Touch mirrors on even on fail to update (#19217) (#19233)
|
||||
* Hide sensitive content on admin panel progress monitor (#19218 & #19226) (#19231)
|
||||
* Fix clone url JS error for the empty repo page (#19209)
|
||||
* Bump goldmark to v1.4.11 (#19201) (#19203)
|
||||
* TESTING
|
||||
* Prevent intermittent failures in RepoIndexerTest (#19225 #19229) (#19228)
|
||||
* BUILD
|
||||
* Revert the minimal golang version requirement from 1.17 to 1.16 and add a warning in Makefile (#19319)
|
||||
* MISC
|
||||
* Performance improvement for add team user when org has more than 1000 repositories (#19227) (#19289)
|
||||
* Check go and nodejs version by go.mod and package.json (#19197) (#19254)
|
||||
|
||||
## [1.16.5](https://github.com/go-gitea/gitea/releases/tag/v1.16.5) - 2022-03-23
|
||||
|
||||
* BREAKING
|
||||
* Bump to build with go1.18 (#19120 et al) (#19127)
|
||||
|
||||
@@ -25,8 +25,6 @@ HAS_GO = $(shell hash $(GO) > /dev/null 2>&1 && echo "GO" || echo "NOGO" )
|
||||
COMMA := ,
|
||||
|
||||
XGO_VERSION := go-1.18.x
|
||||
MIN_GO_VERSION := 001017000
|
||||
MIN_NODE_VERSION := 012017000
|
||||
|
||||
AIR_PACKAGE ?= github.com/cosmtrek/air@v1.29.0
|
||||
EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/cmd/editorconfig-checker@2.4.0
|
||||
@@ -203,10 +201,15 @@ help:
|
||||
|
||||
.PHONY: go-check
|
||||
go-check:
|
||||
$(eval GO_VERSION := $(shell printf "%03d%03d%03d" $(shell $(GO) version | grep -Eo '[0-9]+\.[0-9.]+' | tr '.' ' ');))
|
||||
$(eval MIN_GO_VERSION_STR := $(shell grep -Eo '^go\s+[0-9]+\.[0-9.]+' go.mod | cut -d' ' -f2))
|
||||
$(eval MIN_GO_VERSION := $(shell printf "%03d%03d%03d" $(shell echo '$(MIN_GO_VERSION_STR)' | tr '.' ' ')))
|
||||
$(eval GO_VERSION_STR := $(shell $(GO) version | grep -Eo '[0-9]+\.[0-9.]+'))
|
||||
$(eval GO_VERSION := $(shell printf "%03d%03d%03d" $(shell echo '$(GO_VERSION_STR)' | tr '.' ' ')))
|
||||
@if [ "$(GO_VERSION)" -lt "$(MIN_GO_VERSION)" ]; then \
|
||||
echo "Gitea requires Go 1.16 or greater to build. You can get it at https://golang.org/dl/"; \
|
||||
echo "Gitea requires Go $(MIN_GO_VERSION_STR) or greater to build, but $(GO_VERSION) was found. You can get an updated version at https://go.dev/dl/"; \
|
||||
exit 1; \
|
||||
else \
|
||||
echo "WARNING: Please ensure Go $(GO_VERSION_STR) is still maintained to avoid possible security problems. You can check it at https://go.dev/dl/"; \
|
||||
fi
|
||||
|
||||
.PHONY: git-check
|
||||
@@ -218,11 +221,12 @@ git-check:
|
||||
|
||||
.PHONY: node-check
|
||||
node-check:
|
||||
$(eval MIN_NODE_VERSION_STR := $(shell grep -Eo '"node":.*[0-9.]+"' package.json | sed -n 's/.*[^0-9.]\([0-9.]*\)"/\1/p'))
|
||||
$(eval MIN_NODE_VERSION := $(shell printf "%03d%03d%03d" $(shell echo '$(MIN_NODE_VERSION_STR)' | tr '.' ' ')))
|
||||
$(eval NODE_VERSION := $(shell printf "%03d%03d%03d" $(shell node -v | cut -c2- | tr '.' ' ');))
|
||||
$(eval MIN_NODE_VER_FMT := $(shell printf "%g.%g.%g" $(shell echo $(MIN_NODE_VERSION) | grep -o ...)))
|
||||
$(eval NPM_MISSING := $(shell hash npm > /dev/null 2>&1 || echo 1))
|
||||
@if [ "$(NODE_VERSION)" -lt "$(MIN_NODE_VERSION)" -o "$(NPM_MISSING)" = "1" ]; then \
|
||||
echo "Gitea requires Node.js $(MIN_NODE_VER_FMT) or greater and npm to build. You can get it at https://nodejs.org/en/download/"; \
|
||||
echo "Gitea requires Node.js $(MIN_NODE_VERSION_STR) or greater and npm to build. You can get it at https://nodejs.org/en/download/"; \
|
||||
exit 1; \
|
||||
fi
|
||||
|
||||
|
||||
+7
-2
@@ -86,7 +86,7 @@ func (o outputType) String() string {
|
||||
}
|
||||
|
||||
var outputTypeEnum = &outputType{
|
||||
Enum: []string{"zip", "rar", "tar", "sz", "tar.gz", "tar.xz", "tar.bz2", "tar.br", "tar.lz4"},
|
||||
Enum: []string{"zip", "tar", "tar.sz", "tar.gz", "tar.xz", "tar.bz2", "tar.br", "tar.lz4"},
|
||||
Default: "zip",
|
||||
}
|
||||
|
||||
@@ -160,7 +160,12 @@ func runDump(ctx *cli.Context) error {
|
||||
fatal("Deleting default logger failed. Can not write to stdout: %v", err)
|
||||
}
|
||||
} else {
|
||||
fileName = strings.TrimSuffix(fileName, path.Ext(fileName))
|
||||
for _, suffix := range outputTypeEnum.Enum {
|
||||
if strings.HasSuffix(fileName, "."+suffix) {
|
||||
fileName = strings.TrimSuffix(fileName, "."+suffix)
|
||||
break
|
||||
}
|
||||
}
|
||||
fileName += "." + outType
|
||||
}
|
||||
setting.LoadFromExisting()
|
||||
|
||||
@@ -297,6 +297,15 @@ func runServ(c *cli.Context) error {
|
||||
gitcmd = exec.CommandContext(ctx, verb, repoPath)
|
||||
}
|
||||
|
||||
// Check if setting.RepoRootPath exists. It could be the case that it doesn't exist, this can happen when
|
||||
// `[repository]` `ROOT` is a relative path and $GITEA_WORK_DIR isn't passed to the SSH connection.
|
||||
if _, err := os.Stat(setting.RepoRootPath); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return fail("Incorrect configuration.",
|
||||
"Directory `[repository]` `ROOT` was not found, please check if $GITEA_WORK_DIR is passed to the SSH connection or make `[repository]` `ROOT` an absolute value.")
|
||||
}
|
||||
}
|
||||
|
||||
gitcmd.Dir = setting.RepoRootPath
|
||||
gitcmd.Stdout = os.Stdout
|
||||
gitcmd.Stdin = os.Stdin
|
||||
|
||||
@@ -1075,7 +1075,7 @@ PATH =
|
||||
;SEARCH_REPO_DESCRIPTION = true
|
||||
;;
|
||||
;; Whether to enable a Service Worker to cache frontend assets
|
||||
;USE_SERVICE_WORKER = true
|
||||
;USE_SERVICE_WORKER = false
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
+1
-1
@@ -19,7 +19,7 @@ params:
|
||||
author: The Gitea Authors
|
||||
website: https://docs.gitea.io
|
||||
version: 1.16.4
|
||||
minGoVersion: 1.17
|
||||
minGoVersion: 1.16
|
||||
goVersion: 1.18
|
||||
minNodeVersion: 12.17
|
||||
|
||||
|
||||
@@ -189,7 +189,7 @@ The following configuration set `Content-Type: application/vnd.android.package-a
|
||||
add it to this config.
|
||||
- `DEFAULT_SHOW_FULL_NAME`: **false**: Whether the full name of the users should be shown where possible. If the full name isn't set, the username will be used.
|
||||
- `SEARCH_REPO_DESCRIPTION`: **true**: Whether to search within description at repository search on explore page.
|
||||
- `USE_SERVICE_WORKER`: **true**: Whether to enable a Service Worker to cache frontend assets.
|
||||
- `USE_SERVICE_WORKER`: **false**: Whether to enable a Service Worker to cache frontend assets.
|
||||
|
||||
### UI - Admin (`ui.admin`)
|
||||
|
||||
|
||||
@@ -185,8 +185,6 @@ Before committing, make sure the linters pass:
|
||||
make lint-frontend
|
||||
```
|
||||
|
||||
Note: When working on frontend code, set `USE_SERVICE_WORKER` to `false` in `app.ini` to prevent undesirable caching of frontend assets.
|
||||
|
||||
### Building and adding SVGs
|
||||
|
||||
SVG icons are built using the `make svg` target which compiles the icon sources defined in `build/generate-svg.js` into the output directory `public/img/svg`. Custom icons can be added in the `web_src/svg` directory.
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
module code.gitea.io/gitea
|
||||
|
||||
go 1.17
|
||||
go 1.16
|
||||
|
||||
require (
|
||||
cloud.google.com/go v0.99.0 // indirect
|
||||
code.gitea.io/gitea-vet v0.2.1
|
||||
code.gitea.io/sdk/gitea v0.15.1
|
||||
gitea.com/go-chi/binding v0.0.0-20220309004920-114340dabecb
|
||||
@@ -55,7 +54,6 @@ require (
|
||||
github.com/keybase/go-crypto v0.0.0-20200123153347-de78d2cb44f4
|
||||
github.com/klauspost/compress v1.13.6
|
||||
github.com/klauspost/cpuid/v2 v2.0.9
|
||||
github.com/klauspost/pgzip v1.2.5 // indirect
|
||||
github.com/lib/pq v1.10.2
|
||||
github.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96
|
||||
github.com/markbates/goth v1.69.0
|
||||
@@ -85,7 +83,7 @@ require (
|
||||
github.com/urfave/cli v1.22.5
|
||||
github.com/xanzy/go-gitlab v0.58.0
|
||||
github.com/yohcop/openid-go v1.0.0
|
||||
github.com/yuin/goldmark v1.4.8
|
||||
github.com/yuin/goldmark v1.4.11
|
||||
github.com/yuin/goldmark-highlighting v0.0.0-20220208100518-594be1970594
|
||||
github.com/yuin/goldmark-meta v1.1.0
|
||||
go.jolheiser.com/hcaptcha v0.0.4
|
||||
@@ -93,7 +91,7 @@ require (
|
||||
go.uber.org/atomic v1.9.0 // indirect
|
||||
go.uber.org/multierr v1.8.0 // indirect
|
||||
go.uber.org/zap v1.21.0 // indirect
|
||||
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd
|
||||
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f
|
||||
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b
|
||||
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9
|
||||
@@ -112,174 +110,49 @@ require (
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20211209120228-48547f28849e // indirect
|
||||
github.com/Microsoft/go-winio v0.5.2 // indirect
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20220113124808-70ae35bab23f // indirect
|
||||
github.com/PuerkitoBio/purell v1.1.1 // indirect
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
|
||||
github.com/RoaringBitmap/roaring v0.9.4 // indirect
|
||||
github.com/acomagu/bufpipe v1.0.3 // indirect
|
||||
github.com/andybalholm/brotli v1.0.4 // indirect
|
||||
github.com/andybalholm/cascadia v1.3.1 // indirect
|
||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect
|
||||
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect
|
||||
github.com/aymerick/douceur v0.2.0 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/bgentry/speakeasy v0.1.0 // indirect
|
||||
github.com/bits-and-blooms/bitset v1.2.1 // indirect
|
||||
github.com/blevesearch/bleve_index_api v1.0.1 // indirect
|
||||
github.com/blevesearch/go-porterstemmer v1.0.3 // indirect
|
||||
github.com/blevesearch/mmap-go v1.0.3 // indirect
|
||||
github.com/blevesearch/scorch_segment_api/v2 v2.1.0 // indirect
|
||||
github.com/blevesearch/segment v0.9.0 // indirect
|
||||
github.com/blevesearch/snowballstem v0.9.0 // indirect
|
||||
github.com/blevesearch/upsidedown_store_api v1.0.1 // indirect
|
||||
github.com/blevesearch/vellum v1.0.7 // indirect
|
||||
github.com/blevesearch/zapx/v11 v11.3.3 // indirect
|
||||
github.com/blevesearch/zapx/v12 v12.3.3 // indirect
|
||||
github.com/blevesearch/zapx/v13 v13.3.3 // indirect
|
||||
github.com/blevesearch/zapx/v14 v14.3.3 // indirect
|
||||
github.com/blevesearch/zapx/v15 v15.3.3 // indirect
|
||||
github.com/boombuler/barcode v1.0.1 // indirect
|
||||
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b // indirect
|
||||
github.com/census-instrumentation/opencensus-proto v0.3.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||
github.com/cloudflare/cfssl v1.6.1 // indirect
|
||||
github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4 // indirect
|
||||
github.com/cncf/xds/go v0.0.0-20211130200136-a8f946100490 // indirect
|
||||
github.com/coreos/go-semver v0.3.0 // indirect
|
||||
github.com/coreos/go-systemd/v22 v22.3.2 // indirect
|
||||
github.com/couchbase/go-couchbase v0.0.0-20210224140812-5740cd35f448 // indirect
|
||||
github.com/couchbase/gomemcached v0.1.2 // indirect
|
||||
github.com/couchbase/goutils v0.0.0-20210118111533-e33d3ffb5401 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.1 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/dlclark/regexp2 v1.4.0 // indirect
|
||||
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect
|
||||
github.com/envoyproxy/go-control-plane v0.10.1 // indirect
|
||||
github.com/envoyproxy/protoc-gen-validate v0.6.2 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.2 // indirect
|
||||
github.com/form3tech-oss/jwt-go v3.2.3+incompatible // indirect
|
||||
github.com/fsnotify/fsnotify v1.5.1 // indirect
|
||||
github.com/fullstorydev/grpcurl v1.8.1 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.4.0 // indirect
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.3 // indirect
|
||||
github.com/go-enry/go-oniguruma v1.2.1 // indirect
|
||||
github.com/go-git/gcfg v1.5.0 // indirect
|
||||
github.com/go-openapi/analysis v0.21.2 // indirect
|
||||
github.com/go-openapi/errors v0.20.2 // indirect
|
||||
github.com/go-openapi/inflect v0.19.0 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.19.5 // indirect
|
||||
github.com/go-openapi/jsonreference v0.19.6 // indirect
|
||||
github.com/go-openapi/loads v0.21.0 // indirect
|
||||
github.com/go-openapi/runtime v0.21.1 // indirect
|
||||
github.com/go-openapi/spec v0.20.4 // indirect
|
||||
github.com/go-openapi/strfmt v0.21.1 // indirect
|
||||
github.com/go-openapi/swag v0.19.15 // indirect
|
||||
github.com/go-openapi/validate v0.20.3 // indirect
|
||||
github.com/go-stack/stack v1.8.1 // indirect
|
||||
github.com/goccy/go-json v0.9.5 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
|
||||
github.com/golang-sql/sqlexp v0.0.0-20170517235910-f1bb20e5a188 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/mock v1.6.0 // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/btree v1.0.1 // indirect
|
||||
github.com/google/certificate-transparency-go v1.1.2-0.20210511102531-373a877eec92 // indirect
|
||||
github.com/google/go-querystring v1.1.0 // indirect
|
||||
github.com/gorilla/css v1.0.0 // indirect
|
||||
github.com/gorilla/handlers v1.5.1 // indirect
|
||||
github.com/gorilla/mux v1.8.0 // indirect
|
||||
github.com/gorilla/securecookie v1.1.1 // indirect
|
||||
github.com/gorilla/websocket v1.4.2 // indirect
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
github.com/hashicorp/go-retryablehttp v0.7.0 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/imdario/mergo v0.3.12 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||
github.com/jessevdk/go-flags v1.5.0 // indirect
|
||||
github.com/jhump/protoreflect v1.8.2 // indirect
|
||||
github.com/jonboulle/clockwork v0.2.2 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/kevinburke/ssh_config v1.1.0 // indirect
|
||||
github.com/kr/pretty v0.3.0 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/libdns/libdns v0.2.1 // indirect
|
||||
github.com/magiconair/properties v1.8.5 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/markbates/going v1.0.0 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.13 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
|
||||
github.com/mholt/acmez v1.0.2 // indirect
|
||||
github.com/miekg/dns v1.1.46 // indirect
|
||||
github.com/minio/md5-simd v1.1.2 // indirect
|
||||
github.com/minio/sha256-simd v1.0.0 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/mitchellh/mapstructure v1.4.3 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/mrjones/oauth v0.0.0-20190623134757-126b35219450 // indirect
|
||||
github.com/mschoch/smat v0.2.0 // indirect
|
||||
github.com/nwaples/rardecode v1.1.3 // indirect
|
||||
github.com/oklog/ulid v1.3.1 // indirect
|
||||
github.com/olekukonko/tablewriter v0.0.5 // indirect
|
||||
github.com/pelletier/go-toml v1.9.4 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.14 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/prometheus/client_model v0.2.0 // indirect
|
||||
github.com/prometheus/common v0.32.1 // indirect
|
||||
github.com/prometheus/procfs v0.7.3 // indirect
|
||||
github.com/rivo/uniseg v0.2.0 // indirect
|
||||
github.com/rogpeppe/go-internal v1.8.1 // indirect
|
||||
github.com/rs/xid v1.3.0 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 // indirect
|
||||
github.com/sirupsen/logrus v1.8.1 // indirect
|
||||
github.com/soheilhy/cmux v0.1.5 // indirect
|
||||
github.com/spf13/afero v1.8.0 // indirect
|
||||
github.com/spf13/cast v1.4.1 // indirect
|
||||
github.com/spf13/cobra v1.3.0 // indirect
|
||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/spf13/viper v1.10.1 // indirect
|
||||
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect
|
||||
github.com/steveyen/gtreap v0.1.0 // indirect
|
||||
github.com/subosito/gotenv v1.2.0 // indirect
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802 // indirect
|
||||
github.com/toqueteos/webbrowser v1.2.0 // indirect
|
||||
github.com/ulikunitz/xz v0.5.10 // indirect
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
github.com/xanzy/ssh-agent v0.3.1 // indirect
|
||||
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 // indirect
|
||||
go.etcd.io/bbolt v1.3.6 // indirect
|
||||
go.etcd.io/etcd/api/v3 v3.5.1 // indirect
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.1 // indirect
|
||||
go.etcd.io/etcd/client/v2 v2.305.1 // indirect
|
||||
go.etcd.io/etcd/client/v3 v3.5.0-alpha.0 // indirect
|
||||
go.etcd.io/etcd/etcdctl/v3 v3.5.0-alpha.0 // indirect
|
||||
go.etcd.io/etcd/pkg/v3 v3.5.0-alpha.0 // indirect
|
||||
go.etcd.io/etcd/raft/v3 v3.5.0-alpha.0 // indirect
|
||||
go.etcd.io/etcd/server/v3 v3.5.0-alpha.0 // indirect
|
||||
go.etcd.io/etcd/tests/v3 v3.5.0-alpha.0 // indirect
|
||||
go.etcd.io/etcd/v3 v3.5.0-alpha.0 // indirect
|
||||
go.mongodb.org/mongo-driver v1.8.2 // indirect
|
||||
golang.org/x/mod v0.5.1 // indirect
|
||||
golang.org/x/time v0.0.0-20220224211638-0e9765cccd65 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa // indirect
|
||||
google.golang.org/grpc v1.43.0 // indirect
|
||||
google.golang.org/protobuf v1.27.1 // indirect
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
||||
gopkg.in/cheggaaa/pb.v1 v1.0.28 // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
|
||||
sigs.k8s.io/yaml v1.2.0 // indirect
|
||||
)
|
||||
|
||||
replace github.com/hashicorp/go-version => github.com/6543/go-version v1.3.1
|
||||
|
||||
@@ -1539,8 +1539,8 @@ github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1
|
||||
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.4.5/go.mod h1:rmuwmfZ0+bvzB24eSC//bk1R1Zp3hM0OXYv/G2LIilg=
|
||||
github.com/yuin/goldmark v1.4.6/go.mod h1:rmuwmfZ0+bvzB24eSC//bk1R1Zp3hM0OXYv/G2LIilg=
|
||||
github.com/yuin/goldmark v1.4.8 h1:zHPiabbIRssZOI0MAzJDHsyvG4MXCGqVaMOwR+HeoQQ=
|
||||
github.com/yuin/goldmark v1.4.8/go.mod h1:rmuwmfZ0+bvzB24eSC//bk1R1Zp3hM0OXYv/G2LIilg=
|
||||
github.com/yuin/goldmark v1.4.11 h1:i45YIzqLnUc2tGaTlJCyUxSG8TvgyGqhqOZOUKIjJ6w=
|
||||
github.com/yuin/goldmark v1.4.11/go.mod h1:rmuwmfZ0+bvzB24eSC//bk1R1Zp3hM0OXYv/G2LIilg=
|
||||
github.com/yuin/goldmark-highlighting v0.0.0-20220208100518-594be1970594 h1:yHfZyN55+5dp1wG7wDKv8HQ044moxkyGq12KFFMFDxg=
|
||||
github.com/yuin/goldmark-highlighting v0.0.0-20220208100518-594be1970594/go.mod h1:U9ihbh+1ZN7fR5Se3daSPoz1CGF9IYtSvWwVQtnzGHU=
|
||||
github.com/yuin/goldmark-meta v1.1.0 h1:pWw+JLHGZe8Rk0EGsMVssiNb/AaPMHfSRszZeUeiOUc=
|
||||
@@ -1676,8 +1676,8 @@ golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5y
|
||||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd h1:XcWmESyNjXJMLahc3mqVQJcgSTDxFxhETVlfk9uGc38=
|
||||
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 h1:kUhD7nTDoI3fVd9G4ORWrbV5NY0liEs/Jg2pv5f+bBA=
|
||||
golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
|
||||
@@ -69,6 +69,12 @@ func TestAPIAddEmail(t *testing.T) {
|
||||
Primary: false,
|
||||
},
|
||||
}, emails)
|
||||
|
||||
opts = api.CreateEmailOption{
|
||||
Emails: []string{"notAEmail"},
|
||||
}
|
||||
req = NewRequestWithJSON(t, "POST", "/api/v1/user/emails?token="+token, &opts)
|
||||
session.MakeRequest(t, req, http.StatusUnprocessableEntity)
|
||||
}
|
||||
|
||||
func TestAPIDeleteEmail(t *testing.T) {
|
||||
|
||||
+16
-15
@@ -1238,7 +1238,7 @@ func sortIssuesSession(sess *xorm.Session, sortType string, priorityRepoID int64
|
||||
}
|
||||
}
|
||||
|
||||
func (opts *IssuesOptions) setupSession(sess *xorm.Session) {
|
||||
func (opts *IssuesOptions) setupSessionWithLimit(sess *xorm.Session) {
|
||||
if opts.Page >= 0 && opts.PageSize > 0 {
|
||||
var start int
|
||||
if opts.Page == 0 {
|
||||
@@ -1248,7 +1248,10 @@ func (opts *IssuesOptions) setupSession(sess *xorm.Session) {
|
||||
}
|
||||
sess.Limit(opts.PageSize, start)
|
||||
}
|
||||
opts.setupSessionNoLimit(sess)
|
||||
}
|
||||
|
||||
func (opts *IssuesOptions) setupSessionNoLimit(sess *xorm.Session) {
|
||||
if len(opts.IssueIDs) > 0 {
|
||||
sess.In("issue.id", opts.IssueIDs)
|
||||
}
|
||||
@@ -1414,7 +1417,7 @@ func CountIssuesByRepo(opts *IssuesOptions) (map[int64]int64, error) {
|
||||
|
||||
sess := e.Join("INNER", "repository", "`issue`.repo_id = `repository`.id")
|
||||
|
||||
opts.setupSession(sess)
|
||||
opts.setupSessionNoLimit(sess)
|
||||
|
||||
countsSlice := make([]*struct {
|
||||
RepoID int64
|
||||
@@ -1424,7 +1427,7 @@ func CountIssuesByRepo(opts *IssuesOptions) (map[int64]int64, error) {
|
||||
Select("issue.repo_id AS repo_id, COUNT(*) AS count").
|
||||
Table("issue").
|
||||
Find(&countsSlice); err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("unable to CountIssuesByRepo: %w", err)
|
||||
}
|
||||
|
||||
countMap := make(map[int64]int64, len(countsSlice))
|
||||
@@ -1441,14 +1444,14 @@ func GetRepoIDsForIssuesOptions(opts *IssuesOptions, user *user_model.User) ([]i
|
||||
|
||||
sess := e.Join("INNER", "repository", "`issue`.repo_id = `repository`.id")
|
||||
|
||||
opts.setupSession(sess)
|
||||
opts.setupSessionNoLimit(sess)
|
||||
|
||||
accessCond := accessibleRepositoryCondition(user)
|
||||
if err := sess.Where(accessCond).
|
||||
Distinct("issue.repo_id").
|
||||
Table("issue").
|
||||
Find(&repoIDs); err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("unable to GetRepoIDsForIssuesOptions: %w", err)
|
||||
}
|
||||
|
||||
return repoIDs, nil
|
||||
@@ -1459,17 +1462,16 @@ func Issues(opts *IssuesOptions) ([]*Issue, error) {
|
||||
e := db.GetEngine(db.DefaultContext)
|
||||
|
||||
sess := e.Join("INNER", "repository", "`issue`.repo_id = `repository`.id")
|
||||
opts.setupSession(sess)
|
||||
opts.setupSessionWithLimit(sess)
|
||||
sortIssuesSession(sess, opts.SortType, opts.PriorityRepoID)
|
||||
|
||||
issues := make([]*Issue, 0, opts.ListOptions.PageSize)
|
||||
if err := sess.Find(&issues); err != nil {
|
||||
return nil, fmt.Errorf("Find: %v", err)
|
||||
return nil, fmt.Errorf("unable to query Issues: %w", err)
|
||||
}
|
||||
sess.Close()
|
||||
|
||||
if err := IssueList(issues).LoadAttributes(); err != nil {
|
||||
return nil, fmt.Errorf("LoadAttributes: %v", err)
|
||||
return nil, fmt.Errorf("unable to LoadAttributes for Issues: %w", err)
|
||||
}
|
||||
|
||||
return issues, nil
|
||||
@@ -1480,18 +1482,17 @@ func CountIssues(opts *IssuesOptions) (int64, error) {
|
||||
e := db.GetEngine(db.DefaultContext)
|
||||
|
||||
countsSlice := make([]*struct {
|
||||
RepoID int64
|
||||
Count int64
|
||||
Count int64
|
||||
}, 0, 1)
|
||||
|
||||
sess := e.Select("COUNT(issue.id) AS count").Table("issue")
|
||||
sess.Join("INNER", "repository", "`issue`.repo_id = `repository`.id")
|
||||
opts.setupSession(sess)
|
||||
opts.setupSessionNoLimit(sess)
|
||||
if err := sess.Find(&countsSlice); err != nil {
|
||||
return 0, fmt.Errorf("Find: %v", err)
|
||||
return 0, fmt.Errorf("unable to CountIssues: %w", err)
|
||||
}
|
||||
if len(countsSlice) < 1 {
|
||||
return 0, fmt.Errorf("there is less than one result sql record")
|
||||
if len(countsSlice) != 1 {
|
||||
return 0, fmt.Errorf("unable to get one row result when CountIssues, row count=%d", len(countsSlice))
|
||||
}
|
||||
return countsSlice[0].Count, nil
|
||||
}
|
||||
|
||||
+41
-12
@@ -940,11 +940,6 @@ func AddTeamMember(team *Team, userID int64) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Get team and its repositories.
|
||||
if err := team.GetRepositories(&SearchOrgTeamOptions{}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ctx, committer, err := db.TxContext()
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -966,17 +961,51 @@ func AddTeamMember(team *Team, userID int64) error {
|
||||
team.NumMembers++
|
||||
|
||||
// Give access to team repositories.
|
||||
for _, repo := range team.Repos {
|
||||
if err := recalculateUserAccess(ctx, repo, userID); err != nil {
|
||||
return err
|
||||
}
|
||||
if setting.Service.AutoWatchNewRepos {
|
||||
if err = repo_model.WatchRepoCtx(ctx, userID, repo.ID, true); err != nil {
|
||||
return err
|
||||
// update exist access if mode become bigger
|
||||
subQuery := builder.Select("repo_id").From("team_repo").
|
||||
Where(builder.Eq{"team_id": team.ID})
|
||||
|
||||
if _, err := sess.Where("user_id=?", userID).
|
||||
In("repo_id", subQuery).
|
||||
And("mode < ?", team.AccessMode).
|
||||
SetExpr("mode", team.AccessMode).
|
||||
Update(new(Access)); err != nil {
|
||||
return fmt.Errorf("update user accesses: %v", err)
|
||||
}
|
||||
|
||||
// for not exist access
|
||||
var repoIDs []int64
|
||||
accessSubQuery := builder.Select("repo_id").From("access").Where(builder.Eq{"user_id": userID})
|
||||
if err := sess.SQL(subQuery.And(builder.NotIn("repo_id", accessSubQuery))).Find(&repoIDs); err != nil {
|
||||
return fmt.Errorf("select id accesses: %v", err)
|
||||
}
|
||||
|
||||
accesses := make([]*Access, 0, 100)
|
||||
for i, repoID := range repoIDs {
|
||||
accesses = append(accesses, &Access{RepoID: repoID, UserID: userID, Mode: team.AccessMode})
|
||||
if (i%100 == 0 || i == len(repoIDs)-1) && len(accesses) > 0 {
|
||||
if err = db.Insert(ctx, accesses); err != nil {
|
||||
return fmt.Errorf("insert new user accesses: %v", err)
|
||||
}
|
||||
accesses = accesses[:0]
|
||||
}
|
||||
}
|
||||
|
||||
// watch could be failed, so run it in a goroutine
|
||||
if setting.Service.AutoWatchNewRepos {
|
||||
// Get team and its repositories.
|
||||
if err := team.GetRepositories(&SearchOrgTeamOptions{}); err != nil {
|
||||
log.Error("getRepositories failed: %v", err)
|
||||
}
|
||||
go func(repos []*repo_model.Repository) {
|
||||
for _, repo := range repos {
|
||||
if err = repo_model.WatchRepoCtx(db.DefaultContext, userID, repo.ID, true); err != nil {
|
||||
log.Error("watch repo failed: %v", err)
|
||||
}
|
||||
}
|
||||
}(team.Repos)
|
||||
}
|
||||
|
||||
return committer.Commit()
|
||||
}
|
||||
|
||||
|
||||
+11
-16
@@ -222,22 +222,19 @@ func (pr *PullRequest) loadProtectedBranch(ctx context.Context) (err error) {
|
||||
}
|
||||
|
||||
// GetDefaultMergeMessage returns default message used when merging pull request
|
||||
func (pr *PullRequest) GetDefaultMergeMessage() string {
|
||||
func (pr *PullRequest) GetDefaultMergeMessage() (string, error) {
|
||||
if pr.HeadRepo == nil {
|
||||
var err error
|
||||
pr.HeadRepo, err = repo_model.GetRepositoryByID(pr.HeadRepoID)
|
||||
if err != nil {
|
||||
log.Error("GetRepositoryById[%d]: %v", pr.HeadRepoID, err)
|
||||
return ""
|
||||
return "", fmt.Errorf("GetRepositoryById[%d]: %v", pr.HeadRepoID, err)
|
||||
}
|
||||
}
|
||||
if err := pr.LoadIssue(); err != nil {
|
||||
log.Error("Cannot load issue %d for PR id %d: Error: %v", pr.IssueID, pr.ID, err)
|
||||
return ""
|
||||
return "", fmt.Errorf("Cannot load issue %d for PR id %d: Error: %v", pr.IssueID, pr.ID, err)
|
||||
}
|
||||
if err := pr.LoadBaseRepo(); err != nil {
|
||||
log.Error("LoadBaseRepo: %v", err)
|
||||
return ""
|
||||
return "", fmt.Errorf("LoadBaseRepo: %v", err)
|
||||
}
|
||||
|
||||
issueReference := "#"
|
||||
@@ -246,10 +243,10 @@ func (pr *PullRequest) GetDefaultMergeMessage() string {
|
||||
}
|
||||
|
||||
if pr.BaseRepoID == pr.HeadRepoID {
|
||||
return fmt.Sprintf("Merge pull request '%s' (%s%d) from %s into %s", pr.Issue.Title, issueReference, pr.Issue.Index, pr.HeadBranch, pr.BaseBranch)
|
||||
return fmt.Sprintf("Merge pull request '%s' (%s%d) from %s into %s", pr.Issue.Title, issueReference, pr.Issue.Index, pr.HeadBranch, pr.BaseBranch), nil
|
||||
}
|
||||
|
||||
return fmt.Sprintf("Merge pull request '%s' (%s%d) from %s:%s into %s", pr.Issue.Title, issueReference, pr.Issue.Index, pr.HeadRepo.FullName(), pr.HeadBranch, pr.BaseBranch)
|
||||
return fmt.Sprintf("Merge pull request '%s' (%s%d) from %s:%s into %s", pr.Issue.Title, issueReference, pr.Issue.Index, pr.HeadRepo.FullName(), pr.HeadBranch, pr.BaseBranch), nil
|
||||
}
|
||||
|
||||
// ReviewCount represents a count of Reviews
|
||||
@@ -335,19 +332,17 @@ func (pr *PullRequest) getReviewedByLines(writer io.Writer) error {
|
||||
}
|
||||
|
||||
// GetDefaultSquashMessage returns default message used when squash and merging pull request
|
||||
func (pr *PullRequest) GetDefaultSquashMessage() string {
|
||||
func (pr *PullRequest) GetDefaultSquashMessage() (string, error) {
|
||||
if err := pr.LoadIssue(); err != nil {
|
||||
log.Error("LoadIssue: %v", err)
|
||||
return ""
|
||||
return "", fmt.Errorf("LoadIssue: %v", err)
|
||||
}
|
||||
if err := pr.LoadBaseRepo(); err != nil {
|
||||
log.Error("LoadBaseRepo: %v", err)
|
||||
return ""
|
||||
return "", fmt.Errorf("LoadBaseRepo: %v", err)
|
||||
}
|
||||
if pr.BaseRepo.UnitEnabled(unit.TypeExternalTracker) {
|
||||
return fmt.Sprintf("%s (!%d)", pr.Issue.Title, pr.Issue.Index)
|
||||
return fmt.Sprintf("%s (!%d)", pr.Issue.Title, pr.Issue.Index), nil
|
||||
}
|
||||
return fmt.Sprintf("%s (#%d)", pr.Issue.Title, pr.Issue.Index)
|
||||
return fmt.Sprintf("%s (#%d)", pr.Issue.Title, pr.Issue.Index), nil
|
||||
}
|
||||
|
||||
// GetGitRefName returns git ref for hidden pull request branch
|
||||
|
||||
+12
-4
@@ -261,11 +261,15 @@ func TestPullRequest_GetDefaultMergeMessage_InternalTracker(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
pr := unittest.AssertExistsAndLoadBean(t, &PullRequest{ID: 2}).(*PullRequest)
|
||||
|
||||
assert.Equal(t, "Merge pull request 'issue3' (#3) from branch2 into master", pr.GetDefaultMergeMessage())
|
||||
msg, err := pr.GetDefaultMergeMessage()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "Merge pull request 'issue3' (#3) from branch2 into master", msg)
|
||||
|
||||
pr.BaseRepoID = 1
|
||||
pr.HeadRepoID = 2
|
||||
assert.Equal(t, "Merge pull request 'issue3' (#3) from user2/repo1:branch2 into master", pr.GetDefaultMergeMessage())
|
||||
msg, err = pr.GetDefaultMergeMessage()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "Merge pull request 'issue3' (#3) from user2/repo1:branch2 into master", msg)
|
||||
}
|
||||
|
||||
func TestPullRequest_GetDefaultMergeMessage_ExternalTracker(t *testing.T) {
|
||||
@@ -283,9 +287,13 @@ func TestPullRequest_GetDefaultMergeMessage_ExternalTracker(t *testing.T) {
|
||||
|
||||
pr := unittest.AssertExistsAndLoadBean(t, &PullRequest{ID: 2, BaseRepo: baseRepo}).(*PullRequest)
|
||||
|
||||
assert.Equal(t, "Merge pull request 'issue3' (!3) from branch2 into master", pr.GetDefaultMergeMessage())
|
||||
msg, err := pr.GetDefaultMergeMessage()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "Merge pull request 'issue3' (!3) from branch2 into master", msg)
|
||||
|
||||
pr.BaseRepoID = 1
|
||||
pr.HeadRepoID = 2
|
||||
assert.Equal(t, "Merge pull request 'issue3' (!3) from user2/repo1:branch2 into master", pr.GetDefaultMergeMessage())
|
||||
msg, err = pr.GetDefaultMergeMessage()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "Merge pull request 'issue3' (!3) from user2/repo1:branch2 into master", msg)
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
package repo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
@@ -115,6 +116,13 @@ func UpdateMirror(m *Mirror) error {
|
||||
return updateMirror(db.GetEngine(db.DefaultContext), m)
|
||||
}
|
||||
|
||||
// TouchMirror updates the mirror updatedUnix
|
||||
func TouchMirror(ctx context.Context, m *Mirror) error {
|
||||
m.UpdatedUnix = timeutil.TimeStampNow()
|
||||
_, err := db.GetEngine(ctx).ID(m.ID).Cols("updated_unix").Update(m)
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteMirrorByRepoID deletes a mirror by repoID
|
||||
func DeleteMirrorByRepoID(repoID int64) error {
|
||||
_, err := db.GetEngine(db.DefaultContext).Delete(&Mirror{RepoID: repoID})
|
||||
|
||||
@@ -497,14 +497,19 @@ func GetSystemOrDefaultWebhook(id int64) (*Webhook, error) {
|
||||
}
|
||||
|
||||
// GetSystemWebhooks returns all admin system webhooks.
|
||||
func GetSystemWebhooks() ([]*Webhook, error) {
|
||||
return getSystemWebhooks(db.GetEngine(db.DefaultContext))
|
||||
func GetSystemWebhooks(isActive util.OptionalBool) ([]*Webhook, error) {
|
||||
return getSystemWebhooks(db.GetEngine(db.DefaultContext), isActive)
|
||||
}
|
||||
|
||||
func getSystemWebhooks(e db.Engine) ([]*Webhook, error) {
|
||||
func getSystemWebhooks(e db.Engine, isActive util.OptionalBool) ([]*Webhook, error) {
|
||||
webhooks := make([]*Webhook, 0, 5)
|
||||
if isActive.IsNone() {
|
||||
return webhooks, e.
|
||||
Where("repo_id=? AND org_id=? AND is_system_webhook=?", 0, 0, true).
|
||||
Find(&webhooks)
|
||||
}
|
||||
return webhooks, e.
|
||||
Where("repo_id=? AND org_id=? AND is_system_webhook=?", 0, 0, true).
|
||||
Where("repo_id=? AND org_id=? AND is_system_webhook=? AND is_active = ?", 0, 0, true, isActive.IsTrue()).
|
||||
Find(&webhooks)
|
||||
}
|
||||
|
||||
|
||||
@@ -229,6 +229,7 @@ func Csrfer(opt CsrfOptions, ctx *Context) CSRF {
|
||||
}
|
||||
}
|
||||
|
||||
needsNew = needsNew || ctx.Req.Method == "GET" // If this request is a Get request, it will generate a new token, make sure the token is always up-to-date.
|
||||
if needsNew {
|
||||
// FIXME: actionId.
|
||||
x.Token = GenerateToken(x.Secret, x.ID, "POST")
|
||||
|
||||
+25
-8
@@ -17,6 +17,7 @@ import (
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/process"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -32,10 +33,11 @@ const DefaultLocale = "C"
|
||||
|
||||
// Command represents a command with its subcommands or arguments.
|
||||
type Command struct {
|
||||
name string
|
||||
args []string
|
||||
parentContext context.Context
|
||||
desc string
|
||||
name string
|
||||
args []string
|
||||
parentContext context.Context
|
||||
desc string
|
||||
globalArgsLength int
|
||||
}
|
||||
|
||||
func (c *Command) String() string {
|
||||
@@ -56,9 +58,10 @@ func NewCommandContext(ctx context.Context, args ...string) *Command {
|
||||
cargs := make([]string, len(GlobalCommandArgs))
|
||||
copy(cargs, GlobalCommandArgs)
|
||||
return &Command{
|
||||
name: GitExecutable,
|
||||
args: append(cargs, args...),
|
||||
parentContext: ctx,
|
||||
name: GitExecutable,
|
||||
args: append(cargs, args...),
|
||||
parentContext: ctx,
|
||||
globalArgsLength: len(GlobalCommandArgs),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,7 +148,21 @@ func (c *Command) RunWithContext(rc *RunContext) error {
|
||||
|
||||
desc := c.desc
|
||||
if desc == "" {
|
||||
desc = fmt.Sprintf("%s %s [repo_path: %s]", c.name, strings.Join(c.args, " "), rc.Dir)
|
||||
args := c.args[c.globalArgsLength:]
|
||||
var argSensitiveURLIndexes []int
|
||||
for i, arg := range c.args {
|
||||
if strings.Contains(arg, "://") && strings.Contains(arg, "@") {
|
||||
argSensitiveURLIndexes = append(argSensitiveURLIndexes, i)
|
||||
}
|
||||
}
|
||||
if len(argSensitiveURLIndexes) > 0 {
|
||||
args = make([]string, len(c.args))
|
||||
copy(args, c.args)
|
||||
for _, urlArgIndex := range argSensitiveURLIndexes {
|
||||
args[urlArgIndex] = util.NewStringURLSanitizer(args[urlArgIndex], true).Replace(args[urlArgIndex])
|
||||
}
|
||||
}
|
||||
desc = fmt.Sprintf("%s %s [repo_path: %s]", c.name, strings.Join(args, " "), rc.Dir)
|
||||
}
|
||||
|
||||
ctx, cancel, finished := process.GetManager().AddContextTimeout(c.parentContext, rc.Timeout, desc)
|
||||
|
||||
+3
-3
@@ -112,8 +112,8 @@ func SetExecutablePath(path string) error {
|
||||
|
||||
// VersionInfo returns git version information
|
||||
func VersionInfo() string {
|
||||
var format = "Git Version: %s"
|
||||
var args = []interface{}{gitVersion.Original()}
|
||||
format := "Git Version: %s"
|
||||
args := []interface{}{gitVersion.Original()}
|
||||
// Since git wire protocol has been released from git v2.18
|
||||
if setting.Git.EnableAutoGitWireProtocol && CheckGitVersionAtLeast("2.18") == nil {
|
||||
format += ", Wire Protocol %s Enabled"
|
||||
@@ -148,7 +148,7 @@ func Init(ctx context.Context) error {
|
||||
|
||||
// By default partial clones are disabled, enable them from git v2.22
|
||||
if !setting.Git.DisablePartialClone && CheckGitVersionAtLeast("2.22") == nil {
|
||||
GlobalCommandArgs = append(GlobalCommandArgs, "-c", "uploadpack.allowfilter=true")
|
||||
GlobalCommandArgs = append(GlobalCommandArgs, "-c", "uploadpack.allowfilter=true", "-c", "uploadpack.allowAnySHA1InWant=true")
|
||||
}
|
||||
|
||||
// Save current git version on init to gitVersion otherwise it would require an RWMutex
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/modules/proxy"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
)
|
||||
|
||||
// GPGSettings represents the default GPG settings for this repository
|
||||
@@ -158,6 +159,12 @@ func CloneWithArgs(ctx context.Context, from, to string, args []string, opts Clo
|
||||
}
|
||||
cmd.AddArguments("--", from, to)
|
||||
|
||||
if strings.Contains(from, "://") && strings.Contains(from, "@") {
|
||||
cmd.SetDescription(fmt.Sprintf("clone branch %s from %s to %s (shared: %t, mirror: %t, depth: %d)", opts.Branch, util.NewStringURLSanitizer(from, true).Replace(from), to, opts.Shared, opts.Mirror, opts.Depth))
|
||||
} else {
|
||||
cmd.SetDescription(fmt.Sprintf("clone branch %s from %s to %s (shared: %t, mirror: %t, depth: %d)", opts.Branch, from, to, opts.Shared, opts.Mirror, opts.Depth))
|
||||
}
|
||||
|
||||
if opts.Timeout <= 0 {
|
||||
opts.Timeout = -1
|
||||
}
|
||||
@@ -234,6 +241,11 @@ func Push(ctx context.Context, repoPath string, opts PushOptions) error {
|
||||
if len(opts.Branch) > 0 {
|
||||
cmd.AddArguments(opts.Branch)
|
||||
}
|
||||
if strings.Contains(opts.Remote, "://") && strings.Contains(opts.Remote, "@") {
|
||||
cmd.SetDescription(fmt.Sprintf("push branch %s to %s (force: %t, mirror: %t)", opts.Branch, util.NewStringURLSanitizer(opts.Remote, true).Replace(opts.Remote), opts.Force, opts.Mirror))
|
||||
} else {
|
||||
cmd.SetDescription(fmt.Sprintf("push branch %s to %s (force: %t, mirror: %t)", opts.Branch, opts.Remote, opts.Force, opts.Mirror))
|
||||
}
|
||||
var outbuf, errbuf strings.Builder
|
||||
|
||||
if opts.Timeout == 0 {
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/go-git/go-git/v5/plumbing"
|
||||
"github.com/go-git/go-git/v5/plumbing/storer"
|
||||
)
|
||||
|
||||
// IsObjectExist returns true if given reference exists in the repository.
|
||||
@@ -82,7 +83,8 @@ func (repo *Repository) GetBranchNames(skip, limit int) ([]string, int, error) {
|
||||
}
|
||||
|
||||
// WalkReferences walks all the references from the repository
|
||||
func WalkReferences(ctx context.Context, repoPath string, walkfn func(string) error) (int, error) {
|
||||
// refType should be empty, ObjectTag or ObjectBranch. All other values are equivalent to empty.
|
||||
func WalkReferences(ctx context.Context, repoPath string, walkfn func(sha1, refname string) error) (int, error) {
|
||||
repo, err := OpenRepositoryCtx(ctx, repoPath)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
@@ -97,9 +99,45 @@ func WalkReferences(ctx context.Context, repoPath string, walkfn func(string) er
|
||||
defer iter.Close()
|
||||
|
||||
err = iter.ForEach(func(ref *plumbing.Reference) error {
|
||||
err := walkfn(string(ref.Name()))
|
||||
err := walkfn(ref.Hash().String(), string(ref.Name()))
|
||||
i++
|
||||
return err
|
||||
})
|
||||
return i, err
|
||||
}
|
||||
|
||||
// WalkReferences walks all the references from the repository
|
||||
func (repo *Repository) WalkReferences(arg ObjectType, skip, limit int, walkfn func(sha1, refname string) error) (int, error) {
|
||||
i := 0
|
||||
var iter storer.ReferenceIter
|
||||
var err error
|
||||
switch arg {
|
||||
case ObjectTag:
|
||||
iter, err = repo.gogitRepo.Tags()
|
||||
case ObjectBranch:
|
||||
iter, err = repo.gogitRepo.Branches()
|
||||
default:
|
||||
iter, err = repo.gogitRepo.References()
|
||||
}
|
||||
if err != nil {
|
||||
return i, err
|
||||
}
|
||||
defer iter.Close()
|
||||
|
||||
err = iter.ForEach(func(ref *plumbing.Reference) error {
|
||||
if i < skip {
|
||||
i++
|
||||
return nil
|
||||
}
|
||||
err := walkfn(ref.Hash().String(), string(ref.Name()))
|
||||
i++
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if limit != 0 && i >= skip+limit {
|
||||
return storer.ErrStop
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return i, err
|
||||
}
|
||||
|
||||
@@ -68,13 +68,29 @@ func (repo *Repository) GetBranchNames(skip, limit int) ([]string, int, error) {
|
||||
}
|
||||
|
||||
// WalkReferences walks all the references from the repository
|
||||
func WalkReferences(ctx context.Context, repoPath string, walkfn func(string) error) (int, error) {
|
||||
func WalkReferences(ctx context.Context, repoPath string, walkfn func(sha1, refname string) error) (int, error) {
|
||||
return walkShowRef(ctx, repoPath, "", 0, 0, walkfn)
|
||||
}
|
||||
|
||||
// WalkReferences walks all the references from the repository
|
||||
// refType should be empty, ObjectTag or ObjectBranch. All other values are equivalent to empty.
|
||||
func (repo *Repository) WalkReferences(refType ObjectType, skip, limit int, walkfn func(sha1, refname string) error) (int, error) {
|
||||
var arg string
|
||||
switch refType {
|
||||
case ObjectTag:
|
||||
arg = "--tags"
|
||||
case ObjectBranch:
|
||||
arg = "--heads"
|
||||
default:
|
||||
arg = ""
|
||||
}
|
||||
|
||||
return walkShowRef(repo.Ctx, repo.Path, arg, skip, limit, walkfn)
|
||||
}
|
||||
|
||||
// callShowRef return refs, if limit = 0 it will not limit
|
||||
func callShowRef(ctx context.Context, repoPath, prefix, arg string, skip, limit int) (branchNames []string, countAll int, err error) {
|
||||
countAll, err = walkShowRef(ctx, repoPath, arg, skip, limit, func(branchName string) error {
|
||||
countAll, err = walkShowRef(ctx, repoPath, arg, skip, limit, func(_, branchName string) error {
|
||||
branchName = strings.TrimPrefix(branchName, prefix)
|
||||
branchNames = append(branchNames, branchName)
|
||||
|
||||
@@ -83,7 +99,7 @@ func callShowRef(ctx context.Context, repoPath, prefix, arg string, skip, limit
|
||||
return
|
||||
}
|
||||
|
||||
func walkShowRef(ctx context.Context, repoPath, arg string, skip, limit int, walkfn func(string) error) (countAll int, err error) {
|
||||
func walkShowRef(ctx context.Context, repoPath, arg string, skip, limit int, walkfn func(sha1, refname string) error) (countAll int, err error) {
|
||||
stdoutReader, stdoutWriter := io.Pipe()
|
||||
defer func() {
|
||||
_ = stdoutReader.Close()
|
||||
@@ -125,11 +141,7 @@ func walkShowRef(ctx context.Context, repoPath, arg string, skip, limit int, wal
|
||||
for limit == 0 || i < skip+limit {
|
||||
// The output of show-ref is simply a list:
|
||||
// <sha> SP <ref> LF
|
||||
_, err := bufReader.ReadSlice(' ')
|
||||
for err == bufio.ErrBufferFull {
|
||||
// This shouldn't happen but we'll tolerate it for the sake of peace
|
||||
_, err = bufReader.ReadSlice(' ')
|
||||
}
|
||||
sha, err := bufReader.ReadString(' ')
|
||||
if err == io.EOF {
|
||||
return i, nil
|
||||
}
|
||||
@@ -149,7 +161,12 @@ func walkShowRef(ctx context.Context, repoPath, arg string, skip, limit int, wal
|
||||
if len(branchName) > 0 {
|
||||
branchName = branchName[:len(branchName)-1]
|
||||
}
|
||||
err = walkfn(branchName)
|
||||
|
||||
if len(sha) > 0 {
|
||||
sha = sha[:len(sha)-1]
|
||||
}
|
||||
|
||||
err = walkfn(sha, branchName)
|
||||
if err != nil {
|
||||
return i, err
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package git
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// WriteCommitGraph write commit graph to speed up repo access
|
||||
// this requires git v2.18 to be installed
|
||||
func WriteCommitGraph(ctx context.Context, repoPath string) error {
|
||||
if CheckGitVersionAtLeast("2.18") == nil {
|
||||
if _, err := NewCommandContext(ctx, "commit-graph", "write").RunInDir(repoPath); err != nil {
|
||||
return fmt.Errorf("unable to write commit-graph for '%s' : %w", repoPath, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
+14
-77
@@ -10,7 +10,6 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
)
|
||||
|
||||
@@ -34,69 +33,6 @@ func (repo *Repository) CreateAnnotatedTag(name, message, revision string) error
|
||||
return err
|
||||
}
|
||||
|
||||
func (repo *Repository) getTag(tagID SHA1, name string) (*Tag, error) {
|
||||
t, ok := repo.tagCache.Get(tagID.String())
|
||||
if ok {
|
||||
log.Debug("Hit cache: %s", tagID)
|
||||
tagClone := *t.(*Tag)
|
||||
tagClone.Name = name // This is necessary because lightweight tags may have same id
|
||||
return &tagClone, nil
|
||||
}
|
||||
|
||||
tp, err := repo.GetTagType(tagID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get the commit ID and tag ID (may be different for annotated tag) for the returned tag object
|
||||
commitIDStr, err := repo.GetTagCommitID(name)
|
||||
if err != nil {
|
||||
// every tag should have a commit ID so return all errors
|
||||
return nil, err
|
||||
}
|
||||
commitID, err := NewIDFromString(commitIDStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If type is "commit, the tag is a lightweight tag
|
||||
if ObjectType(tp) == ObjectCommit {
|
||||
commit, err := repo.GetCommit(commitIDStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tag := &Tag{
|
||||
Name: name,
|
||||
ID: tagID,
|
||||
Object: commitID,
|
||||
Type: tp,
|
||||
Tagger: commit.Committer,
|
||||
Message: commit.Message(),
|
||||
}
|
||||
|
||||
repo.tagCache.Set(tagID.String(), tag)
|
||||
return tag, nil
|
||||
}
|
||||
|
||||
// The tag is an annotated tag with a message.
|
||||
data, err := NewCommandContext(repo.Ctx, "cat-file", "-p", tagID.String()).RunInDirBytes(repo.Path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tag, err := parseTagData(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tag.Name = name
|
||||
tag.ID = tagID
|
||||
tag.Type = tp
|
||||
|
||||
repo.tagCache.Set(tagID.String(), tag)
|
||||
return tag, nil
|
||||
}
|
||||
|
||||
// GetTagNameBySHA returns the name of a tag from its tag object SHA or commit SHA
|
||||
func (repo *Repository) GetTagNameBySHA(sha string) (string, error) {
|
||||
if len(sha) < 5 {
|
||||
@@ -159,6 +95,20 @@ func (repo *Repository) GetTag(name string) (*Tag, error) {
|
||||
return tag, nil
|
||||
}
|
||||
|
||||
// GetTagWithID returns a Git tag by given name and ID
|
||||
func (repo *Repository) GetTagWithID(idStr, name string) (*Tag, error) {
|
||||
id, err := NewIDFromString(idStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tag, err := repo.getTag(id, name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return tag, nil
|
||||
}
|
||||
|
||||
// GetTagInfos returns all tag infos of the repository.
|
||||
func (repo *Repository) GetTagInfos(page, pageSize int) ([]*Tag, int, error) {
|
||||
// TODO this a slow implementation, makes one git command per tag
|
||||
@@ -192,19 +142,6 @@ func (repo *Repository) GetTagInfos(page, pageSize int) ([]*Tag, int, error) {
|
||||
return tags, tagsTotal, nil
|
||||
}
|
||||
|
||||
// GetTagType gets the type of the tag, either commit (simple) or tag (annotated)
|
||||
func (repo *Repository) GetTagType(id SHA1) (string, error) {
|
||||
// Get tag type
|
||||
stdout, err := NewCommandContext(repo.Ctx, "cat-file", "-t", id.String()).RunInDir(repo.Path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(stdout) == 0 {
|
||||
return "", ErrNotExist{ID: id.String()}
|
||||
}
|
||||
return strings.TrimSpace(stdout), nil
|
||||
}
|
||||
|
||||
// GetAnnotatedTag returns a Git tag by its SHA, must be an annotated tag
|
||||
func (repo *Repository) GetAnnotatedTag(sha string) (*Tag, error) {
|
||||
id, err := NewIDFromString(sha)
|
||||
|
||||
@@ -11,6 +11,8 @@ package git
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
|
||||
"github.com/go-git/go-git/v5/plumbing"
|
||||
)
|
||||
|
||||
@@ -53,3 +55,83 @@ func (repo *Repository) GetTags(skip, limit int) ([]string, error) {
|
||||
|
||||
return tagNames, nil
|
||||
}
|
||||
|
||||
// GetTagType gets the type of the tag, either commit (simple) or tag (annotated)
|
||||
func (repo *Repository) GetTagType(id SHA1) (string, error) {
|
||||
// Get tag type
|
||||
obj, err := repo.gogitRepo.Object(plumbing.AnyObject, id)
|
||||
if err != nil {
|
||||
if err == plumbing.ErrReferenceNotFound {
|
||||
return "", &ErrNotExist{ID: id.String()}
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
|
||||
return obj.Type().String(), nil
|
||||
}
|
||||
|
||||
func (repo *Repository) getTag(tagID SHA1, name string) (*Tag, error) {
|
||||
t, ok := repo.tagCache.Get(tagID.String())
|
||||
if ok {
|
||||
log.Debug("Hit cache: %s", tagID)
|
||||
tagClone := *t.(*Tag)
|
||||
tagClone.Name = name // This is necessary because lightweight tags may have same id
|
||||
return &tagClone, nil
|
||||
}
|
||||
|
||||
tp, err := repo.GetTagType(tagID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get the commit ID and tag ID (may be different for annotated tag) for the returned tag object
|
||||
commitIDStr, err := repo.GetTagCommitID(name)
|
||||
if err != nil {
|
||||
// every tag should have a commit ID so return all errors
|
||||
return nil, err
|
||||
}
|
||||
commitID, err := NewIDFromString(commitIDStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If type is "commit, the tag is a lightweight tag
|
||||
if ObjectType(tp) == ObjectCommit {
|
||||
commit, err := repo.GetCommit(commitIDStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tag := &Tag{
|
||||
Name: name,
|
||||
ID: tagID,
|
||||
Object: commitID,
|
||||
Type: tp,
|
||||
Tagger: commit.Committer,
|
||||
Message: commit.Message(),
|
||||
}
|
||||
|
||||
repo.tagCache.Set(tagID.String(), tag)
|
||||
return tag, nil
|
||||
}
|
||||
|
||||
gogitTag, err := repo.gogitRepo.TagObject(tagID)
|
||||
if err != nil {
|
||||
if err == plumbing.ErrReferenceNotFound {
|
||||
return nil, &ErrNotExist{ID: tagID.String()}
|
||||
}
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tag := &Tag{
|
||||
Name: name,
|
||||
ID: tagID,
|
||||
Object: gogitTag.Target,
|
||||
Type: tp,
|
||||
Tagger: &gogitTag.Tagger,
|
||||
Message: gogitTag.Message,
|
||||
}
|
||||
|
||||
repo.tagCache.Set(tagID.String(), tag)
|
||||
return tag, nil
|
||||
}
|
||||
|
||||
@@ -8,6 +8,13 @@
|
||||
|
||||
package git
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
)
|
||||
|
||||
// IsTagExist returns true if given tag exists in the repository.
|
||||
func (repo *Repository) IsTagExist(name string) bool {
|
||||
if name == "" {
|
||||
@@ -23,3 +30,104 @@ func (repo *Repository) GetTags(skip, limit int) (tags []string, err error) {
|
||||
tags, _, err = callShowRef(repo.Ctx, repo.Path, TagPrefix, "--tags", skip, limit)
|
||||
return
|
||||
}
|
||||
|
||||
// GetTagType gets the type of the tag, either commit (simple) or tag (annotated)
|
||||
func (repo *Repository) GetTagType(id SHA1) (string, error) {
|
||||
wr, rd, cancel := repo.CatFileBatchCheck(repo.Ctx)
|
||||
defer cancel()
|
||||
_, err := wr.Write([]byte(id.String() + "\n"))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
_, typ, _, err := ReadBatchLine(rd)
|
||||
if IsErrNotExist(err) {
|
||||
return "", ErrNotExist{ID: id.String()}
|
||||
}
|
||||
return typ, nil
|
||||
}
|
||||
|
||||
func (repo *Repository) getTag(tagID SHA1, name string) (*Tag, error) {
|
||||
t, ok := repo.tagCache.Get(tagID.String())
|
||||
if ok {
|
||||
log.Debug("Hit cache: %s", tagID)
|
||||
tagClone := *t.(*Tag)
|
||||
tagClone.Name = name // This is necessary because lightweight tags may have same id
|
||||
return &tagClone, nil
|
||||
}
|
||||
|
||||
tp, err := repo.GetTagType(tagID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get the commit ID and tag ID (may be different for annotated tag) for the returned tag object
|
||||
commitIDStr, err := repo.GetTagCommitID(name)
|
||||
if err != nil {
|
||||
// every tag should have a commit ID so return all errors
|
||||
return nil, err
|
||||
}
|
||||
commitID, err := NewIDFromString(commitIDStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If type is "commit, the tag is a lightweight tag
|
||||
if ObjectType(tp) == ObjectCommit {
|
||||
commit, err := repo.GetCommit(commitIDStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tag := &Tag{
|
||||
Name: name,
|
||||
ID: tagID,
|
||||
Object: commitID,
|
||||
Type: tp,
|
||||
Tagger: commit.Committer,
|
||||
Message: commit.Message(),
|
||||
}
|
||||
|
||||
repo.tagCache.Set(tagID.String(), tag)
|
||||
return tag, nil
|
||||
}
|
||||
|
||||
// The tag is an annotated tag with a message.
|
||||
wr, rd, cancel := repo.CatFileBatch(repo.Ctx)
|
||||
defer cancel()
|
||||
|
||||
if _, err := wr.Write([]byte(tagID.String() + "\n")); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, typ, size, err := ReadBatchLine(rd)
|
||||
if err != nil {
|
||||
if errors.Is(err, io.EOF) || IsErrNotExist(err) {
|
||||
return nil, ErrNotExist{ID: tagID.String()}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
if typ != "tag" {
|
||||
return nil, ErrNotExist{ID: tagID.String()}
|
||||
}
|
||||
|
||||
// then we need to parse the tag
|
||||
// and load the commit
|
||||
data, err := io.ReadAll(io.LimitReader(rd, size))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, err = rd.Discard(1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tag, err := parseTagData(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tag.Name = name
|
||||
tag.ID = tagID
|
||||
tag.Type = tp
|
||||
|
||||
repo.tagCache.Set(tagID.String(), tag)
|
||||
return tag, nil
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@ import (
|
||||
"net"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
)
|
||||
|
||||
// HostMatchList is used to check if a host or IP is in a list.
|
||||
@@ -102,11 +104,11 @@ func (hl *HostMatchList) checkIP(ip net.IP) bool {
|
||||
for _, builtin := range hl.builtins {
|
||||
switch builtin {
|
||||
case MatchBuiltinExternal:
|
||||
if ip.IsGlobalUnicast() && !ip.IsPrivate() {
|
||||
if ip.IsGlobalUnicast() && !util.IsIPPrivate(ip) {
|
||||
return true
|
||||
}
|
||||
case MatchBuiltinPrivate:
|
||||
if ip.IsPrivate() {
|
||||
if util.IsIPPrivate(ip) {
|
||||
return true
|
||||
}
|
||||
case MatchBuiltinLoopback:
|
||||
|
||||
@@ -5,12 +5,15 @@
|
||||
package stats
|
||||
|
||||
import (
|
||||
"context"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/queue"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
_ "code.gitea.io/gitea/models"
|
||||
@@ -24,6 +27,10 @@ func TestMain(m *testing.M) {
|
||||
}
|
||||
|
||||
func TestRepoStatsIndex(t *testing.T) {
|
||||
if err := git.Init(context.Background()); !assert.NoError(t, err) {
|
||||
return
|
||||
}
|
||||
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
setting.Cfg = ini.Empty()
|
||||
|
||||
@@ -32,10 +39,14 @@ func TestRepoStatsIndex(t *testing.T) {
|
||||
err := Init()
|
||||
assert.NoError(t, err)
|
||||
|
||||
time.Sleep(5 * time.Second)
|
||||
|
||||
repo, err := repo_model.GetRepositoryByID(1)
|
||||
assert.NoError(t, err)
|
||||
|
||||
err = UpdateRepoIndexer(repo)
|
||||
assert.NoError(t, err)
|
||||
|
||||
queue.GetManager().FlushAll(context.Background(), 5*time.Second)
|
||||
|
||||
status, err := repo_model.GetIndexerStatus(repo, repo_model.RepoIndexerTypeStats)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, "65f1bf27bc3bf70f64657658635e66094edbcb4d", status.CommitSha)
|
||||
|
||||
@@ -197,6 +197,11 @@ func testAnswers(baseURLContent, baseURLImages string) []string {
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
`, `<ul>
|
||||
<li class="task-list-item"><input type="checkbox" disabled="" data-source-position="3"/> If you want to rebase/retry this PR, click this checkbox.</li>
|
||||
</ul>
|
||||
<hr/>
|
||||
<p>This PR has been generated by <a href="https://github.com/renovatebot/renovate" rel="nofollow">Renovate Bot</a>.</p>
|
||||
`,
|
||||
}
|
||||
}
|
||||
@@ -269,6 +274,14 @@ Here is a simple footnote,[^1] and here is a longer one.[^bignote]
|
||||
|
||||
Add as many paragraphs as you like.
|
||||
`,
|
||||
`
|
||||
- [ ] <!-- rebase-check --> If you want to rebase/retry this PR, click this checkbox.
|
||||
|
||||
---
|
||||
|
||||
This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
|
||||
|
||||
<!-- test-comment -->`,
|
||||
}
|
||||
|
||||
func TestTotal_RenderWiki(t *testing.T) {
|
||||
|
||||
+22
-13
@@ -80,6 +80,10 @@ func MigrateRepositoryGitData(ctx context.Context, u *user_model.User,
|
||||
return repo, fmt.Errorf("Clone: %v", err)
|
||||
}
|
||||
|
||||
if err := git.WriteCommitGraph(ctx, repoPath); err != nil {
|
||||
return repo, err
|
||||
}
|
||||
|
||||
if opts.Wiki {
|
||||
wikiPath := repo_model.WikiPath(u.Name, opts.RepoName)
|
||||
wikiRemotePath := WikiRemoteURL(opts.CloneAddr)
|
||||
@@ -101,6 +105,9 @@ func MigrateRepositoryGitData(ctx context.Context, u *user_model.User,
|
||||
}
|
||||
}
|
||||
}
|
||||
if err := git.WriteCommitGraph(ctx, wikiPath); err != nil {
|
||||
return repo, err
|
||||
}
|
||||
}
|
||||
|
||||
if repo.OwnerID == u.ID {
|
||||
@@ -278,23 +285,25 @@ func SyncReleasesWithTags(repo *repo_model.Repository, gitRepo *git.Repository)
|
||||
}
|
||||
}
|
||||
}
|
||||
tags, err := gitRepo.GetTags(0, 0)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to GetTags in Repo[%d:%s/%s]: %w", repo.ID, repo.OwnerName, repo.Name, err)
|
||||
}
|
||||
for _, tagName := range tags {
|
||||
if _, ok := existingRelTags[strings.ToLower(tagName)]; !ok {
|
||||
if err := PushUpdateAddTag(repo, gitRepo, tagName); err != nil {
|
||||
return fmt.Errorf("unable to PushUpdateAddTag: %q to Repo[%d:%s/%s]: %w", tagName, repo.ID, repo.OwnerName, repo.Name, err)
|
||||
}
|
||||
|
||||
_, err := gitRepo.WalkReferences(git.ObjectTag, 0, 0, func(sha1, refname string) error {
|
||||
tagName := strings.TrimPrefix(refname, git.TagPrefix)
|
||||
if _, ok := existingRelTags[strings.ToLower(tagName)]; ok {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
if err := PushUpdateAddTag(repo, gitRepo, tagName, sha1, refname); err != nil {
|
||||
return fmt.Errorf("unable to PushUpdateAddTag: %q to Repo[%d:%s/%s]: %w", tagName, repo.ID, repo.OwnerName, repo.Name, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
// PushUpdateAddTag must be called for any push actions to add tag
|
||||
func PushUpdateAddTag(repo *repo_model.Repository, gitRepo *git.Repository, tagName string) error {
|
||||
tag, err := gitRepo.GetTag(tagName)
|
||||
func PushUpdateAddTag(repo *repo_model.Repository, gitRepo *git.Repository, tagName, sha1, refname string) error {
|
||||
tag, err := gitRepo.GetTagWithID(sha1, tagName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to GetTag: %w", err)
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ package setting
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"net"
|
||||
"net/url"
|
||||
@@ -1010,7 +1009,7 @@ func loadFromConf(allowEmpty bool, extraConfig string) {
|
||||
UI.ShowUserEmail = Cfg.Section("ui").Key("SHOW_USER_EMAIL").MustBool(true)
|
||||
UI.DefaultShowFullName = Cfg.Section("ui").Key("DEFAULT_SHOW_FULL_NAME").MustBool(false)
|
||||
UI.SearchRepoDescription = Cfg.Section("ui").Key("SEARCH_REPO_DESCRIPTION").MustBool(true)
|
||||
UI.UseServiceWorker = Cfg.Section("ui").Key("USE_SERVICE_WORKER").MustBool(true)
|
||||
UI.UseServiceWorker = Cfg.Section("ui").Key("USE_SERVICE_WORKER").MustBool(false)
|
||||
|
||||
HasRobotsTxt, err = util.IsFile(path.Join(CustomPath, "robots.txt"))
|
||||
if err != nil {
|
||||
@@ -1080,28 +1079,22 @@ func loadInternalToken(sec *ini.Section) string {
|
||||
}
|
||||
switch tempURI.Scheme {
|
||||
case "file":
|
||||
fp, err := os.OpenFile(tempURI.RequestURI(), os.O_RDWR, 0o600)
|
||||
if err != nil {
|
||||
buf, err := os.ReadFile(tempURI.RequestURI())
|
||||
if err != nil && !os.IsNotExist(err) {
|
||||
log.Fatal("Failed to open InternalTokenURI (%s): %v", uri, err)
|
||||
}
|
||||
defer fp.Close()
|
||||
|
||||
buf, err := io.ReadAll(fp)
|
||||
if err != nil {
|
||||
log.Fatal("Failed to read InternalTokenURI (%s): %v", uri, err)
|
||||
}
|
||||
// No token in the file, generate one and store it.
|
||||
if len(buf) == 0 {
|
||||
token, err := generate.NewInternalToken()
|
||||
if err != nil {
|
||||
log.Fatal("Error generate internal token: %v", err)
|
||||
}
|
||||
if _, err := io.WriteString(fp, token); err != nil {
|
||||
err = os.WriteFile(tempURI.RequestURI(), []byte(token), 0o600)
|
||||
if err != nil {
|
||||
log.Fatal("Error writing to InternalTokenURI (%s): %v", uri, err)
|
||||
}
|
||||
return token
|
||||
}
|
||||
|
||||
return strings.TrimSpace(string(buf))
|
||||
default:
|
||||
log.Fatal("Unsupported URI-Scheme %q (INTERNAL_TOKEN_URI = %q)", tempURI.Scheme, uri)
|
||||
|
||||
@@ -183,6 +183,8 @@ type EditRepoOption struct {
|
||||
Archived *bool `json:"archived,omitempty"`
|
||||
// set to a string like `8h30m0s` to set the mirror interval time
|
||||
MirrorInterval *string `json:"mirror_interval,omitempty"`
|
||||
// enable prune - remove obsolete remote-tracking references
|
||||
EnablePrune *bool `json:"enable_prune,omitempty"`
|
||||
}
|
||||
|
||||
// GenerateRepoOption options when creating repository using a template
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
// 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 util
|
||||
|
||||
import (
|
||||
"net"
|
||||
)
|
||||
|
||||
// IsIPPrivate for net.IP.IsPrivate.
|
||||
func IsIPPrivate(ip net.IP) bool {
|
||||
if ip4 := ip.To4(); ip4 != nil {
|
||||
return ip4[0] == 10 ||
|
||||
(ip4[0] == 172 && ip4[1]&0xf0 == 16) ||
|
||||
(ip4[0] == 192 && ip4[1] == 168)
|
||||
}
|
||||
return len(ip) == net.IPv6len && ip[0]&0xfe == 0xfc
|
||||
}
|
||||
@@ -1435,7 +1435,7 @@ pulls.manually_merged=Слито вручную
|
||||
pulls.manually_merged_as=Запрос на слияние был объединён вручную, как <a rel="nofollow" class="ui sha" href="%[1]s"><code>%[2]s</code></a>.
|
||||
pulls.is_closed=Запрос на слияние был закрыт.
|
||||
pulls.has_merged=Слияние этого запроса успешно завершено.
|
||||
pulls.title_wip_desc=`<a href="#">Добавьте <strong>%s</strong> в начало заголовка</a> для защиты от случайного досрочного принятия запроса на слияние
|
||||
pulls.title_wip_desc=`<a href="#">Добавьте <strong>%s</strong> в начало заголовка</a> для защиты от случайного досрочного принятия запроса на слияние`
|
||||
pulls.cannot_merge_work_in_progress=Этот запрос на слияние помечен как в процессе работы.
|
||||
pulls.still_in_progress=Всё ещё в процессе?
|
||||
pulls.add_prefix=Добавить <strong>%s</strong> префикс
|
||||
|
||||
+28
-76
@@ -95,7 +95,6 @@ func ListPullRequests(ctx *context.APIContext) {
|
||||
Labels: ctx.FormStrings("labels"),
|
||||
MilestoneID: ctx.FormInt64("milestone"),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "PullRequests", err)
|
||||
return
|
||||
@@ -724,13 +723,12 @@ func MergePullRequest(ctx *context.APIContext) {
|
||||
return
|
||||
}
|
||||
|
||||
if err = pr.LoadHeadRepo(); err != nil {
|
||||
if err := pr.LoadHeadRepo(); err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "LoadHeadRepo", err)
|
||||
return
|
||||
}
|
||||
|
||||
err = pr.LoadIssue()
|
||||
if err != nil {
|
||||
if err := pr.LoadIssue(); err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "LoadIssue", err)
|
||||
return
|
||||
}
|
||||
@@ -744,29 +742,33 @@ func MergePullRequest(ctx *context.APIContext) {
|
||||
}
|
||||
}
|
||||
|
||||
if pr.Issue.IsClosed {
|
||||
ctx.NotFound()
|
||||
return
|
||||
}
|
||||
manuallMerge := repo_model.MergeStyle(form.Do) == repo_model.MergeStyleManuallyMerged
|
||||
force := form.ForceMerge != nil && *form.ForceMerge
|
||||
|
||||
allowedMerge, err := pull_service.IsUserAllowedToMerge(pr, ctx.Repo.Permission, ctx.User)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "IsUSerAllowedToMerge", err)
|
||||
return
|
||||
}
|
||||
if !allowedMerge {
|
||||
ctx.Error(http.StatusMethodNotAllowed, "Merge", "User not allowed to merge PR")
|
||||
return
|
||||
}
|
||||
|
||||
if pr.HasMerged {
|
||||
ctx.Error(http.StatusMethodNotAllowed, "PR already merged", "")
|
||||
if err := pull_service.CheckPullMergable(ctx, ctx.User, &ctx.Repo.Permission, pr, manuallMerge, force); err != nil {
|
||||
if errors.Is(err, pull_service.ErrIsClosed) {
|
||||
ctx.NotFound()
|
||||
} else if errors.Is(err, pull_service.ErrUserNotAllowedToMerge) {
|
||||
ctx.Error(http.StatusMethodNotAllowed, "Merge", "User not allowed to merge PR")
|
||||
} else if errors.Is(err, pull_service.ErrHasMerged) {
|
||||
ctx.Error(http.StatusMethodNotAllowed, "PR already merged", "")
|
||||
} else if errors.Is(err, pull_service.ErrIsWorkInProgress) {
|
||||
ctx.Error(http.StatusMethodNotAllowed, "PR is a work in progress", "Work in progress PRs cannot be merged")
|
||||
} else if errors.Is(err, pull_service.ErrNotMergableState) {
|
||||
ctx.Error(http.StatusMethodNotAllowed, "PR not in mergeable state", "Please try again later")
|
||||
} else if models.IsErrNotAllowedToMerge(err) {
|
||||
ctx.Error(http.StatusMethodNotAllowed, "PR is not ready to be merged", err)
|
||||
} else if asymkey_service.IsErrWontSign(err) {
|
||||
ctx.Error(http.StatusMethodNotAllowed, fmt.Sprintf("Protected branch %s requires signed commits but this merge would not be signed", pr.BaseBranch), err)
|
||||
} else {
|
||||
ctx.InternalServerError(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// handle manually-merged mark
|
||||
if repo_model.MergeStyle(form.Do) == repo_model.MergeStyleManuallyMerged {
|
||||
if err = pull_service.MergedManually(pr, ctx.User, ctx.Repo.GitRepo, form.MergeCommitID); err != nil {
|
||||
if manuallMerge {
|
||||
if err := pull_service.MergedManually(pr, ctx.User, ctx.Repo.GitRepo, form.MergeCommitID); err != nil {
|
||||
if models.IsErrInvalidMergeStyle(err) {
|
||||
ctx.Error(http.StatusMethodNotAllowed, "Invalid merge style", fmt.Errorf("%s is not allowed an allowed merge style for this repository", repo_model.MergeStyle(form.Do)))
|
||||
return
|
||||
@@ -782,63 +784,13 @@ func MergePullRequest(ctx *context.APIContext) {
|
||||
return
|
||||
}
|
||||
|
||||
if !pr.CanAutoMerge() {
|
||||
ctx.Error(http.StatusMethodNotAllowed, "PR not in mergeable state", "Please try again later")
|
||||
// set defaults to propagate needed fields
|
||||
if err := form.SetDefaults(pr); err != nil {
|
||||
ctx.ServerError("SetDefaults", fmt.Errorf("SetDefaults: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
if pr.IsWorkInProgress() {
|
||||
ctx.Error(http.StatusMethodNotAllowed, "PR is a work in progress", "Work in progress PRs cannot be merged")
|
||||
return
|
||||
}
|
||||
|
||||
if err := pull_service.CheckPRReadyToMerge(pr, false); err != nil {
|
||||
if !models.IsErrNotAllowedToMerge(err) {
|
||||
ctx.Error(http.StatusInternalServerError, "CheckPRReadyToMerge", err)
|
||||
return
|
||||
}
|
||||
if form.ForceMerge != nil && *form.ForceMerge {
|
||||
if isRepoAdmin, err := models.IsUserRepoAdmin(pr.BaseRepo, ctx.User); err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "IsUserRepoAdmin", err)
|
||||
return
|
||||
} else if !isRepoAdmin {
|
||||
ctx.Error(http.StatusMethodNotAllowed, "Merge", "Only repository admin can merge if not all checks are ok (force merge)")
|
||||
}
|
||||
} else {
|
||||
ctx.Error(http.StatusMethodNotAllowed, "PR is not ready to be merged", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := pull_service.IsSignedIfRequired(pr, ctx.User); err != nil {
|
||||
if !asymkey_service.IsErrWontSign(err) {
|
||||
ctx.Error(http.StatusInternalServerError, "IsSignedIfRequired", err)
|
||||
return
|
||||
}
|
||||
ctx.Error(http.StatusMethodNotAllowed, fmt.Sprintf("Protected branch %s requires signed commits but this merge would not be signed", pr.BaseBranch), err)
|
||||
return
|
||||
}
|
||||
|
||||
if len(form.Do) == 0 {
|
||||
form.Do = string(repo_model.MergeStyleMerge)
|
||||
}
|
||||
|
||||
message := strings.TrimSpace(form.MergeTitleField)
|
||||
if len(message) == 0 {
|
||||
if repo_model.MergeStyle(form.Do) == repo_model.MergeStyleMerge {
|
||||
message = pr.GetDefaultMergeMessage()
|
||||
}
|
||||
if repo_model.MergeStyle(form.Do) == repo_model.MergeStyleSquash {
|
||||
message = pr.GetDefaultSquashMessage()
|
||||
}
|
||||
}
|
||||
|
||||
form.MergeMessageField = strings.TrimSpace(form.MergeMessageField)
|
||||
if len(form.MergeMessageField) > 0 {
|
||||
message += "\n\n" + form.MergeMessageField
|
||||
}
|
||||
|
||||
if err := pull_service.Merge(pr, ctx.User, ctx.Repo.GitRepo, repo_model.MergeStyle(form.Do), form.HeadCommitID, message); err != nil {
|
||||
if err := pull_service.Merge(pr, ctx.User, ctx.Repo.GitRepo, repo_model.MergeStyle(form.Do), form.HeadCommitID, form.MergeTitleField); err != nil {
|
||||
if models.IsErrInvalidMergeStyle(err) {
|
||||
ctx.Error(http.StatusMethodNotAllowed, "Invalid merge style", fmt.Errorf("%s is not allowed an allowed merge style for this repository", repo_model.MergeStyle(form.Do)))
|
||||
return
|
||||
|
||||
+53
-23
@@ -624,7 +624,7 @@ func Edit(ctx *context.APIContext) {
|
||||
}
|
||||
|
||||
if opts.MirrorInterval != nil {
|
||||
if err := updateMirrorInterval(ctx, opts); err != nil {
|
||||
if err := updateMirror(ctx, opts); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -949,37 +949,67 @@ func updateRepoArchivedState(ctx *context.APIContext, opts api.EditRepoOption) e
|
||||
return nil
|
||||
}
|
||||
|
||||
// updateMirrorInterval updates the repo's mirror Interval
|
||||
func updateMirrorInterval(ctx *context.APIContext, opts api.EditRepoOption) error {
|
||||
// updateMirror updates a repo's mirror Interval and EnablePrune
|
||||
func updateMirror(ctx *context.APIContext, opts api.EditRepoOption) error {
|
||||
repo := ctx.Repo.Repository
|
||||
|
||||
// only update mirror if interval or enable prune are provided
|
||||
if opts.MirrorInterval == nil && opts.EnablePrune == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// these values only make sense if the repo is a mirror
|
||||
if !repo.IsMirror {
|
||||
err := fmt.Errorf("repo is not a mirror, can not change mirror interval")
|
||||
ctx.Error(http.StatusUnprocessableEntity, err.Error(), err)
|
||||
return err
|
||||
}
|
||||
|
||||
// get the mirror from the repo
|
||||
mirror, err := repo_model.GetMirrorByRepoID(repo.ID)
|
||||
if err != nil {
|
||||
log.Error("Failed to get mirror: %s", err)
|
||||
ctx.Error(http.StatusInternalServerError, "MirrorInterval", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// update MirrorInterval
|
||||
if opts.MirrorInterval != nil {
|
||||
if !repo.IsMirror {
|
||||
err := fmt.Errorf("repo is not a mirror, can not change mirror interval")
|
||||
ctx.Error(http.StatusUnprocessableEntity, err.Error(), err)
|
||||
return err
|
||||
}
|
||||
mirror, err := repo_model.GetMirrorByRepoID(repo.ID)
|
||||
|
||||
// MirrorInterval should be a duration
|
||||
interval, err := time.ParseDuration(*opts.MirrorInterval)
|
||||
if err != nil {
|
||||
log.Error("Failed to get mirror: %s", err)
|
||||
ctx.Error(http.StatusInternalServerError, "MirrorInterval", err)
|
||||
return err
|
||||
}
|
||||
if interval, err := time.ParseDuration(*opts.MirrorInterval); err == nil {
|
||||
mirror.Interval = interval
|
||||
mirror.Repo = repo
|
||||
if err := repo_model.UpdateMirror(mirror); err != nil {
|
||||
log.Error("Failed to Set Mirror Interval: %s", err)
|
||||
ctx.Error(http.StatusUnprocessableEntity, "MirrorInterval", err)
|
||||
return err
|
||||
}
|
||||
log.Trace("Repository %s/%s Mirror Interval was Updated to %s", ctx.Repo.Owner.Name, repo.Name, interval)
|
||||
} else {
|
||||
log.Error("Wrong format for MirrorInternal Sent: %s", err)
|
||||
ctx.Error(http.StatusUnprocessableEntity, "MirrorInterval", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// Ensure the provided duration is not too short
|
||||
if interval != 0 && interval < setting.Mirror.MinInterval {
|
||||
err := fmt.Errorf("invalid mirror interval: %s is below minimum interval: %s", interval, setting.Mirror.MinInterval)
|
||||
ctx.Error(http.StatusUnprocessableEntity, "MirrorInterval", err)
|
||||
return err
|
||||
}
|
||||
|
||||
mirror.Interval = interval
|
||||
mirror.Repo = repo
|
||||
mirror.ScheduleNextUpdate()
|
||||
log.Trace("Repository %s Mirror[%d] Set Interval: %s NextUpdateUnix: %s", repo.FullName(), mirror.ID, interval, mirror.NextUpdateUnix)
|
||||
}
|
||||
|
||||
// update EnablePrune
|
||||
if opts.EnablePrune != nil {
|
||||
mirror.EnablePrune = *opts.EnablePrune
|
||||
log.Trace("Repository %s Mirror[%d] Set EnablePrune: %t", repo.FullName(), mirror.ID, mirror.EnablePrune)
|
||||
}
|
||||
|
||||
// finally update the mirror in the DB
|
||||
if err := repo_model.UpdateMirror(mirror); err != nil {
|
||||
log.Error("Failed to Set Mirror Interval: %s", err)
|
||||
ctx.Error(http.StatusUnprocessableEntity, "MirrorInterval", err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -80,9 +80,16 @@ func AddEmail(ctx *context.APIContext) {
|
||||
if err := user_model.AddEmailAddresses(emails); err != nil {
|
||||
if user_model.IsErrEmailAlreadyUsed(err) {
|
||||
ctx.Error(http.StatusUnprocessableEntity, "", "Email address has been used: "+err.(user_model.ErrEmailAlreadyUsed).Email)
|
||||
} else if user_model.IsErrEmailCharIsNotSupported(err) ||
|
||||
user_model.IsErrEmailInvalid(err) {
|
||||
errMsg := fmt.Sprintf("Email address %s invalid", err.(user_model.ErrEmailInvalid).Email)
|
||||
} else if user_model.IsErrEmailCharIsNotSupported(err) || user_model.IsErrEmailInvalid(err) {
|
||||
email := ""
|
||||
if typedError, ok := err.(user_model.ErrEmailInvalid); ok {
|
||||
email = typedError.Email
|
||||
}
|
||||
if typedError, ok := err.(user_model.ErrEmailCharIsNotSupported); ok {
|
||||
email = typedError.Email
|
||||
}
|
||||
|
||||
errMsg := fmt.Sprintf("Email address %q invalid", email)
|
||||
ctx.Error(http.StatusUnprocessableEntity, "", errMsg)
|
||||
} else {
|
||||
ctx.Error(http.StatusInternalServerError, "AddEmailAddresses", err)
|
||||
|
||||
@@ -266,16 +266,21 @@ func DeletePublicKey(ctx *context.APIContext) {
|
||||
id := ctx.ParamsInt64(":id")
|
||||
externallyManaged, err := asymkey_model.PublicKeyIsExternallyManaged(id)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "PublicKeyIsExternallyManaged", err)
|
||||
if asymkey_model.IsErrKeyNotExist(err) {
|
||||
ctx.NotFound()
|
||||
} else {
|
||||
ctx.Error(http.StatusInternalServerError, "PublicKeyIsExternallyManaged", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if externallyManaged {
|
||||
ctx.Error(http.StatusForbidden, "", "SSH Key is externally managed for this user")
|
||||
return
|
||||
}
|
||||
|
||||
if err := asymkey_service.DeletePublicKey(ctx.User, id); err != nil {
|
||||
if asymkey_model.IsErrKeyNotExist(err) {
|
||||
ctx.NotFound()
|
||||
} else if asymkey_model.IsErrKeyAccessDenied(err) {
|
||||
if asymkey_model.IsErrKeyAccessDenied(err) {
|
||||
ctx.Error(http.StatusForbidden, "", "You do not have access to this key")
|
||||
} else {
|
||||
ctx.Error(http.StatusInternalServerError, "DeletePublicKey", err)
|
||||
|
||||
@@ -246,18 +246,29 @@ func editHook(ctx *context.APIContext, form *api.EditHookOption, w *webhook.Webh
|
||||
w.ChooseEvents = true
|
||||
w.Create = util.IsStringInSlice(string(webhook.HookEventCreate), form.Events, true)
|
||||
w.Push = util.IsStringInSlice(string(webhook.HookEventPush), form.Events, true)
|
||||
w.PullRequest = util.IsStringInSlice(string(webhook.HookEventPullRequest), form.Events, true)
|
||||
w.Create = util.IsStringInSlice(string(webhook.HookEventCreate), form.Events, true)
|
||||
w.Delete = util.IsStringInSlice(string(webhook.HookEventDelete), form.Events, true)
|
||||
w.Fork = util.IsStringInSlice(string(webhook.HookEventFork), form.Events, true)
|
||||
w.Issues = util.IsStringInSlice(string(webhook.HookEventIssues), form.Events, true)
|
||||
w.IssueComment = util.IsStringInSlice(string(webhook.HookEventIssueComment), form.Events, true)
|
||||
w.Push = util.IsStringInSlice(string(webhook.HookEventPush), form.Events, true)
|
||||
w.PullRequest = util.IsStringInSlice(string(webhook.HookEventPullRequest), form.Events, true)
|
||||
w.Repository = util.IsStringInSlice(string(webhook.HookEventRepository), form.Events, true)
|
||||
w.Release = util.IsStringInSlice(string(webhook.HookEventRelease), form.Events, true)
|
||||
w.BranchFilter = form.BranchFilter
|
||||
|
||||
// Issues
|
||||
w.Issues = issuesHook(form.Events, "issues_only")
|
||||
w.IssueAssign = issuesHook(form.Events, string(webhook.HookEventIssueAssign))
|
||||
w.IssueLabel = issuesHook(form.Events, string(webhook.HookEventIssueLabel))
|
||||
w.IssueMilestone = issuesHook(form.Events, string(webhook.HookEventIssueMilestone))
|
||||
w.IssueComment = issuesHook(form.Events, string(webhook.HookEventIssueComment))
|
||||
|
||||
// Pull requests
|
||||
w.PullRequest = pullHook(form.Events, "pull_request_only")
|
||||
w.PullRequestAssign = pullHook(form.Events, string(webhook.HookEventPullRequestAssign))
|
||||
w.PullRequestLabel = pullHook(form.Events, string(webhook.HookEventPullRequestLabel))
|
||||
w.PullRequestMilestone = pullHook(form.Events, string(webhook.HookEventPullRequestMilestone))
|
||||
w.PullRequestComment = pullHook(form.Events, string(webhook.HookEventPullRequestComment))
|
||||
w.PullRequestReview = pullHook(form.Events, "pull_request_review")
|
||||
w.PullRequestSync = pullHook(form.Events, string(webhook.HookEventPullRequestSync))
|
||||
|
||||
if err := w.UpdateEvent(); err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "UpdateEvent", err)
|
||||
return false
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/base"
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -34,7 +35,7 @@ func DefaultOrSystemWebhooks(ctx *context.Context) {
|
||||
|
||||
sys["Title"] = ctx.Tr("admin.systemhooks")
|
||||
sys["Description"] = ctx.Tr("admin.systemhooks.desc")
|
||||
sys["Webhooks"], err = webhook.GetSystemWebhooks()
|
||||
sys["Webhooks"], err = webhook.GetSystemWebhooks(util.OptionalBoolNone)
|
||||
sys["BaseLink"] = setting.AppSubURL + "/admin/hooks"
|
||||
sys["BaseLinkNew"] = setting.AppSubURL + "/admin/system-hooks"
|
||||
if err != nil {
|
||||
|
||||
+54
-101
@@ -33,6 +33,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/web"
|
||||
"code.gitea.io/gitea/modules/web/middleware"
|
||||
"code.gitea.io/gitea/routers/utils"
|
||||
asymkey_service "code.gitea.io/gitea/services/asymkey"
|
||||
"code.gitea.io/gitea/services/forms"
|
||||
"code.gitea.io/gitea/services/gitdiff"
|
||||
pull_service "code.gitea.io/gitea/services/pull"
|
||||
@@ -48,16 +49,14 @@ const (
|
||||
pullRequestTemplateKey = "PullRequestTemplate"
|
||||
)
|
||||
|
||||
var (
|
||||
pullRequestTemplateCandidates = []string{
|
||||
"PULL_REQUEST_TEMPLATE.md",
|
||||
"pull_request_template.md",
|
||||
".gitea/PULL_REQUEST_TEMPLATE.md",
|
||||
".gitea/pull_request_template.md",
|
||||
".github/PULL_REQUEST_TEMPLATE.md",
|
||||
".github/pull_request_template.md",
|
||||
}
|
||||
)
|
||||
var pullRequestTemplateCandidates = []string{
|
||||
"PULL_REQUEST_TEMPLATE.md",
|
||||
"pull_request_template.md",
|
||||
".gitea/PULL_REQUEST_TEMPLATE.md",
|
||||
".gitea/pull_request_template.md",
|
||||
".github/PULL_REQUEST_TEMPLATE.md",
|
||||
".github/pull_request_template.md",
|
||||
}
|
||||
|
||||
func getRepository(ctx *context.Context, repoID int64) *repo_model.Repository {
|
||||
repo, err := repo_model.GetRepositoryByID(repoID)
|
||||
@@ -125,7 +124,7 @@ func getForkRepository(ctx *context.Context) *repo_model.Repository {
|
||||
}
|
||||
}
|
||||
|
||||
var traverseParentRepo = forkRepo
|
||||
traverseParentRepo := forkRepo
|
||||
for {
|
||||
if ctx.User.ID == traverseParentRepo.OwnerID {
|
||||
canForkToUser = false
|
||||
@@ -195,7 +194,7 @@ func ForkPost(ctx *context.Context) {
|
||||
}
|
||||
|
||||
var err error
|
||||
var traverseParentRepo = forkRepo
|
||||
traverseParentRepo := forkRepo
|
||||
for {
|
||||
if ctxUser.ID == traverseParentRepo.OwnerID {
|
||||
ctx.RenderWithErr(ctx.Tr("repo.settings.new_owner_has_same_repo"), tplFork, &form)
|
||||
@@ -853,39 +852,53 @@ func MergePullRequest(ctx *context.Context) {
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
if issue.IsClosed {
|
||||
if issue.IsPull {
|
||||
ctx.Flash.Error(ctx.Tr("repo.pulls.is_closed"))
|
||||
ctx.Redirect(issue.Link())
|
||||
return
|
||||
}
|
||||
ctx.Flash.Error(ctx.Tr("repo.issues.closed_title"))
|
||||
ctx.Redirect(issue.Link())
|
||||
return
|
||||
}
|
||||
|
||||
pr := issue.PullRequest
|
||||
pr.Issue = issue
|
||||
pr.Issue.Repo = ctx.Repo.Repository
|
||||
manuallMerge := repo_model.MergeStyle(form.Do) == repo_model.MergeStyleManuallyMerged
|
||||
forceMerge := form.ForceMerge != nil && *form.ForceMerge
|
||||
|
||||
allowedMerge, err := pull_service.IsUserAllowedToMerge(pr, ctx.Repo.Permission, ctx.User)
|
||||
if err != nil {
|
||||
ctx.ServerError("IsUserAllowedToMerge", err)
|
||||
return
|
||||
}
|
||||
if !allowedMerge {
|
||||
ctx.Flash.Error(ctx.Tr("repo.pulls.update_not_allowed"))
|
||||
ctx.Redirect(issue.Link())
|
||||
return
|
||||
}
|
||||
if err := pull_service.CheckPullMergable(ctx, ctx.User, &ctx.Repo.Permission, pr, manuallMerge, forceMerge); err != nil {
|
||||
if errors.Is(err, pull_service.ErrIsClosed) {
|
||||
if issue.IsPull {
|
||||
ctx.Flash.Error(ctx.Tr("repo.pulls.is_closed"))
|
||||
ctx.Redirect(issue.Link())
|
||||
} else {
|
||||
ctx.Flash.Error(ctx.Tr("repo.issues.closed_title"))
|
||||
ctx.Redirect(issue.Link())
|
||||
}
|
||||
} else if errors.Is(err, pull_service.ErrUserNotAllowedToMerge) {
|
||||
ctx.Flash.Error(ctx.Tr("repo.pulls.update_not_allowed"))
|
||||
ctx.Redirect(issue.Link())
|
||||
} else if errors.Is(err, pull_service.ErrHasMerged) {
|
||||
ctx.Flash.Error(ctx.Tr("repo.pulls.has_merged"))
|
||||
ctx.Redirect(issue.Link())
|
||||
} else if errors.Is(err, pull_service.ErrIsWorkInProgress) {
|
||||
ctx.Flash.Error(ctx.Tr("repo.pulls.no_merge_wip"))
|
||||
ctx.Redirect(issue.Link())
|
||||
} else if errors.Is(err, pull_service.ErrNotMergableState) {
|
||||
ctx.Flash.Error(ctx.Tr("repo.pulls.no_merge_not_ready"))
|
||||
ctx.Redirect(issue.Link())
|
||||
} else if models.IsErrNotAllowedToMerge(err) {
|
||||
ctx.Flash.Error(ctx.Tr("repo.pulls.no_merge_not_ready"))
|
||||
ctx.Redirect(issue.Link())
|
||||
} else if asymkey_service.IsErrWontSign(err) {
|
||||
ctx.Flash.Error(err.Error()) // has not translation ...
|
||||
ctx.Redirect(issue.Link())
|
||||
} else if errors.Is(err, pull_service.ErrDependenciesLeft) {
|
||||
ctx.Flash.Error(ctx.Tr("repo.issues.dependency.pr_close_blocked"))
|
||||
ctx.Redirect(issue.Link())
|
||||
} else {
|
||||
ctx.ServerError("WebCheck", err)
|
||||
}
|
||||
|
||||
if pr.HasMerged {
|
||||
ctx.Flash.Error(ctx.Tr("repo.pulls.has_merged"))
|
||||
ctx.Redirect(issue.Link())
|
||||
return
|
||||
}
|
||||
|
||||
// handle manually-merged mark
|
||||
if repo_model.MergeStyle(form.Do) == repo_model.MergeStyleManuallyMerged {
|
||||
if err = pull_service.MergedManually(pr, ctx.User, ctx.Repo.GitRepo, form.MergeCommitID); err != nil {
|
||||
if manuallMerge {
|
||||
if err := pull_service.MergedManually(pr, ctx.User, ctx.Repo.GitRepo, form.MergeCommitID); err != nil {
|
||||
if models.IsErrInvalidMergeStyle(err) {
|
||||
ctx.Flash.Error(ctx.Tr("repo.pulls.invalid_merge_option"))
|
||||
ctx.Redirect(issue.Link())
|
||||
@@ -904,72 +917,13 @@ func MergePullRequest(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
if !pr.CanAutoMerge() {
|
||||
ctx.Flash.Error(ctx.Tr("repo.pulls.no_merge_not_ready"))
|
||||
ctx.Redirect(issue.Link())
|
||||
// set defaults to propagate needed fields
|
||||
if err := form.SetDefaults(pr); err != nil {
|
||||
ctx.ServerError("SetDefaults", fmt.Errorf("SetDefaults: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
if pr.IsWorkInProgress() {
|
||||
ctx.Flash.Error(ctx.Tr("repo.pulls.no_merge_wip"))
|
||||
ctx.Redirect(issue.Link())
|
||||
return
|
||||
}
|
||||
|
||||
if err := pull_service.CheckPRReadyToMerge(pr, false); err != nil {
|
||||
if !models.IsErrNotAllowedToMerge(err) {
|
||||
ctx.ServerError("Merge PR status", err)
|
||||
return
|
||||
}
|
||||
if isRepoAdmin, err := models.IsUserRepoAdmin(pr.BaseRepo, ctx.User); err != nil {
|
||||
ctx.ServerError("IsUserRepoAdmin", err)
|
||||
return
|
||||
} else if !isRepoAdmin {
|
||||
ctx.Flash.Error(ctx.Tr("repo.pulls.no_merge_not_ready"))
|
||||
ctx.Redirect(issue.Link())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if ctx.HasError() {
|
||||
ctx.Flash.Error(ctx.Data["ErrorMsg"].(string))
|
||||
ctx.Redirect(issue.Link())
|
||||
return
|
||||
}
|
||||
|
||||
message := strings.TrimSpace(form.MergeTitleField)
|
||||
if len(message) == 0 {
|
||||
if repo_model.MergeStyle(form.Do) == repo_model.MergeStyleMerge {
|
||||
message = pr.GetDefaultMergeMessage()
|
||||
}
|
||||
if repo_model.MergeStyle(form.Do) == repo_model.MergeStyleRebaseMerge {
|
||||
message = pr.GetDefaultMergeMessage()
|
||||
}
|
||||
if repo_model.MergeStyle(form.Do) == repo_model.MergeStyleSquash {
|
||||
message = pr.GetDefaultSquashMessage()
|
||||
}
|
||||
}
|
||||
|
||||
form.MergeMessageField = strings.TrimSpace(form.MergeMessageField)
|
||||
if len(form.MergeMessageField) > 0 {
|
||||
message += "\n\n" + form.MergeMessageField
|
||||
}
|
||||
|
||||
pr.Issue = issue
|
||||
pr.Issue.Repo = ctx.Repo.Repository
|
||||
|
||||
noDeps, err := models.IssueNoDependenciesLeft(issue)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if !noDeps {
|
||||
ctx.Flash.Error(ctx.Tr("repo.issues.dependency.pr_close_blocked"))
|
||||
ctx.Redirect(issue.Link())
|
||||
return
|
||||
}
|
||||
|
||||
if err = pull_service.Merge(pr, ctx.User, ctx.Repo.GitRepo, repo_model.MergeStyle(form.Do), form.HeadCommitID, message); err != nil {
|
||||
if err := pull_service.Merge(pr, ctx.User, ctx.Repo.GitRepo, repo_model.MergeStyle(form.Do), form.HeadCommitID, form.MergeTitleField); err != nil {
|
||||
if models.IsErrInvalidMergeStyle(err) {
|
||||
ctx.Flash.Error(ctx.Tr("repo.pulls.invalid_merge_option"))
|
||||
ctx.Redirect(issue.Link())
|
||||
@@ -1079,7 +1033,6 @@ func MergePullRequest(ctx *context.Context) {
|
||||
}
|
||||
|
||||
func stopTimerIfAvailable(user *user_model.User, issue *models.Issue) error {
|
||||
|
||||
if models.StopwatchExists(user.ID, issue.ID) {
|
||||
if err := models.CreateOrStopIssueStopwatch(user, issue); err != nil {
|
||||
return err
|
||||
|
||||
@@ -31,7 +31,6 @@ import (
|
||||
"code.gitea.io/gitea/modules/repository"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
"code.gitea.io/gitea/modules/typesniffer"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
"code.gitea.io/gitea/modules/validation"
|
||||
@@ -194,11 +193,7 @@ func SettingsPost(ctx *context.Context) {
|
||||
} else {
|
||||
ctx.Repo.Mirror.EnablePrune = form.EnablePrune
|
||||
ctx.Repo.Mirror.Interval = interval
|
||||
if interval != 0 {
|
||||
ctx.Repo.Mirror.NextUpdateUnix = timeutil.TimeStampNow().AddDuration(interval)
|
||||
} else {
|
||||
ctx.Repo.Mirror.NextUpdateUnix = 0
|
||||
}
|
||||
ctx.Repo.Mirror.ScheduleNextUpdate()
|
||||
if err := repo_model.UpdateMirror(ctx.Repo.Mirror); err != nil {
|
||||
ctx.Data["Err_Interval"] = true
|
||||
ctx.RenderWithErr(ctx.Tr("repo.mirror_interval_invalid"), tplSettingsOptions, &form)
|
||||
|
||||
+3
-3
@@ -374,8 +374,8 @@ func RegisterRoutes(m *web.Route) {
|
||||
|
||||
m.Group("/user", func() {
|
||||
// r.Get("/feeds", binding.Bind(auth.FeedsForm{}), user.Feeds)
|
||||
m.Get("/activate", auth.Activate, reqSignIn)
|
||||
m.Post("/activate", auth.ActivatePost, reqSignIn)
|
||||
m.Get("/activate", auth.Activate)
|
||||
m.Post("/activate", auth.ActivatePost)
|
||||
m.Any("/activate_email", auth.ActivateEmail)
|
||||
m.Get("/avatar/{username}/{size}", user.AvatarByUserName)
|
||||
m.Get("/recover_account", auth.ResetPasswd)
|
||||
@@ -383,7 +383,7 @@ func RegisterRoutes(m *web.Route) {
|
||||
m.Get("/forgot_password", auth.ForgotPasswd)
|
||||
m.Post("/forgot_password", auth.ForgotPasswdPost)
|
||||
m.Post("/logout", auth.SignOut)
|
||||
m.Get("/task/{task}", user.TaskStatus)
|
||||
m.Get("/task/{task}", reqSignIn, user.TaskStatus)
|
||||
})
|
||||
// ***** END: User *****
|
||||
|
||||
|
||||
+39
-29
@@ -23,19 +23,23 @@ import (
|
||||
// UserSignIn validates user name and password.
|
||||
func UserSignIn(username, password string) (*user_model.User, *auth.Source, error) {
|
||||
var user *user_model.User
|
||||
isEmail := false
|
||||
if strings.Contains(username, "@") {
|
||||
isEmail = true
|
||||
emailAddress := user_model.EmailAddress{LowerEmail: strings.ToLower(strings.TrimSpace(username))}
|
||||
// check same email
|
||||
has, err := db.GetEngine(db.DefaultContext).Where("is_activated=?", true).Get(&emailAddress)
|
||||
has, err := db.GetEngine(db.DefaultContext).Get(&emailAddress)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if !has {
|
||||
return nil, nil, user_model.ErrEmailAddressNotExist{
|
||||
Email: username,
|
||||
if has {
|
||||
if !emailAddress.IsActivated {
|
||||
return nil, nil, user_model.ErrEmailAddressNotExist{
|
||||
Email: username,
|
||||
}
|
||||
}
|
||||
user = &user_model.User{ID: emailAddress.UID}
|
||||
}
|
||||
user = &user_model.User{ID: emailAddress.UID}
|
||||
} else {
|
||||
trimmedUsername := strings.TrimSpace(username)
|
||||
if len(trimmedUsername) == 0 {
|
||||
@@ -45,38 +49,40 @@ func UserSignIn(username, password string) (*user_model.User, *auth.Source, erro
|
||||
user = &user_model.User{LowerName: strings.ToLower(trimmedUsername)}
|
||||
}
|
||||
|
||||
hasUser, err := user_model.GetUser(user)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if hasUser {
|
||||
source, err := auth.GetSourceByID(user.LoginSource)
|
||||
if user != nil {
|
||||
hasUser, err := user_model.GetUser(user)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if !source.IsActive {
|
||||
return nil, nil, oauth2.ErrAuthSourceNotActived
|
||||
}
|
||||
if hasUser {
|
||||
source, err := auth.GetSourceByID(user.LoginSource)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
authenticator, ok := source.Cfg.(PasswordAuthenticator)
|
||||
if !ok {
|
||||
return nil, nil, smtp.ErrUnsupportedLoginType
|
||||
}
|
||||
if !source.IsActive {
|
||||
return nil, nil, oauth2.ErrAuthSourceNotActived
|
||||
}
|
||||
|
||||
user, err := authenticator.Authenticate(user, user.LoginName, password)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
authenticator, ok := source.Cfg.(PasswordAuthenticator)
|
||||
if !ok {
|
||||
return nil, nil, smtp.ErrUnsupportedLoginType
|
||||
}
|
||||
|
||||
// WARN: DON'T check user.IsActive, that will be checked on reqSign so that
|
||||
// user could be hint to resend confirm email.
|
||||
if user.ProhibitLogin {
|
||||
return nil, nil, user_model.ErrUserProhibitLogin{UID: user.ID, Name: user.Name}
|
||||
}
|
||||
user, err := authenticator.Authenticate(user, user.LoginName, password)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return user, source, nil
|
||||
// WARN: DON'T check user.IsActive, that will be checked on reqSign so that
|
||||
// user could be hint to resend confirm email.
|
||||
if user.ProhibitLogin {
|
||||
return nil, nil, user_model.ErrUserProhibitLogin{UID: user.ID, Name: user.Name}
|
||||
}
|
||||
|
||||
return user, source, nil
|
||||
}
|
||||
}
|
||||
|
||||
sources, err := auth.AllActiveSources()
|
||||
@@ -111,5 +117,9 @@ func UserSignIn(username, password string) (*user_model.User, *auth.Source, erro
|
||||
}
|
||||
}
|
||||
|
||||
if isEmail {
|
||||
return nil, nil, user_model.ErrEmailAddressNotExist{Email: username}
|
||||
}
|
||||
|
||||
return nil, nil, user_model.ErrUserNotExist{Name: username}
|
||||
}
|
||||
|
||||
@@ -582,6 +582,31 @@ func (f *MergePullRequestForm) Validate(req *http.Request, errs binding.Errors)
|
||||
return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
|
||||
}
|
||||
|
||||
// SetDefaults if not provided for mergestyle and commit message
|
||||
func (f *MergePullRequestForm) SetDefaults(pr *models.PullRequest) (err error) {
|
||||
if f.Do == "" {
|
||||
f.Do = "merge"
|
||||
}
|
||||
|
||||
f.MergeTitleField = strings.TrimSpace(f.MergeTitleField)
|
||||
if len(f.MergeTitleField) == 0 {
|
||||
switch f.Do {
|
||||
case "merge", "rebase-merge":
|
||||
f.MergeTitleField, err = pr.GetDefaultMergeMessage()
|
||||
case "squash":
|
||||
f.MergeTitleField, err = pr.GetDefaultSquashMessage()
|
||||
}
|
||||
}
|
||||
|
||||
f.MergeMessageField = strings.TrimSpace(f.MergeMessageField)
|
||||
if len(f.MergeMessageField) > 0 {
|
||||
f.MergeTitleField += "\n\n" + f.MergeMessageField
|
||||
f.MergeMessageField = ""
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// CodeCommentForm form for adding code comments for PRs
|
||||
type CodeCommentForm struct {
|
||||
Origin string `binding:"Required;In(timeline,diff)"`
|
||||
|
||||
@@ -39,7 +39,13 @@ func UpdateAddress(m *repo_model.Mirror, addr string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = git.NewCommand("remote", "add", remoteName, "--mirror=fetch", addr).RunInDir(repoPath)
|
||||
cmd := git.NewCommand("remote", "add", remoteName, "--mirror=fetch", addr)
|
||||
if strings.Contains(addr, "://") && strings.Contains(addr, "@") {
|
||||
cmd.SetDescription(fmt.Sprintf("remote add %s --mirror=fetch %s [repo_path: %s]", remoteName, util.NewStringURLSanitizer(addr, true).Replace(addr), repoPath))
|
||||
} else {
|
||||
cmd.SetDescription(fmt.Sprintf("remote add %s --mirror=fetch %s [repo_path: %s]", remoteName, addr, repoPath))
|
||||
}
|
||||
_, err = cmd.RunInDir(repoPath)
|
||||
if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") {
|
||||
return err
|
||||
}
|
||||
@@ -53,7 +59,13 @@ func UpdateAddress(m *repo_model.Mirror, addr string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = git.NewCommand("remote", "add", remoteName, "--mirror=fetch", wikiRemotePath).RunInDir(wikiPath)
|
||||
cmd = git.NewCommand("remote", "add", remoteName, "--mirror=fetch", wikiRemotePath)
|
||||
if strings.Contains(wikiRemotePath, "://") && strings.Contains(wikiRemotePath, "@") {
|
||||
cmd.SetDescription(fmt.Sprintf("remote add %s --mirror=fetch %s [repo_path: %s]", remoteName, util.NewStringURLSanitizer(wikiRemotePath, true).Replace(wikiRemotePath), wikiPath))
|
||||
} else {
|
||||
cmd.SetDescription(fmt.Sprintf("remote add %s --mirror=fetch %s [repo_path: %s]", remoteName, wikiRemotePath, wikiPath))
|
||||
}
|
||||
_, err = cmd.RunInDir(wikiPath)
|
||||
if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") {
|
||||
return err
|
||||
}
|
||||
@@ -150,8 +162,8 @@ func pruneBrokenReferences(ctx context.Context,
|
||||
timeout time.Duration,
|
||||
stdoutBuilder, stderrBuilder *strings.Builder,
|
||||
sanitizer *strings.Replacer,
|
||||
isWiki bool) error {
|
||||
|
||||
isWiki bool,
|
||||
) error {
|
||||
wiki := ""
|
||||
if isWiki {
|
||||
wiki = "Wiki "
|
||||
@@ -188,6 +200,7 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo
|
||||
timeout := time.Duration(setting.Git.Timeout.Mirror) * time.Second
|
||||
|
||||
log.Trace("SyncMirrors [repo: %-v]: running git remote update...", m.Repo)
|
||||
|
||||
gitArgs := []string{"remote", "update"}
|
||||
if m.EnablePrune {
|
||||
gitArgs = append(gitArgs, "--prune")
|
||||
@@ -250,7 +263,11 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo
|
||||
}
|
||||
output := stderrBuilder.String()
|
||||
|
||||
gitRepo, err := git.OpenRepository(repoPath)
|
||||
if err := git.WriteCommitGraph(ctx, repoPath); err != nil {
|
||||
log.Error("SyncMirrors [repo: %-v]: %v", m.Repo, err)
|
||||
}
|
||||
|
||||
gitRepo, err := git.OpenRepositoryCtx(ctx, repoPath)
|
||||
if err != nil {
|
||||
log.Error("SyncMirrors [repo: %-v]: failed to OpenRepository: %v", m.Repo, err)
|
||||
return nil, false
|
||||
@@ -332,6 +349,10 @@ func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bo
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
if err := git.WriteCommitGraph(ctx, wikiPath); err != nil {
|
||||
log.Error("SyncMirrors [repo: %-v]: %v", m.Repo, err)
|
||||
}
|
||||
}
|
||||
log.Trace("SyncMirrors [repo: %-v Wiki]: git remote update complete", m.Repo)
|
||||
}
|
||||
@@ -375,6 +396,9 @@ func SyncPullMirror(ctx context.Context, repoID int64) bool {
|
||||
log.Trace("SyncMirrors [repo: %-v]: Running Sync", m.Repo)
|
||||
results, ok := runSync(ctx, m)
|
||||
if !ok {
|
||||
if err = repo_model.TouchMirror(ctx, m); err != nil {
|
||||
log.Error("SyncMirrors [repo: %-v]: failed to TouchMirror: %v", m.Repo, err)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
@@ -28,7 +29,13 @@ var stripExitStatus = regexp.MustCompile(`exit status \d+ - `)
|
||||
// AddPushMirrorRemote registers the push mirror remote.
|
||||
func AddPushMirrorRemote(m *repo_model.PushMirror, addr string) error {
|
||||
addRemoteAndConfig := func(addr, path string) error {
|
||||
if _, err := git.NewCommand("remote", "add", "--mirror=push", m.RemoteName, addr).RunInDir(path); err != nil {
|
||||
cmd := git.NewCommand("remote", "add", "--mirror=push", m.RemoteName, addr)
|
||||
if strings.Contains(addr, "://") && strings.Contains(addr, "@") {
|
||||
cmd.SetDescription(fmt.Sprintf("remote add %s --mirror=push %s [repo_path: %s]", m.RemoteName, util.NewStringURLSanitizer(addr, true).Replace(addr), path))
|
||||
} else {
|
||||
cmd.SetDescription(fmt.Sprintf("remote add %s --mirror=push %s [repo_path: %s]", m.RemoteName, addr, path))
|
||||
}
|
||||
if _, err := cmd.RunInDir(path); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := git.NewCommand("config", "--add", "remote."+m.RemoteName+".push", "+refs/heads/*:refs/heads/*").RunInDir(path); err != nil {
|
||||
|
||||
@@ -7,6 +7,7 @@ package pull
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
@@ -24,11 +25,22 @@ import (
|
||||
"code.gitea.io/gitea/modules/queue"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
asymkey_service "code.gitea.io/gitea/services/asymkey"
|
||||
)
|
||||
|
||||
// prQueue represents a queue to handle update pull request tests
|
||||
var prQueue queue.UniqueQueue
|
||||
|
||||
var (
|
||||
ErrIsClosed = errors.New("pull is cosed")
|
||||
ErrUserNotAllowedToMerge = errors.New("user not allowed to merge")
|
||||
ErrHasMerged = errors.New("has already been merged")
|
||||
ErrIsWorkInProgress = errors.New("work in progress PRs cannot be merged")
|
||||
ErrIsChecking = errors.New("cannot merge while conflict checking is in progress")
|
||||
ErrNotMergableState = errors.New("not in mergeable state")
|
||||
ErrDependenciesLeft = errors.New("is blocked by an open dependency")
|
||||
)
|
||||
|
||||
// AddToTaskQueue adds itself to pull request test task queue.
|
||||
func AddToTaskQueue(pr *models.PullRequest) {
|
||||
err := prQueue.PushFunc(strconv.FormatInt(pr.ID, 10), func() error {
|
||||
@@ -46,6 +58,83 @@ func AddToTaskQueue(pr *models.PullRequest) {
|
||||
}
|
||||
}
|
||||
|
||||
// CheckPullMergable check if the pull mergable based on all conditions (branch protection, merge options, ...)
|
||||
func CheckPullMergable(ctx context.Context, doer *user_model.User, perm *models.Permission, pr *models.PullRequest, manuallMerge, force bool) error {
|
||||
if pr.HasMerged {
|
||||
return ErrHasMerged
|
||||
}
|
||||
|
||||
if err := pr.LoadIssue(); err != nil {
|
||||
return err
|
||||
} else if pr.Issue.IsClosed {
|
||||
return ErrIsClosed
|
||||
}
|
||||
|
||||
if allowedMerge, err := IsUserAllowedToMerge(pr, *perm, doer); err != nil {
|
||||
return err
|
||||
} else if !allowedMerge {
|
||||
return ErrUserNotAllowedToMerge
|
||||
}
|
||||
|
||||
if manuallMerge {
|
||||
// don't check rules to "auto merge", doer is going to mark this pull as merged manually
|
||||
return nil
|
||||
}
|
||||
|
||||
if pr.IsWorkInProgress() {
|
||||
return ErrIsWorkInProgress
|
||||
}
|
||||
|
||||
if !pr.CanAutoMerge() {
|
||||
return ErrNotMergableState
|
||||
}
|
||||
|
||||
if pr.IsChecking() {
|
||||
return ErrIsChecking
|
||||
}
|
||||
|
||||
if err := CheckPRReadyToMerge(pr, false); err != nil {
|
||||
if models.IsErrNotAllowedToMerge(err) {
|
||||
if force {
|
||||
if isRepoAdmin, err := models.IsUserRepoAdmin(pr.BaseRepo, doer); err != nil {
|
||||
return err
|
||||
} else if !isRepoAdmin {
|
||||
return ErrUserNotAllowedToMerge
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := isSignedIfRequired(ctx, pr, doer); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if noDeps, err := models.IssueNoDependenciesLeft(pr.Issue); err != nil {
|
||||
return err
|
||||
} else if !noDeps {
|
||||
return ErrDependenciesLeft
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// isSignedIfRequired check if merge will be signed if required
|
||||
func isSignedIfRequired(ctx context.Context, pr *models.PullRequest, doer *user_model.User) (bool, error) {
|
||||
if err := pr.LoadProtectedBranch(); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if pr.ProtectedBranch == nil || !pr.ProtectedBranch.RequireSignedCommits {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
sign, _, _, err := asymkey_service.SignMerge(pr, doer, pr.BaseRepo.RepoPath(), pr.BaseBranch, pr.GetGitRefName())
|
||||
|
||||
return sign, err
|
||||
}
|
||||
|
||||
// checkAndUpdateStatus checks if pull request is possible to leaving checking status,
|
||||
// and set to be either conflict or mergeable.
|
||||
func checkAndUpdateStatus(pr *models.PullRequest) {
|
||||
|
||||
+2
-17
@@ -164,13 +164,13 @@ func rawMerge(pr *models.PullRequest, doer *user_model.User, mergeStyle repo_mod
|
||||
}
|
||||
|
||||
infoPath := filepath.Join(tmpBasePath, ".git", "info")
|
||||
if err := os.MkdirAll(infoPath, 0700); err != nil {
|
||||
if err := os.MkdirAll(infoPath, 0o700); err != nil {
|
||||
log.Error("Unable to create .git/info in %s: %v", tmpBasePath, err)
|
||||
return "", fmt.Errorf("Unable to create .git/info in tmpBasePath: %v", err)
|
||||
}
|
||||
|
||||
sparseCheckoutListPath := filepath.Join(infoPath, "sparse-checkout")
|
||||
if err := os.WriteFile(sparseCheckoutListPath, []byte(sparseCheckoutList), 0600); err != nil {
|
||||
if err := os.WriteFile(sparseCheckoutListPath, []byte(sparseCheckoutList), 0o600); err != nil {
|
||||
log.Error("Unable to write .git/info/sparse-checkout file in %s: %v", tmpBasePath, err)
|
||||
return "", fmt.Errorf("Unable to write .git/info/sparse-checkout file in tmpBasePath: %v", err)
|
||||
}
|
||||
@@ -561,21 +561,6 @@ func getDiffTree(repoPath, baseBranch, headBranch string) (string, error) {
|
||||
return out.String(), nil
|
||||
}
|
||||
|
||||
// IsSignedIfRequired check if merge will be signed if required
|
||||
func IsSignedIfRequired(pr *models.PullRequest, doer *user_model.User) (bool, error) {
|
||||
if err := pr.LoadProtectedBranch(); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if pr.ProtectedBranch == nil || !pr.ProtectedBranch.RequireSignedCommits {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
sign, _, _, err := asymkey_service.SignMerge(pr, doer, pr.BaseRepo.RepoPath(), pr.BaseBranch, pr.GetGitRefName())
|
||||
|
||||
return sign, err
|
||||
}
|
||||
|
||||
// IsUserAllowedToMerge check if user is allowed to merge PR with given permissions and branch protections
|
||||
func IsUserAllowedToMerge(pr *models.PullRequest, p models.Permission, user *user_model.User) (bool, error) {
|
||||
if user == nil {
|
||||
|
||||
@@ -69,7 +69,7 @@ func GetBranches(repo *repo_model.Repository, skip, limit int) ([]*git.Branch, i
|
||||
|
||||
// checkBranchName validates branch name with existing repository branches
|
||||
func checkBranchName(ctx context.Context, repo *repo_model.Repository, name string) error {
|
||||
_, err := git.WalkReferences(ctx, repo.RepoPath(), func(refName string) error {
|
||||
_, err := git.WalkReferences(ctx, repo.RepoPath(), func(_, refName string) error {
|
||||
branchRefName := strings.TrimPrefix(refName, git.BranchPrefix)
|
||||
switch {
|
||||
case branchRefName == name:
|
||||
|
||||
@@ -148,6 +148,8 @@ func Deliver(t *webhook_model.HookTask) error {
|
||||
t.Delivered = time.Now().UnixNano()
|
||||
if t.IsSucceed {
|
||||
log.Trace("Hook delivered: %s", t.UUID)
|
||||
} else if !w.IsActive {
|
||||
log.Trace("Hook delivery skipped as webhook is inactive: %s", t.UUID)
|
||||
} else {
|
||||
log.Trace("Hook delivery failed: %s", t.UUID)
|
||||
}
|
||||
@@ -172,6 +174,10 @@ func Deliver(t *webhook_model.HookTask) error {
|
||||
return fmt.Errorf("webhook task skipped (webhooks disabled): [%d]", t.ID)
|
||||
}
|
||||
|
||||
if !w.IsActive {
|
||||
return nil
|
||||
}
|
||||
|
||||
resp, err := webhookHTTPClient.Do(req.WithContext(graceful.GetManager().ShutdownContext()))
|
||||
if err != nil {
|
||||
t.ResponseInfo.Body = fmt.Sprintf("Delivery: %v", err)
|
||||
|
||||
@@ -212,7 +212,7 @@ func prepareWebhooks(repo *repo_model.Repository, event webhook_model.HookEventT
|
||||
}
|
||||
|
||||
// Add any admin-defined system webhooks
|
||||
systemHooks, err := webhook_model.GetSystemWebhooks()
|
||||
systemHooks, err := webhook_model.GetSystemWebhooks(util.OptionalBoolTrue)
|
||||
if err != nil {
|
||||
return fmt.Errorf("GetSystemWebhooks: %v", err)
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<footer>
|
||||
<div class="ui container">
|
||||
<div class="ui left">
|
||||
{{.i18n.Tr "powered_by" "Gitea"}} {{if (or .ShowFooterVersion .PageIsAdmin)}}{{.i18n.Tr "version"}}: {{AppVer}}{{end}} {{if ShowFooterTemplateLoadTime}}{{.i18n.Tr "page"}}: <strong>{{LoadTimes .PageStartTime}}</strong> {{.i18n.Tr "template"}}: <strong>{{call .TmplLoadTimes}}</strong>{{end}}
|
||||
{{.i18n.Tr "powered_by" "Gitea"}} {{if (or .ShowFooterVersion .PageIsAdmin)}}{{.i18n.Tr "version"}}: {{AppVer}}{{end}} {{if and .TmplLoadTimes ShowFooterTemplateLoadTime}}{{.i18n.Tr "page"}}: <strong>{{LoadTimes .PageStartTime}}</strong> {{.i18n.Tr "template"}}: <strong>{{call .TmplLoadTimes}}</strong>{{end}}
|
||||
</div>
|
||||
<div class="ui right links">
|
||||
{{if .ShowFooterBranding}}
|
||||
|
||||
@@ -32,7 +32,6 @@
|
||||
<td class="chroma lines-code blob-hunk">{{/*
|
||||
*/}}<code {{if $inlineDiff.EscapeStatus.Escaped}}class="code-inner has-escaped" title="{{$.root.i18n.Tr "repo.line_unicode"}}"{{else}}class="code-inner"{{end}}>{{$inlineDiff.Content}}</code>{{/*
|
||||
*/}}
|
||||
{{$line.Content}}
|
||||
</td>
|
||||
{{else}}
|
||||
<td class="chroma lines-code{{if (not $line.RightIdx)}} lines-code-old{{end}}">{{/*
|
||||
|
||||
@@ -50,14 +50,17 @@ git push -u origin {{.Repository.DefaultBranch}}</code></pre>
|
||||
git push -u origin {{.Repository.DefaultBranch}}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
<script defer>
|
||||
/* eslint-disable no-undef */
|
||||
const cloneUrls = document.getElementsByClassName('clone-url');
|
||||
if (cloneUrls) {
|
||||
for (let i = 0; i < cloneUrls.length; i++) {
|
||||
cloneUrls[i].textContent = (isSSH ? sshButton : httpsButton).getAttribute('data-link');
|
||||
<script>
|
||||
(() => {
|
||||
const proto = localStorage.getItem('repo-clone-protocol') || 'https';
|
||||
const btn = document.getElementById(`repo-clone-${proto}`) || document.getElementById(`repo-clone-https`) || document.getElementById(`repo-clone-ssh`);
|
||||
if (btn) {
|
||||
const cloneUrls = document.getElementsByClassName('clone-url');
|
||||
for (let i = 0; i < cloneUrls.length; i++) {
|
||||
cloneUrls[i].textContent = btn.getAttribute('data-link');
|
||||
}
|
||||
}
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
{{end}}
|
||||
{{else}}
|
||||
|
||||
@@ -14757,6 +14757,11 @@
|
||||
"type": "string",
|
||||
"x-go-name": "Description"
|
||||
},
|
||||
"enable_prune": {
|
||||
"description": "enable prune - remove obsolete remote-tracking references",
|
||||
"type": "boolean",
|
||||
"x-go-name": "EnablePrune"
|
||||
},
|
||||
"external_tracker": {
|
||||
"$ref": "#/definitions/ExternalTracker"
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user