Compare commits
56 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 | |||
| bab7d885aa | |||
| 42229dc0b8 | |||
| e3d8e92bdc | |||
| 6fc73a8433 | |||
| b1a0a78a51 | |||
| 9c7d8b3096 | |||
| 93feb1a666 | |||
| bb0e2121a3 | |||
| d21b7fd3af | |||
| 743553f3e9 | |||
| a3ccbb5b7f | |||
| 4b7cb813e6 | |||
| 23b8214549 | |||
| 08feb6b664 | |||
| 1aa5dc75df | |||
| ee234aff61 | |||
| a3f3e310fb | |||
| ea56bdca5f | |||
| 45c836badc | |||
| f9ea4ab69a | |||
| e6d46eeb55 | |||
| 5bb0c92b6c | |||
| c1e6be47d7 | |||
| 79a5e68816 | |||
| 9bcbbd419f |
+55
-46
@@ -25,7 +25,7 @@ steps:
|
||||
- make deps-frontend
|
||||
|
||||
- name: deps-backend
|
||||
image: golang:1.17
|
||||
image: golang:1.18
|
||||
pull: always
|
||||
commands:
|
||||
- make deps-backend
|
||||
@@ -45,32 +45,41 @@ 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]
|
||||
volumes:
|
||||
- name: deps
|
||||
path: /go
|
||||
|
||||
- name: lint-backend-windows
|
||||
image: gitea/test_env:linux-amd64 # https://gitea.com/gitea/test-env
|
||||
commands:
|
||||
- make golangci-lint vet
|
||||
- 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
|
||||
GOARCH: amd64
|
||||
depends_on: [deps-backend]
|
||||
volumes:
|
||||
- name: deps
|
||||
path: /go
|
||||
|
||||
- name: lint-backend-gogit
|
||||
image: gitea/test_env:linux-amd64 # https://gitea.com/gitea/test-env
|
||||
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]
|
||||
volumes:
|
||||
- name: deps
|
||||
path: /go
|
||||
|
||||
- name: checks-frontend
|
||||
image: node:16
|
||||
@@ -79,7 +88,7 @@ steps:
|
||||
depends_on: [deps-frontend]
|
||||
|
||||
- name: checks-backend
|
||||
image: golang:1.17
|
||||
image: golang:1.18
|
||||
commands:
|
||||
- make checks-backend
|
||||
depends_on: [deps-backend]
|
||||
@@ -104,7 +113,7 @@ steps:
|
||||
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]
|
||||
@@ -113,10 +122,10 @@ steps:
|
||||
path: /go
|
||||
|
||||
- name: build-backend-arm64
|
||||
image: golang:1.17
|
||||
image: golang:1.18
|
||||
environment:
|
||||
GO111MODULE: on
|
||||
GOPROXY: https://goproxy.cn
|
||||
GOPROXY: https://goproxy.io
|
||||
GOOS: linux
|
||||
GOARCH: arm64
|
||||
TAGS: bindata gogit
|
||||
@@ -129,10 +138,10 @@ steps:
|
||||
path: /go
|
||||
|
||||
- name: build-backend-windows
|
||||
image: golang:1.17
|
||||
image: golang:1.18
|
||||
environment:
|
||||
GO111MODULE: on
|
||||
GOPROXY: https://goproxy.cn
|
||||
GOPROXY: https://goproxy.io
|
||||
GOOS: windows
|
||||
GOARCH: amd64
|
||||
TAGS: bindata gogit
|
||||
@@ -144,10 +153,10 @@ steps:
|
||||
path: /go
|
||||
|
||||
- name: build-backend-386
|
||||
image: golang:1.17
|
||||
image: golang:1.18
|
||||
environment:
|
||||
GO111MODULE: on
|
||||
GOPROXY: https://goproxy.cn
|
||||
GOPROXY: https://goproxy.io
|
||||
GOOS: linux
|
||||
GOARCH: 386
|
||||
commands:
|
||||
@@ -233,7 +242,7 @@ steps:
|
||||
- pull_request
|
||||
|
||||
- name: deps-backend
|
||||
image: golang:1.17
|
||||
image: golang:1.18
|
||||
pull: always
|
||||
commands:
|
||||
- make deps-backend
|
||||
@@ -260,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]
|
||||
@@ -274,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:
|
||||
@@ -290,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:
|
||||
@@ -306,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
|
||||
@@ -323,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
|
||||
@@ -339,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
|
||||
@@ -350,11 +359,11 @@ steps:
|
||||
path: /go
|
||||
|
||||
- name: generate-coverage
|
||||
image: golang:1.17
|
||||
image: golang:1.18
|
||||
commands:
|
||||
- make coverage
|
||||
environment:
|
||||
GOPROXY: https://goproxy.cn
|
||||
GOPROXY: https://goproxy.io
|
||||
TAGS: bindata
|
||||
depends_on: [unit-test, test-mysql]
|
||||
when:
|
||||
@@ -425,7 +434,7 @@ steps:
|
||||
- pull_request
|
||||
|
||||
- name: deps-backend
|
||||
image: golang:1.17
|
||||
image: golang:1.18
|
||||
pull: always
|
||||
commands:
|
||||
- make deps-backend
|
||||
@@ -446,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]
|
||||
@@ -460,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
|
||||
@@ -476,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
|
||||
@@ -567,7 +576,7 @@ trigger:
|
||||
|
||||
steps:
|
||||
- name: download
|
||||
image: golang:1.17
|
||||
image: golang:1.18
|
||||
pull: always
|
||||
commands:
|
||||
- timeout -s ABRT 40m make generate-license generate-gitignore
|
||||
@@ -628,7 +637,7 @@ steps:
|
||||
- make deps-frontend
|
||||
|
||||
- name: deps-backend
|
||||
image: golang:1.17
|
||||
image: golang:1.18
|
||||
pull: always
|
||||
commands:
|
||||
- make deps-backend
|
||||
@@ -637,14 +646,14 @@ steps:
|
||||
path: /go
|
||||
|
||||
- name: static
|
||||
image: techknowlogick/xgo:go-1.17.x
|
||||
image: techknowlogick/xgo:go-1.18.x
|
||||
pull: always
|
||||
commands:
|
||||
- curl -sL https://deb.nodesource.com/setup_16.x | bash - && apt-get install -y nodejs
|
||||
- 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
|
||||
@@ -746,7 +755,7 @@ steps:
|
||||
- make deps-frontend
|
||||
|
||||
- name: deps-backend
|
||||
image: golang:1.17
|
||||
image: golang:1.18
|
||||
pull: always
|
||||
commands:
|
||||
- make deps-backend
|
||||
@@ -755,14 +764,14 @@ steps:
|
||||
path: /go
|
||||
|
||||
- name: static
|
||||
image: techknowlogick/xgo:go-1.17.x
|
||||
image: techknowlogick/xgo:go-1.18.x
|
||||
pull: always
|
||||
commands:
|
||||
- curl -sL https://deb.nodesource.com/setup_16.x | bash - && apt-get install -y nodejs
|
||||
- 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:
|
||||
@@ -892,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:
|
||||
@@ -910,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:
|
||||
@@ -955,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:
|
||||
@@ -973,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:
|
||||
@@ -1017,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:
|
||||
@@ -1035,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:
|
||||
@@ -1070,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
|
||||
@@ -1113,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:
|
||||
@@ -1131,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:
|
||||
@@ -1176,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:
|
||||
@@ -1194,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:
|
||||
@@ -1238,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:
|
||||
@@ -1256,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:
|
||||
|
||||
+13
-2
@@ -13,7 +13,7 @@ linters:
|
||||
#- gocyclo # The cyclomatic complexety of a lot of functions is too high, we should refactor those another time.
|
||||
- gofmt
|
||||
- misspell
|
||||
- gocritic
|
||||
#- gocritic # TODO: disabled until fixed with go 1.18
|
||||
- bidichk
|
||||
- ineffassign
|
||||
- revive
|
||||
@@ -22,7 +22,7 @@ linters:
|
||||
fast: false
|
||||
|
||||
run:
|
||||
timeout: 3m
|
||||
timeout: 10m
|
||||
skip-dirs:
|
||||
- node_modules
|
||||
- public
|
||||
@@ -61,6 +61,9 @@ linters-settings:
|
||||
- name: errorf
|
||||
- name: duplicated-imports
|
||||
- name: modifies-value-receiver
|
||||
gofumpt:
|
||||
extra-rules: true
|
||||
lang-version: 1.18
|
||||
|
||||
issues:
|
||||
exclude-rules:
|
||||
@@ -148,3 +151,11 @@ issues:
|
||||
- path: models/user/openid.go
|
||||
linters:
|
||||
- golint
|
||||
- linters: staticcheck
|
||||
text: "strings.Title is deprecated: The rule Title uses for word boundaries does not handle Unicode punctuation properly. Use golang.org/x/text/cases instead."
|
||||
- linters: staticcheck
|
||||
text: "util.FindClosure is deprecated: This function can not handle newlines. Many elements can be existed over multiple lines(e.g. link labels). Use text.Reader.FindClosure."
|
||||
- linters: staticcheck
|
||||
text: "gossh.SigAlgoRSASHA2256 is deprecated: use KeyAlgoRSASHA256."
|
||||
- linters: staticcheck
|
||||
text: "gossh.SigAlgoRSASHA2512 is deprecated: use KeyAlgoRSASHA512."
|
||||
|
||||
@@ -4,6 +4,72 @@ 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.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)
|
||||
* SECURITY
|
||||
* Prevent redirect to Host (2) (#19175) (#19186)
|
||||
* Try to prevent autolinking of displaynames by email readers (#19169) (#19183)
|
||||
* Clean paths when looking in Storage (#19124) (#19179)
|
||||
* Do not send notification emails to inactive users (#19131) (#19139)
|
||||
* Do not send activation email if manual confirm is set (#19119) (#19122)
|
||||
* ENHANCEMENTS
|
||||
* Use the new/choose link for New Issue on project page (#19172) (#19176)
|
||||
* BUGFIXES
|
||||
* Fix showing issues in your repositories (#18916) (#19191)
|
||||
* Fix compare link in active feeds for new branch (#19149) (#19185)
|
||||
* Redirect .wiki/* ui link to /wiki (#18831) (#19184)
|
||||
* Ensure deploy keys with write access can push (#19010) (#19182)
|
||||
* Ensure that setting.LocalURL always has a trailing slash (#19171) (#19177)
|
||||
* Cleanup protected branches when deleting users & teams (#19158) (#19174)
|
||||
* Use IterateBufferSize whilst querying repositories during adoption check (#19140) (#19160)
|
||||
* Fix NPE /repos/issues/search when not signed in (#19154) (#19155)
|
||||
* Use custom favicon when viewing static files if it exists (#19130) (#19152)
|
||||
* Fix the editor height in review box (#19003) (#19147)
|
||||
* Ensure isSSH is set whenever DISABLE_HTTP_GIT is set (#19028) (#19146)
|
||||
* Fix wrong scopes caused by empty scope input (#19029) (#19145)
|
||||
* Make migrations SKIP_TLS_VERIFY apply to git too (#19132) (#19141)
|
||||
* Handle email address not exist (#19089) (#19121)
|
||||
* MISC
|
||||
* Update json-iterator to allow compilation with go1.18 (#18644) (#19100)
|
||||
* Update golang.org/x/crypto (#19097) (#19098)
|
||||
|
||||
## [1.16.4](https://github.com/go-gitea/gitea/releases/tag/v1.16.4) - 2022-03-14
|
||||
|
||||
* SECURITY
|
||||
|
||||
+2
-4
@@ -1,7 +1,5 @@
|
||||
|
||||
###################################
|
||||
#Build stage - temporarily using techknowlogick image until we upgrade to latest official alpine/go image
|
||||
FROM techknowlogick/go:1.17-alpine3.13 AS build-env
|
||||
#Build stage
|
||||
FROM golang:1.18-alpine3.15 AS build-env
|
||||
|
||||
ARG GOPROXY
|
||||
ENV GOPROXY ${GOPROXY:-direct}
|
||||
|
||||
+2
-4
@@ -1,7 +1,5 @@
|
||||
|
||||
###################################
|
||||
#Build stage - temporarily using techknowlogick image until we upgrade to latest official alpine/go image
|
||||
FROM techknowlogick/go:1.17-alpine3.13 AS build-env
|
||||
#Build stage
|
||||
FROM golang:1.18-alpine3.15 AS build-env
|
||||
|
||||
ARG GOPROXY
|
||||
ENV GOPROXY ${GOPROXY:-direct}
|
||||
|
||||
@@ -24,10 +24,17 @@ SHASUM ?= shasum -a 256
|
||||
HAS_GO = $(shell hash $(GO) > /dev/null 2>&1 && echo "GO" || echo "NOGO" )
|
||||
COMMA := ,
|
||||
|
||||
XGO_VERSION := go-1.17.x
|
||||
MIN_GO_VERSION := 001016000
|
||||
MIN_NODE_VERSION := 012017000
|
||||
MIN_GOLANGCI_LINT_VERSION := 001043000
|
||||
XGO_VERSION := go-1.18.x
|
||||
|
||||
AIR_PACKAGE ?= github.com/cosmtrek/air@v1.29.0
|
||||
EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/cmd/editorconfig-checker@2.4.0
|
||||
ERRCHECK_PACKAGE ?= github.com/kisielk/errcheck@v1.6.0
|
||||
GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.3.0
|
||||
GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/cmd/golangci-lint@v1.44.2
|
||||
GXZ_PAGAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.10
|
||||
MISSPELL_PACKAGE ?= github.com/client9/misspell/cmd/misspell@v0.3.4
|
||||
SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.29.0
|
||||
XGO_PACKAGE ?= src.techknowlogick.com/xgo@latest
|
||||
|
||||
DOCKER_IMAGE ?= gitea/gitea
|
||||
DOCKER_TAG ?= latest
|
||||
@@ -125,8 +132,6 @@ ifeq ($(filter $(TAGS_SPLIT),bindata),bindata)
|
||||
GO_SOURCES += $(BINDATA_DEST)
|
||||
endif
|
||||
|
||||
#To update swagger use: GO111MODULE=on go get -u github.com/go-swagger/go-swagger/cmd/swagger
|
||||
SWAGGER := $(GO) run github.com/go-swagger/go-swagger/cmd/swagger
|
||||
SWAGGER_SPEC := templates/swagger/v1_json.tmpl
|
||||
SWAGGER_SPEC_S_TMPL := s|"basePath": *"/api/v1"|"basePath": "{{AppSubUrl \| JSEscape \| Safe}}/api/v1"|g
|
||||
SWAGGER_SPEC_S_JSON := s|"basePath": *"{{AppSubUrl \| JSEscape \| Safe}}/api/v1"|"basePath": "/api/v1"|g
|
||||
@@ -196,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
|
||||
@@ -211,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
|
||||
|
||||
@@ -234,8 +245,8 @@ clean:
|
||||
|
||||
.PHONY: fmt
|
||||
fmt:
|
||||
@echo "Running gitea-fmt(with gofmt)..."
|
||||
@$(GO) run build/code-batch-process.go gitea-fmt -s -w '{file-list}'
|
||||
@echo "Running gitea-fmt (with gofumpt)..."
|
||||
@MISSPELL_PACKAGE=$(MISSPELL_PACKAGE) GOFUMPT_PACKAGE=$(GOFUMPT_PACKAGE) $(GO) run build/code-batch-process.go gitea-fmt -w '{file-list}'
|
||||
|
||||
.PHONY: vet
|
||||
vet:
|
||||
@@ -254,7 +265,7 @@ endif
|
||||
|
||||
.PHONY: generate-swagger
|
||||
generate-swagger:
|
||||
$(SWAGGER) generate spec -x "$(SWAGGER_EXCLUDE)" -o './$(SWAGGER_SPEC)'
|
||||
$(GO) run $(SWAGGER_PACKAGE) generate spec -x "$(SWAGGER_EXCLUDE)" -o './$(SWAGGER_SPEC)'
|
||||
$(SED_INPLACE) '$(SWAGGER_SPEC_S_TMPL)' './$(SWAGGER_SPEC)'
|
||||
$(SED_INPLACE) $(SWAGGER_NEWLINE_COMMAND) './$(SWAGGER_SPEC)'
|
||||
|
||||
@@ -270,21 +281,18 @@ swagger-check: generate-swagger
|
||||
.PHONY: swagger-validate
|
||||
swagger-validate:
|
||||
$(SED_INPLACE) '$(SWAGGER_SPEC_S_JSON)' './$(SWAGGER_SPEC)'
|
||||
$(SWAGGER) validate './$(SWAGGER_SPEC)'
|
||||
$(GO) run $(SWAGGER_PACKAGE) validate './$(SWAGGER_SPEC)'
|
||||
$(SED_INPLACE) '$(SWAGGER_SPEC_S_TMPL)' './$(SWAGGER_SPEC)'
|
||||
|
||||
.PHONY: errcheck
|
||||
errcheck:
|
||||
@hash errcheck > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
$(GO) install github.com/kisielk/errcheck@8ddee489636a8311a376fc92e27a6a13c6658344; \
|
||||
fi
|
||||
@echo "Running errcheck..."
|
||||
@errcheck $(GO_PACKAGES)
|
||||
$(GO) run $(ERRCHECK_PACKAGE) $(GO_PACKAGES)
|
||||
|
||||
.PHONY: fmt-check
|
||||
fmt-check:
|
||||
# get all go files and run gitea-fmt (with gofmt) on them
|
||||
@diff=$$($(GO) run build/code-batch-process.go gitea-fmt -s -d '{file-list}'); \
|
||||
@diff=$$(MISSPELL_PACKAGE=$(MISSPELL_PACKAGE) GOFUMPT_PACKAGE=$(GOFUMPT_PACKAGE) $(GO) run build/code-batch-process.go gitea-fmt -l '{file-list}'); \
|
||||
if [ -n "$$diff" ]; then \
|
||||
echo "Please run 'make fmt' and commit the result:"; \
|
||||
echo "$${diff}"; \
|
||||
@@ -323,10 +331,7 @@ watch-frontend: node-check node_modules
|
||||
|
||||
.PHONY: watch-backend
|
||||
watch-backend: go-check
|
||||
@hash air > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
$(GO) install github.com/cosmtrek/air@bedc18201271882c2be66d216d0e1a275b526ec4; \
|
||||
fi
|
||||
air -c .air.toml
|
||||
$(GO) run $(AIR_PACKAGE) -c .air.toml
|
||||
|
||||
.PHONY: test
|
||||
test: test-frontend test-backend
|
||||
@@ -599,12 +604,9 @@ $(DIST_DIRS):
|
||||
|
||||
.PHONY: release-windows
|
||||
release-windows: | $(DIST_DIRS)
|
||||
@hash xgo > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
$(GO) install src.techknowlogick.com/xgo@latest; \
|
||||
fi
|
||||
CGO_CFLAGS="$(CGO_CFLAGS)" xgo -go $(XGO_VERSION) -buildmode exe -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'windows/*' -out gitea-$(VERSION) .
|
||||
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -buildmode exe -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'windows/*' -out gitea-$(VERSION) .
|
||||
ifeq (,$(findstring gogit,$(TAGS)))
|
||||
CGO_CFLAGS="$(CGO_CFLAGS)" xgo -go $(XGO_VERSION) -buildmode exe -dest $(DIST)/binaries -tags 'netgo osusergo gogit $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'windows/*' -out gitea-$(VERSION)-gogit .
|
||||
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -buildmode exe -dest $(DIST)/binaries -tags 'netgo osusergo gogit $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets 'windows/*' -out gitea-$(VERSION)-gogit .
|
||||
endif
|
||||
ifeq ($(CI),drone)
|
||||
cp /build/* $(DIST)/binaries
|
||||
@@ -612,20 +614,14 @@ endif
|
||||
|
||||
.PHONY: release-linux
|
||||
release-linux: | $(DIST_DIRS)
|
||||
@hash xgo > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
$(GO) install src.techknowlogick.com/xgo@latest; \
|
||||
fi
|
||||
CGO_CFLAGS="$(CGO_CFLAGS)" xgo -go $(XGO_VERSION) -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets '$(LINUX_ARCHS)' -out gitea-$(VERSION) .
|
||||
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-linkmode external -extldflags "-static" $(LDFLAGS)' -targets '$(LINUX_ARCHS)' -out gitea-$(VERSION) .
|
||||
ifeq ($(CI),drone)
|
||||
cp /build/* $(DIST)/binaries
|
||||
endif
|
||||
|
||||
.PHONY: release-darwin
|
||||
release-darwin: | $(DIST_DIRS)
|
||||
@hash xgo > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
$(GO) install src.techknowlogick.com/xgo@latest; \
|
||||
fi
|
||||
CGO_CFLAGS="$(CGO_CFLAGS)" xgo -go $(XGO_VERSION) -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '$(LDFLAGS)' -targets 'darwin-10.12/amd64,darwin-10.12/arm64' -out gitea-$(VERSION) .
|
||||
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '$(LDFLAGS)' -targets 'darwin-10.12/amd64,darwin-10.12/arm64' -out gitea-$(VERSION) .
|
||||
ifeq ($(CI),drone)
|
||||
cp /build/* $(DIST)/binaries
|
||||
endif
|
||||
@@ -640,10 +636,7 @@ release-check: | $(DIST_DIRS)
|
||||
|
||||
.PHONY: release-compress
|
||||
release-compress: | $(DIST_DIRS)
|
||||
@hash gxz > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
$(GO) install github.com/ulikunitz/xz/cmd/gxz@v0.5.10; \
|
||||
fi
|
||||
cd $(DIST)/release/; for file in `find . -type f -name "*"`; do echo "compressing $${file}" && gxz -k -9 $${file}; done;
|
||||
cd $(DIST)/release/; for file in `find . -type f -name "*"`; do echo "compressing $${file}" && $(GO) run $(GXZ_PAGAGE) -k -9 $${file}; done;
|
||||
|
||||
.PHONY: release-sources
|
||||
release-sources: | $(DIST_DIRS)
|
||||
@@ -673,6 +666,15 @@ deps-frontend: node_modules
|
||||
.PHONY: deps-backend
|
||||
deps-backend:
|
||||
$(GO) mod download
|
||||
$(GO) install $(AIR_PACKAGE)
|
||||
$(GO) install $(EDITORCONFIG_CHECKER_PACKAGE)
|
||||
$(GO) install $(ERRCHECK_PACKAGE)
|
||||
$(GO) install $(GOFUMPT_PACKAGE)
|
||||
$(GO) install $(GOLANGCI_LINT_PACKAGE)
|
||||
$(GO) install $(GXZ_PAGAGE)
|
||||
$(GO) install $(MISSPELL_PACKAGE)
|
||||
$(GO) install $(SWAGGER_PACKAGE)
|
||||
$(GO) install $(XGO_PACKAGE)
|
||||
|
||||
node_modules: package-lock.json
|
||||
npm install --no-save
|
||||
@@ -766,22 +768,19 @@ pr\#%: clean-all
|
||||
$(GO) run contrib/pr/checkout.go $*
|
||||
|
||||
.PHONY: golangci-lint
|
||||
golangci-lint: golangci-lint-check
|
||||
golangci-lint run --timeout 10m
|
||||
golangci-lint:
|
||||
$(GO) run $(GOLANGCI_LINT_PACKAGE) run
|
||||
|
||||
.PHONY: golangci-lint-check
|
||||
golangci-lint-check:
|
||||
$(eval GOLANGCI_LINT_VERSION := $(shell printf "%03d%03d%03d" $(shell golangci-lint --version | grep -Eo '[0-9]+\.[0-9.]+' | tr '.' ' ');))
|
||||
$(eval MIN_GOLANGCI_LINT_VER_FMT := $(shell printf "%g.%g.%g" $(shell echo $(MIN_GOLANGCI_LINT_VERSION) | grep -o ...)))
|
||||
@hash golangci-lint > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
|
||||
echo "Downloading golangci-lint v${MIN_GOLANGCI_LINT_VER_FMT}"; \
|
||||
export BINARY="golangci-lint"; \
|
||||
curl -sfL "https://raw.githubusercontent.com/golangci/golangci-lint/v${MIN_GOLANGCI_LINT_VER_FMT}/install.sh" | sh -s -- -b $(GOPATH)/bin v$(MIN_GOLANGCI_LINT_VER_FMT); \
|
||||
elif [ "$(GOLANGCI_LINT_VERSION)" -lt "$(MIN_GOLANGCI_LINT_VERSION)" ]; then \
|
||||
echo "Downloading newer version of golangci-lint v${MIN_GOLANGCI_LINT_VER_FMT}"; \
|
||||
export BINARY="golangci-lint"; \
|
||||
curl -sfL "https://raw.githubusercontent.com/golangci/golangci-lint/v${MIN_GOLANGCI_LINT_VER_FMT}/install.sh" | sh -s -- -b $(GOPATH)/bin v$(MIN_GOLANGCI_LINT_VER_FMT); \
|
||||
fi
|
||||
# workaround step for the lint-backend-windows CI task because 'go run' can not
|
||||
# have distinct GOOS/GOARCH for its build and run steps
|
||||
.PHONY: golangci-lint-windows
|
||||
golangci-lint-windows:
|
||||
@GOOS= GOARCH= $(GO) install $(GOLANGCI_LINT_PACKAGE)
|
||||
golangci-lint run
|
||||
|
||||
.PHONY: editorconfig-checker
|
||||
editorconfig-checker:
|
||||
$(GO) run $(EDITORCONFIG_CHECKER_PACKAGE) templates
|
||||
|
||||
.PHONY: docker
|
||||
docker:
|
||||
|
||||
@@ -73,7 +73,7 @@ or if SQLite support is required:
|
||||
|
||||
The `build` target is split into two sub-targets:
|
||||
|
||||
- `make backend` which requires [Go 1.16](https://golang.org/dl/) or greater.
|
||||
- `make backend` which requires [Go 1.17](https://go.dev/dl/) or greater.
|
||||
- `make frontend` which requires [Node.js LTS](https://nodejs.org/en/download/) or greater and Internet connectivity to download npm dependencies.
|
||||
|
||||
When building from the official source tarballs which include pre-built frontend files, the `frontend` target will not be triggered, making it possible to build without Node.js and Internet connectivity.
|
||||
|
||||
@@ -40,7 +40,7 @@ func passThroughCmd(cmd string, args []string) error {
|
||||
}
|
||||
c := exec.Cmd{
|
||||
Path: foundCmd,
|
||||
Args: args,
|
||||
Args: append([]string{cmd}, args...),
|
||||
Stdin: os.Stdin,
|
||||
Stdout: os.Stdout,
|
||||
Stderr: os.Stderr,
|
||||
@@ -270,9 +270,10 @@ func main() {
|
||||
if containsString(subArgs, "-w") {
|
||||
cmdErrors = append(cmdErrors, giteaFormatGoImports(files))
|
||||
}
|
||||
cmdErrors = append(cmdErrors, passThroughCmd("gofmt", substArgs))
|
||||
cmdErrors = append(cmdErrors, giteaFormatGoImports(files, containsString(subArgs, "-l"), containsString(subArgs, "-w")))
|
||||
cmdErrors = append(cmdErrors, passThroughCmd("go", append([]string{"run", os.Getenv("GOFUMPT_PACKAGE"), "-extra", "-lang", "1.17"}, substArgs...)))
|
||||
case "misspell":
|
||||
cmdErrors = append(cmdErrors, passThroughCmd("misspell", substArgs))
|
||||
cmdErrors = append(cmdErrors, passThroughCmd("go", append([]string{"run", os.Getenv("MISSPELL_PACKAGE")}, substArgs...)))
|
||||
default:
|
||||
log.Fatalf("unknown cmd: %s %v", subCmd, subArgs)
|
||||
}
|
||||
|
||||
+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()
|
||||
|
||||
+2
-2
@@ -185,7 +185,7 @@ Gitea or set your environment appropriately.`, "")
|
||||
reponame := os.Getenv(models.EnvRepoName)
|
||||
userID, _ := strconv.ParseInt(os.Getenv(models.EnvPusherID), 10, 64)
|
||||
prID, _ := strconv.ParseInt(os.Getenv(models.EnvPRID), 10, 64)
|
||||
isDeployKey, _ := strconv.ParseBool(os.Getenv(models.EnvIsDeployKey))
|
||||
deployKeyID, _ := strconv.ParseInt(os.Getenv(models.EnvDeployKeyID), 10, 64)
|
||||
|
||||
hookOptions := private.HookOptions{
|
||||
UserID: userID,
|
||||
@@ -194,7 +194,7 @@ Gitea or set your environment appropriately.`, "")
|
||||
GitQuarantinePath: os.Getenv(private.GitQuarantinePath),
|
||||
GitPushOptions: pushOptions(),
|
||||
PullRequestID: prID,
|
||||
IsDeployKey: isDeployKey,
|
||||
DeployKeyID: deployKeyID,
|
||||
}
|
||||
|
||||
scanner := bufio.NewScanner(os.Stdin)
|
||||
|
||||
+10
-1
@@ -243,7 +243,7 @@ func runServ(c *cli.Context) error {
|
||||
os.Setenv(models.EnvPusherID, strconv.FormatInt(results.UserID, 10))
|
||||
os.Setenv(models.EnvRepoID, strconv.FormatInt(results.RepoID, 10))
|
||||
os.Setenv(models.EnvPRID, fmt.Sprintf("%d", 0))
|
||||
os.Setenv(models.EnvIsDeployKey, fmt.Sprintf("%t", results.IsDeployKey))
|
||||
os.Setenv(models.EnvDeployKeyID, fmt.Sprintf("%d", results.DeployKeyID))
|
||||
os.Setenv(models.EnvKeyID, fmt.Sprintf("%d", results.KeyID))
|
||||
os.Setenv(models.EnvAppURL, setting.AppURL)
|
||||
|
||||
@@ -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
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
+2
-2
@@ -18,9 +18,9 @@ params:
|
||||
description: Git with a cup of tea
|
||||
author: The Gitea Authors
|
||||
website: https://docs.gitea.io
|
||||
version: 1.16.0
|
||||
version: 1.16.4
|
||||
minGoVersion: 1.16
|
||||
goVersion: 1.17
|
||||
goVersion: 1.18
|
||||
minNodeVersion: 12.17
|
||||
|
||||
outputs:
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -3,7 +3,6 @@ module code.gitea.io/gitea
|
||||
go 1.16
|
||||
|
||||
require (
|
||||
cloud.google.com/go v0.78.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
|
||||
@@ -12,22 +11,13 @@ require (
|
||||
gitea.com/go-chi/session v0.0.0-20211218221615-e3605d8b28b8
|
||||
gitea.com/lunny/levelqueue v0.4.1
|
||||
github.com/42wim/sshsig v0.0.0-20211121163825-841cf5bbc121
|
||||
github.com/Microsoft/go-winio v0.5.0 // indirect
|
||||
github.com/NYTimes/gziphandler v1.1.1
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20210705153151-cc34b1f6908b // indirect
|
||||
github.com/PuerkitoBio/goquery v1.7.0
|
||||
github.com/alecthomas/chroma v0.9.4
|
||||
github.com/andybalholm/brotli v1.0.3 // indirect
|
||||
github.com/andybalholm/cascadia v1.2.0 // indirect
|
||||
github.com/blevesearch/bleve/v2 v2.3.0
|
||||
github.com/boombuler/barcode v1.0.1 // indirect
|
||||
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b // indirect
|
||||
github.com/caddyserver/certmagic v0.15.2
|
||||
github.com/PuerkitoBio/goquery v1.8.0
|
||||
github.com/alecthomas/chroma v0.10.0
|
||||
github.com/blevesearch/bleve/v2 v2.3.1
|
||||
github.com/caddyserver/certmagic v0.15.4
|
||||
github.com/chi-middleware/proxy v1.1.1
|
||||
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/denisenkom/go-mssqldb v0.10.0
|
||||
github.com/denisenkom/go-mssqldb v0.12.0
|
||||
github.com/djherbis/buffer v1.2.0
|
||||
github.com/djherbis/nio/v3 v3.0.1
|
||||
github.com/duo-labs/webauthn v0.0.0-20220122034320-81aea484c951
|
||||
@@ -36,8 +26,7 @@ require (
|
||||
github.com/emirpasic/gods v1.12.0
|
||||
github.com/ethantkoenig/rupture v1.0.0
|
||||
github.com/gliderlabs/ssh v0.3.3
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.3 // indirect
|
||||
github.com/go-chi/chi/v5 v5.0.4
|
||||
github.com/go-chi/chi/v5 v5.0.7
|
||||
github.com/go-chi/cors v1.2.0
|
||||
github.com/go-enry/go-enry/v2 v2.7.1
|
||||
github.com/go-git/go-billy/v5 v5.3.1
|
||||
@@ -51,87 +40,65 @@ require (
|
||||
github.com/gogs/chardet v0.0.0-20191104214054-4b6791f73a28
|
||||
github.com/gogs/cron v0.0.0-20171120032916-9f6c956d3e14
|
||||
github.com/gogs/go-gogs-client v0.0.0-20210131175652-1d7215cd8d85
|
||||
github.com/golang-jwt/jwt/v4 v4.2.0
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.3.0
|
||||
github.com/google/go-github/v39 v39.2.0
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/gorilla/feeds v1.1.1
|
||||
github.com/gorilla/mux v1.8.0 // indirect
|
||||
github.com/gorilla/sessions v1.2.1
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
github.com/hashicorp/go-retryablehttp v0.7.0 // indirect
|
||||
github.com/hashicorp/go-version v1.3.1
|
||||
github.com/hashicorp/go-version v1.4.0
|
||||
github.com/hashicorp/golang-lru v0.5.4
|
||||
github.com/huandu/xstrings v1.3.2
|
||||
github.com/jaytaylor/html2text v0.0.0-20200412013138-3577fbdbcff7
|
||||
github.com/json-iterator/go v1.1.11
|
||||
github.com/json-iterator/go v1.1.12
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
|
||||
github.com/kevinburke/ssh_config v1.1.0 // indirect
|
||||
github.com/keybase/go-crypto v0.0.0-20200123153347-de78d2cb44f4
|
||||
github.com/klauspost/compress v1.13.1
|
||||
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.68.0
|
||||
github.com/mattn/go-isatty v0.0.13
|
||||
github.com/mattn/go-runewidth v0.0.13 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.8
|
||||
github.com/mholt/archiver/v3 v3.5.0
|
||||
github.com/microcosm-cc/bluemonday v1.0.16
|
||||
github.com/minio/md5-simd v1.1.2 // indirect
|
||||
github.com/minio/minio-go/v7 v7.0.12
|
||||
github.com/minio/sha256-simd v1.0.0 // indirect
|
||||
github.com/mrjones/oauth v0.0.0-20190623134757-126b35219450 // indirect
|
||||
github.com/msteinert/pam v0.0.0-20201130170657-e61372126161
|
||||
github.com/markbates/goth v1.69.0
|
||||
github.com/mattn/go-isatty v0.0.14
|
||||
github.com/mattn/go-sqlite3 v1.14.12
|
||||
github.com/mholt/archiver/v3 v3.5.1
|
||||
github.com/microcosm-cc/bluemonday v1.0.18
|
||||
github.com/minio/minio-go/v7 v7.0.23
|
||||
github.com/msteinert/pam v1.0.0
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
|
||||
github.com/niklasfasching/go-org v1.6.1
|
||||
github.com/olekukonko/tablewriter v0.0.5 // indirect
|
||||
github.com/niklasfasching/go-org v1.6.2
|
||||
github.com/oliamb/cutter v0.2.2
|
||||
github.com/olivere/elastic/v7 v7.0.25
|
||||
github.com/pelletier/go-toml v1.9.0 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.8 // indirect
|
||||
github.com/olivere/elastic/v7 v7.0.31
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/pquerna/otp v1.3.0
|
||||
github.com/prometheus/client_golang v1.11.0
|
||||
github.com/quasoft/websspi v1.0.0
|
||||
github.com/rs/xid v1.3.0 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/prometheus/client_golang v1.12.1
|
||||
github.com/quasoft/websspi v1.1.2
|
||||
github.com/sergi/go-diff v1.2.0
|
||||
github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 // indirect
|
||||
github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546
|
||||
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect
|
||||
github.com/stretchr/testify v1.7.0
|
||||
github.com/syndtr/goleveldb v1.0.0
|
||||
github.com/tstranex/u2f v1.0.0
|
||||
github.com/ulikunitz/xz v0.5.10 // indirect
|
||||
github.com/unknwon/com v1.0.1
|
||||
github.com/unknwon/i18n v0.0.0-20210321134014-0ebbf2df1c44
|
||||
github.com/unknwon/paginater v0.0.0-20200328080006-042474bd0eae
|
||||
github.com/unrolled/render v1.4.0
|
||||
github.com/urfave/cli v1.22.5
|
||||
github.com/xanzy/go-gitlab v0.50.1
|
||||
github.com/xanzy/go-gitlab v0.58.0
|
||||
github.com/yohcop/openid-go v1.0.0
|
||||
github.com/yuin/goldmark v1.4.0
|
||||
github.com/yuin/goldmark-highlighting v0.0.0-20210516132338-9216f9c5aa01
|
||||
github.com/yuin/goldmark-meta v1.0.0
|
||||
go.etcd.io/bbolt v1.3.6 // indirect
|
||||
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
|
||||
go.jolheiser.com/pwn v0.0.3
|
||||
go.uber.org/atomic v1.9.0 // indirect
|
||||
go.uber.org/multierr v1.7.0 // indirect
|
||||
go.uber.org/zap v1.19.0 // indirect
|
||||
golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871
|
||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd
|
||||
golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e
|
||||
go.uber.org/multierr v1.8.0 // indirect
|
||||
go.uber.org/zap v1.21.0 // indirect
|
||||
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
|
||||
golang.org/x/text v0.3.7
|
||||
golang.org/x/time v0.0.0-20210611083556-38a9dc6acbc6 // indirect
|
||||
golang.org/x/tools v0.1.0
|
||||
google.golang.org/protobuf v1.27.1 // indirect
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
||||
golang.org/x/tools v0.1.9
|
||||
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
|
||||
gopkg.in/ini.v1 v1.62.0
|
||||
gopkg.in/ini.v1 v1.66.2
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
mvdan.cc/xurls/v2 v2.2.0
|
||||
strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251
|
||||
@@ -139,6 +106,55 @@ require (
|
||||
xorm.io/xorm v1.2.5
|
||||
)
|
||||
|
||||
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/andybalholm/brotli v1.0.4 // indirect
|
||||
github.com/bits-and-blooms/bitset v1.2.1 // indirect
|
||||
github.com/boombuler/barcode v1.0.1 // indirect
|
||||
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b // indirect
|
||||
github.com/cloudflare/cfssl v1.6.1 // 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/felixge/httpsnoop v1.0.2 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.4.0 // indirect
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.3 // indirect
|
||||
github.com/go-openapi/analysis v0.21.2 // indirect
|
||||
github.com/go-openapi/errors v0.20.2 // indirect
|
||||
github.com/go-openapi/runtime v0.21.1 // indirect
|
||||
github.com/go-openapi/strfmt v0.21.1 // indirect
|
||||
github.com/go-stack/stack v1.8.1 // indirect
|
||||
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/gorilla/mux v1.8.0 // indirect
|
||||
github.com/hashicorp/go-retryablehttp v0.7.0 // indirect
|
||||
github.com/kevinburke/ssh_config v1.1.0 // indirect
|
||||
github.com/kr/pretty v0.3.0 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.13 // 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/mrjones/oauth v0.0.0-20190623134757-126b35219450 // indirect
|
||||
github.com/nwaples/rardecode v1.1.3 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.14 // indirect
|
||||
github.com/rogpeppe/go-internal v1.8.1 // indirect
|
||||
github.com/rs/xid v1.3.0 // indirect
|
||||
github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 // indirect
|
||||
github.com/spf13/afero v1.8.0 // indirect
|
||||
github.com/spf13/cobra v1.3.0 // indirect
|
||||
github.com/spf13/viper v1.10.1 // indirect
|
||||
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect
|
||||
github.com/ulikunitz/xz v0.5.10 // indirect
|
||||
github.com/xanzy/ssh-agent v0.3.1 // indirect
|
||||
go.etcd.io/bbolt v1.3.6 // indirect
|
||||
go.mongodb.org/mongo-driver v1.8.2 // indirect
|
||||
golang.org/x/time v0.0.0-20220224211638-0e9765cccd65 // indirect
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
||||
)
|
||||
|
||||
replace github.com/hashicorp/go-version => github.com/6543/go-version v1.3.1
|
||||
|
||||
replace github.com/markbates/goth v1.68.0 => github.com/zeripath/goth v1.68.1-0.20220109111530-754359885dce
|
||||
|
||||
@@ -47,7 +47,7 @@ func TestAPIPrivateServ(t *testing.T) {
|
||||
results, err := private.ServCommand(ctx, 1, "user2", "repo1", perm.AccessModeWrite, "git-upload-pack", "")
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, results.IsWiki)
|
||||
assert.False(t, results.IsDeployKey)
|
||||
assert.Zero(t, results.DeployKeyID)
|
||||
assert.Equal(t, int64(1), results.KeyID)
|
||||
assert.Equal(t, "user2@localhost", results.KeyName)
|
||||
assert.Equal(t, "user2", results.UserName)
|
||||
@@ -70,7 +70,7 @@ func TestAPIPrivateServ(t *testing.T) {
|
||||
results, err = private.ServCommand(ctx, 1, "user15", "big_test_public_1", perm.AccessModeRead, "git-upload-pack", "")
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, results.IsWiki)
|
||||
assert.False(t, results.IsDeployKey)
|
||||
assert.Zero(t, results.DeployKeyID)
|
||||
assert.Equal(t, int64(1), results.KeyID)
|
||||
assert.Equal(t, "user2@localhost", results.KeyName)
|
||||
assert.Equal(t, "user2", results.UserName)
|
||||
@@ -92,7 +92,7 @@ func TestAPIPrivateServ(t *testing.T) {
|
||||
results, err = private.ServCommand(ctx, deployKey.KeyID, "user15", "big_test_private_1", perm.AccessModeRead, "git-upload-pack", "")
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, results.IsWiki)
|
||||
assert.True(t, results.IsDeployKey)
|
||||
assert.NotZero(t, results.DeployKeyID)
|
||||
assert.Equal(t, deployKey.KeyID, results.KeyID)
|
||||
assert.Equal(t, "test-deploy", results.KeyName)
|
||||
assert.Equal(t, "user15", results.UserName)
|
||||
@@ -129,7 +129,7 @@ func TestAPIPrivateServ(t *testing.T) {
|
||||
results, err = private.ServCommand(ctx, deployKey.KeyID, "user15", "big_test_private_2", perm.AccessModeRead, "git-upload-pack", "")
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, results.IsWiki)
|
||||
assert.True(t, results.IsDeployKey)
|
||||
assert.NotZero(t, results.DeployKeyID)
|
||||
assert.Equal(t, deployKey.KeyID, results.KeyID)
|
||||
assert.Equal(t, "test-deploy", results.KeyName)
|
||||
assert.Equal(t, "user15", results.UserName)
|
||||
@@ -142,7 +142,7 @@ func TestAPIPrivateServ(t *testing.T) {
|
||||
results, err = private.ServCommand(ctx, deployKey.KeyID, "user15", "big_test_private_2", perm.AccessModeWrite, "git-upload-pack", "")
|
||||
assert.NoError(t, err)
|
||||
assert.False(t, results.IsWiki)
|
||||
assert.True(t, results.IsDeployKey)
|
||||
assert.NotZero(t, results.DeployKeyID)
|
||||
assert.Equal(t, deployKey.KeyID, results.KeyID)
|
||||
assert.Equal(t, "test-deploy", results.KeyName)
|
||||
assert.Equal(t, "user15", results.UserName)
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -58,7 +58,7 @@ func (key *DeployKey) GetContent() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsReadOnly checks if the key can only be used for read operations
|
||||
// IsReadOnly checks if the key can only be used for read operations, used by template
|
||||
func (key *DeployKey) IsReadOnly() bool {
|
||||
return key.Mode == perm.AccessModeRead
|
||||
}
|
||||
@@ -203,12 +203,6 @@ func UpdateDeployKeyCols(key *DeployKey, cols ...string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// UpdateDeployKey updates deploy key information.
|
||||
func UpdateDeployKey(key *DeployKey) error {
|
||||
_, err := db.GetEngine(db.DefaultContext).ID(key.ID).AllCols().Update(key)
|
||||
return err
|
||||
}
|
||||
|
||||
// ListDeployKeysOptions are options for ListDeployKeys
|
||||
type ListDeployKeysOptions struct {
|
||||
db.ListOptions
|
||||
|
||||
@@ -23,8 +23,8 @@ const (
|
||||
EnvPusherName = "GITEA_PUSHER_NAME"
|
||||
EnvPusherEmail = "GITEA_PUSHER_EMAIL"
|
||||
EnvPusherID = "GITEA_PUSHER_ID"
|
||||
EnvKeyID = "GITEA_KEY_ID"
|
||||
EnvIsDeployKey = "GITEA_IS_DEPLOY_KEY"
|
||||
EnvKeyID = "GITEA_KEY_ID" // public key ID
|
||||
EnvDeployKeyID = "GITEA_DEPLOY_KEY_ID"
|
||||
EnvPRID = "GITEA_PR_ID"
|
||||
EnvIsInternal = "GITEA_INTERNAL_PUSH"
|
||||
EnvAppURL = "GITEA_ROOT_URL"
|
||||
|
||||
+22
-16
@@ -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
|
||||
}
|
||||
@@ -1551,6 +1552,7 @@ const (
|
||||
FilterModeCreate
|
||||
FilterModeMention
|
||||
FilterModeReviewRequested
|
||||
FilterModeYourRepositories
|
||||
)
|
||||
|
||||
func parseCountResult(results []map[string][]byte) int64 {
|
||||
@@ -1695,6 +1697,7 @@ type UserIssueStatsOptions struct {
|
||||
IssueIDs []int64
|
||||
IsArchived util.OptionalBool
|
||||
LabelIDs []int64
|
||||
RepoCond builder.Cond
|
||||
Org *Organization
|
||||
Team *Team
|
||||
}
|
||||
@@ -1712,6 +1715,9 @@ func GetUserIssueStats(opts UserIssueStatsOptions) (*IssueStats, error) {
|
||||
if len(opts.IssueIDs) > 0 {
|
||||
cond = cond.And(builder.In("issue.id", opts.IssueIDs))
|
||||
}
|
||||
if opts.RepoCond != nil {
|
||||
cond = cond.And(opts.RepoCond)
|
||||
}
|
||||
|
||||
if opts.UserID > 0 {
|
||||
cond = cond.And(issuePullAccessibleRepoCond("issue.repo_id", opts.UserID, opts.Org, opts.Team, opts.IsPull))
|
||||
@@ -1733,7 +1739,7 @@ func GetUserIssueStats(opts UserIssueStatsOptions) (*IssueStats, error) {
|
||||
}
|
||||
|
||||
switch opts.FilterMode {
|
||||
case FilterModeAll:
|
||||
case FilterModeAll, FilterModeYourRepositories:
|
||||
stats.OpenCount, err = sess(cond).
|
||||
And("issue.is_closed = ?", false).
|
||||
Count(new(Issue))
|
||||
|
||||
+81
-14
@@ -19,6 +19,7 @@ import (
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"xorm.io/builder"
|
||||
)
|
||||
@@ -776,8 +777,45 @@ func DeleteTeam(t *Team) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := t.removeAllRepositories(ctx); err != nil {
|
||||
return err
|
||||
// update branch protections
|
||||
{
|
||||
protections := make([]*ProtectedBranch, 0, 10)
|
||||
err := sess.In("repo_id",
|
||||
builder.Select("id").From("repository").Where(builder.Eq{"owner_id": t.OrgID})).
|
||||
Find(&protections)
|
||||
if err != nil {
|
||||
return fmt.Errorf("findProtectedBranches: %v", err)
|
||||
}
|
||||
for _, p := range protections {
|
||||
var matched1, matched2, matched3 bool
|
||||
if len(p.WhitelistTeamIDs) != 0 {
|
||||
p.WhitelistTeamIDs, matched1 = util.RemoveIDFromList(
|
||||
p.WhitelistTeamIDs, t.ID)
|
||||
}
|
||||
if len(p.ApprovalsWhitelistTeamIDs) != 0 {
|
||||
p.ApprovalsWhitelistTeamIDs, matched2 = util.RemoveIDFromList(
|
||||
p.ApprovalsWhitelistTeamIDs, t.ID)
|
||||
}
|
||||
if len(p.MergeWhitelistTeamIDs) != 0 {
|
||||
p.MergeWhitelistTeamIDs, matched3 = util.RemoveIDFromList(
|
||||
p.MergeWhitelistTeamIDs, t.ID)
|
||||
}
|
||||
if matched1 || matched2 || matched3 {
|
||||
if _, err = sess.ID(p.ID).Cols(
|
||||
"whitelist_team_i_ds",
|
||||
"merge_whitelist_team_i_ds",
|
||||
"approvals_whitelist_team_i_ds",
|
||||
).Update(p); err != nil {
|
||||
return fmt.Errorf("updateProtectedBranches: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !t.IncludesAllRepositories {
|
||||
if err := t.removeAllRepositories(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Delete team-user.
|
||||
@@ -902,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
|
||||
@@ -928,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})
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"xorm.io/builder"
|
||||
)
|
||||
@@ -130,6 +131,50 @@ func DeleteUser(ctx context.Context, u *user_model.User) (err error) {
|
||||
}
|
||||
}
|
||||
|
||||
// ***** START: Branch Protections *****
|
||||
{
|
||||
const batchSize = 50
|
||||
for start := 0; ; start += batchSize {
|
||||
protections := make([]*ProtectedBranch, 0, batchSize)
|
||||
// @perf: We can't filter on DB side by u.ID, as those IDs are serialized as JSON strings.
|
||||
// We could filter down with `WHERE repo_id IN (reposWithPushPermission(u))`,
|
||||
// though that query will be quite complex and tricky to maintain (compare `getRepoAssignees()`).
|
||||
// Also, as we didn't update branch protections when removing entries from `access` table,
|
||||
// it's safer to iterate all protected branches.
|
||||
if err = e.Limit(batchSize, start).Find(&protections); err != nil {
|
||||
return fmt.Errorf("findProtectedBranches: %v", err)
|
||||
}
|
||||
if len(protections) == 0 {
|
||||
break
|
||||
}
|
||||
for _, p := range protections {
|
||||
var matched1, matched2, matched3 bool
|
||||
if len(p.WhitelistUserIDs) != 0 {
|
||||
p.WhitelistUserIDs, matched1 = util.RemoveIDFromList(
|
||||
p.WhitelistUserIDs, u.ID)
|
||||
}
|
||||
if len(p.ApprovalsWhitelistUserIDs) != 0 {
|
||||
p.ApprovalsWhitelistUserIDs, matched2 = util.RemoveIDFromList(
|
||||
p.ApprovalsWhitelistUserIDs, u.ID)
|
||||
}
|
||||
if len(p.MergeWhitelistUserIDs) != 0 {
|
||||
p.MergeWhitelistUserIDs, matched3 = util.RemoveIDFromList(
|
||||
p.MergeWhitelistUserIDs, u.ID)
|
||||
}
|
||||
if matched1 || matched2 || matched3 {
|
||||
if _, err = e.ID(p.ID).Cols(
|
||||
"whitelist_user_i_ds",
|
||||
"merge_whitelist_user_i_ds",
|
||||
"approvals_whitelist_user_i_ds",
|
||||
).Update(p); err != nil {
|
||||
return fmt.Errorf("updateProtectedBranches: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// ***** END: Branch Protections *****
|
||||
|
||||
// ***** START: PublicKey *****
|
||||
if _, err = e.Delete(&asymkey_model.PublicKey{OwnerID: u.ID}); err != nil {
|
||||
return fmt.Errorf("deletePublicKeys: %v", err)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -181,6 +181,12 @@ func (ctx *Context) RedirectToFirst(location ...string) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Unfortunately browsers consider a redirect Location with preceding "//" and "/\" as meaning redirect to "http(s)://REST_OF_PATH"
|
||||
// Therefore we should ignore these redirect locations to prevent open redirects
|
||||
if len(loc) > 1 && loc[0] == '/' && (loc[1] == '/' || loc[1] == '\\') {
|
||||
continue
|
||||
}
|
||||
|
||||
u, err := url.Parse(loc)
|
||||
if err != nil || ((u.Scheme != "" || u.Host != "") && !strings.HasPrefix(strings.ToLower(loc), strings.ToLower(setting.AppURL))) {
|
||||
continue
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -440,6 +440,26 @@ func RepoAssignment(ctx *Context) (cancel context.CancelFunc) {
|
||||
ctx.Repo.Owner = owner
|
||||
ctx.Data["Username"] = ctx.Repo.Owner.Name
|
||||
|
||||
// redirect link to wiki
|
||||
if strings.HasSuffix(repoName, ".wiki") {
|
||||
// ctx.Req.URL.Path does not have the preceding appSubURL - any redirect must have this added
|
||||
// Now we happen to know that all of our paths are: /:username/:reponame/whatever_else
|
||||
originalRepoName := ctx.Params(":reponame")
|
||||
redirectRepoName := strings.TrimSuffix(repoName, ".wiki")
|
||||
redirectRepoName += originalRepoName[len(redirectRepoName)+5:]
|
||||
redirectPath := strings.Replace(
|
||||
ctx.Req.URL.EscapedPath(),
|
||||
url.PathEscape(userName)+"/"+url.PathEscape(originalRepoName),
|
||||
url.PathEscape(userName)+"/"+url.PathEscape(redirectRepoName)+"/wiki",
|
||||
1,
|
||||
)
|
||||
if ctx.Req.URL.RawQuery != "" {
|
||||
redirectPath += "?" + ctx.Req.URL.RawQuery
|
||||
}
|
||||
ctx.Redirect(path.Join(setting.AppSubURL, redirectPath))
|
||||
return
|
||||
}
|
||||
|
||||
// Get repository.
|
||||
repo, err := repo_model.GetRepositoryByName(owner.ID, repoName)
|
||||
if err != nil {
|
||||
|
||||
+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
|
||||
|
||||
+25
-9
@@ -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
|
||||
@@ -97,15 +98,16 @@ func (repo *Repository) IsEmpty() (bool, error) {
|
||||
|
||||
// CloneRepoOptions options when clone a repository
|
||||
type CloneRepoOptions struct {
|
||||
Timeout time.Duration
|
||||
Mirror bool
|
||||
Bare bool
|
||||
Quiet bool
|
||||
Branch string
|
||||
Shared bool
|
||||
NoCheckout bool
|
||||
Depth int
|
||||
Filter string
|
||||
Timeout time.Duration
|
||||
Mirror bool
|
||||
Bare bool
|
||||
Quiet bool
|
||||
Branch string
|
||||
Shared bool
|
||||
NoCheckout bool
|
||||
Depth int
|
||||
Filter string
|
||||
SkipTLSVerify bool
|
||||
}
|
||||
|
||||
// Clone clones original repository to target path.
|
||||
@@ -128,6 +130,9 @@ func CloneWithArgs(ctx context.Context, from, to string, args []string, opts Clo
|
||||
}
|
||||
|
||||
cmd := NewCommandContextNoGlobals(ctx, args...).AddArguments("clone")
|
||||
if opts.SkipTLSVerify {
|
||||
cmd.AddArguments("-c", "http.sslVerify=false")
|
||||
}
|
||||
if opts.Mirror {
|
||||
cmd.AddArguments("--mirror")
|
||||
}
|
||||
@@ -154,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
|
||||
}
|
||||
@@ -230,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
|
||||
}
|
||||
|
||||
@@ -40,20 +40,19 @@ steps:
|
||||
- go test -v -race -coverprofile=coverage.txt -covermode=atomic
|
||||
`,
|
||||
want: []string{
|
||||
`<span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">pipeline</span>`,
|
||||
`<span class="w"></span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">default</span>`,
|
||||
`<span class="w">
|
||||
</span>`,
|
||||
`<span class="w"></span><span class="nt">steps</span><span class="p">:</span>`,
|
||||
`<span class="w"></span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">test</span>`,
|
||||
`<span class="w"> </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">golang:1.13</span>`,
|
||||
`<span class="w"> </span><span class="nt">environment</span><span class="p">:</span>`,
|
||||
`<span class="w"></span><span class="w"> </span><span class="nt">GOPROXY</span><span class="p">:</span><span class="w"> </span><span class="l">https://goproxy.cn</span>`,
|
||||
`<span class="w"> </span><span class="nt">commands</span><span class="p">:</span>`,
|
||||
`<span class="w"></span><span class="w"> </span>- <span class="l">go get -u</span>`,
|
||||
`<span class="w"> </span>- <span class="l">go build -v</span>`,
|
||||
`<span class="w"> </span>- <span class="l">go test -v -race -coverprofile=coverage.txt -covermode=atomic</span><span class="w">
|
||||
</span>`,
|
||||
`<span class="line"><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">pipeline</span>`,
|
||||
`</span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">default</span>`,
|
||||
`</span></span><span class="line"><span class="cl">`,
|
||||
`</span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">steps</span><span class="p">:</span>`,
|
||||
`</span></span><span class="line"><span class="cl"><span class="w"></span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">test</span>`,
|
||||
`</span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">golang:1.13</span>`,
|
||||
`</span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">environment</span><span class="p">:</span>`,
|
||||
`</span></span><span class="line"><span class="cl"><span class="w"></span><span class="w"> </span><span class="nt">GOPROXY</span><span class="p">:</span><span class="w"> </span><span class="l">https://goproxy.cn</span>`,
|
||||
`</span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">commands</span><span class="p">:</span>`,
|
||||
`</span></span><span class="line"><span class="cl"><span class="w"></span><span class="w"> </span>- <span class="l">go get -u</span>`,
|
||||
`</span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="l">go build -v</span>`,
|
||||
`</span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="l">go test -v -race -coverprofile=coverage.txt -covermode=atomic</span><span class="w">
|
||||
</span></span></span>`,
|
||||
`<span class="w">
|
||||
</span>`,
|
||||
},
|
||||
@@ -76,20 +75,19 @@ steps:
|
||||
- go test -v -race -coverprofile=coverage.txt -covermode=atomic
|
||||
`,
|
||||
want: []string{
|
||||
`<span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">pipeline</span>`,
|
||||
`<span class="w"></span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">default </span>`,
|
||||
`<span class="w">
|
||||
</span>`,
|
||||
`<span class="w"></span><span class="nt">steps</span><span class="p">:</span>`,
|
||||
`<span class="w"></span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">test</span>`,
|
||||
`<span class="w"> </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">golang:1.13</span>`,
|
||||
`<span class="w"> </span><span class="nt">environment</span><span class="p">:</span>`,
|
||||
`<span class="w"></span><span class="w"> </span><span class="nt">GOPROXY</span><span class="p">:</span><span class="w"> </span><span class="l">https://goproxy.cn</span>`,
|
||||
`<span class="w"> </span><span class="nt">commands</span><span class="p">:</span>`,
|
||||
`<span class="w"></span><span class="w"> </span>- <span class="l">go get -u</span>`,
|
||||
`<span class="w"> </span>- <span class="l">go build -v</span>`,
|
||||
`<span class="w"> </span>- <span class="l">go test -v -race -coverprofile=coverage.txt -covermode=atomic</span>`,
|
||||
`<span class="w"> </span>`,
|
||||
`<span class="line"><span class="cl"><span class="nt">kind</span><span class="p">:</span><span class="w"> </span><span class="l">pipeline</span>`,
|
||||
`</span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">default </span>`,
|
||||
`</span></span><span class="line"><span class="cl">`,
|
||||
`</span></span><span class="line"><span class="cl"><span class="w"></span><span class="nt">steps</span><span class="p">:</span>`,
|
||||
`</span></span><span class="line"><span class="cl"><span class="w"></span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">test</span>`,
|
||||
`</span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l">golang:1.13</span>`,
|
||||
`</span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">environment</span><span class="p">:</span>`,
|
||||
`</span></span><span class="line"><span class="cl"><span class="w"></span><span class="w"> </span><span class="nt">GOPROXY</span><span class="p">:</span><span class="w"> </span><span class="l">https://goproxy.cn</span>`,
|
||||
`</span></span><span class="line"><span class="cl"><span class="w"> </span><span class="nt">commands</span><span class="p">:</span>`,
|
||||
`</span></span><span class="line"><span class="cl"><span class="w"></span><span class="w"> </span>- <span class="l">go get -u</span>`,
|
||||
`</span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="l">go build -v</span>`,
|
||||
`</span></span><span class="line"><span class="cl"><span class="w"> </span>- <span class="l">go test -v -race -coverprofile=coverage.txt -covermode=atomic</span>`,
|
||||
`</span></span><span class="line"><span class="cl"><span class="w"> </span></span></span>`,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -205,7 +205,7 @@ func (b *footnoteBlockParser) Open(parent ast.Node, reader text.Reader, pc parse
|
||||
}
|
||||
open := pos + 1
|
||||
closes := 0
|
||||
closure := util.FindClosure(line[pos+1:], '[', ']', false, false)
|
||||
closure := util.FindClosure(line[pos+1:], '[', ']', false, false) //nolint
|
||||
closes = pos + 1 + closure
|
||||
next := closes + 1
|
||||
if closure > -1 {
|
||||
@@ -296,7 +296,7 @@ func (s *footnoteParser) Parse(parent ast.Node, block text.Reader, pc parser.Con
|
||||
return nil
|
||||
}
|
||||
open := pos
|
||||
closure := util.FindClosure(line[pos:], '[', ']', false, false)
|
||||
closure := util.FindClosure(line[pos:], '[', ']', false, false) //nolint
|
||||
if closure < 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -77,9 +77,9 @@ func HelloWorld() {
|
||||
}
|
||||
#+end_src
|
||||
`, `<div class="src src-go">
|
||||
<pre><code class="chroma language-go"><span class="c1">// HelloWorld prints "Hello World"
|
||||
</span><span class="c1"></span><span class="kd">func</span> <span class="nf">HelloWorld</span><span class="p">()</span> <span class="p">{</span>
|
||||
<span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">"Hello World"</span><span class="p">)</span>
|
||||
<span class="p">}</span></code></pre>
|
||||
<pre><code class="chroma language-go"><span class="line"><span class="cl"><span class="c1">// HelloWorld prints "Hello World"
|
||||
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">HelloWorld</span><span class="p">()</span> <span class="p">{</span>
|
||||
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">"Hello World"</span><span class="p">)</span>
|
||||
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre>
|
||||
</div>`)
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ type HookOptions struct {
|
||||
GitQuarantinePath string
|
||||
GitPushOptions GitPushOptions
|
||||
PullRequestID int64
|
||||
IsDeployKey bool
|
||||
DeployKeyID int64 // if the pusher is a DeployKey, then UserID is the repo's org user.
|
||||
IsWiki bool
|
||||
}
|
||||
|
||||
|
||||
@@ -46,9 +46,9 @@ func ServNoCommand(ctx context.Context, keyID int64) (*asymkey_model.PublicKey,
|
||||
// ServCommandResults are the results of a call to the private route serv
|
||||
type ServCommandResults struct {
|
||||
IsWiki bool
|
||||
IsDeployKey bool
|
||||
KeyID int64
|
||||
KeyName string
|
||||
DeployKeyID int64
|
||||
KeyID int64 // public key
|
||||
KeyName string // this field is ambiguous, it can be the name of DeployKey, or the name of the PublicKey
|
||||
UserName string
|
||||
UserEmail string
|
||||
UserID int64
|
||||
|
||||
@@ -43,7 +43,7 @@ var defaultTransformers = []transformer{
|
||||
{Name: "PASCAL", Transform: xstrings.ToCamelCase},
|
||||
{Name: "LOWER", Transform: strings.ToLower},
|
||||
{Name: "UPPER", Transform: strings.ToUpper},
|
||||
{Name: "TITLE", Transform: strings.Title},
|
||||
{Name: "TITLE", Transform: strings.Title}, // nolint
|
||||
}
|
||||
|
||||
func generateExpansion(src string, templateRepo, generateRepo *repo_model.Repository) string {
|
||||
@@ -62,7 +62,7 @@ func generateExpansion(src string, templateRepo, generateRepo *repo_model.Reposi
|
||||
{Name: "TEMPLATE_SSH_URL", Value: templateRepo.CloneLink().SSH, Transformers: nil},
|
||||
}
|
||||
|
||||
var expansionMap = make(map[string]string)
|
||||
expansionMap := make(map[string]string)
|
||||
for _, e := range expansions {
|
||||
expansionMap[e.Name] = e.Value
|
||||
for _, tr := range e.Transformers {
|
||||
@@ -159,7 +159,7 @@ func generateRepoCommit(repo, templateRepo, generateRepo *repo_model.Repository,
|
||||
|
||||
if err := os.WriteFile(path,
|
||||
[]byte(generateExpansion(string(content), templateRepo, generateRepo)),
|
||||
0644); err != nil {
|
||||
0o644); err != nil {
|
||||
return err
|
||||
}
|
||||
break
|
||||
|
||||
+33
-22
@@ -72,13 +72,18 @@ func MigrateRepositoryGitData(ctx context.Context, u *user_model.User,
|
||||
}
|
||||
|
||||
if err = git.CloneWithContext(ctx, opts.CloneAddr, repoPath, git.CloneRepoOptions{
|
||||
Mirror: true,
|
||||
Quiet: true,
|
||||
Timeout: migrateTimeout,
|
||||
Mirror: true,
|
||||
Quiet: true,
|
||||
Timeout: migrateTimeout,
|
||||
SkipTLSVerify: setting.Migrations.SkipTLSVerify,
|
||||
}); err != nil {
|
||||
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)
|
||||
@@ -88,10 +93,11 @@ func MigrateRepositoryGitData(ctx context.Context, u *user_model.User,
|
||||
}
|
||||
|
||||
if err = git.CloneWithContext(ctx, wikiRemotePath, wikiPath, git.CloneRepoOptions{
|
||||
Mirror: true,
|
||||
Quiet: true,
|
||||
Timeout: migrateTimeout,
|
||||
Branch: "master",
|
||||
Mirror: true,
|
||||
Quiet: true,
|
||||
Timeout: migrateTimeout,
|
||||
Branch: "master",
|
||||
SkipTLSVerify: setting.Migrations.SkipTLSVerify,
|
||||
}); err != nil {
|
||||
log.Warn("Clone wiki: %v", err)
|
||||
if err := util.RemoveAll(wikiPath); err != nil {
|
||||
@@ -99,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 {
|
||||
@@ -276,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)
|
||||
}
|
||||
@@ -310,7 +321,7 @@ func PushUpdateAddTag(repo *repo_model.Repository, gitRepo *git.Repository, tagN
|
||||
}
|
||||
|
||||
var author *user_model.User
|
||||
var createdAt = time.Unix(1, 0)
|
||||
createdAt := time.Unix(1, 0)
|
||||
|
||||
if sig != nil {
|
||||
author, err = user_model.GetUserByEmail(sig.Email)
|
||||
@@ -325,7 +336,7 @@ func PushUpdateAddTag(repo *repo_model.Repository, gitRepo *git.Repository, tagN
|
||||
return fmt.Errorf("unable to get CommitsCount: %w", err)
|
||||
}
|
||||
|
||||
var rel = models.Release{
|
||||
rel := models.Release{
|
||||
RepoID: repo.ID,
|
||||
TagName: tagName,
|
||||
LowerTagName: strings.ToLower(tagName),
|
||||
|
||||
+10
-6
@@ -15,13 +15,17 @@ import (
|
||||
|
||||
"code.gitea.io/gitea/modules/json"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"golang.org/x/text/cases"
|
||||
"golang.org/x/text/language"
|
||||
|
||||
ini "gopkg.in/ini.v1"
|
||||
)
|
||||
|
||||
var filenameSuffix = ""
|
||||
var descriptionLock = sync.RWMutex{}
|
||||
var logDescriptions = make(map[string]*LogDescription)
|
||||
var (
|
||||
filenameSuffix = ""
|
||||
descriptionLock = sync.RWMutex{}
|
||||
logDescriptions = make(map[string]*LogDescription)
|
||||
)
|
||||
|
||||
// GetLogDescriptions returns a race safe set of descriptions
|
||||
func GetLogDescriptions() map[string]*LogDescription {
|
||||
@@ -86,7 +90,7 @@ func RemoveSubLogDescription(key, name string) bool {
|
||||
type defaultLogOptions struct {
|
||||
levelName string // LogLevel
|
||||
flags string
|
||||
filename string //path.Join(LogRootPath, "gitea.log")
|
||||
filename string // path.Join(LogRootPath, "gitea.log")
|
||||
bufferLength int64
|
||||
disableConsole bool
|
||||
}
|
||||
@@ -243,7 +247,7 @@ func generateNamedLogger(key string, options defaultLogOptions) *LogDescription
|
||||
Provider: provider,
|
||||
Config: config,
|
||||
})
|
||||
log.Info("%s Log: %s(%s:%s)", strings.Title(key), strings.Title(name), provider, levelName)
|
||||
log.Info("%s Log: %s(%s:%s)", cases.Title(language.English).String(key), cases.Title(language.English).String(name), provider, levelName)
|
||||
}
|
||||
|
||||
AddLogDescription(key, &description)
|
||||
@@ -327,7 +331,7 @@ func newLogService() {
|
||||
Provider: provider,
|
||||
Config: config,
|
||||
})
|
||||
log.Info("Gitea Log Mode: %s(%s:%s)", strings.Title(name), strings.Title(provider), levelName)
|
||||
log.Info("Gitea Log Mode: %s(%s:%s)", cases.Title(language.English).String(name), cases.Title(language.English).String(provider), levelName)
|
||||
}
|
||||
|
||||
AddLogDescription(log.DEFAULT, &description)
|
||||
|
||||
+18
-19
@@ -8,7 +8,6 @@ package setting
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"net"
|
||||
"net/url"
|
||||
@@ -30,6 +29,8 @@ import (
|
||||
|
||||
"github.com/unknwon/com"
|
||||
gossh "golang.org/x/crypto/ssh"
|
||||
"golang.org/x/text/cases"
|
||||
"golang.org/x/text/language"
|
||||
ini "gopkg.in/ini.v1"
|
||||
)
|
||||
|
||||
@@ -90,13 +91,15 @@ var (
|
||||
// AppDataPath is the default path for storing data.
|
||||
// It maps to ini:"APP_DATA_PATH" and defaults to AppWorkPath + "/data"
|
||||
AppDataPath string
|
||||
// LocalURL is the url for locally running applications to contact Gitea. It always has a '/' suffix
|
||||
// It maps to ini:"LOCAL_ROOT_URL"
|
||||
LocalURL string
|
||||
|
||||
// Server settings
|
||||
Protocol Scheme
|
||||
Domain string
|
||||
HTTPAddr string
|
||||
HTTPPort string
|
||||
LocalURL string
|
||||
RedirectOtherPort bool
|
||||
PortToRedirect string
|
||||
OfflineMode bool
|
||||
@@ -637,7 +640,7 @@ func loadFromConf(allowEmpty bool, extraConfig string) {
|
||||
}
|
||||
UnixSocketPermissionRaw := sec.Key("UNIX_SOCKET_PERMISSION").MustString("666")
|
||||
UnixSocketPermissionParsed, err := strconv.ParseUint(UnixSocketPermissionRaw, 8, 32)
|
||||
if err != nil || UnixSocketPermissionParsed > 0777 {
|
||||
if err != nil || UnixSocketPermissionParsed > 0o777 {
|
||||
log.Fatal("Failed to parse unixSocketPermission: %s", UnixSocketPermissionRaw)
|
||||
}
|
||||
|
||||
@@ -710,6 +713,7 @@ func loadFromConf(allowEmpty bool, extraConfig string) {
|
||||
}
|
||||
}
|
||||
LocalURL = sec.Key("LOCAL_ROOT_URL").MustString(defaultLocalURL)
|
||||
LocalURL = strings.TrimRight(LocalURL, "/") + "/"
|
||||
RedirectOtherPort = sec.Key("REDIRECT_OTHER_PORT").MustBool(false)
|
||||
PortToRedirect = sec.Key("PORT_TO_REDIRECT").MustString("80")
|
||||
OfflineMode = sec.Key("OFFLINE_MODE").MustBool()
|
||||
@@ -793,16 +797,16 @@ func loadFromConf(allowEmpty bool, extraConfig string) {
|
||||
SSH.AuthorizedPrincipalsAllow, SSH.AuthorizedPrincipalsEnabled = parseAuthorizedPrincipalsAllow(sec.Key("SSH_AUTHORIZED_PRINCIPALS_ALLOW").Strings(","))
|
||||
|
||||
if !SSH.Disabled && !SSH.StartBuiltinServer {
|
||||
if err := os.MkdirAll(SSH.RootPath, 0700); err != nil {
|
||||
if err := os.MkdirAll(SSH.RootPath, 0o700); err != nil {
|
||||
log.Fatal("Failed to create '%s': %v", SSH.RootPath, err)
|
||||
} else if err = os.MkdirAll(SSH.KeyTestPath, 0644); err != nil {
|
||||
} else if err = os.MkdirAll(SSH.KeyTestPath, 0o644); err != nil {
|
||||
log.Fatal("Failed to create '%s': %v", SSH.KeyTestPath, err)
|
||||
}
|
||||
|
||||
if len(trustedUserCaKeys) > 0 && SSH.AuthorizedPrincipalsEnabled {
|
||||
fname := sec.Key("SSH_TRUSTED_USER_CA_KEYS_FILENAME").MustString(filepath.Join(SSH.RootPath, "gitea-trusted-user-ca-keys.pem"))
|
||||
if err := os.WriteFile(fname,
|
||||
[]byte(strings.Join(trustedUserCaKeys, "\n")), 0600); err != nil {
|
||||
[]byte(strings.Join(trustedUserCaKeys, "\n")), 0o600); err != nil {
|
||||
log.Fatal("Failed to create '%s': %v", fname, err)
|
||||
}
|
||||
}
|
||||
@@ -943,8 +947,9 @@ func loadFromConf(allowEmpty bool, extraConfig string) {
|
||||
// The following is a purposefully undocumented option. Please do not run Gitea as root. It will only cause future headaches.
|
||||
// Please don't use root as a bandaid to "fix" something that is broken, instead the broken thing should instead be fixed properly.
|
||||
unsafeAllowRunAsRoot := Cfg.Section("").Key("I_AM_BEING_UNSAFE_RUNNING_AS_ROOT").MustBool(false)
|
||||
RunMode = Cfg.Section("").Key("RUN_MODE").MustString("prod")
|
||||
IsProd = strings.EqualFold(RunMode, "prod")
|
||||
RunMode = Cfg.Section("").Key("RUN_MODE").MustString("Prod")
|
||||
RunMode = cases.Title(language.English).String(strings.ToLower(RunMode))
|
||||
IsProd = RunMode == "Prod"
|
||||
// Does not check run user when the install lock is off.
|
||||
if InstallLock {
|
||||
currentUser, match := IsRunUserMatchCurrentUser(RunUser)
|
||||
@@ -1004,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 {
|
||||
@@ -1074,28 +1079,22 @@ func loadInternalToken(sec *ini.Section) string {
|
||||
}
|
||||
switch tempURI.Scheme {
|
||||
case "file":
|
||||
fp, err := os.OpenFile(tempURI.RequestURI(), os.O_RDWR, 0600)
|
||||
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)
|
||||
|
||||
@@ -317,64 +317,7 @@ func Listen(host string, port int, ciphers, keyExchanges, macs []string) {
|
||||
}
|
||||
}
|
||||
|
||||
// Workaround slightly broken behaviour in x/crypto/ssh/handshake.go:458-463
|
||||
//
|
||||
// Fundamentally the issue here is that HostKeyAlgos make the incorrect assumption
|
||||
// that the PublicKey().Type() matches the signature algorithm.
|
||||
//
|
||||
// Therefore we need to add duplicates for the RSA with different signing algorithms.
|
||||
signers := make([]ssh.Signer, 0, len(srv.HostSigners))
|
||||
for _, signer := range srv.HostSigners {
|
||||
if signer.PublicKey().Type() == "ssh-rsa" {
|
||||
signers = append(signers,
|
||||
&wrapSigner{
|
||||
Signer: signer,
|
||||
algorithm: gossh.SigAlgoRSASHA2512,
|
||||
},
|
||||
&wrapSigner{
|
||||
Signer: signer,
|
||||
algorithm: gossh.SigAlgoRSASHA2256,
|
||||
},
|
||||
)
|
||||
}
|
||||
signers = append(signers, signer)
|
||||
}
|
||||
srv.HostSigners = signers
|
||||
|
||||
go listen(&srv)
|
||||
|
||||
}
|
||||
|
||||
// wrapSigner wraps a signer and overrides its public key type with the provided algorithm
|
||||
type wrapSigner struct {
|
||||
ssh.Signer
|
||||
algorithm string
|
||||
}
|
||||
|
||||
// PublicKey returns an associated PublicKey instance.
|
||||
func (s *wrapSigner) PublicKey() gossh.PublicKey {
|
||||
return &wrapPublicKey{
|
||||
PublicKey: s.Signer.PublicKey(),
|
||||
algorithm: s.algorithm,
|
||||
}
|
||||
}
|
||||
|
||||
// Sign returns raw signature for the given data. This method
|
||||
// will apply the hash specified for the keytype to the data using
|
||||
// the algorithm assigned for this key
|
||||
func (s *wrapSigner) Sign(rand io.Reader, data []byte) (*gossh.Signature, error) {
|
||||
return s.Signer.(gossh.AlgorithmSigner).SignWithAlgorithm(rand, data, s.algorithm)
|
||||
}
|
||||
|
||||
// wrapPublicKey wraps a PublicKey and overrides its type
|
||||
type wrapPublicKey struct {
|
||||
gossh.PublicKey
|
||||
algorithm string
|
||||
}
|
||||
|
||||
// Type returns the algorithm
|
||||
func (k *wrapPublicKey) Type() string {
|
||||
return k.algorithm
|
||||
}
|
||||
|
||||
// GenKeyPair make a pair of public and private keys for SSH access.
|
||||
|
||||
@@ -6,7 +6,6 @@ package storage
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"net/url"
|
||||
"os"
|
||||
@@ -18,8 +17,6 @@ import (
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
)
|
||||
|
||||
// ErrLocalPathNotSupported represents an error that path is not supported
|
||||
var ErrLocalPathNotSupported = errors.New("local path is not supported")
|
||||
var _ ObjectStorage = &LocalStorage{}
|
||||
|
||||
// LocalStorageType is the type descriptor for local storage
|
||||
@@ -62,21 +59,18 @@ func NewLocalStorage(ctx context.Context, cfg interface{}) (ObjectStorage, error
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (l *LocalStorage) buildLocalPath(p string) string {
|
||||
return filepath.Join(l.dir, path.Clean("/" + strings.ReplaceAll(p, "\\", "/"))[1:])
|
||||
}
|
||||
|
||||
// Open a file
|
||||
func (l *LocalStorage) Open(path string) (Object, error) {
|
||||
if !isLocalPathValid(path) {
|
||||
return nil, ErrLocalPathNotSupported
|
||||
}
|
||||
return os.Open(filepath.Join(l.dir, path))
|
||||
return os.Open(l.buildLocalPath(path))
|
||||
}
|
||||
|
||||
// Save a file
|
||||
func (l *LocalStorage) Save(path string, r io.Reader, size int64) (int64, error) {
|
||||
if !isLocalPathValid(path) {
|
||||
return 0, ErrLocalPathNotSupported
|
||||
}
|
||||
|
||||
p := filepath.Join(l.dir, path)
|
||||
p := l.buildLocalPath(path)
|
||||
if err := os.MkdirAll(filepath.Dir(p), os.ModePerm); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@@ -116,24 +110,12 @@ func (l *LocalStorage) Save(path string, r io.Reader, size int64) (int64, error)
|
||||
|
||||
// Stat returns the info of the file
|
||||
func (l *LocalStorage) Stat(path string) (os.FileInfo, error) {
|
||||
return os.Stat(filepath.Join(l.dir, path))
|
||||
}
|
||||
|
||||
func isLocalPathValid(p string) bool {
|
||||
a := path.Clean(p)
|
||||
if strings.HasPrefix(a, "../") || strings.HasPrefix(a, "..\\") {
|
||||
return false
|
||||
}
|
||||
return a == p
|
||||
return os.Stat(l.buildLocalPath(path))
|
||||
}
|
||||
|
||||
// Delete delete a file
|
||||
func (l *LocalStorage) Delete(path string) error {
|
||||
if !isLocalPathValid(path) {
|
||||
return ErrLocalPathNotSupported
|
||||
}
|
||||
p := filepath.Join(l.dir, path)
|
||||
return util.Remove(p)
|
||||
return util.Remove(l.buildLocalPath(path))
|
||||
}
|
||||
|
||||
// URL gets the redirect URL to a file
|
||||
|
||||
@@ -10,36 +10,44 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestLocalPathIsValid(t *testing.T) {
|
||||
func TestBuildLocalPath(t *testing.T) {
|
||||
kases := []struct {
|
||||
path string
|
||||
valid bool
|
||||
localDir string
|
||||
path string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
"a",
|
||||
"0/a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a14",
|
||||
"a/0/a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a14",
|
||||
true,
|
||||
},
|
||||
{
|
||||
"../a/0/a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a14",
|
||||
false,
|
||||
"a",
|
||||
"../0/a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a14",
|
||||
"a/0/a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a14",
|
||||
},
|
||||
{
|
||||
"a\\0\\a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a14",
|
||||
true,
|
||||
"a",
|
||||
"0\\a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a14",
|
||||
"a/0/a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a14",
|
||||
},
|
||||
{
|
||||
"b/../a/0/a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a14",
|
||||
false,
|
||||
"b",
|
||||
"a/../0/a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a14",
|
||||
"b/0/a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a14",
|
||||
},
|
||||
{
|
||||
"..\\a/0/a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a14",
|
||||
false,
|
||||
"b",
|
||||
"a\\..\\0/a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a14",
|
||||
"b/0/a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a14",
|
||||
},
|
||||
}
|
||||
|
||||
for _, k := range kases {
|
||||
t.Run(k.path, func(t *testing.T) {
|
||||
assert.EqualValues(t, k.valid, isLocalPathValid(k.path))
|
||||
l := LocalStorage{dir: k.localDir}
|
||||
|
||||
assert.EqualValues(t, k.expected, l.buildLocalPath(k.path))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,7 +117,7 @@ func NewMinioStorage(ctx context.Context, cfg interface{}) (ObjectStorage, error
|
||||
}
|
||||
|
||||
func (m *MinioStorage) buildMinioPath(p string) string {
|
||||
return strings.TrimPrefix(path.Join(m.basePath, p), "/")
|
||||
return strings.TrimPrefix(path.Join(m.basePath, path.Clean("/" + strings.ReplaceAll(p, "\\", "/"))[1:]), "/")
|
||||
}
|
||||
|
||||
// Open open a file
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -38,6 +38,8 @@ import (
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
"code.gitea.io/gitea/services/gitdiff"
|
||||
"golang.org/x/text/cases"
|
||||
"golang.org/x/text/language"
|
||||
|
||||
"github.com/editorconfig/editorconfig-core-go/v2"
|
||||
)
|
||||
@@ -49,7 +51,7 @@ var mailSubjectSplit = regexp.MustCompile(`(?m)^-{3,}[\s]*$`)
|
||||
func NewFuncMap() []template.FuncMap {
|
||||
return []template.FuncMap{map[string]interface{}{
|
||||
"GoVer": func() string {
|
||||
return strings.Title(runtime.Version())
|
||||
return cases.Title(language.English).String(runtime.Version())
|
||||
},
|
||||
"UseHTTPS": func() bool {
|
||||
return strings.HasPrefix(setting.AppURL, "https")
|
||||
@@ -285,7 +287,7 @@ func NewFuncMap() []template.FuncMap {
|
||||
return util.MergeInto(dict, values...)
|
||||
},
|
||||
"percentage": func(n int, values ...int) float32 {
|
||||
var sum = 0
|
||||
sum := 0
|
||||
for i := 0; i < len(values); i++ {
|
||||
sum += values[i]
|
||||
}
|
||||
@@ -378,6 +380,7 @@ func NewFuncMap() []template.FuncMap {
|
||||
},
|
||||
"Join": strings.Join,
|
||||
"QueryEscape": url.QueryEscape,
|
||||
"DotEscape": DotEscape,
|
||||
}}
|
||||
}
|
||||
|
||||
@@ -386,7 +389,7 @@ func NewFuncMap() []template.FuncMap {
|
||||
func NewTextFuncMap() []texttmpl.FuncMap {
|
||||
return []texttmpl.FuncMap{map[string]interface{}{
|
||||
"GoVer": func() string {
|
||||
return strings.Title(runtime.Version())
|
||||
return cases.Title(language.English).String(runtime.Version())
|
||||
},
|
||||
"AppName": func() string {
|
||||
return setting.AppName
|
||||
@@ -477,7 +480,7 @@ func NewTextFuncMap() []texttmpl.FuncMap {
|
||||
return dict, nil
|
||||
},
|
||||
"percentage": func(n int, values ...int) float32 {
|
||||
var sum = 0
|
||||
sum := 0
|
||||
for i := 0; i < len(values); i++ {
|
||||
sum += values[i]
|
||||
}
|
||||
@@ -501,8 +504,10 @@ func NewTextFuncMap() []texttmpl.FuncMap {
|
||||
}}
|
||||
}
|
||||
|
||||
var widthRe = regexp.MustCompile(`width="[0-9]+?"`)
|
||||
var heightRe = regexp.MustCompile(`height="[0-9]+?"`)
|
||||
var (
|
||||
widthRe = regexp.MustCompile(`width="[0-9]+?"`)
|
||||
heightRe = regexp.MustCompile(`height="[0-9]+?"`)
|
||||
)
|
||||
|
||||
func parseOthers(defaultSize int, defaultClass string, others ...interface{}) (int, string) {
|
||||
size := defaultSize
|
||||
@@ -629,6 +634,11 @@ func JSEscape(raw string) string {
|
||||
return template.JSEscapeString(raw)
|
||||
}
|
||||
|
||||
// DotEscape wraps a dots in names with ZWJ [U+200D] in order to prevent autolinkers from detecting these as urls
|
||||
func DotEscape(raw string) string {
|
||||
return strings.ReplaceAll(raw, ".", "\u200d.\u200d")
|
||||
}
|
||||
|
||||
// Sha1 returns sha1 sum of string
|
||||
func Sha1(str string) string {
|
||||
return base.EncodeSha1(str)
|
||||
@@ -736,7 +746,7 @@ func RenderEmoji(text string) template.HTML {
|
||||
return template.HTML(renderedText)
|
||||
}
|
||||
|
||||
//ReactionToEmoji renders emoji for use in reactions
|
||||
// ReactionToEmoji renders emoji for use in reactions
|
||||
func ReactionToEmoji(reaction string) template.HTML {
|
||||
val := emoji.FromCode(reaction)
|
||||
if val != nil {
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ import (
|
||||
"net"
|
||||
)
|
||||
|
||||
// IsIPPrivate for net.IP.IsPrivate. TODO: replace with `ip.IsPrivate()` if min go version is bumped to 1.17
|
||||
// IsIPPrivate for net.IP.IsPrivate.
|
||||
func IsIPPrivate(ip net.IP) bool {
|
||||
if ip4 := ip.To4(); ip4 != nil {
|
||||
return ip4[0] == 10 ||
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
// Copyright 2022 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package util
|
||||
|
||||
// RemoveIDFromList removes the given ID from the slice, if found.
|
||||
// It does not preserve order, and assumes the ID is unique.
|
||||
func RemoveIDFromList(list []int64, id int64) ([]int64, bool) {
|
||||
n := len(list) - 1
|
||||
for i, item := range list {
|
||||
if item == id {
|
||||
list[i] = list[n]
|
||||
return list[:n], true
|
||||
}
|
||||
}
|
||||
return list, false
|
||||
}
|
||||
@@ -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> префикс
|
||||
|
||||
@@ -245,18 +245,23 @@ func SearchIssues(ctx *context.APIContext) {
|
||||
UpdatedAfterUnix: since,
|
||||
}
|
||||
|
||||
ctxUserID := int64(0)
|
||||
if ctx.IsSigned {
|
||||
ctxUserID = ctx.User.ID
|
||||
}
|
||||
|
||||
// Filter for: Created by User, Assigned to User, Mentioning User, Review of User Requested
|
||||
if ctx.FormBool("created") {
|
||||
issuesOpt.PosterID = ctx.User.ID
|
||||
issuesOpt.PosterID = ctxUserID
|
||||
}
|
||||
if ctx.FormBool("assigned") {
|
||||
issuesOpt.AssigneeID = ctx.User.ID
|
||||
issuesOpt.AssigneeID = ctxUserID
|
||||
}
|
||||
if ctx.FormBool("mentioned") {
|
||||
issuesOpt.MentionedID = ctx.User.ID
|
||||
issuesOpt.MentionedID = ctxUserID
|
||||
}
|
||||
if ctx.FormBool("review_requested") {
|
||||
issuesOpt.ReviewRequestedID = ctx.User.ID
|
||||
issuesOpt.ReviewRequestedID = ctxUserID
|
||||
}
|
||||
|
||||
if issues, err = models.Issues(issuesOpt); err != nil {
|
||||
@@ -599,7 +604,7 @@ func CreateIssue(ctx *context.APIContext) {
|
||||
DeadlineUnix: deadlineUnix,
|
||||
}
|
||||
|
||||
var assigneeIDs = make([]int64, 0)
|
||||
assigneeIDs := make([]int64, 0)
|
||||
var err error
|
||||
if ctx.Repo.CanWrite(unit.TypeIssues) {
|
||||
issue.MilestoneID = form.Milestone
|
||||
|
||||
@@ -144,7 +144,7 @@ func GetDeployKey(ctx *context.APIContext) {
|
||||
// "200":
|
||||
// "$ref": "#/responses/DeployKey"
|
||||
|
||||
key, err := asymkey_model.GetDeployKeyByID(db.DefaultContext, ctx.ParamsInt64(":id"))
|
||||
key, err := asymkey_model.GetDeployKeyByID(ctx, ctx.ParamsInt64(":id"))
|
||||
if err != nil {
|
||||
if asymkey_model.IsErrDeployKeyNotExist(err) {
|
||||
ctx.NotFound()
|
||||
|
||||
+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
|
||||
|
||||
+3
-2
@@ -10,7 +10,6 @@ import (
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
asymkey_model "code.gitea.io/gitea/models/asymkey"
|
||||
@@ -47,6 +46,8 @@ import (
|
||||
"code.gitea.io/gitea/services/repository/archiver"
|
||||
"code.gitea.io/gitea/services/task"
|
||||
"code.gitea.io/gitea/services/webhook"
|
||||
"golang.org/x/text/cases"
|
||||
"golang.org/x/text/language"
|
||||
|
||||
"gitea.com/go-chi/session"
|
||||
)
|
||||
@@ -111,7 +112,7 @@ func GlobalInitInstalled(ctx context.Context) {
|
||||
log.Info("Custom path: %s", setting.CustomPath)
|
||||
log.Info("Log path: %s", setting.LogRootPath)
|
||||
log.Info("Configuration file: %s", setting.CustomConf)
|
||||
log.Info("Run Mode: %s", strings.Title(setting.RunMode))
|
||||
log.Info("Run Mode: %s", cases.Title(language.English).String(setting.RunMode))
|
||||
|
||||
// Setup i18n
|
||||
translation.InitLocales()
|
||||
|
||||
@@ -12,6 +12,8 @@ import (
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
asymkey_model "code.gitea.io/gitea/models/asymkey"
|
||||
perm_model "code.gitea.io/gitea/models/perm"
|
||||
"code.gitea.io/gitea/models/unit"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
gitea_context "code.gitea.io/gitea/modules/context"
|
||||
@@ -24,8 +26,12 @@ import (
|
||||
|
||||
type preReceiveContext struct {
|
||||
*gitea_context.PrivateContext
|
||||
user *user_model.User
|
||||
perm models.Permission
|
||||
|
||||
// loadedPusher indicates that where the following information are loaded
|
||||
loadedPusher bool
|
||||
user *user_model.User // it's the org user if a DeployKey is used
|
||||
userPerm models.Permission
|
||||
deployKeyAccessMode perm_model.AccessMode
|
||||
|
||||
canCreatePullRequest bool
|
||||
checkedCanCreatePullRequest bool
|
||||
@@ -41,62 +47,52 @@ type preReceiveContext struct {
|
||||
opts *private.HookOptions
|
||||
}
|
||||
|
||||
// User gets or loads User
|
||||
func (ctx *preReceiveContext) User() *user_model.User {
|
||||
if ctx.user == nil {
|
||||
ctx.user, ctx.perm = loadUserAndPermission(ctx.PrivateContext, ctx.opts.UserID)
|
||||
}
|
||||
return ctx.user
|
||||
}
|
||||
|
||||
// Perm gets or loads Perm
|
||||
func (ctx *preReceiveContext) Perm() *models.Permission {
|
||||
if ctx.user == nil {
|
||||
ctx.user, ctx.perm = loadUserAndPermission(ctx.PrivateContext, ctx.opts.UserID)
|
||||
}
|
||||
return &ctx.perm
|
||||
}
|
||||
|
||||
// CanWriteCode returns true if can write code
|
||||
// CanWriteCode returns true if pusher can write code
|
||||
func (ctx *preReceiveContext) CanWriteCode() bool {
|
||||
if !ctx.checkedCanWriteCode {
|
||||
ctx.canWriteCode = ctx.Perm().CanWrite(unit.TypeCode)
|
||||
if !ctx.loadPusherAndPermission() {
|
||||
return false
|
||||
}
|
||||
ctx.canWriteCode = ctx.userPerm.CanWrite(unit.TypeCode) || ctx.deployKeyAccessMode >= perm_model.AccessModeWrite
|
||||
ctx.checkedCanWriteCode = true
|
||||
}
|
||||
return ctx.canWriteCode
|
||||
}
|
||||
|
||||
// AssertCanWriteCode returns true if can write code
|
||||
// AssertCanWriteCode returns true if pusher can write code
|
||||
func (ctx *preReceiveContext) AssertCanWriteCode() bool {
|
||||
if !ctx.CanWriteCode() {
|
||||
if ctx.Written() {
|
||||
return false
|
||||
}
|
||||
ctx.JSON(http.StatusForbidden, map[string]interface{}{
|
||||
"err": "User permission denied.",
|
||||
"err": "User permission denied for writing.",
|
||||
})
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// CanCreatePullRequest returns true if can create pull requests
|
||||
// CanCreatePullRequest returns true if pusher can create pull requests
|
||||
func (ctx *preReceiveContext) CanCreatePullRequest() bool {
|
||||
if !ctx.checkedCanCreatePullRequest {
|
||||
ctx.canCreatePullRequest = ctx.Perm().CanRead(unit.TypePullRequests)
|
||||
if !ctx.loadPusherAndPermission() {
|
||||
return false
|
||||
}
|
||||
ctx.canCreatePullRequest = ctx.userPerm.CanRead(unit.TypePullRequests)
|
||||
ctx.checkedCanCreatePullRequest = true
|
||||
}
|
||||
return ctx.canCreatePullRequest
|
||||
}
|
||||
|
||||
// AssertCanCreatePullRequest returns true if can create pull requests
|
||||
// AssertCreatePullRequest returns true if can create pull requests
|
||||
func (ctx *preReceiveContext) AssertCreatePullRequest() bool {
|
||||
if !ctx.CanCreatePullRequest() {
|
||||
if ctx.Written() {
|
||||
return false
|
||||
}
|
||||
ctx.JSON(http.StatusForbidden, map[string]interface{}{
|
||||
"err": "User permission denied.",
|
||||
"err": "User permission denied for creating pull-request.",
|
||||
})
|
||||
return false
|
||||
}
|
||||
@@ -246,7 +242,7 @@ func preReceiveBranch(ctx *preReceiveContext, oldCommitID, newCommitID, refFullN
|
||||
|
||||
// 5. Check if the doer is allowed to push
|
||||
canPush := false
|
||||
if ctx.opts.IsDeployKey {
|
||||
if ctx.opts.DeployKeyID != 0 {
|
||||
canPush = !changedProtectedfiles && protectBranch.CanPush && (!protectBranch.EnableWhitelist || protectBranch.WhitelistDeployKeys)
|
||||
} else {
|
||||
canPush = !changedProtectedfiles && protectBranch.CanUserPush(ctx.opts.UserID)
|
||||
@@ -303,9 +299,15 @@ func preReceiveBranch(ctx *preReceiveContext, oldCommitID, newCommitID, refFullN
|
||||
return
|
||||
}
|
||||
|
||||
// although we should have called `loadPusherAndPermission` before, here we call it explicitly again because we need to access ctx.user below
|
||||
if !ctx.loadPusherAndPermission() {
|
||||
// if error occurs, loadPusherAndPermission had written the error response
|
||||
return
|
||||
}
|
||||
|
||||
// Now check if the user is allowed to merge PRs for this repository
|
||||
// Note: we can use ctx.perm and ctx.user directly as they will have been loaded above
|
||||
allowedMerge, err := pull_service.IsUserAllowedToMerge(pr, ctx.perm, ctx.user)
|
||||
allowedMerge, err := pull_service.IsUserAllowedToMerge(pr, ctx.userPerm, ctx.user)
|
||||
if err != nil {
|
||||
log.Error("Error calculating if allowed to merge: %v", err)
|
||||
ctx.JSON(http.StatusInternalServerError, private.Response{
|
||||
@@ -323,7 +325,7 @@ func preReceiveBranch(ctx *preReceiveContext, oldCommitID, newCommitID, refFullN
|
||||
}
|
||||
|
||||
// If we're an admin for the repository we can ignore status checks, reviews and override protected files
|
||||
if ctx.perm.IsAdmin() {
|
||||
if ctx.userPerm.IsAdmin() {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -450,24 +452,44 @@ func generateGitEnv(opts *private.HookOptions) (env []string) {
|
||||
return env
|
||||
}
|
||||
|
||||
func loadUserAndPermission(ctx *gitea_context.PrivateContext, id int64) (user *user_model.User, perm models.Permission) {
|
||||
user, err := user_model.GetUserByID(id)
|
||||
if err != nil {
|
||||
log.Error("Unable to get User id %d Error: %v", id, err)
|
||||
ctx.JSON(http.StatusInternalServerError, private.Response{
|
||||
Err: fmt.Sprintf("Unable to get User id %d Error: %v", id, err),
|
||||
})
|
||||
return
|
||||
// loadPusherAndPermission returns false if an error occurs, and it writes the error response
|
||||
func (ctx *preReceiveContext) loadPusherAndPermission() bool {
|
||||
if ctx.loadedPusher {
|
||||
return true
|
||||
}
|
||||
|
||||
perm, err = models.GetUserRepoPermission(ctx.Repo.Repository, user)
|
||||
user, err := user_model.GetUserByID(ctx.opts.UserID)
|
||||
if err != nil {
|
||||
log.Error("Unable to get User id %d Error: %v", ctx.opts.UserID, err)
|
||||
ctx.JSON(http.StatusInternalServerError, private.Response{
|
||||
Err: fmt.Sprintf("Unable to get User id %d Error: %v", ctx.opts.UserID, err),
|
||||
})
|
||||
return false
|
||||
}
|
||||
ctx.user = user
|
||||
|
||||
userPerm, err := models.GetUserRepoPermission(ctx.Repo.Repository, user)
|
||||
if err != nil {
|
||||
log.Error("Unable to get Repo permission of repo %s/%s of User %s", ctx.Repo.Repository.OwnerName, ctx.Repo.Repository.Name, user.Name, err)
|
||||
ctx.JSON(http.StatusInternalServerError, private.Response{
|
||||
Err: fmt.Sprintf("Unable to get Repo permission of repo %s/%s of User %s: %v", ctx.Repo.Repository.OwnerName, ctx.Repo.Repository.Name, user.Name, err),
|
||||
})
|
||||
return
|
||||
return false
|
||||
}
|
||||
ctx.userPerm = userPerm
|
||||
|
||||
if ctx.opts.DeployKeyID != 0 {
|
||||
deployKey, err := asymkey_model.GetDeployKeyByID(ctx, ctx.opts.DeployKeyID)
|
||||
if err != nil {
|
||||
log.Error("Unable to get DeployKey id %d Error: %v", ctx.opts.DeployKeyID, err)
|
||||
ctx.JSON(http.StatusInternalServerError, private.Response{
|
||||
Err: fmt.Sprintf("Unable to get DeployKey id %d Error: %v", ctx.opts.DeployKeyID, err),
|
||||
})
|
||||
return false
|
||||
}
|
||||
ctx.deployKeyAccessMode = deployKey.Mode
|
||||
}
|
||||
|
||||
return
|
||||
ctx.loadedPusher = true
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ func SendEmail(ctx *context.PrivateContext) {
|
||||
}
|
||||
} else {
|
||||
err := user_model.IterateUser(func(user *user_model.User) error {
|
||||
if len(user.Email) > 0 {
|
||||
if len(user.Email) > 0 && user.IsActive {
|
||||
emails = append(emails, user.Email)
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -229,8 +229,6 @@ func ServCommand(ctx *context.PrivateContext) {
|
||||
var deployKey *asymkey_model.DeployKey
|
||||
var user *user_model.User
|
||||
if key.Type == asymkey_model.KeyTypeDeploy {
|
||||
results.IsDeployKey = true
|
||||
|
||||
var err error
|
||||
deployKey, err = asymkey_model.GetDeployKeyByRepo(key.ID, repo.ID)
|
||||
if err != nil {
|
||||
@@ -248,6 +246,7 @@ func ServCommand(ctx *context.PrivateContext) {
|
||||
})
|
||||
return
|
||||
}
|
||||
results.DeployKeyID = deployKey.ID
|
||||
results.KeyName = deployKey.Name
|
||||
|
||||
// FIXME: Deploy keys aren't really the owner of the repo pushing changes
|
||||
@@ -410,9 +409,9 @@ func ServCommand(ctx *context.PrivateContext) {
|
||||
return
|
||||
}
|
||||
}
|
||||
log.Debug("Serv Results:\nIsWiki: %t\nIsDeployKey: %t\nKeyID: %d\tKeyName: %s\nUserName: %s\nUserID: %d\nOwnerName: %s\nRepoName: %s\nRepoID: %d",
|
||||
log.Debug("Serv Results:\nIsWiki: %t\nDeployKeyID: %d\nKeyID: %d\tKeyName: %s\nUserName: %s\nUserID: %d\nOwnerName: %s\nRepoName: %s\nRepoID: %d",
|
||||
results.IsWiki,
|
||||
results.IsDeployKey,
|
||||
results.DeployKeyID,
|
||||
results.KeyID,
|
||||
results.KeyName,
|
||||
results.UserName,
|
||||
|
||||
@@ -209,7 +209,7 @@ func shadowPassword(provider, cfgItem string) string {
|
||||
case "redis":
|
||||
return shadowPasswordKV(cfgItem, ",")
|
||||
case "mysql":
|
||||
//root:@tcp(localhost:3306)/macaron?charset=utf8
|
||||
// root:@tcp(localhost:3306)/macaron?charset=utf8
|
||||
atIdx := strings.Index(cfgItem, "@")
|
||||
if atIdx > 0 {
|
||||
colonIdx := strings.Index(cfgItem[:atIdx], ":")
|
||||
@@ -244,7 +244,7 @@ func Config(ctx *context.Context) {
|
||||
ctx.Data["OfflineMode"] = setting.OfflineMode
|
||||
ctx.Data["DisableRouterLog"] = setting.DisableRouterLog
|
||||
ctx.Data["RunUser"] = setting.RunUser
|
||||
ctx.Data["RunMode"] = strings.Title(setting.RunMode)
|
||||
ctx.Data["RunMode"] = setting.RunMode
|
||||
if version, err := git.LocalVersion(); err == nil {
|
||||
ctx.Data["GitVersion"] = version.Original()
|
||||
}
|
||||
|
||||
@@ -181,6 +181,14 @@ func parseOAuth2Config(form forms.AuthenticationForm) *oauth2.Source {
|
||||
} else {
|
||||
customURLMapping = nil
|
||||
}
|
||||
var scopes []string
|
||||
for _, s := range strings.Split(form.Oauth2Scopes, ",") {
|
||||
s = strings.TrimSpace(s)
|
||||
if s != "" {
|
||||
scopes = append(scopes, s)
|
||||
}
|
||||
}
|
||||
|
||||
return &oauth2.Source{
|
||||
Provider: form.Oauth2Provider,
|
||||
ClientID: form.Oauth2Key,
|
||||
@@ -188,7 +196,7 @@ func parseOAuth2Config(form forms.AuthenticationForm) *oauth2.Source {
|
||||
OpenIDConnectAutoDiscoveryURL: form.OpenIDConnectAutoDiscoveryURL,
|
||||
CustomURLMapping: customURLMapping,
|
||||
IconURL: form.Oauth2IconURL,
|
||||
Scopes: strings.Split(form.Oauth2Scopes, ","),
|
||||
Scopes: scopes,
|
||||
RequiredClaimName: form.Oauth2RequiredClaimName,
|
||||
RequiredClaimValue: form.Oauth2RequiredClaimValue,
|
||||
SkipLocalTwoFA: form.SkipLocalTwoFA,
|
||||
@@ -243,6 +251,9 @@ func NewAuthSourcePost(ctx *context.Context) {
|
||||
ctx.Data["SSPISeparatorReplacement"] = "_"
|
||||
ctx.Data["SSPIDefaultLanguage"] = ""
|
||||
|
||||
// FIXME: most error path to render tplAuthNew will fail and result in 500
|
||||
// * template: admin/auth/new:17:68: executing "admin/auth/new" at <.type.Int>: can't evaluate field Int in type interface {}
|
||||
// * template: admin/auth/source/oauth:5:93: executing "admin/auth/source/oauth" at <.oauth2_provider.Name>: can't evaluate field Name in type interface {}
|
||||
hasTLS := false
|
||||
var config convert.Conversion
|
||||
switch auth.Type(form.Type) {
|
||||
@@ -393,6 +404,7 @@ func EditAuthSourcePost(ctx *context.Context) {
|
||||
source.IsActive = form.IsActive
|
||||
source.IsSyncEnabled = form.IsSyncEnabled
|
||||
source.Cfg = config
|
||||
// FIXME: if the name conflicts, it will result in 500: Error 1062: Duplicate entry 'aa' for key 'login_source.UQE_login_source_name'
|
||||
if err := auth.UpdateSource(source); err != nil {
|
||||
if oauth2.IsErrOpenIDConnectInitialize(err) {
|
||||
ctx.Flash.Error(err.Error(), true)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -195,7 +195,7 @@ func SignInPost(ctx *context.Context) {
|
||||
form := web.GetForm(ctx).(*forms.SignInForm)
|
||||
u, source, err := auth_service.UserSignIn(form.UserName, form.Password)
|
||||
if err != nil {
|
||||
if user_model.IsErrUserNotExist(err) {
|
||||
if user_model.IsErrUserNotExist(err) || user_model.IsErrEmailAddressNotExist(err) {
|
||||
ctx.RenderWithErr(ctx.Tr("form.username_password_incorrect"), tplSignIn, &form)
|
||||
log.Info("Failed authentication attempt for %s from %s: %v", form.UserName, ctx.RemoteAddr(), err)
|
||||
} else if user_model.IsErrEmailAlreadyUsed(err) {
|
||||
@@ -621,6 +621,12 @@ func handleUserCreated(ctx *context.Context, u *user_model.User, gothUser *goth.
|
||||
|
||||
// Send confirmation email
|
||||
if !u.IsActive && u.ID > 1 {
|
||||
if setting.Service.RegisterManualConfirm {
|
||||
ctx.Data["ManualActivationOnly"] = true
|
||||
ctx.HTML(http.StatusOK, TplActivate)
|
||||
return
|
||||
}
|
||||
|
||||
mailer.SendActivateAccountMail(ctx.Locale, u)
|
||||
|
||||
ctx.Data["IsSendRegisterMail"] = true
|
||||
|
||||
+16
-16
@@ -11,7 +11,6 @@ import (
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
@@ -27,6 +26,8 @@ import (
|
||||
)
|
||||
|
||||
func storageHandler(storageSetting setting.Storage, prefix string, objStore storage.ObjectStorage) func(next http.Handler) http.Handler {
|
||||
prefix = strings.Trim(prefix, "/")
|
||||
|
||||
return func(next http.Handler) http.Handler {
|
||||
if storageSetting.ServeDirect {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
@@ -35,12 +36,14 @@ func storageHandler(storageSetting setting.Storage, prefix string, objStore stor
|
||||
return
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(req.URL.RequestURI(), "/"+prefix) {
|
||||
if !strings.HasPrefix(req.URL.Path, "/"+prefix+"/") {
|
||||
next.ServeHTTP(w, req)
|
||||
return
|
||||
}
|
||||
|
||||
rPath := strings.TrimPrefix(req.URL.RequestURI(), "/"+prefix)
|
||||
rPath := strings.TrimPrefix(req.URL.Path, "/"+prefix+"/")
|
||||
rPath = path.Clean("/" + strings.ReplaceAll(rPath, "\\", "/"))[1:]
|
||||
|
||||
u, err := objStore.URL(rPath, path.Base(rPath))
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) || errors.Is(err, os.ErrNotExist) {
|
||||
@@ -52,11 +55,12 @@ func storageHandler(storageSetting setting.Storage, prefix string, objStore stor
|
||||
http.Error(w, fmt.Sprintf("Error whilst getting URL for %s %s", prefix, rPath), 500)
|
||||
return
|
||||
}
|
||||
|
||||
http.Redirect(
|
||||
w,
|
||||
req,
|
||||
u.String(),
|
||||
301,
|
||||
http.StatusMovedPermanently,
|
||||
)
|
||||
})
|
||||
}
|
||||
@@ -67,28 +71,24 @@ func storageHandler(storageSetting setting.Storage, prefix string, objStore stor
|
||||
return
|
||||
}
|
||||
|
||||
prefix := strings.Trim(prefix, "/")
|
||||
|
||||
if !strings.HasPrefix(req.URL.EscapedPath(), "/"+prefix+"/") {
|
||||
if !strings.HasPrefix(req.URL.Path, "/"+prefix+"/") {
|
||||
next.ServeHTTP(w, req)
|
||||
return
|
||||
}
|
||||
|
||||
rPath := strings.TrimPrefix(req.URL.EscapedPath(), "/"+prefix+"/")
|
||||
rPath = strings.TrimPrefix(rPath, "/")
|
||||
rPath := strings.TrimPrefix(req.URL.Path, "/"+prefix+"/")
|
||||
rPath = path.Clean("/" + strings.ReplaceAll(rPath, "\\", "/"))[1:]
|
||||
if rPath == "" {
|
||||
http.Error(w, "file not found", 404)
|
||||
return
|
||||
}
|
||||
rPath = path.Clean("/" + filepath.ToSlash(rPath))
|
||||
rPath = rPath[1:]
|
||||
|
||||
fi, err := objStore.Stat(rPath)
|
||||
if err == nil && httpcache.HandleTimeCache(req, w, fi) {
|
||||
return
|
||||
}
|
||||
|
||||
//If we have matched and access to release or issue
|
||||
// If we have matched and access to release or issue
|
||||
fr, err := objStore.Open(rPath)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) || errors.Is(err, os.ErrNotExist) {
|
||||
@@ -121,7 +121,7 @@ func (d *dataStore) GetData() map[string]interface{} {
|
||||
// Recovery returns a middleware that recovers from any panics and writes a 500 and a log if so.
|
||||
// This error will be created with the gitea 500 page.
|
||||
func Recovery() func(next http.Handler) http.Handler {
|
||||
var rnd = templates.HTMLRenderer()
|
||||
rnd := templates.HTMLRenderer()
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
defer func() {
|
||||
@@ -131,14 +131,14 @@ func Recovery() func(next http.Handler) http.Handler {
|
||||
|
||||
sessionStore := session.GetSession(req)
|
||||
|
||||
var lc = middleware.Locale(w, req)
|
||||
var store = dataStore{
|
||||
lc := middleware.Locale(w, req)
|
||||
store := dataStore{
|
||||
"Language": lc.Language(),
|
||||
"CurrentURL": setting.AppSubURL + req.URL.RequestURI(),
|
||||
"i18n": lc,
|
||||
}
|
||||
|
||||
var user = context.GetContextUser(req)
|
||||
user := context.GetContextUser(req)
|
||||
if user == nil {
|
||||
// Get user from session if logged in - do not attempt to sign-in
|
||||
user = auth.SessionUser(sessionStore)
|
||||
|
||||
@@ -298,6 +298,13 @@ func ParseCompareInfo(ctx *context.Context) *CompareInfo {
|
||||
ci.BaseBranch = baseCommit.ID.String()
|
||||
ctx.Data["BaseBranch"] = ci.BaseBranch
|
||||
baseIsCommit = true
|
||||
} else if ci.BaseBranch == git.EmptySHA {
|
||||
if isSameRepo {
|
||||
ctx.Redirect(ctx.Repo.RepoLink + "/compare/" + util.PathEscapeSegments(ci.HeadBranch))
|
||||
} else {
|
||||
ctx.Redirect(ctx.Repo.RepoLink + "/compare/" + util.PathEscapeSegments(ci.HeadRepo.FullName()) + ":" + util.PathEscapeSegments(ci.HeadBranch))
|
||||
}
|
||||
return nil
|
||||
} else {
|
||||
ctx.NotFound("IsRefExist", nil)
|
||||
return nil
|
||||
|
||||
@@ -223,7 +223,6 @@ func httpBase(ctx *context.Context) (h *serviceHandler) {
|
||||
models.EnvRepoName + "=" + reponame,
|
||||
models.EnvPusherName + "=" + ctx.User.Name,
|
||||
models.EnvPusherID + fmt.Sprintf("=%d", ctx.User.ID),
|
||||
models.EnvIsDeployKey + "=false",
|
||||
models.EnvAppURL + "=" + setting.AppURL,
|
||||
}
|
||||
|
||||
|
||||
@@ -843,12 +843,19 @@ func NewIssue(ctx *context.Context) {
|
||||
func NewIssueChooseTemplate(ctx *context.Context) {
|
||||
ctx.Data["Title"] = ctx.Tr("repo.issues.new")
|
||||
ctx.Data["PageIsIssueList"] = true
|
||||
ctx.Data["milestone"] = ctx.FormInt64("milestone")
|
||||
|
||||
issueTemplates := ctx.IssueTemplatesFromDefaultBranch()
|
||||
ctx.Data["NewIssueChooseTemplate"] = len(issueTemplates) > 0
|
||||
ctx.Data["IssueTemplates"] = issueTemplates
|
||||
|
||||
if len(issueTemplates) == 0 {
|
||||
// The "issues/new" and "issues/new/choose" share the same query parameters "project" and "milestone", if no template here, just redirect to the "issues/new" page with these parameters.
|
||||
ctx.Redirect(fmt.Sprintf("%s/issues/new?%s", ctx.Repo.Repository.HTMLURL(), ctx.Req.URL.RawQuery), http.StatusSeeOther)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Data["milestone"] = ctx.FormInt64("milestone")
|
||||
ctx.Data["project"] = ctx.FormInt64("project")
|
||||
|
||||
ctx.HTML(http.StatusOK, tplIssueChoose)
|
||||
}
|
||||
|
||||
|
||||
+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)
|
||||
|
||||
+55
-14
@@ -197,7 +197,7 @@ func Milestones(ctx *context.Context) {
|
||||
if issueReposQueryPattern.MatchString(reposQuery) {
|
||||
// remove "[" and "]" from string
|
||||
reposQuery = reposQuery[1 : len(reposQuery)-1]
|
||||
//for each ID (delimiter ",") add to int to repoIDs
|
||||
// for each ID (delimiter ",") add to int to repoIDs
|
||||
|
||||
for _, rID := range strings.Split(reposQuery, ",") {
|
||||
// Ensure nonempty string entries
|
||||
@@ -350,7 +350,6 @@ func Issues(ctx *context.Context) {
|
||||
var issueReposQueryPattern = regexp.MustCompile(`^\[\d+(,\d+)*,?\]$`)
|
||||
|
||||
func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
|
||||
|
||||
// ----------------------------------------------------
|
||||
// Determine user; can be either user or organization.
|
||||
// Return with NotFound or ServerError if unsuccessful.
|
||||
@@ -364,7 +363,7 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
|
||||
var (
|
||||
viewType string
|
||||
sortType = ctx.FormString("sort")
|
||||
filterMode = models.FilterModeAll
|
||||
filterMode int
|
||||
)
|
||||
|
||||
// --------------------------------------------------------------------------------
|
||||
@@ -390,8 +389,10 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
|
||||
filterMode = models.FilterModeMention
|
||||
case "review_requested":
|
||||
filterMode = models.FilterModeReviewRequested
|
||||
case "your_repositories": // filterMode already set to All
|
||||
case "your_repositories":
|
||||
fallthrough
|
||||
default:
|
||||
filterMode = models.FilterModeYourRepositories
|
||||
viewType = "your_repositories"
|
||||
}
|
||||
|
||||
@@ -421,6 +422,30 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
|
||||
User: ctx.User,
|
||||
}
|
||||
|
||||
// Search all repositories which
|
||||
//
|
||||
// As user:
|
||||
// - Owns the repository.
|
||||
// - Have collaborator permissions in repository.
|
||||
//
|
||||
// As org:
|
||||
// - Owns the repository.
|
||||
//
|
||||
// As team:
|
||||
// - Team org's owns the repository.
|
||||
// - Team has read permission to repository.
|
||||
repoOpts := &models.SearchRepoOptions{
|
||||
Actor: ctx.User,
|
||||
OwnerID: ctx.User.ID,
|
||||
Private: true,
|
||||
AllPublic: false,
|
||||
AllLimited: false,
|
||||
}
|
||||
|
||||
if ctxUser.IsOrganization() && ctx.Org.Team != nil {
|
||||
repoOpts.TeamID = ctx.Org.Team.ID
|
||||
}
|
||||
|
||||
switch filterMode {
|
||||
case models.FilterModeAll:
|
||||
case models.FilterModeAssign:
|
||||
@@ -431,6 +456,19 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
|
||||
opts.MentionedID = ctx.User.ID
|
||||
case models.FilterModeReviewRequested:
|
||||
opts.ReviewRequestedID = ctx.User.ID
|
||||
case models.FilterModeYourRepositories:
|
||||
if ctxUser.IsOrganization() && ctx.Org.Team != nil {
|
||||
// Fixes a issue whereby the user's ID would be used
|
||||
// to check if it's in the team(which possible isn't the case).
|
||||
opts.User = nil
|
||||
}
|
||||
userRepoIDs, _, err := models.SearchRepositoryIDs(repoOpts)
|
||||
if err != nil {
|
||||
ctx.ServerError("models.SearchRepositoryIDs: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
opts.RepoIDs = userRepoIDs
|
||||
}
|
||||
|
||||
// keyword holds the search term entered into the search field.
|
||||
@@ -562,8 +600,12 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
|
||||
Org: org,
|
||||
Team: team,
|
||||
}
|
||||
if len(repoIDs) > 0 {
|
||||
statsOpts.RepoIDs = repoIDs
|
||||
if filterMode == models.FilterModeYourRepositories {
|
||||
statsOpts.RepoCond = models.SearchRepositoryCondition(repoOpts)
|
||||
}
|
||||
// Detect when we only should search by team.
|
||||
if opts.User == nil {
|
||||
statsOpts.UserID = 0
|
||||
}
|
||||
issueStats, err = models.GetUserIssueStats(statsOpts)
|
||||
if err != nil {
|
||||
@@ -586,8 +628,7 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
|
||||
|
||||
ctx.Data["IsShowClosed"] = isShowClosed
|
||||
|
||||
ctx.Data["IssueRefEndNames"], ctx.Data["IssueRefURLs"] =
|
||||
issue_service.GetRefEndNamesAndURLs(issues, ctx.FormString("RepoLink"))
|
||||
ctx.Data["IssueRefEndNames"], ctx.Data["IssueRefURLs"] = issue_service.GetRefEndNamesAndURLs(issues, ctx.FormString("RepoLink"))
|
||||
|
||||
ctx.Data["Issues"] = issues
|
||||
|
||||
@@ -661,7 +702,7 @@ func getRepoIDs(reposQuery string) []int64 {
|
||||
var repoIDs []int64
|
||||
// remove "[" and "]" from string
|
||||
reposQuery = reposQuery[1 : len(reposQuery)-1]
|
||||
//for each ID (delimiter ",") add to int to repoIDs
|
||||
// for each ID (delimiter ",") add to int to repoIDs
|
||||
for _, rID := range strings.Split(reposQuery, ",") {
|
||||
// Ensure nonempty string entries
|
||||
if rID != "" && rID != "0" {
|
||||
@@ -693,8 +734,8 @@ func issueIDsFromSearch(ctxUser *user_model.User, keyword string, opts *models.I
|
||||
}
|
||||
|
||||
func loadRepoByIDs(ctxUser *user_model.User, issueCountByRepo map[int64]int64, unitType unit.Type) (map[int64]*repo_model.Repository, error) {
|
||||
var totalRes = make(map[int64]*repo_model.Repository, len(issueCountByRepo))
|
||||
var repoIDs = make([]int64, 0, 500)
|
||||
totalRes := make(map[int64]*repo_model.Repository, len(issueCountByRepo))
|
||||
repoIDs := make([]int64, 0, 500)
|
||||
for id := range issueCountByRepo {
|
||||
if id <= 0 {
|
||||
continue
|
||||
@@ -745,7 +786,7 @@ func ShowGPGKeys(ctx *context.Context, uid int64) {
|
||||
if err != nil {
|
||||
if asymkey_model.IsErrGPGKeyImportNotExist(err) {
|
||||
failedEntitiesID = append(failedEntitiesID, k.KeyID)
|
||||
continue //Skip previous import without backup of imported armored key
|
||||
continue // Skip previous import without backup of imported armored key
|
||||
}
|
||||
ctx.ServerError("ShowGPGKeys", err)
|
||||
return
|
||||
@@ -755,12 +796,12 @@ func ShowGPGKeys(ctx *context.Context, uid int64) {
|
||||
var buf bytes.Buffer
|
||||
|
||||
headers := make(map[string]string)
|
||||
if len(failedEntitiesID) > 0 { //If some key need re-import to be exported
|
||||
if len(failedEntitiesID) > 0 { // If some key need re-import to be exported
|
||||
headers["Note"] = fmt.Sprintf("The keys with the following IDs couldn't be exported and need to be reuploaded %s", strings.Join(failedEntitiesID, ", "))
|
||||
}
|
||||
writer, _ := armor.Encode(&buf, "PGP PUBLIC KEY BLOCK", headers)
|
||||
for _, e := range entities {
|
||||
err = e.Serialize(writer) //TODO find why key are exported with a different cipherTypeByte as original (should not be blocking but strange)
|
||||
err = e.Serialize(writer) // TODO find why key are exported with a different cipherTypeByte as original (should not be blocking but strange)
|
||||
if err != nil {
|
||||
ctx.ServerError("ShowGPGKeys", err)
|
||||
return
|
||||
|
||||
+8
-3
@@ -98,6 +98,11 @@ func Routes(sessioner func(http.Handler) http.Handler) *web.Route {
|
||||
http.Redirect(w, req, path.Join(setting.StaticURLPrefix, "/assets/img/apple-touch-icon.png"), 301)
|
||||
})
|
||||
|
||||
// redirect default favicon to the path of the custom favicon with a default as a fallback
|
||||
routes.Get("/favicon.ico", func(w http.ResponseWriter, req *http.Request) {
|
||||
http.Redirect(w, req, path.Join(setting.StaticURLPrefix, "/assets/img/favicon.png"), 301)
|
||||
})
|
||||
|
||||
common := []interface{}{}
|
||||
|
||||
if setting.EnableGzip {
|
||||
@@ -369,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)
|
||||
@@ -378,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)"`
|
||||
|
||||
@@ -715,7 +715,8 @@ func TestDiffToHTML_14231(t *testing.T) {
|
||||
diffRecord := diffMatchPatch.DiffMain(highlight.Code("main.v", "", " run()\n"), highlight.Code("main.v", "", " run(db)\n"), true)
|
||||
diffRecord = diffMatchPatch.DiffCleanupEfficiency(diffRecord)
|
||||
|
||||
expected := ` <span class="n">run</span><span class="added-code"><span class="o">(</span><span class="n">db</span></span><span class="o">)</span>`
|
||||
expected := `<span class="line"><span class="cl"> <span class="n">run</span><span class="added-code"><span class="o">(</span><span class="n">db</span></span><span class="o">)</span>
|
||||
</span></span>`
|
||||
output := diffToHTML("main.v", diffRecord, DiffLineAdd)
|
||||
|
||||
assertEqual(t, expected, output.Content)
|
||||
|
||||
+23
-14
@@ -77,8 +77,9 @@ func sendUserMail(language string, u *user_model.User, tpl base.TplName, code, s
|
||||
"Code": code,
|
||||
"Language": locale.Language(),
|
||||
// helper
|
||||
"i18n": locale,
|
||||
"Str2html": templates.Str2html,
|
||||
"i18n": locale,
|
||||
"Str2html": templates.Str2html,
|
||||
"DotEscape": templates.DotEscape,
|
||||
}
|
||||
|
||||
var content bytes.Buffer
|
||||
@@ -127,8 +128,9 @@ func SendActivateEmailMail(u *user_model.User, email *user_model.EmailAddress) {
|
||||
"Email": email.Email,
|
||||
"Language": locale.Language(),
|
||||
// helper
|
||||
"i18n": locale,
|
||||
"Str2html": templates.Str2html,
|
||||
"i18n": locale,
|
||||
"Str2html": templates.Str2html,
|
||||
"DotEscape": templates.DotEscape,
|
||||
}
|
||||
|
||||
var content bytes.Buffer
|
||||
@@ -146,8 +148,8 @@ func SendActivateEmailMail(u *user_model.User, email *user_model.EmailAddress) {
|
||||
|
||||
// SendRegisterNotifyMail triggers a notify e-mail by admin created a account.
|
||||
func SendRegisterNotifyMail(u *user_model.User) {
|
||||
if setting.MailService == nil {
|
||||
// No mail service configured
|
||||
if setting.MailService == nil || !u.IsActive {
|
||||
// No mail service configured OR user is inactive
|
||||
return
|
||||
}
|
||||
locale := translation.NewLocale(u.Language)
|
||||
@@ -157,8 +159,9 @@ func SendRegisterNotifyMail(u *user_model.User) {
|
||||
"Username": u.Name,
|
||||
"Language": locale.Language(),
|
||||
// helper
|
||||
"i18n": locale,
|
||||
"Str2html": templates.Str2html,
|
||||
"i18n": locale,
|
||||
"Str2html": templates.Str2html,
|
||||
"DotEscape": templates.DotEscape,
|
||||
}
|
||||
|
||||
var content bytes.Buffer
|
||||
@@ -176,8 +179,8 @@ func SendRegisterNotifyMail(u *user_model.User) {
|
||||
|
||||
// SendCollaboratorMail sends mail notification to new collaborator.
|
||||
func SendCollaboratorMail(u, doer *user_model.User, repo *repo_model.Repository) {
|
||||
if setting.MailService == nil {
|
||||
// No mail service configured
|
||||
if setting.MailService == nil || !u.IsActive {
|
||||
// No mail service configured OR the user is inactive
|
||||
return
|
||||
}
|
||||
locale := translation.NewLocale(u.Language)
|
||||
@@ -190,8 +193,9 @@ func SendCollaboratorMail(u, doer *user_model.User, repo *repo_model.Repository)
|
||||
"Link": repo.HTMLURL(),
|
||||
"Language": locale.Language(),
|
||||
// helper
|
||||
"i18n": locale,
|
||||
"Str2html": templates.Str2html,
|
||||
"i18n": locale,
|
||||
"Str2html": templates.Str2html,
|
||||
"DotEscape": templates.DotEscape,
|
||||
}
|
||||
|
||||
var content bytes.Buffer
|
||||
@@ -273,8 +277,9 @@ func composeIssueCommentMessages(ctx *mailCommentContext, lang string, recipient
|
||||
"ReviewComments": reviewComments,
|
||||
"Language": locale.Language(),
|
||||
// helper
|
||||
"i18n": locale,
|
||||
"Str2html": templates.Str2html,
|
||||
"i18n": locale,
|
||||
"Str2html": templates.Str2html,
|
||||
"DotEscape": templates.DotEscape,
|
||||
}
|
||||
|
||||
var mailSubject bytes.Buffer
|
||||
@@ -404,6 +409,10 @@ func SendIssueAssignedMail(issue *models.Issue, doer *user_model.User, content s
|
||||
|
||||
langMap := make(map[string][]*user_model.User)
|
||||
for _, user := range recipients {
|
||||
if !user.IsActive {
|
||||
// don't send emails to inactive users
|
||||
continue
|
||||
}
|
||||
langMap[user.Language] = append(langMap[user.Language], user)
|
||||
}
|
||||
|
||||
|
||||
@@ -125,6 +125,10 @@ func mailIssueCommentBatch(ctx *mailCommentContext, users []*user_model.User, vi
|
||||
|
||||
langMap := make(map[string][]*user_model.User)
|
||||
for _, user := range users {
|
||||
if !user.IsActive {
|
||||
// Exclude deactivated users
|
||||
continue
|
||||
}
|
||||
// At this point we exclude:
|
||||
// user that don't have all mails enabled or users only get mail on mention and this is one ...
|
||||
if !(user.EmailNotificationsPreference == user_model.EmailNotificationsEnabled ||
|
||||
|
||||
@@ -74,8 +74,9 @@ func mailNewRelease(lang string, tos []string, rel *models.Release) {
|
||||
"Subject": subject,
|
||||
"Language": locale.Language(),
|
||||
// helper
|
||||
"i18n": locale,
|
||||
"Str2html": templates.Str2html,
|
||||
"i18n": locale,
|
||||
"Str2html": templates.Str2html,
|
||||
"DotEscape": templates.DotEscape,
|
||||
}
|
||||
|
||||
var mailBody bytes.Buffer
|
||||
|
||||
@@ -31,6 +31,10 @@ func SendRepoTransferNotifyMail(doer, newOwner *user_model.User, repo *repo_mode
|
||||
|
||||
langMap := make(map[string][]string)
|
||||
for _, user := range users {
|
||||
if !user.IsActive {
|
||||
// don't send emails to inactive users
|
||||
continue
|
||||
}
|
||||
langMap[user.Language] = append(langMap[user.Language], user.Email)
|
||||
}
|
||||
|
||||
@@ -69,8 +73,9 @@ func sendRepoTransferNotifyMailPerLang(lang string, newOwner, doer *user_model.U
|
||||
"Language": locale.Language(),
|
||||
"Destination": destination,
|
||||
// helper
|
||||
"i18n": locale,
|
||||
"Str2html": templates.Str2html,
|
||||
"i18n": locale,
|
||||
"Str2html": templates.Str2html,
|
||||
"DotEscape": templates.DotEscape,
|
||||
}
|
||||
|
||||
if err := bodyTemplates.ExecuteTemplate(&content, string(mailRepoTransferNotify), data); err != nil {
|
||||
|
||||
+15
-14
@@ -22,14 +22,13 @@ import (
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
base "code.gitea.io/gitea/modules/migration"
|
||||
"code.gitea.io/gitea/modules/repository"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/structs"
|
||||
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
var (
|
||||
_ base.Uploader = &RepositoryDumper{}
|
||||
)
|
||||
var _ base.Uploader = &RepositoryDumper{}
|
||||
|
||||
// RepositoryDumper implements an Uploader to the local directory
|
||||
type RepositoryDumper struct {
|
||||
@@ -151,9 +150,10 @@ func (g *RepositoryDumper) CreateRepo(repo *base.Repository, opts base.MigrateOp
|
||||
}
|
||||
|
||||
err = git.Clone(remoteAddr, repoPath, git.CloneRepoOptions{
|
||||
Mirror: true,
|
||||
Quiet: true,
|
||||
Timeout: migrateTimeout,
|
||||
Mirror: true,
|
||||
Quiet: true,
|
||||
Timeout: migrateTimeout,
|
||||
SkipTLSVerify: setting.Migrations.SkipTLSVerify,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("Clone: %v", err)
|
||||
@@ -168,10 +168,11 @@ func (g *RepositoryDumper) CreateRepo(repo *base.Repository, opts base.MigrateOp
|
||||
}
|
||||
|
||||
if err := git.Clone(wikiRemotePath, wikiPath, git.CloneRepoOptions{
|
||||
Mirror: true,
|
||||
Quiet: true,
|
||||
Timeout: migrateTimeout,
|
||||
Branch: "master",
|
||||
Mirror: true,
|
||||
Quiet: true,
|
||||
Timeout: migrateTimeout,
|
||||
Branch: "master",
|
||||
SkipTLSVerify: setting.Migrations.SkipTLSVerify,
|
||||
}); err != nil {
|
||||
log.Warn("Clone wiki: %v", err)
|
||||
if err := os.RemoveAll(wikiPath); err != nil {
|
||||
@@ -403,7 +404,7 @@ func (g *RepositoryDumper) createItems(dir string, itemFiles map[int64]*os.File,
|
||||
|
||||
// CreateComments creates comments of issues
|
||||
func (g *RepositoryDumper) CreateComments(comments ...*base.Comment) error {
|
||||
var commentsMap = make(map[int64][]interface{}, len(comments))
|
||||
commentsMap := make(map[int64][]interface{}, len(comments))
|
||||
for _, comment := range comments {
|
||||
commentsMap[comment.IssueIndex] = append(commentsMap[comment.IssueIndex], comment)
|
||||
}
|
||||
@@ -532,7 +533,7 @@ func (g *RepositoryDumper) CreatePullRequests(prs ...*base.PullRequest) error {
|
||||
|
||||
// CreateReviews create pull request reviews
|
||||
func (g *RepositoryDumper) CreateReviews(reviews ...*base.Review) error {
|
||||
var reviewsMap = make(map[int64][]interface{}, len(reviews))
|
||||
reviewsMap := make(map[int64][]interface{}, len(reviews))
|
||||
for _, review := range reviews {
|
||||
reviewsMap[review.IssueIndex] = append(reviewsMap[review.IssueIndex], review)
|
||||
}
|
||||
@@ -611,7 +612,7 @@ func RestoreRepository(ctx context.Context, baseDir, ownerName, repoName string,
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var uploader = NewGiteaLocalUploader(ctx, doer, ownerName, repoName)
|
||||
uploader := NewGiteaLocalUploader(ctx, doer, ownerName, repoName)
|
||||
downloader, err := NewRepositoryRestorer(ctx, baseDir, ownerName, repoName)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -622,7 +623,7 @@ func RestoreRepository(ctx context.Context, baseDir, ownerName, repoName string,
|
||||
}
|
||||
tp, _ := strconv.Atoi(opts["service_type"])
|
||||
|
||||
var migrateOpts = base.MigrateOptions{
|
||||
migrateOpts := base.MigrateOptions{
|
||||
GitServiceType: structs.GitServiceType(tp),
|
||||
}
|
||||
updateOptionsUnits(&migrateOpts, units)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -335,6 +335,13 @@ func ListUnadoptedRepositories(query string, opts *db.ListOptions) ([]string, in
|
||||
}
|
||||
|
||||
repoNamesToCheck = append(repoNamesToCheck, name)
|
||||
if len(repoNamesToCheck) > setting.Database.IterateBufferSize {
|
||||
if err = checkUnadoptedRepositories(userName, repoNamesToCheck, unadopted); err != nil {
|
||||
return err
|
||||
}
|
||||
repoNamesToCheck = repoNamesToCheck[:0]
|
||||
|
||||
}
|
||||
return filepath.SkipDir
|
||||
}); err != nil {
|
||||
return nil, 0, err
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -216,7 +216,34 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error {
|
||||
if len(commits.Commits) > setting.UI.FeedMaxCommitNum {
|
||||
commits.Commits = commits.Commits[:setting.UI.FeedMaxCommitNum]
|
||||
}
|
||||
commits.CompareURL = repo.ComposeCompareURL(opts.OldCommitID, opts.NewCommitID)
|
||||
|
||||
oldCommitID := opts.OldCommitID
|
||||
if oldCommitID == git.EmptySHA && len(commits.Commits) > 0 {
|
||||
oldCommit, err := gitRepo.GetCommit(commits.Commits[len(commits.Commits)-1].Sha1)
|
||||
if err != nil && !git.IsErrNotExist(err) {
|
||||
log.Error("unable to GetCommit %s from %-v: %v", oldCommitID, repo, err)
|
||||
}
|
||||
if oldCommit != nil {
|
||||
for i := 0; i < oldCommit.ParentCount(); i++ {
|
||||
commitID, _ := oldCommit.ParentID(i)
|
||||
if !commitID.IsZero() {
|
||||
oldCommitID = commitID.String()
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if oldCommitID == git.EmptySHA && repo.DefaultBranch != branch {
|
||||
oldCommitID = repo.DefaultBranch
|
||||
}
|
||||
|
||||
if oldCommitID != git.EmptySHA {
|
||||
commits.CompareURL = repo.ComposeCompareURL(oldCommitID, opts.NewCommitID)
|
||||
} else {
|
||||
commits.CompareURL = ""
|
||||
}
|
||||
|
||||
notification.NotifyPushCommits(pusher, repo, opts, commits)
|
||||
|
||||
if err = models.RemoveDeletedBranchByName(repo.ID, branch); err != nil {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user